Add a webstorePrivate API to show a permission prompt for delegated bundle installs
[chromium-blink-merge.git] / chrome / browser / extensions / unpacked_installer.cc
blob5f702b9b8a2a3882a36934f9e330dcc248423044
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.
5 #include "chrome/browser/extensions/unpacked_installer.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/files/file_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "chrome/browser/extensions/extension_error_reporter.h"
13 #include "chrome/browser/extensions/extension_install_prompt.h"
14 #include "chrome/browser/extensions/extension_management.h"
15 #include "chrome/browser/extensions/extension_service.h"
16 #include "chrome/browser/extensions/permissions_updater.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
19 #include "chrome/common/extensions/api/plugins/plugins_handler.h"
20 #include "components/crx_file/id_util.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "extensions/browser/extension_dialog_auto_confirm.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/install/extension_install_ui.h"
26 #include "extensions/browser/install_flag.h"
27 #include "extensions/common/extension.h"
28 #include "extensions/common/extension_l10n_util.h"
29 #include "extensions/common/file_util.h"
30 #include "extensions/common/manifest.h"
31 #include "extensions/common/manifest_handlers/shared_module_info.h"
32 #include "sync/api/string_ordinal.h"
34 using content::BrowserThread;
35 using extensions::Extension;
36 using extensions::SharedModuleInfo;
38 namespace {
40 const char kUnpackedExtensionsBlacklistedError[] =
41 "Loading of unpacked extensions is disabled by the administrator.";
43 const char kImportMinVersionNewer[] =
44 "'import' version requested is newer than what is installed.";
45 const char kImportMissing[] = "'import' extension is not installed.";
46 const char kImportNotSharedModule[] = "'import' is not a shared module.";
48 // Manages an ExtensionInstallPrompt for a particular extension.
49 class SimpleExtensionLoadPrompt : public ExtensionInstallPrompt::Delegate {
50 public:
51 SimpleExtensionLoadPrompt(const Extension* extension,
52 Profile* profile,
53 const base::Closure& callback);
54 ~SimpleExtensionLoadPrompt() override;
56 void ShowPrompt();
58 // ExtensionInstallUI::Delegate
59 void InstallUIProceed() override;
60 void InstallUIAbort(bool user_initiated) override;
62 private:
63 scoped_ptr<ExtensionInstallPrompt> install_ui_;
64 scoped_refptr<const Extension> extension_;
65 base::Closure callback_;
68 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
69 const Extension* extension,
70 Profile* profile,
71 const base::Closure& callback)
72 : extension_(extension), callback_(callback) {
73 scoped_ptr<extensions::ExtensionInstallUI> ui(
74 extensions::CreateExtensionInstallUI(profile));
75 install_ui_.reset(new ExtensionInstallPrompt(
76 profile, ui->GetDefaultInstallDialogParent()));
79 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
82 void SimpleExtensionLoadPrompt::ShowPrompt() {
83 switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
84 case extensions::ScopedTestDialogAutoConfirm::NONE:
85 install_ui_->ConfirmInstall(
86 this,
87 extension_.get(),
88 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
89 break;
90 case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
91 InstallUIProceed();
92 break;
93 case extensions::ScopedTestDialogAutoConfirm::CANCEL:
94 InstallUIAbort(false);
95 break;
99 void SimpleExtensionLoadPrompt::InstallUIProceed() {
100 callback_.Run();
101 delete this;
104 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated) {
105 delete this;
108 } // namespace
110 namespace extensions {
112 // static
113 scoped_refptr<UnpackedInstaller> UnpackedInstaller::Create(
114 ExtensionService* extension_service) {
115 DCHECK(extension_service);
116 return scoped_refptr<UnpackedInstaller>(
117 new UnpackedInstaller(extension_service));
120 UnpackedInstaller::UnpackedInstaller(ExtensionService* extension_service)
121 : service_weak_(extension_service->AsWeakPtr()),
122 prompt_for_plugins_(true),
123 require_modern_manifest_version_(true),
124 be_noisy_on_failure_(true),
125 install_checker_(extension_service->profile()) {
126 DCHECK_CURRENTLY_ON(BrowserThread::UI);
129 UnpackedInstaller::~UnpackedInstaller() {
132 void UnpackedInstaller::Load(const base::FilePath& path_in) {
133 DCHECK(extension_path_.empty());
134 extension_path_ = path_in;
135 BrowserThread::PostTask(
136 BrowserThread::FILE,
137 FROM_HERE,
138 base::Bind(&UnpackedInstaller::GetAbsolutePath, this));
141 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath& path_in,
142 std::string* extension_id) {
143 DCHECK_CURRENTLY_ON(BrowserThread::UI);
144 DCHECK(extension_path_.empty());
146 if (!service_weak_.get())
147 return false;
148 // Load extensions from the command line synchronously to avoid a race
149 // between extension loading and loading an URL from the command line.
150 base::ThreadRestrictions::ScopedAllowIO allow_io;
152 extension_path_ = base::MakeAbsoluteFilePath(path_in);
154 if (!IsLoadingUnpackedAllowed()) {
155 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
156 return false;
159 std::string error;
160 install_checker_.set_extension(
161 file_util::LoadExtension(
162 extension_path_, Manifest::COMMAND_LINE, GetFlags(), &error).get());
164 if (!extension() ||
165 !extension_l10n_util::ValidateExtensionLocales(
166 extension_path_, extension()->manifest()->value(), &error)) {
167 ReportExtensionLoadError(error);
168 return false;
171 PermissionsUpdater(
172 service_weak_->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT)
173 .InitializePermissions(extension());
174 ShowInstallPrompt();
176 *extension_id = extension()->id();
177 return true;
180 void UnpackedInstaller::ShowInstallPrompt() {
181 DCHECK_CURRENTLY_ON(BrowserThread::UI);
182 if (!service_weak_.get())
183 return;
185 const ExtensionSet& disabled_extensions =
186 ExtensionRegistry::Get(service_weak_->profile())->disabled_extensions();
187 if (service_weak_->show_extensions_prompts() && prompt_for_plugins_ &&
188 PluginInfo::HasPlugins(extension()) &&
189 !disabled_extensions.Contains(extension()->id())) {
190 SimpleExtensionLoadPrompt* prompt = new SimpleExtensionLoadPrompt(
191 extension(),
192 install_checker_.profile(),
193 base::Bind(&UnpackedInstaller::StartInstallChecks, this));
194 prompt->ShowPrompt();
195 return;
197 StartInstallChecks();
200 void UnpackedInstaller::StartInstallChecks() {
201 // TODO(crbug.com/421128): Enable these checks all the time. The reason
202 // they are disabled for extensions loaded from the command-line is that
203 // installing unpacked extensions is asynchronous, but there can be
204 // dependencies between the extensions loaded by the command line.
205 if (extension()->manifest()->location() != Manifest::COMMAND_LINE) {
206 ExtensionService* service = service_weak_.get();
207 if (!service || service->browser_terminating())
208 return;
210 // TODO(crbug.com/420147): Move this code to a utility class to avoid
211 // duplication of SharedModuleService::CheckImports code.
212 if (SharedModuleInfo::ImportsModules(extension())) {
213 const std::vector<SharedModuleInfo::ImportInfo>& imports =
214 SharedModuleInfo::GetImports(extension());
215 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
216 for (i = imports.begin(); i != imports.end(); ++i) {
217 Version version_required(i->minimum_version);
218 const Extension* imported_module =
219 service->GetExtensionById(i->extension_id, true);
220 if (!imported_module) {
221 ReportExtensionLoadError(kImportMissing);
222 return;
223 } else if (imported_module &&
224 !SharedModuleInfo::IsSharedModule(imported_module)) {
225 ReportExtensionLoadError(kImportNotSharedModule);
226 return;
227 } else if (imported_module && (version_required.IsValid() &&
228 imported_module->version()->CompareTo(
229 version_required) < 0)) {
230 ReportExtensionLoadError(kImportMinVersionNewer);
231 return;
237 install_checker_.Start(
238 ExtensionInstallChecker::CHECK_REQUIREMENTS |
239 ExtensionInstallChecker::CHECK_MANAGEMENT_POLICY,
240 true /* fail fast */,
241 base::Bind(&UnpackedInstaller::OnInstallChecksComplete, this));
244 void UnpackedInstaller::OnInstallChecksComplete(int failed_checks) {
245 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 if (!install_checker_.policy_error().empty()) {
248 ReportExtensionLoadError(install_checker_.policy_error());
249 return;
252 if (!install_checker_.requirement_errors().empty()) {
253 ReportExtensionLoadError(
254 JoinString(install_checker_.requirement_errors(), ' '));
255 return;
258 InstallExtension();
261 int UnpackedInstaller::GetFlags() {
262 std::string id = crx_file::id_util::GenerateIdForPath(extension_path_);
263 bool allow_file_access =
264 Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED);
265 ExtensionPrefs* prefs = ExtensionPrefs::Get(service_weak_->profile());
266 if (prefs->HasAllowFileAccessSetting(id))
267 allow_file_access = prefs->AllowFileAccess(id);
269 int result = Extension::FOLLOW_SYMLINKS_ANYWHERE;
270 if (allow_file_access)
271 result |= Extension::ALLOW_FILE_ACCESS;
272 if (require_modern_manifest_version_)
273 result |= Extension::REQUIRE_MODERN_MANIFEST_VERSION;
275 return result;
278 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
279 if (!service_weak_.get())
280 return true;
281 // If there is a "*" in the extension blacklist, then no extensions should be
282 // allowed at all (except explicitly whitelisted extensions).
283 return !ExtensionManagementFactory::GetForBrowserContext(
284 service_weak_->profile())->BlacklistedByDefault();
287 void UnpackedInstaller::GetAbsolutePath() {
288 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
290 extension_path_ = base::MakeAbsoluteFilePath(extension_path_);
292 std::string error;
293 if (!file_util::CheckForIllegalFilenames(extension_path_, &error)) {
294 BrowserThread::PostTask(
295 BrowserThread::UI,
296 FROM_HERE,
297 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
298 return;
300 BrowserThread::PostTask(
301 BrowserThread::UI, FROM_HERE,
302 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess, this));
305 void UnpackedInstaller::CheckExtensionFileAccess() {
306 DCHECK_CURRENTLY_ON(BrowserThread::UI);
307 if (!service_weak_.get())
308 return;
310 if (!IsLoadingUnpackedAllowed()) {
311 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError);
312 return;
315 BrowserThread::PostTask(
316 BrowserThread::FILE,
317 FROM_HERE,
318 base::Bind(&UnpackedInstaller::LoadWithFileAccess, this, GetFlags()));
321 void UnpackedInstaller::LoadWithFileAccess(int flags) {
322 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
324 std::string error;
325 install_checker_.set_extension(
326 file_util::LoadExtension(
327 extension_path_, Manifest::UNPACKED, flags, &error).get());
329 if (!extension() ||
330 !extension_l10n_util::ValidateExtensionLocales(
331 extension_path_, extension()->manifest()->value(), &error)) {
332 BrowserThread::PostTask(
333 BrowserThread::UI,
334 FROM_HERE,
335 base::Bind(&UnpackedInstaller::ReportExtensionLoadError, this, error));
336 return;
339 BrowserThread::PostTask(
340 BrowserThread::UI,
341 FROM_HERE,
342 base::Bind(&UnpackedInstaller::ShowInstallPrompt, this));
345 void UnpackedInstaller::ReportExtensionLoadError(const std::string &error) {
346 DCHECK_CURRENTLY_ON(BrowserThread::UI);
348 if (service_weak_.get()) {
349 ExtensionErrorReporter::GetInstance()->ReportLoadError(
350 extension_path_,
351 error,
352 service_weak_->profile(),
353 be_noisy_on_failure_);
356 if (!callback_.is_null()) {
357 callback_.Run(nullptr, extension_path_, error);
358 callback_.Reset();
362 void UnpackedInstaller::InstallExtension() {
363 DCHECK_CURRENTLY_ON(BrowserThread::UI);
365 PermissionsUpdater perms_updater(service_weak_->profile());
366 perms_updater.InitializePermissions(extension());
367 perms_updater.GrantActivePermissions(extension());
369 service_weak_->OnExtensionInstalled(
370 extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately);
372 if (!callback_.is_null()) {
373 callback_.Run(extension(), extension_path_, std::string());
374 callback_.Reset();
378 } // namespace extensions