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 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
127 UnpackedInstaller::~UnpackedInstaller() {
128 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
) ||
129 BrowserThread::CurrentlyOn(BrowserThread::FILE));
132 void UnpackedInstaller::Load(const base::FilePath
& path_in
) {
133 DCHECK(extension_path_
.empty());
134 extension_path_
= path_in
;
135 BrowserThread::PostTask(
138 base::Bind(&UnpackedInstaller::GetAbsolutePath
, this));
141 bool UnpackedInstaller::LoadFromCommandLine(const base::FilePath
& path_in
,
142 std::string
* extension_id
) {
143 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
144 DCHECK(extension_path_
.empty());
146 if (!service_weak_
.get())
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
);
160 install_checker_
.set_extension(
161 file_util::LoadExtension(
162 extension_path_
, Manifest::COMMAND_LINE
, GetFlags(), &error
).get());
165 !extension_l10n_util::ValidateExtensionLocales(
166 extension_path_
, extension()->manifest()->value(), &error
)) {
167 ReportExtensionLoadError(error
);
172 service_weak_
->profile(), PermissionsUpdater::INIT_FLAG_TRANSIENT
)
173 .InitializePermissions(extension());
176 *extension_id
= extension()->id();
180 void UnpackedInstaller::ShowInstallPrompt() {
181 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
182 if (!service_weak_
.get())
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(
192 install_checker_
.profile(),
193 base::Bind(&UnpackedInstaller::StartInstallChecks
, this));
194 prompt
->ShowPrompt();
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())
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
);
223 } else if (imported_module
&&
224 !SharedModuleInfo::IsSharedModule(imported_module
)) {
225 ReportExtensionLoadError(kImportNotSharedModule
);
227 } else if (imported_module
&& (version_required
.IsValid() &&
228 imported_module
->version()->CompareTo(
229 version_required
) < 0)) {
230 ReportExtensionLoadError(kImportMinVersionNewer
);
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 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
247 if (!install_checker_
.policy_error().empty()) {
248 ReportExtensionLoadError(install_checker_
.policy_error());
252 if (!install_checker_
.requirement_errors().empty()) {
253 ReportExtensionLoadError(
254 JoinString(install_checker_
.requirement_errors(), ' '));
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
;
278 bool UnpackedInstaller::IsLoadingUnpackedAllowed() const {
279 if (!service_weak_
.get())
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 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
290 extension_path_
= base::MakeAbsoluteFilePath(extension_path_
);
293 if (!file_util::CheckForIllegalFilenames(extension_path_
, &error
)) {
294 BrowserThread::PostTask(
297 base::Bind(&UnpackedInstaller::ReportExtensionLoadError
, this, error
));
300 BrowserThread::PostTask(
301 BrowserThread::UI
, FROM_HERE
,
302 base::Bind(&UnpackedInstaller::CheckExtensionFileAccess
, this));
305 void UnpackedInstaller::CheckExtensionFileAccess() {
306 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
307 if (!service_weak_
.get())
310 if (!IsLoadingUnpackedAllowed()) {
311 ReportExtensionLoadError(kUnpackedExtensionsBlacklistedError
);
315 BrowserThread::PostTask(
318 base::Bind(&UnpackedInstaller::LoadWithFileAccess
, this, GetFlags()));
321 void UnpackedInstaller::LoadWithFileAccess(int flags
) {
322 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
325 install_checker_
.set_extension(
326 file_util::LoadExtension(
327 extension_path_
, Manifest::UNPACKED
, flags
, &error
).get());
330 !extension_l10n_util::ValidateExtensionLocales(
331 extension_path_
, extension()->manifest()->value(), &error
)) {
332 BrowserThread::PostTask(
335 base::Bind(&UnpackedInstaller::ReportExtensionLoadError
, this, error
));
339 BrowserThread::PostTask(
342 base::Bind(&UnpackedInstaller::ShowInstallPrompt
, this));
345 void UnpackedInstaller::ReportExtensionLoadError(const std::string
&error
) {
346 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
348 if (service_weak_
.get()) {
349 ExtensionErrorReporter::GetInstance()->ReportLoadError(
352 service_weak_
->profile(),
353 be_noisy_on_failure_
);
357 void UnpackedInstaller::InstallExtension() {
358 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
360 PermissionsUpdater
perms_updater(service_weak_
->profile());
361 perms_updater
.InitializePermissions(extension());
362 perms_updater
.GrantActivePermissions(extension());
364 service_weak_
->OnExtensionInstalled(
365 extension(), syncer::StringOrdinal(), kInstallFlagInstallImmediately
);
368 } // namespace extensions