unpacked_installer.cc revision 3551c9c881056c480085172ff9840cab31610854
1// Copyright (c) 2013 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/unpacked_installer.h"
6
7#include "base/bind.h"
8#include "base/callback.h"
9#include "base/file_util.h"
10#include "base/strings/string_util.h"
11#include "base/strings/utf_string_conversions.h"
12#include "base/threading/thread_restrictions.h"
13#include "chrome/browser/extensions/extension_install_prompt.h"
14#include "chrome/browser/extensions/extension_install_ui.h"
15#include "chrome/browser/extensions/extension_prefs.h"
16#include "chrome/browser/extensions/extension_service.h"
17#include "chrome/browser/extensions/permissions_updater.h"
18#include "chrome/common/extensions/api/plugins/plugins_handler.h"
19#include "chrome/common/extensions/extension.h"
20#include "chrome/common/extensions/extension_file_util.h"
21#include "content/public/browser/browser_thread.h"
22#include "extensions/common/id_util.h"
23#include "extensions/common/manifest.h"
24#include "sync/api/string_ordinal.h"
25
26using content::BrowserThread;
27using extensions::Extension;
28
29namespace {
30
31const char kUnpackedExtensionsBlacklistedError[] =
32    "Loading of unpacked extensions is disabled by the administrator.";
33
34// Manages an ExtensionInstallPrompt for a particular extension.
35class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate {
36 public:
37  SimpleExtensionLoadPrompt(const Extension* extension,
38                            Profile* profile,
39                            const base::Closure& callback);
40  virtual ~SimpleExtensionLoadPrompt();
41
42  void ShowPrompt();
43
44  // ExtensionInstallUI::Delegate
45  virtual void InstallUIProceed() OVERRIDE;
46  virtual void InstallUIAbort(bool user_initiated) OVERRIDE;
47
48 private:
49  scoped_ptr<ExtensionInstallPrompt> install_ui_;
50  scoped_refptr<const Extension> extension_;
51  base::Closure callback_;
52};
53
54SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
55    const Extension* extension,
56    Profile* profile,
57    const base::Closure& callback)
58    : install_ui_(ExtensionInstallUI::CreateInstallPromptWithProfile(
59          profile)),
60      extension_(extension),
61      callback_(callback) {
62}
63
64SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
65}
66
67void SimpleExtensionLoadPrompt::ShowPrompt() {
68  install_ui_->ConfirmInstall(
69      this,
70      extension_.get(),
71      ExtensionInstallPrompt::GetDefaultShowDialogCallback());
72}
73
74void SimpleExtensionLoadPrompt::InstallUIProceed() {
75  callback_.Run();
76  delete this;
77}
78
79void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) {
80  delete this;
81}
82
83}  // namespace
84
85namespace extensions {
86
87// static
88scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
89    ExtensionService* extension_service) {
90  return scoped_refptr<UnpackedInstaller>(
91      new UnpackedInstaller(extension_service));
92}
93
94UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
95    : service_weak_(extension_service->AsWeakPtr()),
96      prompt_for_plugins_(true),
97      require_modern_manifest_version_(true),
98      installer_(extension_service->profile()) {
99  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
100}
101
102UnpackedInstaller::~UnpackedInstaller() {
103  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
104        BrowserThread::CurrentlyOn(BrowserThread::FILE));
105}
106
107void UnpackedInstaller::Load(const base::FilePath& path_in) {
108  DCHECK(extension_path_.empty());
109  extension_path_ = path_in;
110  BrowserThread::PostTask(
111      BrowserThread::FILE,
112      FROM_HERE,
113      base::Bind(&UnpackedInstaller::GetAbsolutePath, this));
114}
115
116bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
117                                            std::string* extension_id) {
118  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
119  DCHECK(extension_path_.empty());
120
121  if (!service_weak_.get())
122    return false;
123  // Load extensions from the command line synchronously to avoid a race
124  // between extension loading and loading an URL from the command line.
125  base::ThreadRestrictions::ScopedAllowIO allow_io;
126
127  extension_path_ = base::MakeAbsoluteFilePath(path_in);
128
129  if (!IsLoadingUnpackedAllowed()) {
130    ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
131    return false;
132  }
133
134  std::string error;
135  installer_.set_extension(extension_file_util::LoadExtension(
136      extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get());
137
138  if (!installer_.extension().get()) {
139    ReportExtensionLoadError(error);
140    return false;
141  }
142
143  ShowInstallPrompt();
144
145  *extension_id = installer_.extension()->id();
146  return true;
147}
148
149void UnpackedInstaller::ShowInstallPrompt() {
150  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
151  if (!service_weak_.get())
152    return;
153
154  const ExtensionSet* disabled_extensions =
155      service_weak_->disabled_extensions();
156  if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ &&
157      PluginInfo::HasPlugins(installer_.extension().get()) &&
158      !disabled_extensions->Contains(installer_.extension()->id())) {
159    SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
160        installer_.extension().get(),
161        installer_.profile(),
162        base::Bind(&UnpackedInstaller::CallCheckRequirements, this));
163    prompt->ShowPrompt();
164    return;
165  }
166  CallCheckRequirements();
167}
168
169void UnpackedInstaller::CallCheckRequirements() {
170  installer_.CheckRequirements(
171      base::Bind(&UnpackedInstaller::OnRequirementsChecked, this));
172}
173
174void UnpackedInstaller::OnRequirementsChecked(
175    std::vector<std::string> requirement_errors) {
176  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177
178  if (!requirement_errors.empty()) {
179    ReportExtensionLoadError(JoinString(requirement_errors, ' '));
180    return;
181  }
182
183  ConfirmInstall();
184}
185
186int UnpackedInstaller::GetFlags() {
187  std::string id = id_util::GenerateIdForPath(extension_path_);
188  bool allow_file_access =
189      Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
190  ExtensionPrefs* prefs = service_weak_->extension_prefs();
191  if (prefs->HasAllowFileAccessSetting(id))
192    allow_file_access = prefs->AllowFileAccess(id);
193
194  int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
195  if (allow_file_access)
196    result |= Extension::ALLOW_FILE_ACCESS;
197  if (require_modern_manifest_version_)
198    result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
199
200  return result;
201}
202
203bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
204  if (!service_weak_.get())
205    return true;
206  // If there is a "*" in the extension blacklist, then no extensions should be
207  // allowed at all (except explicitly whitelisted extensions).
208  ExtensionPrefs* prefs = service_weak_->extension_prefs();
209  return !prefs->ExtensionsBlacklistedByDefault();
210}
211
212void UnpackedInstaller::GetAbsolutePath() {
213  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
214
215  extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
216
217  std::string error;
218  if (!extension_file_util::CheckForIllegalFilenames(extension_path_,
219                                                     &error)) {
220    BrowserThread::PostTask(
221        BrowserThread::UI,
222        FROM_HERE,
223        base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
224    return;
225  }
226  BrowserThread::PostTask(
227      BrowserThread::UI, FROM_HERE,
228      base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this));
229}
230
231void UnpackedInstaller::CheckExtensionFileAccess() {
232  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233  if (!service_weak_.get())
234    return;
235
236  if (!IsLoadingUnpackedAllowed()) {
237    ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
238    return;
239  }
240
241  BrowserThread::PostTask(
242      BrowserThread::FILE,
243      FROM_HERE,
244      base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
245}
246
247void UnpackedInstaller::LoadWithFileAccess(int flags) {
248  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
249
250  std::string error;
251  installer_.set_extension(extension_file_util::LoadExtension(
252      extension_path_, Manifest::UNPACKED, flags, &error).get());
253
254  if (!installer_.extension().get()) {
255    BrowserThread::PostTask(
256        BrowserThread::UI,
257        FROM_HERE,
258        base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
259    return;
260  }
261
262  BrowserThread::PostTask(
263      BrowserThread::UI,
264      FROM_HERE,
265      base::Bind(&UnpackedInstaller::ShowInstallPrompt, this));
266}
267
268void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
269  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
270  if (!service_weak_.get())
271    return;
272  service_weak_->ReportExtensionLoadError(extension_path_, error, true);
273}
274
275void UnpackedInstaller::ConfirmInstall() {
276  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
277  string16 error = installer_.CheckManagementPolicy();
278  if (!error.empty()) {
279    ReportExtensionLoadError(UTF16ToUTF8(error));
280    return;
281  }
282
283  PermissionsUpdater perms_updater(service_weak_->profile());
284  perms_updater.GrantActivePermissions(installer_.extension().get());
285
286  service_weak_->OnExtensionInstalled(
287      installer_.extension().get(),
288      syncer::StringOrdinal(),
289      false /* no requirement errors */,
290      Blacklist::NOT_BLACKLISTED,
291      false /* don't wait for idle */);
292}
293
294}  // namespace extensions
295