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/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/path_service.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "base/time/time.h"
23 #include "base/version.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/browser/extensions/convert_user_script.h"
26 #include "chrome/browser/extensions/convert_web_app.h"
27 #include "chrome/browser/extensions/crx_installer_error.h"
28 #include "chrome/browser/extensions/extension_error_reporter.h"
29 #include "chrome/browser/extensions/extension_install_ui.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/install_tracker.h"
32 #include "chrome/browser/extensions/install_tracker_factory.h"
33 #include "chrome/browser/extensions/permissions_updater.h"
34 #include "chrome/browser/extensions/webstore_installer.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/web_applications/web_app.h"
37 #include "chrome/common/chrome_paths.h"
38 #include "chrome/common/extensions/extension_constants.h"
39 #include "chrome/common/extensions/manifest_url_handler.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "content/public/browser/notification_service.h"
42 #include "content/public/browser/resource_dispatcher_host.h"
43 #include "content/public/browser/user_metrics.h"
44 #include "extensions/browser/extension_system.h"
45 #include "extensions/common/extension_icon_set.h"
46 #include "extensions/common/feature_switch.h"
47 #include "extensions/common/file_util.h"
48 #include "extensions/common/manifest.h"
49 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
50 #include "extensions/common/manifest_handlers/shared_module_info.h"
51 #include "extensions/common/permissions/permission_message_provider.h"
52 #include "extensions/common/permissions/permission_set.h"
53 #include "extensions/common/permissions/permissions_data.h"
54 #include "extensions/common/user_script.h"
55 #include "grit/chromium_strings.h"
56 #include "grit/extensions_strings.h"
57 #include "grit/generated_resources.h"
58 #include "grit/theme_resources.h"
59 #include "third_party/skia/include/core/SkBitmap.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
63 #if defined(OS_CHROMEOS)
64 #include "chrome/browser/chromeos/login/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(
109 base::WeakPtr
<ExtensionService
> service_weak
,
110 scoped_ptr
<ExtensionInstallPrompt
> client
,
111 const WebstoreInstaller::Approval
* approval
)
112 : install_directory_(service_weak
->install_directory()),
113 install_source_(Manifest::INTERNAL
),
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 install_cause_(extension_misc::INSTALL_CAUSE_UNSET
),
127 creation_flags_(Extension::NO_FLAGS
),
128 off_store_install_allow_reason_(OffStoreInstallDisallowed
),
129 did_handle_successfully_(true),
130 error_on_unsupported_requirements_(false),
131 has_requirement_errors_(false),
132 blacklist_state_(extensions::NOT_BLACKLISTED
),
133 install_wait_for_idle_(true),
134 update_from_settings_page_(false),
135 installer_(service_weak
->profile()) {
136 installer_task_runner_
= service_weak
->GetFileTaskRunner();
140 CHECK(profile()->IsSameProfile(approval
->profile
));
142 client_
->install_ui()->SetUseAppInstalledBubble(
143 approval
->use_app_installed_bubble
);
144 client_
->install_ui()->set_skip_post_install_ui(
145 approval
->skip_post_install_ui
);
148 if (approval
->skip_install_dialog
) {
149 // Mark the extension as approved, but save the expected manifest and ID
150 // so we can check that they match the CRX's.
152 expected_manifest_check_level_
= approval
->manifest_check_level
;
153 if (expected_manifest_check_level_
!=
154 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE
)
155 expected_manifest_
.reset(approval
->manifest
->DeepCopy());
156 expected_id_
= approval
->extension_id
;
158 if (approval
->minimum_version
.get()) {
159 expected_version_
.reset(new Version(*approval
->minimum_version
));
160 expected_version_strict_checking_
= false;
163 show_dialog_callback_
= approval
->show_dialog_callback
;
165 if (approval
->is_ephemeral
)
166 creation_flags_
|= Extension::IS_EPHEMERAL
;
169 CrxInstaller::~CrxInstaller() {
170 // Make sure the UI is deleted on the ui thread.
172 BrowserThread::DeleteSoon(BrowserThread::UI
, FROM_HERE
, client_
);
177 void CrxInstaller::InstallCrx(const base::FilePath
& source_file
) {
178 ExtensionService
* service
= service_weak_
.get();
179 if (!service
|| service
->browser_terminating())
182 NotifyCrxInstallBegin();
184 source_file_
= source_file
;
186 scoped_refptr
<SandboxedUnpacker
> unpacker(
187 new SandboxedUnpacker(source_file
,
191 installer_task_runner_
.get(),
194 if (!installer_task_runner_
->PostTask(
196 base::Bind(&SandboxedUnpacker::Start
, unpacker
.get())))
200 void CrxInstaller::InstallUserScript(const base::FilePath
& source_file
,
201 const GURL
& download_url
) {
202 DCHECK(!download_url
.is_empty());
204 NotifyCrxInstallBegin();
206 source_file_
= source_file
;
207 download_url_
= download_url
;
209 if (!installer_task_runner_
->PostTask(
211 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread
, this)))
215 void CrxInstaller::ConvertUserScriptOnFileThread() {
216 base::string16 error
;
217 scoped_refptr
<Extension
> extension
= ConvertUserScriptToExtension(
218 source_file_
, download_url_
, install_directory_
, &error
);
219 if (!extension
.get()) {
220 ReportFailureFromFileThread(CrxInstallerError(error
));
224 OnUnpackSuccess(extension
->path(), extension
->path(), NULL
, extension
.get(),
228 void CrxInstaller::InstallWebApp(const WebApplicationInfo
& web_app
) {
229 NotifyCrxInstallBegin();
231 if (!installer_task_runner_
->PostTask(
233 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread
,
236 install_directory_
)))
240 void CrxInstaller::ConvertWebAppOnFileThread(
241 const WebApplicationInfo
& web_app
,
242 const base::FilePath
& install_directory
) {
243 base::string16 error
;
244 scoped_refptr
<Extension
> extension(
245 ConvertWebAppToExtension(web_app
, base::Time::Now(), install_directory
));
246 if (!extension
.get()) {
247 // Validation should have stopped any potential errors before getting here.
248 NOTREACHED() << "Could not convert web app to extension.";
252 // TODO(aa): conversion data gets lost here :(
254 OnUnpackSuccess(extension
->path(), extension
->path(), NULL
, extension
.get(),
258 CrxInstallerError
CrxInstaller::AllowInstall(const Extension
* extension
) {
259 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
261 // Make sure the expected ID matches if one was supplied or if we want to
262 // bypass the prompt.
263 if ((approved_
|| !expected_id_
.empty()) &&
264 expected_id_
!= extension
->id()) {
265 return CrxInstallerError(
266 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID
,
267 base::ASCIIToUTF16(expected_id_
),
268 base::ASCIIToUTF16(extension
->id())));
271 if (expected_version_
.get()) {
272 if (expected_version_strict_checking_
) {
273 if (!expected_version_
->Equals(*extension
->version())) {
274 return CrxInstallerError(
275 l10n_util::GetStringFUTF16(
276 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION
,
277 base::ASCIIToUTF16(expected_version_
->GetString()),
278 base::ASCIIToUTF16(extension
->version()->GetString())));
281 if (extension
->version()->CompareTo(*expected_version_
) < 0) {
282 return CrxInstallerError(
283 l10n_util::GetStringFUTF16(
284 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION
,
285 base::ASCIIToUTF16(expected_version_
->GetString() + "+"),
286 base::ASCIIToUTF16(extension
->version()->GetString())));
291 // Make sure the manifests match if we want to bypass the prompt.
294 if (expected_manifest_check_level_
==
295 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE
) {
296 // To skip manifest checking, the extension must be a shared module
297 // and not request any permissions.
298 if (SharedModuleInfo::IsSharedModule(extension
) &&
299 PermissionsData::GetActivePermissions(extension
)->IsEmpty()) {
303 valid
= expected_manifest_
->Equals(original_manifest_
.get());
304 if (!valid
&& expected_manifest_check_level_
==
305 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE
) {
307 scoped_refptr
<Extension
> dummy_extension
=
308 Extension::Create(base::FilePath(),
310 *expected_manifest_
->value(),
314 scoped_refptr
<const PermissionSet
> expected_permissions
=
315 PermissionsData::GetActivePermissions(dummy_extension
.get());
316 valid
= !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
317 expected_permissions
,
318 PermissionsData::GetActivePermissions(extension
),
319 extension
->GetType()));
325 return CrxInstallerError(
326 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID
));
329 // The checks below are skipped for themes and external installs.
330 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
331 // and other uses of install_source_ that are no longer needed now that the
332 // SandboxedUnpacker sets extension->location.
333 if (extension
->is_theme() || Manifest::IsExternalLocation(install_source_
))
334 return CrxInstallerError();
336 if (!extensions_enabled_
) {
337 return CrxInstallerError(
338 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED
));
341 if (install_cause_
== extension_misc::INSTALL_CAUSE_USER_DOWNLOAD
) {
342 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
343 const char* kHistogramName
= "Extensions.OffStoreInstallDecisionEasy";
344 if (is_gallery_install()) {
345 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OnStoreInstall
,
346 NumOffStoreInstallDecision
);
348 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OffStoreInstallAllowed
,
349 NumOffStoreInstallDecision
);
352 const char* kHistogramName
= "Extensions.OffStoreInstallDecisionHard";
353 if (is_gallery_install()) {
354 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OnStoreInstall
,
355 NumOffStoreInstallDecision
);
356 } else if (off_store_install_allow_reason_
!= OffStoreInstallDisallowed
) {
357 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OffStoreInstallAllowed
,
358 NumOffStoreInstallDecision
);
359 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
360 off_store_install_allow_reason_
,
361 NumOffStoreInstallAllowReasons
);
363 UMA_HISTOGRAM_ENUMERATION(kHistogramName
, OffStoreInstallDisallowed
,
364 NumOffStoreInstallDecision
);
365 // Don't delete source in this case so that the user can install
366 // manually if they want.
367 delete_source_
= false;
368 did_handle_successfully_
= false;
370 return CrxInstallerError(
371 CrxInstallerError::ERROR_OFF_STORE
,
372 l10n_util::GetStringUTF16(
373 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE
));
378 if (installer_
.extension()->is_app()) {
379 // If the app was downloaded, apps_require_extension_mime_type_
380 // will be set. In this case, check that it was served with the
381 // right mime type. Make an exception for file URLs, which come
382 // from the users computer and have no headers.
383 if (!download_url_
.SchemeIsFile() &&
384 apps_require_extension_mime_type_
&&
385 original_mime_type_
!= Extension::kMimeType
) {
386 return CrxInstallerError(
387 l10n_util::GetStringFUTF16(
388 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE
,
389 base::ASCIIToUTF16(Extension::kMimeType
)));
392 // If the client_ is NULL, then the app is either being installed via
393 // an internal mechanism like sync, external_extensions, or default apps.
394 // In that case, we don't want to enforce things like the install origin.
395 if (!is_gallery_install() && client_
) {
396 // For apps with a gallery update URL, require that they be installed
398 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
399 if (ManifestURL::UpdatesFromGallery(extension
)) {
400 return CrxInstallerError(
401 l10n_util::GetStringFUTF16(
402 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS
,
403 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
)));
406 // For self-hosted apps, verify that the entire extent is on the same
407 // host (or a subdomain of the host) the download happened from. There's
408 // no way for us to verify that the app controls any other hosts.
409 URLPattern
pattern(UserScript::ValidUserScriptSchemes());
410 pattern
.SetHost(download_url_
.host());
411 pattern
.SetMatchSubdomains(true);
413 URLPatternSet patterns
= installer_
.extension()->web_extent();
414 for (URLPatternSet::const_iterator i
= patterns
.begin();
415 i
!= patterns
.end(); ++i
) {
416 if (!pattern
.MatchesHost(i
->host())) {
417 return CrxInstallerError(
418 l10n_util::GetStringUTF16(
419 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST
));
425 return CrxInstallerError();
428 void CrxInstaller::OnUnpackFailure(const base::string16
& error_message
) {
429 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
431 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
432 install_source(), Manifest::NUM_LOCATIONS
);
434 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
436 extension_misc::NUM_INSTALL_CAUSES
);
438 ReportFailureFromFileThread(CrxInstallerError(error_message
));
441 void CrxInstaller::OnUnpackSuccess(
442 const base::FilePath
& temp_dir
,
443 const base::FilePath
& extension_dir
,
444 const base::DictionaryValue
* original_manifest
,
445 const Extension
* extension
,
446 const SkBitmap
& install_icon
) {
447 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
449 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
450 install_source(), Manifest::NUM_LOCATIONS
);
453 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
455 extension_misc::NUM_INSTALL_CAUSES
);
457 installer_
.set_extension(extension
);
458 temp_dir_
= temp_dir
;
459 if (!install_icon
.empty())
460 install_icon_
.reset(new SkBitmap(install_icon
));
462 if (original_manifest
)
463 original_manifest_
.reset(new Manifest(
464 Manifest::INVALID_LOCATION
,
465 scoped_ptr
<base::DictionaryValue
>(original_manifest
->DeepCopy())));
467 // We don't have to delete the unpack dir explicity since it is a child of
469 unpacked_extension_root_
= extension_dir
;
471 CrxInstallerError error
= AllowInstall(extension
);
472 if (error
.type() != CrxInstallerError::ERROR_NONE
) {
473 ReportFailureFromFileThread(error
);
477 if (!BrowserThread::PostTask(
478 BrowserThread::UI
, FROM_HERE
,
479 base::Bind(&CrxInstaller::CheckImportsAndRequirements
, this)))
483 void CrxInstaller::CheckImportsAndRequirements() {
484 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
485 ExtensionService
* service
= service_weak_
.get();
486 if (!service
|| service
->browser_terminating())
489 if (SharedModuleInfo::ImportsModules(extension())) {
490 const std::vector
<SharedModuleInfo::ImportInfo
>& imports
=
491 SharedModuleInfo::GetImports(extension());
492 std::vector
<SharedModuleInfo::ImportInfo
>::const_iterator i
;
493 for (i
= imports
.begin(); i
!= imports
.end(); ++i
) {
494 const Extension
* imported_module
=
495 service
->GetExtensionById(i
->extension_id
, true);
496 if (imported_module
&&
497 !SharedModuleInfo::IsSharedModule(imported_module
)) {
498 ReportFailureFromUIThread(
499 CrxInstallerError(l10n_util::GetStringFUTF16(
500 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE
,
501 base::ASCIIToUTF16(i
->extension_id
))));
503 } else if (imported_module
&&
504 !SharedModuleInfo::IsExportAllowedByWhitelist(imported_module
,
505 extension()->id())) {
506 ReportFailureFromUIThread(
507 CrxInstallerError(l10n_util::GetStringFUTF16(
508 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED
,
509 base::ASCIIToUTF16(i
->extension_id
))));
514 installer_
.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked
,
518 void CrxInstaller::OnRequirementsChecked(
519 std::vector
<std::string
> requirement_errors
) {
520 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
524 if (!requirement_errors
.empty()) {
525 if (error_on_unsupported_requirements_
) {
526 ReportFailureFromUIThread(CrxInstallerError(
527 base::UTF8ToUTF16(JoinString(requirement_errors
, ' '))));
530 has_requirement_errors_
= true;
533 ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
535 base::Bind(&CrxInstaller::OnBlacklistChecked
, this));
538 void CrxInstaller::OnBlacklistChecked(
539 extensions::BlacklistState blacklist_state
) {
540 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
544 blacklist_state_
= blacklist_state
;
546 if ((blacklist_state_
== extensions::BLACKLISTED_MALWARE
||
547 blacklist_state_
== extensions::BLACKLISTED_UNKNOWN
) &&
548 !allow_silent_install_
) {
549 // User tried to install a blacklisted extension. Show an error and
550 // refuse to install it.
551 ReportFailureFromUIThread(extensions::CrxInstallerError(
552 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED
,
553 base::UTF8ToUTF16(extension()->name()))));
554 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
555 extension()->location(),
556 Manifest::NUM_LOCATIONS
);
560 // NOTE: extension may still be blacklisted, but we're forced to silently
561 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
566 void CrxInstaller::ConfirmInstall() {
567 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
568 ExtensionService
* service
= service_weak_
.get();
569 if (!service
|| service
->browser_terminating())
572 if (KioskModeInfo::IsKioskOnly(installer_
.extension())) {
573 bool in_kiosk_mode
= false;
574 #if defined(OS_CHROMEOS)
575 chromeos::UserManager
* user_manager
= chromeos::UserManager::Get();
576 in_kiosk_mode
= user_manager
&& user_manager
->IsLoggedInAsKioskApp();
578 if (!in_kiosk_mode
) {
579 ReportFailureFromUIThread(CrxInstallerError(
580 l10n_util::GetStringUTF16(
581 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY
)));
586 base::string16 error
= installer_
.CheckManagementPolicy();
587 if (!error
.empty()) {
588 // We don't want to show the error infobar for installs from the WebStore,
589 // because the WebStore already shows an error dialog itself.
590 // Note: |client_| can be NULL in unit_tests!
591 if (extension()->from_webstore() && client_
)
592 client_
->install_ui()->set_skip_post_install_ui(true);
593 ReportFailureFromUIThread(CrxInstallerError(error
));
597 // Check whether this install is initiated from the settings page to
598 // update an existing extension or app.
599 CheckUpdateFromSettingsPage();
601 GURL overlapping_url
;
602 const Extension
* overlapping_extension
=
603 service
->extensions()->GetHostedAppByOverlappingWebExtent(
604 extension()->web_extent());
605 if (overlapping_extension
&&
606 overlapping_extension
->id() != extension()->id()) {
607 ReportFailureFromUIThread(
609 l10n_util::GetStringFUTF16(
610 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT
,
611 base::UTF8ToUTF16(overlapping_extension
->name()))));
615 current_version_
= ExtensionPrefs::Get(service
->profile())
616 ->GetVersionString(extension()->id());
619 (!allow_silent_install_
|| !approved_
) &&
620 !update_from_settings_page_
) {
621 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
622 client_
->ConfirmInstall(this, extension(), show_dialog_callback_
);
624 if (!installer_task_runner_
->PostTask(
626 base::Bind(&CrxInstaller::CompleteInstall
, this)))
632 void CrxInstaller::InstallUIProceed() {
633 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
635 ExtensionService
* service
= service_weak_
.get();
636 if (!service
|| service
->browser_terminating())
639 // If update_from_settings_page_ boolean is true, this functions is
640 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
641 // and if it is false, this function is called in response to
642 // ExtensionInstallPrompt::ConfirmInstall().
643 if (update_from_settings_page_
) {
644 service
->GrantPermissionsAndEnableExtension(extension());
646 if (!installer_task_runner_
->PostTask(
648 base::Bind(&CrxInstaller::CompleteInstall
, this)))
652 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
655 void CrxInstaller::InstallUIAbort(bool user_initiated
) {
656 // If update_from_settings_page_ boolean is true, this functions is
657 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
658 // and if it is false, this function is called in response to
659 // ExtensionInstallPrompt::ConfirmInstall().
660 if (!update_from_settings_page_
) {
661 std::string histogram_name
= user_initiated
?
662 "Extensions.Permissions_InstallCancel" :
663 "Extensions.Permissions_InstallAbort";
664 ExtensionService::RecordPermissionMessagesHistogram(
665 extension(), histogram_name
.c_str());
667 NotifyCrxInstallComplete(false);
670 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
672 // We're done. Since we don't post any more tasks to ourself, our ref count
673 // should go to zero and we die. The destructor will clean up the temp dir.
676 void CrxInstaller::CompleteInstall() {
677 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
679 if (!current_version_
.empty()) {
680 Version
current_version(current_version_
);
681 if (current_version
.CompareTo(*(extension()->version())) > 0) {
682 ReportFailureFromFileThread(
684 l10n_util::GetStringUTF16(extension()->is_app() ?
685 IDS_APP_CANT_DOWNGRADE_VERSION
:
686 IDS_EXTENSION_CANT_DOWNGRADE_VERSION
)));
691 // See how long extension install paths are. This is important on
692 // windows, because file operations may fail if the path to a file
693 // exceeds a small constant. See crbug.com/69693 .
694 UMA_HISTOGRAM_CUSTOM_COUNTS(
695 "Extensions.CrxInstallDirPathLength",
696 install_directory_
.value().length(), 0, 500, 100);
698 base::FilePath version_dir
=
699 file_util::InstallExtension(unpacked_extension_root_
,
701 extension()->VersionString(),
703 if (version_dir
.empty()) {
704 ReportFailureFromFileThread(
706 l10n_util::GetStringUTF16(
707 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED
)));
711 // This is lame, but we must reload the extension because absolute paths
712 // inside the content scripts are established inside InitFromValue() and we
713 // just moved the extension.
714 // TODO(aa): All paths to resources inside extensions should be created
715 // lazily and based on the Extension's root path at that moment.
716 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
717 // with base::string16
718 std::string extension_id
= extension()->id();
720 installer_
.set_extension(
721 file_util::LoadExtension(
724 extension()->creation_flags() | Extension::REQUIRE_KEY
,
728 ReportSuccessFromFileThread();
730 LOG(ERROR
) << error
<< " " << extension_id
<< " " << download_url_
;
731 ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error
)));
736 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError
& error
) {
737 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
738 if (!BrowserThread::PostTask(
739 BrowserThread::UI
, FROM_HERE
,
740 base::Bind(&CrxInstaller::ReportFailureFromUIThread
, this, error
))) {
745 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError
& error
) {
746 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
748 if (!service_weak_
.get() || service_weak_
->browser_terminating())
751 content::NotificationService
* service
=
752 content::NotificationService::current();
753 service
->Notify(chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR
,
754 content::Source
<CrxInstaller
>(this),
755 content::Details
<const base::string16
>(&error
.message()));
757 // This isn't really necessary, it is only used because unit tests expect to
758 // see errors get reported via this interface.
760 // TODO(aa): Need to go through unit tests and clean them up too, probably get
762 ExtensionErrorReporter::GetInstance()->ReportError(
767 client_
->OnInstallFailure(error
);
769 NotifyCrxInstallComplete(false);
771 // Delete temporary files.
775 void CrxInstaller::ReportSuccessFromFileThread() {
776 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
778 // Tracking number of extensions installed by users
779 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD
)
780 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
782 if (!BrowserThread::PostTask(
783 BrowserThread::UI
, FROM_HERE
,
784 base::Bind(&CrxInstaller::ReportSuccessFromUIThread
, this)))
787 // Delete temporary files.
791 void CrxInstaller::ReportSuccessFromUIThread() {
792 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
794 if (!service_weak_
.get() || service_weak_
->browser_terminating())
797 if (!update_from_settings_page_
) {
798 // If there is a client, tell the client about installation.
800 client_
->OnInstallSuccess(extension(), install_icon_
.get());
802 // We update the extension's granted permissions if the user already
803 // approved the install (client_ is non NULL), or we are allowed to install
805 if (client_
|| allow_silent_install_
) {
806 PermissionsUpdater
perms_updater(profile());
807 perms_updater
.GrantActivePermissions(extension());
811 service_weak_
->OnExtensionInstalled(extension(),
813 has_requirement_errors_
,
815 install_wait_for_idle_
);
816 NotifyCrxInstallComplete(true);
819 void CrxInstaller::NotifyCrxInstallBegin() {
820 InstallTrackerFactory::GetForProfile(profile())
821 ->OnBeginCrxInstall(expected_id_
);
824 void CrxInstaller::NotifyCrxInstallComplete(bool success
) {
825 // Some users (such as the download shelf) need to know when a
826 // CRXInstaller is done. Listening for the EXTENSION_* events
827 // is problematic because they don't know anything about the
828 // extension before it is unpacked, so they cannot filter based
830 content::NotificationService::current()->Notify(
831 chrome::NOTIFICATION_CRX_INSTALLER_DONE
,
832 content::Source
<CrxInstaller
>(this),
833 content::Details
<const Extension
>(
834 success
? extension() : NULL
));
836 InstallTrackerFactory::GetForProfile(profile())
837 ->OnFinishCrxInstall(success
? extension()->id() : expected_id_
, success
);
843 void CrxInstaller::CleanupTempFiles() {
844 if (!installer_task_runner_
->RunsTasksOnCurrentThread()) {
845 if (!installer_task_runner_
->PostTask(
847 base::Bind(&CrxInstaller::CleanupTempFiles
, this))) {
853 // Delete the temp directory and crx file as necessary.
854 if (!temp_dir_
.value().empty()) {
855 file_util::DeleteFile(temp_dir_
, true);
856 temp_dir_
= base::FilePath();
859 if (delete_source_
&& !source_file_
.value().empty()) {
860 file_util::DeleteFile(source_file_
, false);
861 source_file_
= base::FilePath();
865 void CrxInstaller::CheckUpdateFromSettingsPage() {
866 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
868 ExtensionService
* service
= service_weak_
.get();
869 if (!service
|| service
->browser_terminating())
872 if (off_store_install_allow_reason_
!= OffStoreInstallAllowedFromSettingsPage
)
875 const Extension
* installed_extension
=
876 service
->GetInstalledExtension(extension()->id());
877 if (installed_extension
) {
878 // Previous version of the extension exists.
879 update_from_settings_page_
= true;
880 expected_id_
= installed_extension
->id();
881 install_source_
= installed_extension
->location();
882 install_cause_
= extension_misc::INSTALL_CAUSE_UPDATE
;
886 void CrxInstaller::ConfirmReEnable() {
887 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
889 ExtensionService
* service
= service_weak_
.get();
890 if (!service
|| service
->browser_terminating())
893 if (!update_from_settings_page_
)
896 ExtensionPrefs
* prefs
= ExtensionPrefs::Get(service
->profile());
897 if (!prefs
->DidExtensionEscalatePermissions(extension()->id()))
901 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
902 client_
->ConfirmReEnable(this, extension());
906 } // namespace extensions