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_system.h"
43 #include "extensions/browser/install/crx_installer_error.h"
44 #include "extensions/browser/install/extension_install_ui.h"
45 #include "extensions/browser/install_flag.h"
46 #include "extensions/browser/notification_types.h"
47 #include "extensions/common/extension_icon_set.h"
48 #include "extensions/common/feature_switch.h"
49 #include "extensions/common/file_util.h"
50 #include "extensions/common/manifest.h"
51 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
52 #include "extensions/common/manifest_handlers/shared_module_info.h"
53 #include "extensions/common/manifest_url_handlers.h"
54 #include "extensions/common/permissions/permission_message_provider.h"
55 #include "extensions/common/permissions/permission_set.h"
56 #include "extensions/common/permissions/permissions_data.h"
57 #include "extensions/common/user_script.h"
58 #include "grit/extensions_strings.h"
59 #include "third_party/skia/include/core/SkBitmap.h"
60 #include "ui/base/l10n/l10n_util.h"
62 #if defined(OS_CHROMEOS)
63 #include "components/user_manager/user_manager.h"
66 using base::UserMetricsAction;
67 using content::BrowserThread;
68 using extensions::SharedModuleInfo;
70 namespace extensions {
74 // Used in histograms; do not change order.
75 enum OffStoreInstallDecision {
77 OffStoreInstallAllowed,
78 OffStoreInstallDisallowed,
79 NumOffStoreInstallDecision
85 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
86 ExtensionService* frontend) {
87 return new CrxInstaller(frontend->AsWeakPtr(),
88 scoped_ptr<ExtensionInstallPrompt>(),
93 scoped_refptr<CrxInstaller> CrxInstaller::Create(
94 ExtensionService* frontend,
95 scoped_ptr<ExtensionInstallPrompt> client) {
96 return new CrxInstaller(frontend->AsWeakPtr(), client.Pass(), NULL);
100 scoped_refptr<CrxInstaller> CrxInstaller::Create(
101 ExtensionService* service,
102 scoped_ptr<ExtensionInstallPrompt> client,
103 const WebstoreInstaller::Approval* approval) {
104 return new CrxInstaller(service->AsWeakPtr(), client.Pass(), approval);
107 CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
108 scoped_ptr<ExtensionInstallPrompt> client,
109 const WebstoreInstaller::Approval* approval)
110 : install_directory_(service_weak->install_directory()),
111 install_source_(Manifest::INTERNAL),
113 expected_manifest_check_level_(
114 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
115 expected_version_strict_checking_(false),
116 extensions_enabled_(service_weak->extensions_enabled()),
117 delete_source_(false),
118 create_app_shortcut_(false),
119 service_weak_(service_weak),
120 // See header file comment on |client_| for why we use a raw pointer here.
121 client_(client.release()),
122 apps_require_extension_mime_type_(false),
123 allow_silent_install_(false),
124 grant_permissions_(true),
125 install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
126 creation_flags_(Extension::NO_FLAGS),
127 off_store_install_allow_reason_(OffStoreInstallDisallowed),
128 did_handle_successfully_(true),
129 error_on_unsupported_requirements_(false),
130 update_from_settings_page_(false),
131 install_flags_(kInstallFlagNone),
132 install_checker_(service_weak->profile()) {
133 installer_task_runner_ = service_weak->GetFileTaskRunner();
137 CHECK(profile()->IsSameProfile(approval->profile));
139 client_->install_ui()->SetUseAppInstalledBubble(
140 approval->use_app_installed_bubble);
141 client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui);
144 if (approval->skip_install_dialog) {
145 // Mark the extension as approved, but save the expected manifest and ID
146 // so we can check that they match the CRX's.
148 expected_manifest_check_level_ = approval->manifest_check_level;
149 if (expected_manifest_check_level_ !=
150 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE)
151 expected_manifest_.reset(approval->manifest->DeepCopy());
152 expected_id_ = approval->extension_id;
154 if (approval->minimum_version.get()) {
155 expected_version_.reset(new Version(*approval->minimum_version));
156 expected_version_strict_checking_ = false;
159 show_dialog_callback_ = approval->show_dialog_callback;
160 set_is_ephemeral(approval->is_ephemeral);
163 CrxInstaller::~CrxInstaller() {
164 // Make sure the UI is deleted on the ui thread.
166 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
171 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
172 ExtensionService* service = service_weak_.get();
173 if (!service || service->browser_terminating())
176 NotifyCrxInstallBegin();
178 source_file_ = source_file;
180 scoped_refptr<SandboxedUnpacker> unpacker(
181 new SandboxedUnpacker(source_file,
185 installer_task_runner_.get(),
188 if (!installer_task_runner_->PostTask(
190 base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
194 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
195 const GURL& download_url) {
196 DCHECK(!download_url.is_empty());
198 NotifyCrxInstallBegin();
200 source_file_ = source_file;
201 download_url_ = download_url;
203 if (!installer_task_runner_->PostTask(
205 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
209 void CrxInstaller::ConvertUserScriptOnFileThread() {
210 base::string16 error;
211 scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
212 source_file_, download_url_, install_directory_, &error);
213 if (!extension.get()) {
214 ReportFailureFromFileThread(CrxInstallerError(error));
218 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
222 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
223 NotifyCrxInstallBegin();
225 if (!installer_task_runner_->PostTask(
227 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread, this, web_app)))
231 void CrxInstaller::ConvertWebAppOnFileThread(
232 const WebApplicationInfo& web_app) {
233 scoped_refptr<Extension> extension(ConvertWebAppToExtension(
234 web_app, base::Time::Now(), install_directory_));
235 if (!extension.get()) {
236 // Validation should have stopped any potential errors before getting here.
237 NOTREACHED() << "Could not convert web app to extension.";
241 // TODO(aa): conversion data gets lost here :(
243 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
247 CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) {
248 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
250 // Make sure the expected ID matches if one was supplied or if we want to
251 // bypass the prompt.
252 if ((approved_ || !expected_id_.empty()) &&
253 expected_id_ != extension->id()) {
254 return CrxInstallerError(
255 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID,
256 base::ASCIIToUTF16(expected_id_),
257 base::ASCIIToUTF16(extension->id())));
260 if (expected_version_.get()) {
261 if (expected_version_strict_checking_) {
262 if (!expected_version_->Equals(*extension->version())) {
263 return CrxInstallerError(
264 l10n_util::GetStringFUTF16(
265 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
266 base::ASCIIToUTF16(expected_version_->GetString()),
267 base::ASCIIToUTF16(extension->version()->GetString())));
270 if (extension->version()->CompareTo(*expected_version_) < 0) {
271 return CrxInstallerError(
272 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(),
303 scoped_refptr<const PermissionSet> expected_permissions =
304 dummy_extension->permissions_data()->active_permissions();
305 valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
306 expected_permissions.get(),
307 extension->permissions_data()->active_permissions().get(),
308 extension->GetType()));
314 return CrxInstallerError(
315 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
318 // The checks below are skipped for themes and external installs.
319 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
320 // and other uses of install_source_ that are no longer needed now that the
321 // SandboxedUnpacker sets extension->location.
322 if (extension->is_theme() || Manifest::IsExternalLocation(install_source_))
323 return CrxInstallerError();
325 if (!extensions_enabled_) {
326 return CrxInstallerError(
327 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED));
330 if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) {
331 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
332 const char kHistogramName[] = "Extensions.OffStoreInstallDecisionEasy";
333 if (is_gallery_install()) {
334 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
335 NumOffStoreInstallDecision);
337 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
338 NumOffStoreInstallDecision);
341 const char kHistogramName[] = "Extensions.OffStoreInstallDecisionHard";
342 if (is_gallery_install()) {
343 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
344 NumOffStoreInstallDecision);
345 } else if (off_store_install_allow_reason_ != OffStoreInstallDisallowed) {
346 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
347 NumOffStoreInstallDecision);
348 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
349 off_store_install_allow_reason_,
350 NumOffStoreInstallAllowReasons);
352 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallDisallowed,
353 NumOffStoreInstallDecision);
354 // Don't delete source in this case so that the user can install
355 // manually if they want.
356 delete_source_ = false;
357 did_handle_successfully_ = false;
359 return CrxInstallerError(
360 CrxInstallerError::ERROR_OFF_STORE,
361 l10n_util::GetStringUTF16(
362 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE));
367 if (install_checker_.extension()->is_app()) {
368 // If the app was downloaded, apps_require_extension_mime_type_
369 // will be set. In this case, check that it was served with the
370 // right mime type. Make an exception for file URLs, which come
371 // from the users computer and have no headers.
372 if (!download_url_.SchemeIsFile() &&
373 apps_require_extension_mime_type_ &&
374 original_mime_type_ != Extension::kMimeType) {
375 return CrxInstallerError(
376 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 CrxInstallerError(
390 l10n_util::GetStringFUTF16(
391 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS,
392 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
395 // For self-hosted apps, verify that the entire extent is on the same
396 // host (or a subdomain of the host) the download happened from. There's
397 // no way for us to verify that the app controls any other hosts.
398 URLPattern pattern(UserScript::ValidUserScriptSchemes());
399 pattern.SetHost(download_url_.host());
400 pattern.SetMatchSubdomains(true);
402 URLPatternSet patterns = install_checker_.extension()->web_extent();
403 for (URLPatternSet::const_iterator i = patterns.begin();
404 i != patterns.end(); ++i) {
405 if (!pattern.MatchesHost(i->host())) {
406 return CrxInstallerError(
407 l10n_util::GetStringUTF16(
408 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST));
414 return CrxInstallerError();
417 void CrxInstaller::OnUnpackFailure(const base::string16& error_message) {
418 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
420 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
421 install_source(), Manifest::NUM_LOCATIONS);
423 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
425 extension_misc::NUM_INSTALL_CAUSES);
427 ReportFailureFromFileThread(CrxInstallerError(error_message));
430 void CrxInstaller::OnUnpackSuccess(
431 const base::FilePath& temp_dir,
432 const base::FilePath& extension_dir,
433 const base::DictionaryValue* original_manifest,
434 const Extension* extension,
435 const SkBitmap& install_icon) {
436 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
438 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
439 install_source(), Manifest::NUM_LOCATIONS);
442 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
444 extension_misc::NUM_INSTALL_CAUSES);
446 install_checker_.set_extension(extension);
447 temp_dir_ = temp_dir;
448 if (!install_icon.empty())
449 install_icon_.reset(new SkBitmap(install_icon));
451 if (original_manifest)
452 original_manifest_.reset(new Manifest(
453 Manifest::INVALID_LOCATION,
454 scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy())));
456 // We don't have to delete the unpack dir explicity since it is a child of
458 unpacked_extension_root_ = extension_dir;
460 CrxInstallerError error = AllowInstall(extension);
461 if (error.type() != CrxInstallerError::ERROR_NONE) {
462 ReportFailureFromFileThread(error);
466 if (!BrowserThread::PostTask(BrowserThread::UI,
468 base::Bind(&CrxInstaller::CheckInstall, this)))
472 void CrxInstaller::CheckInstall() {
473 DCHECK_CURRENTLY_ON(BrowserThread::UI);
474 ExtensionService* service = service_weak_.get();
475 if (!service || service->browser_terminating())
478 // TODO(crbug.com/420147): Move this code to a utility class to avoid
479 // duplication of SharedModuleService::CheckImports code.
480 if (SharedModuleInfo::ImportsModules(extension())) {
481 const std::vector<SharedModuleInfo::ImportInfo>& imports =
482 SharedModuleInfo::GetImports(extension());
483 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
484 for (i = imports.begin(); i != imports.end(); ++i) {
485 Version version_required(i->minimum_version);
486 const Extension* imported_module =
487 service->GetExtensionById(i->extension_id, true);
488 if (imported_module &&
489 !SharedModuleInfo::IsSharedModule(imported_module)) {
490 ReportFailureFromUIThread(CrxInstallerError(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(CrxInstallerError(l10n_util::GetStringFUTF16(
498 IDS_EXTENSION_INSTALL_DEPENDENCY_OLD_VERSION,
499 base::UTF8ToUTF16(imported_module->name()),
500 base::ASCIIToUTF16(i->minimum_version),
501 base::ASCIIToUTF16(imported_module->version()->GetString()))));
503 } else if (imported_module &&
504 !SharedModuleInfo::IsExportAllowedByWhitelist(
505 imported_module, extension()->id())) {
506 ReportFailureFromUIThread(CrxInstallerError(l10n_util::GetStringFUTF16(
507 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED,
508 base::UTF8ToUTF16(extension()->name()),
509 base::UTF8ToUTF16(imported_module->name()))));
515 // Run the policy, requirements and blacklist checks in parallel.
516 install_checker_.Start(
517 ExtensionInstallChecker::CHECK_ALL,
518 false /* fail fast */,
519 base::Bind(&CrxInstaller::OnInstallChecksComplete, this));
522 void CrxInstaller::OnInstallChecksComplete(int failed_checks) {
523 DCHECK_CURRENTLY_ON(BrowserThread::UI);
527 // Check for requirement errors.
528 if (!install_checker_.requirement_errors().empty()) {
529 if (error_on_unsupported_requirements_) {
530 ReportFailureFromUIThread(CrxInstallerError(base::UTF8ToUTF16(
531 JoinString(install_checker_.requirement_errors(), ' '))));
534 install_flags_ |= kInstallFlagHasRequirementErrors;
537 // Check the blacklist state.
538 if (install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE) {
539 install_flags_ |= kInstallFlagIsBlacklistedForMalware;
542 if ((install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE ||
543 install_checker_.blacklist_state() == extensions::BLACKLISTED_UNKNOWN) &&
544 !allow_silent_install_) {
545 // User tried to install a blacklisted extension. Show an error and
546 // refuse to install it.
547 ReportFailureFromUIThread(extensions::CrxInstallerError(
548 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
549 base::UTF8ToUTF16(extension()->name()))));
550 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
551 extension()->location(),
552 Manifest::NUM_LOCATIONS);
556 // NOTE: extension may still be blacklisted, but we're forced to silently
557 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
560 // Check for policy errors.
561 if (!install_checker_.policy_error().empty()) {
562 // We don't want to show the error infobar for installs from the WebStore,
563 // because the WebStore already shows an error dialog itself.
564 // Note: |client_| can be NULL in unit_tests!
565 if (extension()->from_webstore() && client_)
566 client_->install_ui()->SetSkipPostInstallUI(true);
567 ReportFailureFromUIThread(
568 CrxInstallerError(base::UTF8ToUTF16(install_checker_.policy_error())));
575 void CrxInstaller::ConfirmInstall() {
576 DCHECK_CURRENTLY_ON(BrowserThread::UI);
577 ExtensionService* service = service_weak_.get();
578 if (!service || service->browser_terminating())
581 if (KioskModeInfo::IsKioskOnly(install_checker_.extension().get())) {
582 bool in_kiosk_mode = false;
583 #if defined(OS_CHROMEOS)
584 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
585 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
587 if (!in_kiosk_mode) {
588 ReportFailureFromUIThread(CrxInstallerError(
589 l10n_util::GetStringUTF16(
590 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
595 // Check whether this install is initiated from the settings page to
596 // update an existing extension or app.
597 CheckUpdateFromSettingsPage();
599 GURL overlapping_url;
600 const Extension* overlapping_extension =
601 service->extensions()->GetHostedAppByOverlappingWebExtent(
602 extension()->web_extent());
603 if (overlapping_extension &&
604 overlapping_extension->id() != extension()->id()) {
605 ReportFailureFromUIThread(
607 l10n_util::GetStringFUTF16(
608 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
609 base::UTF8ToUTF16(overlapping_extension->name()))));
613 current_version_ = ExtensionPrefs::Get(service->profile())
614 ->GetVersionString(extension()->id());
617 (!allow_silent_install_ || !approved_) &&
618 !update_from_settings_page_) {
619 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
620 client_->ConfirmInstall(this, extension(), show_dialog_callback_);
622 if (!installer_task_runner_->PostTask(
624 base::Bind(&CrxInstaller::CompleteInstall, this)))
630 void CrxInstaller::InstallUIProceed() {
631 DCHECK_CURRENTLY_ON(BrowserThread::UI);
633 ExtensionService* service = service_weak_.get();
634 if (!service || service->browser_terminating())
637 // If update_from_settings_page_ boolean is true, this functions is
638 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
639 // and if it is false, this function is called in response to
640 // ExtensionInstallPrompt::ConfirmInstall().
641 if (update_from_settings_page_) {
642 service->GrantPermissionsAndEnableExtension(extension());
644 if (!installer_task_runner_->PostTask(
646 base::Bind(&CrxInstaller::CompleteInstall, this)))
650 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
653 void CrxInstaller::InstallUIAbort(bool user_initiated) {
654 // If update_from_settings_page_ boolean is true, this functions is
655 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
656 // and if it is false, this function is called in response to
657 // ExtensionInstallPrompt::ConfirmInstall().
658 if (!update_from_settings_page_) {
659 std::string histogram_name = user_initiated
660 ? "Extensions.Permissions_InstallCancel2"
661 : "Extensions.Permissions_InstallAbort2";
662 ExtensionService::RecordPermissionMessagesHistogram(
663 extension(), histogram_name.c_str());
665 NotifyCrxInstallComplete(false);
668 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
670 // We're done. Since we don't post any more tasks to ourself, our ref count
671 // should go to zero and we die. The destructor will clean up the temp dir.
674 void CrxInstaller::CompleteInstall() {
675 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
677 if (!current_version_.empty()) {
678 Version current_version(current_version_);
679 if (current_version.CompareTo(*(extension()->version())) > 0) {
680 ReportFailureFromFileThread(
682 l10n_util::GetStringUTF16(extension()->is_app() ?
683 IDS_APP_CANT_DOWNGRADE_VERSION :
684 IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
689 // See how long extension install paths are. This is important on
690 // windows, because file operations may fail if the path to a file
691 // exceeds a small constant. See crbug.com/69693 .
692 UMA_HISTOGRAM_CUSTOM_COUNTS(
693 "Extensions.CrxInstallDirPathLength",
694 install_directory_.value().length(), 0, 500, 100);
696 ExtensionAssetsManager* assets_manager =
697 ExtensionAssetsManager::GetInstance();
698 assets_manager->InstallExtension(
700 unpacked_extension_root_,
703 base::Bind(&CrxInstaller::ReloadExtensionAfterInstall, this));
706 void CrxInstaller::ReloadExtensionAfterInstall(
707 const base::FilePath& version_dir) {
708 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
710 if (version_dir.empty()) {
711 ReportFailureFromFileThread(
713 l10n_util::GetStringUTF16(
714 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
718 // This is lame, but we must reload the extension because absolute paths
719 // inside the content scripts are established inside InitFromValue() and we
720 // just moved the extension.
721 // TODO(aa): All paths to resources inside extensions should be created
722 // lazily and based on the Extension's root path at that moment.
723 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
724 // with base::string16
725 std::string extension_id = extension()->id();
727 install_checker_.set_extension(
728 file_util::LoadExtension(
731 extension()->creation_flags() | Extension::REQUIRE_KEY,
735 ReportSuccessFromFileThread();
737 LOG(ERROR) << error << " " << extension_id << " " << download_url_;
738 ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error)));
742 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
743 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
744 if (!BrowserThread::PostTask(
745 BrowserThread::UI, FROM_HERE,
746 base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
751 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
752 DCHECK_CURRENTLY_ON(BrowserThread::UI);
754 if (!service_weak_.get() || service_weak_->browser_terminating())
757 content::NotificationService* service =
758 content::NotificationService::current();
759 service->Notify(extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
760 content::Source<CrxInstaller>(this),
761 content::Details<const base::string16>(&error.message()));
763 // This isn't really necessary, it is only used because unit tests expect to
764 // see errors get reported via this interface.
766 // TODO(aa): Need to go through unit tests and clean them up too, probably get
768 ExtensionErrorReporter::GetInstance()->ReportError(
773 client_->OnInstallFailure(error);
775 NotifyCrxInstallComplete(false);
777 // Delete temporary files.
781 void CrxInstaller::ReportSuccessFromFileThread() {
782 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
784 // Tracking number of extensions installed by users
785 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
786 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
788 if (!BrowserThread::PostTask(
789 BrowserThread::UI, FROM_HERE,
790 base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this)))
793 // Delete temporary files.
797 void CrxInstaller::ReportSuccessFromUIThread() {
798 DCHECK_CURRENTLY_ON(BrowserThread::UI);
800 if (!service_weak_.get() || service_weak_->browser_terminating())
803 if (!update_from_settings_page_) {
804 // If there is a client, tell the client about installation.
806 client_->OnInstallSuccess(extension(), install_icon_.get());
808 // We update the extension's granted permissions if the user already
809 // approved the install (client_ is non NULL), or we are allowed to install
811 if ((client_ || allow_silent_install_) && grant_permissions_) {
812 PermissionsUpdater perms_updater(profile());
813 perms_updater.InitializePermissions(extension());
814 perms_updater.GrantActivePermissions(extension());
818 service_weak_->OnExtensionInstalled(
819 extension(), page_ordinal_, install_flags_);
820 NotifyCrxInstallComplete(true);
823 void CrxInstaller::NotifyCrxInstallBegin() {
824 InstallTrackerFactory::GetForBrowserContext(profile())
825 ->OnBeginCrxInstall(expected_id_);
828 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
829 // Some users (such as the download shelf) need to know when a
830 // CRXInstaller is done. Listening for the EXTENSION_* events
831 // is problematic because they don't know anything about the
832 // extension before it is unpacked, so they cannot filter based
834 content::NotificationService::current()->Notify(
835 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
836 content::Source<CrxInstaller>(this),
837 content::Details<const Extension>(success ? extension() : NULL));
839 InstallTrackerFactory::GetForBrowserContext(profile())
840 ->OnFinishCrxInstall(success ? extension()->id() : expected_id_, success);
846 void CrxInstaller::CleanupTempFiles() {
847 if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
848 if (!installer_task_runner_->PostTask(
850 base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
856 // Delete the temp directory and crx file as necessary.
857 if (!temp_dir_.value().empty()) {
858 file_util::DeleteFile(temp_dir_, true);
859 temp_dir_ = base::FilePath();
862 if (delete_source_ && !source_file_.value().empty()) {
863 file_util::DeleteFile(source_file_, false);
864 source_file_ = base::FilePath();
868 void CrxInstaller::CheckUpdateFromSettingsPage() {
869 DCHECK_CURRENTLY_ON(BrowserThread::UI);
871 ExtensionService* service = service_weak_.get();
872 if (!service || service->browser_terminating())
875 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
878 const Extension* installed_extension =
879 service->GetInstalledExtension(extension()->id());
880 if (installed_extension) {
881 // Previous version of the extension exists.
882 update_from_settings_page_ = true;
883 expected_id_ = installed_extension->id();
884 install_source_ = installed_extension->location();
885 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
889 void CrxInstaller::ConfirmReEnable() {
890 DCHECK_CURRENTLY_ON(BrowserThread::UI);
892 ExtensionService* service = service_weak_.get();
893 if (!service || service->browser_terminating())
896 if (!update_from_settings_page_)
899 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
900 if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
904 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
905 client_->ConfirmReEnable(this, extension());
909 } // namespace extensions