1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/extension_install_checker.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/extensions/blacklist.h"
9#include "chrome/browser/extensions/requirements_checker.h"
10#include "chrome/browser/profiles/profile.h"
11#include "content/public/browser/browser_thread.h"
12#include "extensions/browser/extension_system.h"
13#include "extensions/browser/management_policy.h"
14
15namespace extensions {
16
17ExtensionInstallChecker::ExtensionInstallChecker(Profile* profile)
18    : profile_(profile),
19      blacklist_state_(NOT_BLACKLISTED),
20      policy_allows_load_(true),
21      current_sequence_number_(0),
22      running_checks_(0),
23      fail_fast_(false),
24      weak_ptr_factory_(this) {
25}
26
27ExtensionInstallChecker::~ExtensionInstallChecker() {
28}
29
30void ExtensionInstallChecker::Start(int enabled_checks,
31                                    bool fail_fast,
32                                    const Callback& callback) {
33  // Profile is null in tests.
34  if (profile_) {
35    DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
36    if (!extension_.get()) {
37      NOTREACHED();
38      return;
39    }
40  }
41
42  if (is_running() || !enabled_checks || callback.is_null()) {
43    NOTREACHED();
44    return;
45  }
46
47  running_checks_ = enabled_checks;
48  fail_fast_ = fail_fast;
49  callback_ = callback;
50  ResetResults();
51
52  // Execute the management policy check first as it is synchronous.
53  if (enabled_checks & CHECK_MANAGEMENT_POLICY) {
54    CheckManagementPolicy();
55    if (!is_running())
56      return;
57  }
58
59  if (enabled_checks & CHECK_REQUIREMENTS) {
60    CheckRequirements();
61    if (!is_running())
62      return;
63  }
64
65  if (enabled_checks & CHECK_BLACKLIST)
66    CheckBlacklistState();
67}
68
69void ExtensionInstallChecker::CheckManagementPolicy() {
70  DCHECK(extension_.get());
71
72  base::string16 error;
73  bool allow = ExtensionSystem::Get(profile_)->management_policy()->UserMayLoad(
74      extension_.get(), &error);
75  OnManagementPolicyCheckDone(allow, base::UTF16ToUTF8(error));
76}
77
78void ExtensionInstallChecker::OnManagementPolicyCheckDone(
79    bool allows_load,
80    const std::string& error) {
81  policy_allows_load_ = allows_load;
82  policy_error_ = error;
83  DCHECK(policy_allows_load_ || !policy_error_.empty());
84
85  running_checks_ &= ~CHECK_MANAGEMENT_POLICY;
86  MaybeInvokeCallback();
87}
88
89void ExtensionInstallChecker::CheckRequirements() {
90  DCHECK(extension_.get());
91
92  if (!requirements_checker_.get())
93    requirements_checker_.reset(new RequirementsChecker());
94  requirements_checker_->Check(
95      extension_,
96      base::Bind(&ExtensionInstallChecker::OnRequirementsCheckDone,
97                 weak_ptr_factory_.GetWeakPtr(),
98                 current_sequence_number_));
99}
100
101void ExtensionInstallChecker::OnRequirementsCheckDone(
102    int sequence_number,
103    std::vector<std::string> errors) {
104  // Some pending results may arrive after fail fast.
105  if (sequence_number != current_sequence_number_)
106    return;
107
108  requirement_errors_ = errors;
109
110  running_checks_ &= ~CHECK_REQUIREMENTS;
111  MaybeInvokeCallback();
112}
113
114void ExtensionInstallChecker::CheckBlacklistState() {
115  DCHECK(extension_.get());
116
117  extensions::Blacklist* blacklist =
118      ExtensionSystem::Get(profile_)->blacklist();
119  blacklist->IsBlacklisted(
120      extension_->id(),
121      base::Bind(&ExtensionInstallChecker::OnBlacklistStateCheckDone,
122                 weak_ptr_factory_.GetWeakPtr(),
123                 current_sequence_number_));
124}
125
126void ExtensionInstallChecker::OnBlacklistStateCheckDone(int sequence_number,
127                                                        BlacklistState state) {
128  // Some pending results may arrive after fail fast.
129  if (sequence_number != current_sequence_number_)
130    return;
131
132  blacklist_state_ = state;
133
134  running_checks_ &= ~CHECK_BLACKLIST;
135  MaybeInvokeCallback();
136}
137
138void ExtensionInstallChecker::ResetResults() {
139  requirement_errors_.clear();
140  blacklist_state_ = NOT_BLACKLISTED;
141  policy_allows_load_ = true;
142  policy_error_.clear();
143}
144
145void ExtensionInstallChecker::MaybeInvokeCallback() {
146  if (callback_.is_null())
147    return;
148
149  // Set bits for failed checks.
150  int failed_mask = 0;
151  if (blacklist_state_ == BLACKLISTED_MALWARE)
152    failed_mask |= CHECK_BLACKLIST;
153  if (!requirement_errors_.empty())
154    failed_mask |= CHECK_REQUIREMENTS;
155  if (!policy_allows_load_)
156    failed_mask |= CHECK_MANAGEMENT_POLICY;
157
158  // Invoke callback if all checks are complete or there was at least one
159  // failure and |fail_fast_| is true.
160  if (!is_running() || (failed_mask && fail_fast_)) {
161    // If we are failing fast, discard any pending results.
162    weak_ptr_factory_.InvalidateWeakPtrs();
163    running_checks_ = 0;
164    ++current_sequence_number_;
165
166    Callback callback_copy = callback_;
167    callback_.Reset();
168
169    // This instance may be owned by the callback recipient and deleted here,
170    // so reset |callback_| first and invoke a copy of the callback.
171    callback_copy.Run(failed_mask);
172  }
173}
174
175}  // namespace extensions
176