1 // Copyright (c) 2012 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/crx_installer.h"
10 #include "base/bind.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/lazy_instance.h"
14 #include "base/metrics/histogram.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/sequenced_worker_pool.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "base/time/time.h"
22 #include "base/version.h"
23 #include "chrome/browser/extensions/convert_user_script.h"
24 #include "chrome/browser/extensions/convert_web_app.h"
25 #include "chrome/browser/extensions/extension_assets_manager.h"
26 #include "chrome/browser/extensions/extension_error_reporter.h"
27 #include "chrome/browser/extensions/extension_service.h"
28 #include "chrome/browser/extensions/install_tracker.h"
29 #include "chrome/browser/extensions/install_tracker_factory.h"
30 #include "chrome/browser/extensions/permissions_updater.h"
31 #include "chrome/browser/extensions/webstore_installer.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/web_applications/web_app.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/extensions/extension_constants.h"
36 #include "chrome/grit/generated_resources.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/notification_service.h"
39 #include "content/public/browser/resource_dispatcher_host.h"
40 #include "content/public/browser/user_metrics.h"
41 #include "extensions/browser/extension_prefs.h"
42 #include "extensions/browser/extension_registry.h"
43 #include "extensions/browser/extension_system.h"
44 #include "extensions/browser/install/crx_install_error.h"
45 #include "extensions/browser/install/extension_install_ui.h"
46 #include "extensions/browser/install_flag.h"
47 #include "extensions/browser/notification_types.h"
48 #include "extensions/common/extension_icon_set.h"
49 #include "extensions/common/feature_switch.h"
50 #include "extensions/common/file_util.h"
51 #include "extensions/common/manifest.h"
52 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
53 #include "extensions/common/manifest_handlers/shared_module_info.h"
54 #include "extensions/common/manifest_url_handlers.h"
55 #include "extensions/common/permissions/permission_message_provider.h"
56 #include "extensions/common/permissions/permission_set.h"
57 #include "extensions/common/permissions/permissions_data.h"
58 #include "extensions/common/user_script.h"
59 #include "grit/extensions_strings.h"
60 #include "third_party/skia/include/core/SkBitmap.h"
61 #include "ui/base/l10n/l10n_util.h"
63 #if defined(OS_CHROMEOS)
64 #include "components/user_manager/user_manager.h"
67 using base::UserMetricsAction
;
68 using content::BrowserThread
;
69 using extensions::SharedModuleInfo
;
71 namespace extensions
{
75 // Used in histograms; do not change order.
76 enum OffStoreInstallDecision
{
78 OffStoreInstallAllowed
,
79 OffStoreInstallDisallowed
,
80 NumOffStoreInstallDecision
86 scoped_refptr
<CrxInstaller
> CrxInstaller::CreateSilent(
87 ExtensionService
* frontend
) {
88 return new CrxInstaller(frontend
->AsWeakPtr(),
89 scoped_ptr
<ExtensionInstallPrompt
>(),
94 scoped_refptr
<CrxInstaller
> CrxInstaller::Create(
95 ExtensionService
* frontend
,
96 scoped_ptr
<ExtensionInstallPrompt
> client
) {
97 return new CrxInstaller(frontend
->AsWeakPtr(), client
.Pass(), NULL
);
101 scoped_refptr
<CrxInstaller
> CrxInstaller::Create(
102 ExtensionService
* service
,
103 scoped_ptr
<ExtensionInstallPrompt
> client
,
104 const WebstoreInstaller::Approval
* approval
) {
105 return new CrxInstaller(service
->AsWeakPtr(), client
.Pass(), approval
);
108 CrxInstaller::CrxInstaller(base::WeakPtr
<ExtensionService
> service_weak
,
109 scoped_ptr
<ExtensionInstallPrompt
> client
,
110 const WebstoreInstaller::Approval
* approval
)
111 : install_directory_(service_weak
->install_directory()),
112 install_source_(Manifest::INTERNAL
),
114 hash_check_failed_(false),
115 expected_manifest_check_level_(
116 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT
),
117 expected_version_strict_checking_(false),
118 extensions_enabled_(service_weak
->extensions_enabled()),
119 delete_source_(false),
120 create_app_shortcut_(false),
121 service_weak_(service_weak
),
122 // See header file comment on |client_| for why we use a raw pointer here.
123 client_(client
.release()),
124 apps_require_extension_mime_type_(false),
125 allow_silent_install_(false),
126 grant_permissions_(true),
127 install_cause_(extension_misc::INSTALL_CAUSE_UNSET
),
128 creation_flags_(Extension::NO_FLAGS
),
129 off_store_install_allow_reason_(OffStoreInstallDisallowed
),
130 did_handle_successfully_(true),
131 error_on_unsupported_requirements_(false),
132 update_from_settings_page_(false),
133 install_flags_(kInstallFlagNone
),
134 install_checker_(service_weak
->profile()) {
135 installer_task_runner_
= service_weak
->GetFileTaskRunner();
139 CHECK(profile()->IsSameProfile(approval
->profile
));
141 client_
->install_ui()->SetUseAppInstalledBubble(
142 approval
->use_app_installed_bubble
);
143 client_
->install_ui()->SetSkipPostInstallUI(approval
->skip_post_install_ui
);
146 if (approval
->skip_install_dialog
) {
147 // Mark the extension as approved, but save the expected manifest and ID
148 // so we can check that they match the CRX's.
150 expected_manifest_check_level_
= approval
->manifest_check_level
;
151 if (expected_manifest_check_level_
!=
152 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE
)
153 expected_manifest_
.reset(approval
->manifest
->DeepCopy());
154 expected_id_
= approval
->extension_id
;
156 if (approval
->minimum_version
.get()) {
157 expected_version_
.reset(new Version(*approval
->minimum_version
));
158 expected_version_strict_checking_
= false;
161 show_dialog_callback_
= approval
->show_dialog_callback
;
162 set_is_ephemeral(approval
->is_ephemeral
);
165 CrxInstaller::~CrxInstaller() {
166 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
167 // Ensure |client_| and |install_checker_| data members are destroyed on the
168 // UI thread. The |client_| dialog has a weak reference as |this| is its
169 // delegate, and |install_checker_| owns WeakPtrs, so must be destroyed on the
170 // same thread that created it.
173 void CrxInstaller::InstallCrx(const base::FilePath
& source_file
) {
174 InstallCrxFile(CRXFileInfo(source_file
));
177 void CrxInstaller::InstallCrxFile(const CRXFileInfo
& source_file
) {
178 ExtensionService
* service
= service_weak_
.get();
179 if (!service
|| service
->browser_terminating())
182 NotifyCrxInstallBegin();
184 source_file_
= source_file
.path
;
186 scoped_refptr
<SandboxedUnpacker
> unpacker(new SandboxedUnpacker(
187 install_source_
, creation_flags_
, install_directory_
,
188 installer_task_runner_
.get(), this));
190 if (!installer_task_runner_
->PostTask(
191 FROM_HERE
, base::Bind(&SandboxedUnpacker::StartWithCrx
,
192 unpacker
.get(), source_file
))) {
197 void CrxInstaller::InstallUserScript(const base::FilePath
& source_file
,
198 const GURL
& download_url
) {
199 DCHECK(!download_url
.is_empty());
201 NotifyCrxInstallBegin();
203 source_file_
= source_file
;
204 download_url_
= download_url
;
206 if (!installer_task_runner_
->PostTask(
208 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread
, this)))
212 void CrxInstaller::ConvertUserScriptOnFileThread() {
213 base::string16 error
;
214 scoped_refptr
<Extension
> extension
= ConvertUserScriptToExtension(
215 source_file_
, download_url_
, install_directory_
, &error
);
216 if (!extension
.get()) {
217 ReportFailureFromFileThread(CrxInstallError(error
));
221 OnUnpackSuccess(extension
->path(), extension
->path(), NULL
, extension
.get(),
225 void CrxInstaller::InstallWebApp(const WebApplicationInfo
& web_app
) {
226 NotifyCrxInstallBegin();
228 if (!installer_task_runner_
->PostTask(
230 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread
, this, web_app
)))
234 void CrxInstaller::ConvertWebAppOnFileThread(
235 const WebApplicationInfo
& web_app
) {
236 scoped_refptr
<Extension
> extension(ConvertWebAppToExtension(
237 web_app
, base::Time::Now(), install_directory_
));
238 if (!extension
.get()) {
239 // Validation should have stopped any potential errors before getting here.
240 NOTREACHED() << "Could not convert web app to extension.";
244 // TODO(aa): conversion data gets lost here :(
246 OnUnpackSuccess(extension
->path(), extension
->path(), NULL
, extension
.get(),
250 CrxInstallError
CrxInstaller::AllowInstall(const Extension
* extension
) {
251 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
253 // Make sure the expected ID matches if one was supplied or if we want to
254 // bypass the prompt.
255 if ((approved_
|| !expected_id_
.empty()) &&
256 expected_id_
!= extension
->id()) {
257 return CrxInstallError(l10n_util::GetStringFUTF16(
258 IDS_EXTENSION_INSTALL_UNEXPECTED_ID
, base::ASCIIToUTF16(expected_id_
),
259 base::ASCIIToUTF16(extension
->id())));
262 if (expected_version_
.get()) {
263 if (expected_version_strict_checking_
) {
264 if (!expected_version_
->Equals(*extension
->version())) {
265 return CrxInstallError(l10n_util::GetStringFUTF16(
266 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION
,
267 base::ASCIIToUTF16(expected_version_
->GetString()),
268 base::ASCIIToUTF16(extension
->version()->GetString())));
271 if (extension
->version()->CompareTo(*expected_version_
) < 0) {
272 return CrxInstallError(l10n_util::GetStringFUTF16(
273 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION
,
274 base::ASCIIToUTF16(expected_version_
->GetString() + "+"),
275 base::ASCIIToUTF16(extension
->version()->GetString())));
280 // Make sure the manifests match if we want to bypass the prompt.
283 if (expected_manifest_check_level_
==
284 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE
) {
285 // To skip manifest checking, the extension must be a shared module
286 // and not request any permissions.
287 if (SharedModuleInfo::IsSharedModule(extension
) &&
288 extension
->permissions_data()->active_permissions()->IsEmpty()) {
292 valid
= expected_manifest_
->Equals(original_manifest_
.get());
293 if (!valid
&& expected_manifest_check_level_
==
294 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE
) {
296 scoped_refptr
<Extension
> dummy_extension
=
297 Extension::Create(base::FilePath(),
299 *expected_manifest_
->value(),
304 scoped_refptr
<const PermissionSet
> expected_permissions
=
305 dummy_extension
->permissions_data()->active_permissions();
306 valid
= !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
307 expected_permissions
.get(),
308 extension
->permissions_data()->active_permissions().get(),
309 extension
->GetType()));
315 return CrxInstallError(
316 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID
));
319 // The checks below are skipped for themes and external installs.
320 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
321 // and other uses of install_source_ that are no longer needed now that the
322 // SandboxedUnpacker sets extension->location.
323 if (extension
->is_theme() || Manifest::IsExternalLocation(install_source_
))
324 return CrxInstallError();
326 if (!extensions_enabled_
) {
327 return CrxInstallError(
328 CrxInstallError::ERROR_DECLINED
,
329 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED
));
332 if (install_cause_
== extension_misc::INSTALL_CAUSE_USER_DOWNLOAD
) {
333 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
334 const char kHistogramName
[] = "Extensions.OffStoreInstallDecisionEasy";
335 if (is_gallery_install()) {
336 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OnStoreInstall
,
337 NumOffStoreInstallDecision
);
339 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OffStoreInstallAllowed
,
340 NumOffStoreInstallDecision
);
343 const char kHistogramName
[] = "Extensions.OffStoreInstallDecisionHard";
344 if (is_gallery_install()) {
345 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OnStoreInstall
,
346 NumOffStoreInstallDecision
);
347 } else if (off_store_install_allow_reason_
!= OffStoreInstallDisallowed
) {
348 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OffStoreInstallAllowed
,
349 NumOffStoreInstallDecision
);
350 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
351 off_store_install_allow_reason_
,
352 NumOffStoreInstallAllowReasons
);
354 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OffStoreInstallDisallowed
,
355 NumOffStoreInstallDecision
);
356 // Don't delete source in this case so that the user can install
357 // manually if they want.
358 delete_source_
= false;
359 did_handle_successfully_
= false;
361 return CrxInstallError(CrxInstallError::ERROR_OFF_STORE
,
362 l10n_util::GetStringUTF16(
363 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE
));
368 if (install_checker_
.extension()->is_app()) {
369 // If the app was downloaded, apps_require_extension_mime_type_
370 // will be set. In this case, check that it was served with the
371 // right mime type. Make an exception for file URLs, which come
372 // from the users computer and have no headers.
373 if (!download_url_
.SchemeIsFile() &&
374 apps_require_extension_mime_type_
&&
375 original_mime_type_
!= Extension::kMimeType
) {
376 return CrxInstallError(l10n_util::GetStringFUTF16(
377 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE
,
378 base::ASCIIToUTF16(Extension::kMimeType
)));
381 // If the client_ is NULL, then the app is either being installed via
382 // an internal mechanism like sync, external_extensions, or default apps.
383 // In that case, we don't want to enforce things like the install origin.
384 if (!is_gallery_install() && client_
) {
385 // For apps with a gallery update URL, require that they be installed
387 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
388 if (ManifestURL::UpdatesFromGallery(extension
)) {
389 return CrxInstallError(l10n_util::GetStringFUTF16(
390 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS
,
391 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
)));
394 // For self-hosted apps, verify that the entire extent is on the same
395 // host (or a subdomain of the host) the download happened from. There's
396 // no way for us to verify that the app controls any other hosts.
397 URLPattern
pattern(UserScript::ValidUserScriptSchemes());
398 pattern
.SetHost(download_url_
.host());
399 pattern
.SetMatchSubdomains(true);
401 URLPatternSet patterns
= install_checker_
.extension()->web_extent();
402 for (URLPatternSet::const_iterator i
= patterns
.begin();
403 i
!= patterns
.end(); ++i
) {
404 if (!pattern
.MatchesHost(i
->host())) {
405 return CrxInstallError(l10n_util::GetStringUTF16(
406 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST
));
412 return CrxInstallError();
415 void CrxInstaller::OnUnpackFailure(const CrxInstallError
& error
) {
416 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
418 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
419 install_source(), Manifest::NUM_LOCATIONS
);
421 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
423 extension_misc::NUM_INSTALL_CAUSES
);
425 ReportFailureFromFileThread(error
);
428 void CrxInstaller::OnUnpackSuccess(
429 const base::FilePath
& temp_dir
,
430 const base::FilePath
& extension_dir
,
431 const base::DictionaryValue
* original_manifest
,
432 const Extension
* extension
,
433 const SkBitmap
& install_icon
) {
434 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
436 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
437 install_source(), Manifest::NUM_LOCATIONS
);
440 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
442 extension_misc::NUM_INSTALL_CAUSES
);
444 install_checker_
.set_extension(extension
);
445 temp_dir_
= temp_dir
;
446 if (!install_icon
.empty())
447 install_icon_
.reset(new SkBitmap(install_icon
));
449 if (original_manifest
)
450 original_manifest_
.reset(new Manifest(
451 Manifest::INVALID_LOCATION
,
452 scoped_ptr
<base::DictionaryValue
>(original_manifest
->DeepCopy())));
454 // We don't have to delete the unpack dir explicity since it is a child of
456 unpacked_extension_root_
= extension_dir
;
458 CrxInstallError error
= AllowInstall(extension
);
459 if (error
.type() != CrxInstallError::ERROR_NONE
) {
460 ReportFailureFromFileThread(error
);
464 if (!BrowserThread::PostTask(BrowserThread::UI
,
466 base::Bind(&CrxInstaller::CheckInstall
, this)))
470 void CrxInstaller::CheckInstall() {
471 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
472 ExtensionService
* service
= service_weak_
.get();
473 if (!service
|| service
->browser_terminating())
476 // TODO(crbug.com/420147): Move this code to a utility class to avoid
477 // duplication of SharedModuleService::CheckImports code.
478 if (SharedModuleInfo::ImportsModules(extension())) {
479 const std::vector
<SharedModuleInfo::ImportInfo
>& imports
=
480 SharedModuleInfo::GetImports(extension());
481 std::vector
<SharedModuleInfo::ImportInfo
>::const_iterator i
;
482 for (i
= imports
.begin(); i
!= imports
.end(); ++i
) {
483 Version
version_required(i
->minimum_version
);
484 const Extension
* imported_module
=
485 service
->GetExtensionById(i
->extension_id
, true);
486 if (imported_module
&&
487 !SharedModuleInfo::IsSharedModule(imported_module
)) {
488 ReportFailureFromUIThread(CrxInstallError(
489 CrxInstallError::ERROR_DECLINED
,
490 l10n_util::GetStringFUTF16(
491 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE
,
492 base::UTF8ToUTF16(imported_module
->name()))));
494 } else if (imported_module
&& (version_required
.IsValid() &&
495 imported_module
->version()->CompareTo(
496 version_required
) < 0)) {
497 ReportFailureFromUIThread(CrxInstallError(
498 CrxInstallError::ERROR_DECLINED
,
499 l10n_util::GetStringFUTF16(
500 IDS_EXTENSION_INSTALL_DEPENDENCY_OLD_VERSION
,
501 base::UTF8ToUTF16(imported_module
->name()),
502 base::ASCIIToUTF16(i
->minimum_version
),
503 base::ASCIIToUTF16(imported_module
->version()->GetString()))));
505 } else if (imported_module
&&
506 !SharedModuleInfo::IsExportAllowedByWhitelist(
507 imported_module
, extension()->id())) {
508 ReportFailureFromUIThread(CrxInstallError(
509 CrxInstallError::ERROR_DECLINED
,
510 l10n_util::GetStringFUTF16(
511 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED
,
512 base::UTF8ToUTF16(extension()->name()),
513 base::UTF8ToUTF16(imported_module
->name()))));
519 // Run the policy, requirements and blacklist checks in parallel.
520 install_checker_
.Start(
521 ExtensionInstallChecker::CHECK_ALL
,
522 false /* fail fast */,
523 base::Bind(&CrxInstaller::OnInstallChecksComplete
, this));
526 void CrxInstaller::OnInstallChecksComplete(int failed_checks
) {
527 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
531 // Check for requirement errors.
532 if (!install_checker_
.requirement_errors().empty()) {
533 if (error_on_unsupported_requirements_
) {
534 ReportFailureFromUIThread(
535 CrxInstallError(CrxInstallError::ERROR_DECLINED
,
536 base::UTF8ToUTF16(base::JoinString(
537 install_checker_
.requirement_errors(), " "))));
540 install_flags_
|= kInstallFlagHasRequirementErrors
;
543 // Check the blacklist state.
544 if (install_checker_
.blacklist_state() == extensions::BLACKLISTED_MALWARE
) {
545 install_flags_
|= kInstallFlagIsBlacklistedForMalware
;
548 if ((install_checker_
.blacklist_state() == extensions::BLACKLISTED_MALWARE
||
549 install_checker_
.blacklist_state() == extensions::BLACKLISTED_UNKNOWN
) &&
550 !allow_silent_install_
) {
551 // User tried to install a blacklisted extension. Show an error and
552 // refuse to install it.
553 ReportFailureFromUIThread(extensions::CrxInstallError(
554 CrxInstallError::ERROR_DECLINED
,
555 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED
,
556 base::UTF8ToUTF16(extension()->name()))));
557 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
558 extension()->location(),
559 Manifest::NUM_LOCATIONS
);
563 // NOTE: extension may still be blacklisted, but we're forced to silently
564 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
567 // Check for policy errors.
568 if (!install_checker_
.policy_error().empty()) {
569 // We don't want to show the error infobar for installs from the WebStore,
570 // because the WebStore already shows an error dialog itself.
571 // Note: |client_| can be NULL in unit_tests!
572 if (extension()->from_webstore() && client_
)
573 client_
->install_ui()->SetSkipPostInstallUI(true);
574 ReportFailureFromUIThread(
575 CrxInstallError(CrxInstallError::ERROR_DECLINED
,
576 base::UTF8ToUTF16(install_checker_
.policy_error())));
583 void CrxInstaller::ConfirmInstall() {
584 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
585 ExtensionService
* service
= service_weak_
.get();
586 if (!service
|| service
->browser_terminating())
589 if (KioskModeInfo::IsKioskOnly(install_checker_
.extension().get())) {
590 bool in_kiosk_mode
= false;
591 #if defined(OS_CHROMEOS)
592 user_manager::UserManager
* user_manager
= user_manager::UserManager::Get();
593 in_kiosk_mode
= user_manager
&& user_manager
->IsLoggedInAsKioskApp();
595 if (!in_kiosk_mode
) {
596 ReportFailureFromUIThread(CrxInstallError(
597 CrxInstallError::ERROR_DECLINED
,
598 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY
)));
603 // Check whether this install is initiated from the settings page to
604 // update an existing extension or app.
605 CheckUpdateFromSettingsPage();
607 GURL overlapping_url
;
608 ExtensionRegistry
* registry
= ExtensionRegistry::Get(service
->profile());
609 const Extension
* overlapping_extension
=
610 registry
->enabled_extensions().GetHostedAppByOverlappingWebExtent(
611 extension()->web_extent());
612 if (overlapping_extension
&&
613 overlapping_extension
->id() != extension()->id()) {
614 ReportFailureFromUIThread(CrxInstallError(l10n_util::GetStringFUTF16(
615 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT
,
616 base::UTF8ToUTF16(overlapping_extension
->name()))));
620 current_version_
= ExtensionPrefs::Get(service
->profile())
621 ->GetVersionString(extension()->id());
624 (!allow_silent_install_
|| !approved_
) &&
625 !update_from_settings_page_
) {
626 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
627 client_
->ConfirmInstall(this, extension(), show_dialog_callback_
);
629 if (!installer_task_runner_
->PostTask(
631 base::Bind(&CrxInstaller::CompleteInstall
, this)))
637 void CrxInstaller::InstallUIProceed() {
638 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
640 ExtensionService
* service
= service_weak_
.get();
641 if (!service
|| service
->browser_terminating())
644 // If update_from_settings_page_ boolean is true, this functions is
645 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
646 // and if it is false, this function is called in response to
647 // ExtensionInstallPrompt::ConfirmInstall().
648 if (update_from_settings_page_
) {
649 service
->GrantPermissionsAndEnableExtension(extension());
651 if (!installer_task_runner_
->PostTask(
653 base::Bind(&CrxInstaller::CompleteInstall
, this)))
657 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
660 void CrxInstaller::InstallUIAbort(bool user_initiated
) {
661 // If update_from_settings_page_ boolean is true, this functions is
662 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
663 // and if it is false, this function is called in response to
664 // ExtensionInstallPrompt::ConfirmInstall().
665 if (!update_from_settings_page_
) {
666 const char* histogram_name
= user_initiated
? "InstallCancel"
668 ExtensionService::RecordPermissionMessagesHistogram(
669 extension(), histogram_name
);
671 NotifyCrxInstallComplete(false);
674 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
676 // We're done. Since we don't post any more tasks to ourself, our ref count
677 // should go to zero and we die. The destructor will clean up the temp dir.
680 void CrxInstaller::CompleteInstall() {
681 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
683 if (!current_version_
.empty()) {
684 Version
current_version(current_version_
);
685 if (current_version
.CompareTo(*(extension()->version())) > 0) {
686 ReportFailureFromFileThread(CrxInstallError(
687 CrxInstallError::ERROR_DECLINED
,
688 l10n_util::GetStringUTF16(
689 extension()->is_app() ? IDS_APP_CANT_DOWNGRADE_VERSION
690 : IDS_EXTENSION_CANT_DOWNGRADE_VERSION
)));
695 // See how long extension install paths are. This is important on
696 // windows, because file operations may fail if the path to a file
697 // exceeds a small constant. See crbug.com/69693 .
698 UMA_HISTOGRAM_CUSTOM_COUNTS(
699 "Extensions.CrxInstallDirPathLength",
700 install_directory_
.value().length(), 0, 500, 100);
702 ExtensionAssetsManager
* assets_manager
=
703 ExtensionAssetsManager::GetInstance();
704 assets_manager
->InstallExtension(
706 unpacked_extension_root_
,
709 base::Bind(&CrxInstaller::ReloadExtensionAfterInstall
, this));
712 void CrxInstaller::ReloadExtensionAfterInstall(
713 const base::FilePath
& version_dir
) {
714 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
716 if (version_dir
.empty()) {
717 ReportFailureFromFileThread(CrxInstallError(l10n_util::GetStringUTF16(
718 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED
)));
722 // This is lame, but we must reload the extension because absolute paths
723 // inside the content scripts are established inside InitFromValue() and we
724 // just moved the extension.
725 // TODO(aa): All paths to resources inside extensions should be created
726 // lazily and based on the Extension's root path at that moment.
727 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
728 // with base::string16
729 std::string extension_id
= extension()->id();
731 install_checker_
.set_extension(
732 file_util::LoadExtension(
735 extension()->creation_flags() | Extension::REQUIRE_KEY
,
739 ReportSuccessFromFileThread();
741 LOG(ERROR
) << error
<< " " << extension_id
<< " " << download_url_
;
742 ReportFailureFromFileThread(CrxInstallError(base::UTF8ToUTF16(error
)));
746 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallError
& error
) {
747 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
748 if (!BrowserThread::PostTask(
749 BrowserThread::UI
, FROM_HERE
,
750 base::Bind(&CrxInstaller::ReportFailureFromUIThread
, this, error
))) {
755 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallError
& error
) {
756 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
758 if (!service_weak_
.get() || service_weak_
->browser_terminating())
761 content::NotificationService
* service
=
762 content::NotificationService::current();
763 service
->Notify(extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR
,
764 content::Source
<CrxInstaller
>(this),
765 content::Details
<const extensions::CrxInstallError
>(&error
));
767 // This isn't really necessary, it is only used because unit tests expect to
768 // see errors get reported via this interface.
770 // TODO(aa): Need to go through unit tests and clean them up too, probably get
772 ExtensionErrorReporter::GetInstance()->ReportError(
777 client_
->OnInstallFailure(error
);
779 NotifyCrxInstallComplete(false);
781 // Delete temporary files.
785 void CrxInstaller::ReportSuccessFromFileThread() {
786 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
788 // Tracking number of extensions installed by users
789 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD
)
790 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
792 if (!BrowserThread::PostTask(
793 BrowserThread::UI
, FROM_HERE
,
794 base::Bind(&CrxInstaller::ReportSuccessFromUIThread
, this)))
797 // Delete temporary files.
801 void CrxInstaller::ReportSuccessFromUIThread() {
802 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
804 if (!service_weak_
.get() || service_weak_
->browser_terminating())
807 if (!update_from_settings_page_
) {
808 // If there is a client, tell the client about installation.
810 client_
->OnInstallSuccess(extension(), install_icon_
.get());
812 // We update the extension's granted permissions if the user already
813 // approved the install (client_ is non NULL), or we are allowed to install
815 if ((client_
|| allow_silent_install_
) && grant_permissions_
) {
816 PermissionsUpdater
perms_updater(profile());
817 perms_updater
.InitializePermissions(extension());
818 perms_updater
.GrantActivePermissions(extension());
822 service_weak_
->OnExtensionInstalled(
823 extension(), page_ordinal_
, install_flags_
);
824 NotifyCrxInstallComplete(true);
827 void CrxInstaller::NotifyCrxInstallBegin() {
828 InstallTrackerFactory::GetForBrowserContext(profile())
829 ->OnBeginCrxInstall(expected_id_
);
832 void CrxInstaller::NotifyCrxInstallComplete(bool success
) {
833 // Some users (such as the download shelf) need to know when a
834 // CRXInstaller is done. Listening for the EXTENSION_* events
835 // is problematic because they don't know anything about the
836 // extension before it is unpacked, so they cannot filter based
838 content::NotificationService::current()->Notify(
839 extensions::NOTIFICATION_CRX_INSTALLER_DONE
,
840 content::Source
<CrxInstaller
>(this),
841 content::Details
<const Extension
>(success
? extension() : NULL
));
843 InstallTrackerFactory::GetForBrowserContext(profile())
844 ->OnFinishCrxInstall(success
? extension()->id() : expected_id_
, success
);
850 void CrxInstaller::CleanupTempFiles() {
851 if (!installer_task_runner_
->RunsTasksOnCurrentThread()) {
852 if (!installer_task_runner_
->PostTask(
854 base::Bind(&CrxInstaller::CleanupTempFiles
, this))) {
860 // Delete the temp directory and crx file as necessary.
861 if (!temp_dir_
.value().empty()) {
862 file_util::DeleteFile(temp_dir_
, true);
863 temp_dir_
= base::FilePath();
866 if (delete_source_
&& !source_file_
.value().empty()) {
867 file_util::DeleteFile(source_file_
, false);
868 source_file_
= base::FilePath();
872 void CrxInstaller::CheckUpdateFromSettingsPage() {
873 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
875 ExtensionService
* service
= service_weak_
.get();
876 if (!service
|| service
->browser_terminating())
879 if (off_store_install_allow_reason_
!= OffStoreInstallAllowedFromSettingsPage
)
882 const Extension
* installed_extension
=
883 service
->GetInstalledExtension(extension()->id());
884 if (installed_extension
) {
885 // Previous version of the extension exists.
886 update_from_settings_page_
= true;
887 expected_id_
= installed_extension
->id();
888 install_source_
= installed_extension
->location();
889 install_cause_
= extension_misc::INSTALL_CAUSE_UPDATE
;
893 void CrxInstaller::ConfirmReEnable() {
894 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
896 ExtensionService
* service
= service_weak_
.get();
897 if (!service
|| service
->browser_terminating())
900 if (!update_from_settings_page_
)
903 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(service
->profile());
904 if (!prefs
->DidExtensionEscalatePermissions(extension()->id()))
908 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
909 client_
->ConfirmReEnable(this, extension());
913 } // namespace extensions