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"
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_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/install/extension_install_ui.h"
25 #include "extensions/browser/install_flag.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/extension_l10n_util.h"
28 #include "extensions/common/file_util.h"
29 #include "extensions/common/manifest.h"
30 #include "extensions/common/manifest_handlers/shared_module_info.h"
31 #include "sync/api/string_ordinal.h"
33 using content::BrowserThread
;
34 using extensions::Extension
;
35 using extensions::SharedModuleInfo
;
39 const char kUnpackedExtensionsBlacklistedError
[] =
40 "Loading of unpacked extensions is disabled by the administrator.";
42 const char kImportMinVersionNewer
[] =
43 "'import' version requested is newer than what is installed.";
44 const char kImportMissing
[] = "'import' extension is not installed.";
45 const char kImportNotSharedModule
[] = "'import' is not a shared module.";
47 // Manages an ExtensionInstallPrompt for a particular extension.
48 class SimpleExtensionLoadPrompt
: public ExtensionInstallPrompt::Delegate
{
50 SimpleExtensionLoadPrompt(const Extension
* extension
,
52 const base::Closure
& callback
);
53 ~SimpleExtensionLoadPrompt() override
;
57 // ExtensionInstallUI::Delegate
58 void InstallUIProceed() override
;
59 void InstallUIAbort(bool user_initiated
) override
;
62 scoped_ptr
<ExtensionInstallPrompt
> install_ui_
;
63 scoped_refptr
<const Extension
> extension_
;
64 base::Closure callback_
;
67 SimpleExtensionLoadPrompt::SimpleExtensionLoadPrompt(
68 const Extension
* extension
,
70 const base::Closure
& callback
)
71 : extension_(extension
), callback_(callback
) {
72 scoped_ptr
<extensions::ExtensionInstallUI
> ui(
73 extensions::CreateExtensionInstallUI(profile
));
74 install_ui_
.reset(new ExtensionInstallPrompt(
75 profile
, ui
->GetDefaultInstallDialogParent()));
78 SimpleExtensionLoadPrompt::~SimpleExtensionLoadPrompt() {
81 void SimpleExtensionLoadPrompt::ShowPrompt() {
82 switch (ExtensionInstallPrompt::g_auto_confirm_for_tests
) {
83 case ExtensionInstallPrompt::NONE
:
84 install_ui_
->ConfirmInstall(
87 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
89 case ExtensionInstallPrompt::ACCEPT
:
92 case ExtensionInstallPrompt::CANCEL
:
93 InstallUIAbort(false);
97 void SimpleExtensionLoadPrompt::InstallUIProceed() {
102 void SimpleExtensionLoadPrompt::InstallUIAbort(bool user_initiated
) {
108 namespace extensions
{
111 scoped_refptr
<UnpackedInstaller
> UnpackedInstaller::Create(
112 ExtensionService
* extension_service
) {
113 DCHECK(extension_service
);
114 return scoped_refptr
<UnpackedInstaller
>(
115 new UnpackedInstaller(extension_service
));
118 UnpackedInstaller::UnpackedInstaller(ExtensionService
* extension_service
)
119 : service_weak_(extension_service
->AsWeakPtr()),
120 prompt_for_plugins_(true),
121 require_modern_manifest_version_(true),
122 be_noisy_on_failure_(true),
123 install_checker_(extension_service
->profile()) {
124 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
127 UnpackedInstaller::~UnpackedInstaller() {
130 void UnpackedInstaller::Load(const base::FilePath
& path_in
) {
131 DCHECK(extension_path_
.empty());
132 extension_path_
= path_in
;
133 BrowserThread::PostTask(
136 base::Bind(&UnpackedInstaller::GetAbsolutePath
, this));
139 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath
& path_in
,
140 std::string
* extension_id
) {
141 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
142 DCHECK(extension_path_
.empty());
144 if (!service_weak_
.get())
146 // Load extensions from the command line synchronously to avoid a race
147 // between extension loading and loading an URL from the command line.
148 base::ThreadRestrictions::ScopedAllowIO allow_io
;
150 extension_path_
= base::MakeAbsoluteFilePath(path_in
);
152 if (!IsLoadingUnpackedAllowed()) {
153 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError
);
158 install_checker_
.set_extension(
159 file_util::LoadExtension(
160 extension_path_
, Manifest::COMMAND_LINE
, GetFlags(), &error
).get());
163 !extension_l10n_util::ValidateExtensionLocales(
164 extension_path_
, extension()->manifest()->value(), &error
)) {
165 ReportExtensionLoadError(error
);
170 service_weak_
->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT
)
171 .InitializePermissions(extension());
174 *extension_id
= extension()->id();
178 void UnpackedInstaller::ShowInstallPrompt() {
179 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
180 if (!service_weak_
.get())
183 const ExtensionSet
& disabled_extensions
=
184 ExtensionRegistry::Get(service_weak_
->profile())->disabled_extensions();
185 if (service_weak_
->show_extensions_prompts() && prompt_for_plugins_
&&
186 PluginInfo::HasPlugins(extension()) &&
187 !disabled_extensions
.Contains(extension()->id())) {
188 SimpleExtensionLoadPrompt
* prompt
= new SimpleExtensionLoadPrompt(
190 install_checker_
.profile(),
191 base::Bind(&UnpackedInstaller::StartInstallChecks
, this));
192 prompt
->ShowPrompt();
195 StartInstallChecks();
198 void UnpackedInstaller::StartInstallChecks() {
199 // TODO(crbug.com/421128): Enable these checks all the time. The reason
200 // they are disabled for extensions loaded from the command-line is that
201 // installing unpacked extensions is asynchronous, but there can be
202 // dependencies between the extensions loaded by the command line.
203 if (extension()->manifest()->location() != Manifest::COMMAND_LINE
) {
204 ExtensionService
* service
= service_weak_
.get();
205 if (!service
|| service
->browser_terminating())
208 // TODO(crbug.com/420147): Move this code to a utility class to avoid
209 // duplication of SharedModuleService::CheckImports code.
210 if (SharedModuleInfo::ImportsModules(extension())) {
211 const std::vector
<SharedModuleInfo::ImportInfo
>& imports
=
212 SharedModuleInfo::GetImports(extension());
213 std::vector
<SharedModuleInfo::ImportInfo
>::const_iterator i
;
214 for (i
= imports
.begin(); i
!= imports
.end(); ++i
) {
215 Version
version_required(i
->minimum_version
);
216 const Extension
* imported_module
=
217 service
->GetExtensionById(i
->extension_id
, true);
218 if (!imported_module
) {
219 ReportExtensionLoadError(kImportMissing
);
221 } else if (imported_module
&&
222 !SharedModuleInfo::IsSharedModule(imported_module
)) {
223 ReportExtensionLoadError(kImportNotSharedModule
);
225 } else if (imported_module
&& (version_required
.IsValid() &&
226 imported_module
->version()->CompareTo(
227 version_required
) < 0)) {
228 ReportExtensionLoadError(kImportMinVersionNewer
);
235 install_checker_
.Start(
236 ExtensionInstallChecker::CHECK_REQUIREMENTS
|
237 ExtensionInstallChecker::CHECK_MANAGEMENT_POLICY
,
238 true /* fail fast */,
239 base::Bind(&UnpackedInstaller::OnInstallChecksComplete
, this));
242 void UnpackedInstaller::OnInstallChecksComplete(int failed_checks
) {
243 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
245 if (!install_checker_
.policy_error().empty()) {
246 ReportExtensionLoadError(install_checker_
.policy_error());
250 if (!install_checker_
.requirement_errors().empty()) {
251 ReportExtensionLoadError(
252 JoinString(install_checker_
.requirement_errors(), ' '));
259 int UnpackedInstaller::GetFlags() {
260 std::string id
= crx_file::id_util::GenerateIdForPath(extension_path_
);
261 bool allow_file_access
=
262 Manifest::ShouldAlwaysAllowFileAccess(Manifest::UNPACKED
);
263 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(service_weak_
->profile());
264 if (prefs
->HasAllowFileAccessSetting(id
))
265 allow_file_access
= prefs
->AllowFileAccess(id
);
267 int result
= Extension::FOLLOW_SYMLINKS_ANYWHERE
;
268 if (allow_file_access
)
269 result
|= Extension::ALLOW_FILE_ACCESS
;
270 if (require_modern_manifest_version_
)
271 result
|= Extension::REQUIRE_MODERN_MANIFEST_VERSION
;
276 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
277 if (!service_weak_
.get())
279 // If there is a "*" in the extension blacklist, then no extensions should be
280 // allowed at all (except explicitly whitelisted extensions).
281 return !ExtensionManagementFactory::GetForBrowserContext(
282 service_weak_
->profile())->BlacklistedByDefault();
285 void UnpackedInstaller::GetAbsolutePath() {
286 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
288 extension_path_
= base::MakeAbsoluteFilePath(extension_path_
);
291 if (!file_util::CheckForIllegalFilenames(extension_path_
, &error
)) {
292 BrowserThread::PostTask(
295 base::Bind(&UnpackedInstaller::ReportExtensionLoadError
, this, error
));
298 BrowserThread::PostTask(
299 BrowserThread::UI
, FROM_HERE
,
300 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess
, this));
303 void UnpackedInstaller::CheckExtensionFileAccess() {
304 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
305 if (!service_weak_
.get())
308 if (!IsLoadingUnpackedAllowed()) {
309 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError
);
313 BrowserThread::PostTask(
316 base::Bind(&UnpackedInstaller::LoadWithFileAccess
, this, GetFlags()));
319 void UnpackedInstaller::LoadWithFileAccess(int flags
) {
320 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
323 install_checker_
.set_extension(
324 file_util::LoadExtension(
325 extension_path_
, Manifest::UNPACKED
, flags
, &error
).get());
328 !extension_l10n_util::ValidateExtensionLocales(
329 extension_path_
, extension()->manifest()->value(), &error
)) {
330 BrowserThread::PostTask(
333 base::Bind(&UnpackedInstaller::ReportExtensionLoadError
, this, error
));
337 BrowserThread::PostTask(
340 base::Bind(&UnpackedInstaller::ShowInstallPrompt
, this));
343 void UnpackedInstaller::ReportExtensionLoadError(const std::string
&error
) {
344 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
346 if (service_weak_
.get()) {
347 ExtensionErrorReporter::GetInstance()->ReportLoadError(
350 service_weak_
->profile(),
351 be_noisy_on_failure_
);
354 if (!callback_
.is_null()) {
355 callback_
.Run(nullptr, extension_path_
, error
);
360 void UnpackedInstaller::InstallExtension() {
361 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
363 PermissionsUpdater
perms_updater(service_weak_
->profile());
364 perms_updater
.InitializePermissions(extension());
365 perms_updater
.GrantActivePermissions(extension());
367 service_weak_
->OnExtensionInstalled(
368 extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately
);
370 if (!callback_
.is_null()) {
371 callback_
.Run(extension(), extension_path_
, std::string());
376 } // namespace extensions