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_assets_manager.h"
29 #include "chrome/browser/extensions/extension_error_reporter.h"
30 #include "chrome/browser/extensions/extension_install_ui.h"
31 #include "chrome/browser/extensions/extension_service.h"
32 #include "chrome/browser/extensions/install_tracker.h"
33 #include "chrome/browser/extensions/install_tracker_factory.h"
34 #include "chrome/browser/extensions/permissions_updater.h"
35 #include "chrome/browser/extensions/webstore_installer.h"
36 #include "chrome/browser/profiles/profile.h"
37 #include "chrome/browser/web_applications/web_app.h"
38 #include "chrome/common/chrome_paths.h"
39 #include "chrome/common/extensions/extension_constants.h"
40 #include "chrome/common/extensions/manifest_url_handler.h"
41 #include "content/public/browser/browser_thread.h"
42 #include "content/public/browser/notification_service.h"
43 #include "content/public/browser/resource_dispatcher_host.h"
44 #include "content/public/browser/user_metrics.h"
45 #include "extensions/browser/extension_prefs.h"
46 #include "extensions/browser/extension_system.h"
47 #include "extensions/browser/install_flag.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/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/chromium_strings.h"
59 #include "grit/extensions_strings.h"
60 #include "grit/generated_resources.h"
61 #include "grit/theme_resources.h"
62 #include "third_party/skia/include/core/SkBitmap.h"
63 #include "ui/base/l10n/l10n_util.h"
64 #include "ui/base/resource/resource_bundle.h"
66 #if defined(OS_CHROMEOS)
67 #include "chrome/browser/chromeos/login/users/user_manager.h"
70 using base::UserMetricsAction;
71 using content::BrowserThread;
72 using extensions::SharedModuleInfo;
74 namespace extensions {
78 // Used in histograms; do not change order.
79 enum OffStoreInstallDecision {
81 OffStoreInstallAllowed,
82 OffStoreInstallDisallowed,
83 NumOffStoreInstallDecision
89 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
90 ExtensionService* frontend) {
91 return new CrxInstaller(frontend->AsWeakPtr(),
92 scoped_ptr<ExtensionInstallPrompt>(),
97 scoped_refptr<CrxInstaller> CrxInstaller::Create(
98 ExtensionService* frontend,
99 scoped_ptr<ExtensionInstallPrompt> client) {
100 return new CrxInstaller(frontend->AsWeakPtr(), client.Pass(), NULL);
104 scoped_refptr<CrxInstaller> CrxInstaller::Create(
105 ExtensionService* service,
106 scoped_ptr<ExtensionInstallPrompt> client,
107 const WebstoreInstaller::Approval* approval) {
108 return new CrxInstaller(service->AsWeakPtr(), client.Pass(), approval);
111 CrxInstaller::CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
112 scoped_ptr<ExtensionInstallPrompt> client,
113 const WebstoreInstaller::Approval* approval)
114 : install_directory_(service_weak->install_directory()),
115 install_source_(Manifest::INTERNAL),
117 expected_manifest_check_level_(
118 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
119 expected_version_strict_checking_(false),
120 extensions_enabled_(service_weak->extensions_enabled()),
121 delete_source_(false),
122 create_app_shortcut_(false),
123 service_weak_(service_weak),
124 // See header file comment on |client_| for why we use a raw pointer here.
125 client_(client.release()),
126 apps_require_extension_mime_type_(false),
127 allow_silent_install_(false),
128 grant_permissions_(true),
129 install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
130 creation_flags_(Extension::NO_FLAGS),
131 off_store_install_allow_reason_(OffStoreInstallDisallowed),
132 did_handle_successfully_(true),
133 error_on_unsupported_requirements_(false),
134 update_from_settings_page_(false),
135 install_flags_(kInstallFlagNone),
136 installer_(service_weak->profile()) {
137 installer_task_runner_ = service_weak->GetFileTaskRunner();
141 CHECK(profile()->IsSameProfile(approval->profile));
143 client_->install_ui()->SetUseAppInstalledBubble(
144 approval->use_app_installed_bubble);
145 client_->install_ui()->set_skip_post_install_ui(
146 approval->skip_post_install_ui);
149 if (approval->skip_install_dialog) {
150 // Mark the extension as approved, but save the expected manifest and ID
151 // so we can check that they match the CRX's.
153 expected_manifest_check_level_ = approval->manifest_check_level;
154 if (expected_manifest_check_level_ !=
155 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE)
156 expected_manifest_.reset(approval->manifest->DeepCopy());
157 expected_id_ = approval->extension_id;
159 if (approval->minimum_version.get()) {
160 expected_version_.reset(new Version(*approval->minimum_version));
161 expected_version_strict_checking_ = false;
164 show_dialog_callback_ = approval->show_dialog_callback;
165 set_is_ephemeral(approval->is_ephemeral);
168 CrxInstaller::~CrxInstaller() {
169 // Make sure the UI is deleted on the ui thread.
171 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
176 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
177 ExtensionService* service = service_weak_.get();
178 if (!service || service->browser_terminating())
181 NotifyCrxInstallBegin();
183 source_file_ = source_file;
185 scoped_refptr<SandboxedUnpacker> unpacker(
186 new SandboxedUnpacker(source_file,
190 installer_task_runner_.get(),
193 if (!installer_task_runner_->PostTask(
195 base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
199 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
200 const GURL& download_url) {
201 DCHECK(!download_url.is_empty());
203 NotifyCrxInstallBegin();
205 source_file_ = source_file;
206 download_url_ = download_url;
208 if (!installer_task_runner_->PostTask(
210 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
214 void CrxInstaller::ConvertUserScriptOnFileThread() {
215 base::string16 error;
216 scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
217 source_file_, download_url_, install_directory_, &error);
218 if (!extension.get()) {
219 ReportFailureFromFileThread(CrxInstallerError(error));
223 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
227 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
228 NotifyCrxInstallBegin();
230 if (!installer_task_runner_->PostTask(
232 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread, this, web_app)))
236 void CrxInstaller::ConvertWebAppOnFileThread(
237 const WebApplicationInfo& web_app) {
238 scoped_refptr<Extension> extension(ConvertWebAppToExtension(
239 web_app, base::Time::Now(), install_directory_));
240 if (!extension.get()) {
241 // Validation should have stopped any potential errors before getting here.
242 NOTREACHED() << "Could not convert web app to extension.";
246 // TODO(aa): conversion data gets lost here :(
248 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
252 CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) {
253 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
255 // Make sure the expected ID matches if one was supplied or if we want to
256 // bypass the prompt.
257 if ((approved_ || !expected_id_.empty()) &&
258 expected_id_ != extension->id()) {
259 return CrxInstallerError(
260 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID,
261 base::ASCIIToUTF16(expected_id_),
262 base::ASCIIToUTF16(extension->id())));
265 if (expected_version_.get()) {
266 if (expected_version_strict_checking_) {
267 if (!expected_version_->Equals(*extension->version())) {
268 return CrxInstallerError(
269 l10n_util::GetStringFUTF16(
270 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
271 base::ASCIIToUTF16(expected_version_->GetString()),
272 base::ASCIIToUTF16(extension->version()->GetString())));
275 if (extension->version()->CompareTo(*expected_version_) < 0) {
276 return CrxInstallerError(
277 l10n_util::GetStringFUTF16(
278 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
279 base::ASCIIToUTF16(expected_version_->GetString() + "+"),
280 base::ASCIIToUTF16(extension->version()->GetString())));
285 // Make sure the manifests match if we want to bypass the prompt.
288 if (expected_manifest_check_level_ ==
289 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) {
290 // To skip manifest checking, the extension must be a shared module
291 // and not request any permissions.
292 if (SharedModuleInfo::IsSharedModule(extension) &&
293 extension->permissions_data()->active_permissions()->IsEmpty()) {
297 valid = expected_manifest_->Equals(original_manifest_.get());
298 if (!valid && expected_manifest_check_level_ ==
299 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) {
301 scoped_refptr<Extension> dummy_extension =
302 Extension::Create(base::FilePath(),
304 *expected_manifest_->value(),
308 scoped_refptr<const PermissionSet> expected_permissions =
309 dummy_extension->permissions_data()->active_permissions();
310 valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
311 expected_permissions,
312 extension->permissions_data()->active_permissions(),
313 extension->GetType()));
319 return CrxInstallerError(
320 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
323 // The checks below are skipped for themes and external installs.
324 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
325 // and other uses of install_source_ that are no longer needed now that the
326 // SandboxedUnpacker sets extension->location.
327 if (extension->is_theme() || Manifest::IsExternalLocation(install_source_))
328 return CrxInstallerError();
330 if (!extensions_enabled_) {
331 return CrxInstallerError(
332 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED));
335 if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) {
336 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
337 const char* kHistogramName = "Extensions.OffStoreInstallDecisionEasy";
338 if (is_gallery_install()) {
339 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
340 NumOffStoreInstallDecision);
342 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
343 NumOffStoreInstallDecision);
346 const char* kHistogramName = "Extensions.OffStoreInstallDecisionHard";
347 if (is_gallery_install()) {
348 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
349 NumOffStoreInstallDecision);
350 } else if (off_store_install_allow_reason_ != OffStoreInstallDisallowed) {
351 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
352 NumOffStoreInstallDecision);
353 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
354 off_store_install_allow_reason_,
355 NumOffStoreInstallAllowReasons);
357 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallDisallowed,
358 NumOffStoreInstallDecision);
359 // Don't delete source in this case so that the user can install
360 // manually if they want.
361 delete_source_ = false;
362 did_handle_successfully_ = false;
364 return CrxInstallerError(
365 CrxInstallerError::ERROR_OFF_STORE,
366 l10n_util::GetStringUTF16(
367 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE));
372 if (installer_.extension()->is_app()) {
373 // If the app was downloaded, apps_require_extension_mime_type_
374 // will be set. In this case, check that it was served with the
375 // right mime type. Make an exception for file URLs, which come
376 // from the users computer and have no headers.
377 if (!download_url_.SchemeIsFile() &&
378 apps_require_extension_mime_type_ &&
379 original_mime_type_ != Extension::kMimeType) {
380 return CrxInstallerError(
381 l10n_util::GetStringFUTF16(
382 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE,
383 base::ASCIIToUTF16(Extension::kMimeType)));
386 // If the client_ is NULL, then the app is either being installed via
387 // an internal mechanism like sync, external_extensions, or default apps.
388 // In that case, we don't want to enforce things like the install origin.
389 if (!is_gallery_install() && client_) {
390 // For apps with a gallery update URL, require that they be installed
392 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
393 if (ManifestURL::UpdatesFromGallery(extension)) {
394 return CrxInstallerError(
395 l10n_util::GetStringFUTF16(
396 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS,
397 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
400 // For self-hosted apps, verify that the entire extent is on the same
401 // host (or a subdomain of the host) the download happened from. There's
402 // no way for us to verify that the app controls any other hosts.
403 URLPattern pattern(UserScript::ValidUserScriptSchemes());
404 pattern.SetHost(download_url_.host());
405 pattern.SetMatchSubdomains(true);
407 URLPatternSet patterns = installer_.extension()->web_extent();
408 for (URLPatternSet::const_iterator i = patterns.begin();
409 i != patterns.end(); ++i) {
410 if (!pattern.MatchesHost(i->host())) {
411 return CrxInstallerError(
412 l10n_util::GetStringUTF16(
413 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST));
419 return CrxInstallerError();
422 void CrxInstaller::OnUnpackFailure(const base::string16& error_message) {
423 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
425 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
426 install_source(), Manifest::NUM_LOCATIONS);
428 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
430 extension_misc::NUM_INSTALL_CAUSES);
432 ReportFailureFromFileThread(CrxInstallerError(error_message));
435 void CrxInstaller::OnUnpackSuccess(
436 const base::FilePath& temp_dir,
437 const base::FilePath& extension_dir,
438 const base::DictionaryValue* original_manifest,
439 const Extension* extension,
440 const SkBitmap& install_icon) {
441 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
443 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
444 install_source(), Manifest::NUM_LOCATIONS);
447 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
449 extension_misc::NUM_INSTALL_CAUSES);
451 installer_.set_extension(extension);
452 temp_dir_ = temp_dir;
453 if (!install_icon.empty())
454 install_icon_.reset(new SkBitmap(install_icon));
456 if (original_manifest)
457 original_manifest_.reset(new Manifest(
458 Manifest::INVALID_LOCATION,
459 scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy())));
461 // We don't have to delete the unpack dir explicity since it is a child of
463 unpacked_extension_root_ = extension_dir;
465 CrxInstallerError error = AllowInstall(extension);
466 if (error.type() != CrxInstallerError::ERROR_NONE) {
467 ReportFailureFromFileThread(error);
471 if (!BrowserThread::PostTask(
472 BrowserThread::UI, FROM_HERE,
473 base::Bind(&CrxInstaller::CheckImportsAndRequirements, this)))
477 void CrxInstaller::CheckImportsAndRequirements() {
478 DCHECK_CURRENTLY_ON(BrowserThread::UI);
479 ExtensionService* service = service_weak_.get();
480 if (!service || service->browser_terminating())
483 if (SharedModuleInfo::ImportsModules(extension())) {
484 const std::vector<SharedModuleInfo::ImportInfo>& imports =
485 SharedModuleInfo::GetImports(extension());
486 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
487 for (i = imports.begin(); i != imports.end(); ++i) {
488 const Extension* imported_module =
489 service->GetExtensionById(i->extension_id, true);
490 if (imported_module &&
491 !SharedModuleInfo::IsSharedModule(imported_module)) {
492 ReportFailureFromUIThread(
493 CrxInstallerError(l10n_util::GetStringFUTF16(
494 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE,
495 base::ASCIIToUTF16(i->extension_id))));
497 } else if (imported_module &&
498 !SharedModuleInfo::IsExportAllowedByWhitelist(imported_module,
499 extension()->id())) {
500 ReportFailureFromUIThread(
501 CrxInstallerError(l10n_util::GetStringFUTF16(
502 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED,
503 base::ASCIIToUTF16(i->extension_id))));
508 installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked,
512 void CrxInstaller::OnRequirementsChecked(
513 std::vector<std::string> requirement_errors) {
514 DCHECK_CURRENTLY_ON(BrowserThread::UI);
518 if (!requirement_errors.empty()) {
519 if (error_on_unsupported_requirements_) {
520 ReportFailureFromUIThread(CrxInstallerError(
521 base::UTF8ToUTF16(JoinString(requirement_errors, ' '))));
524 install_flags_ |= kInstallFlagHasRequirementErrors;
527 ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
529 base::Bind(&CrxInstaller::OnBlacklistChecked, this));
532 void CrxInstaller::OnBlacklistChecked(
533 extensions::BlacklistState blacklist_state) {
534 DCHECK_CURRENTLY_ON(BrowserThread::UI);
538 if (blacklist_state == extensions::BLACKLISTED_MALWARE) {
539 install_flags_ |= kInstallFlagIsBlacklistedForMalware;
542 if ((blacklist_state == extensions::BLACKLISTED_MALWARE ||
543 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
562 void CrxInstaller::ConfirmInstall() {
563 DCHECK_CURRENTLY_ON(BrowserThread::UI);
564 ExtensionService* service = service_weak_.get();
565 if (!service || service->browser_terminating())
568 if (KioskModeInfo::IsKioskOnly(installer_.extension())) {
569 bool in_kiosk_mode = false;
570 #if defined(OS_CHROMEOS)
571 chromeos::UserManager* user_manager = chromeos::UserManager::Get();
572 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
574 if (!in_kiosk_mode) {
575 ReportFailureFromUIThread(CrxInstallerError(
576 l10n_util::GetStringUTF16(
577 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
582 base::string16 error = installer_.CheckManagementPolicy();
583 if (!error.empty()) {
584 // We don't want to show the error infobar for installs from the WebStore,
585 // because the WebStore already shows an error dialog itself.
586 // Note: |client_| can be NULL in unit_tests!
587 if (extension()->from_webstore() && client_)
588 client_->install_ui()->set_skip_post_install_ui(true);
589 ReportFailureFromUIThread(CrxInstallerError(error));
593 // Check whether this install is initiated from the settings page to
594 // update an existing extension or app.
595 CheckUpdateFromSettingsPage();
597 GURL overlapping_url;
598 const Extension* overlapping_extension =
599 service->extensions()->GetHostedAppByOverlappingWebExtent(
600 extension()->web_extent());
601 if (overlapping_extension &&
602 overlapping_extension->id() != extension()->id()) {
603 ReportFailureFromUIThread(
605 l10n_util::GetStringFUTF16(
606 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
607 base::UTF8ToUTF16(overlapping_extension->name()))));
611 current_version_ = ExtensionPrefs::Get(service->profile())
612 ->GetVersionString(extension()->id());
615 (!allow_silent_install_ || !approved_) &&
616 !update_from_settings_page_) {
617 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
618 client_->ConfirmInstall(this, extension(), show_dialog_callback_);
620 if (!installer_task_runner_->PostTask(
622 base::Bind(&CrxInstaller::CompleteInstall, this)))
628 void CrxInstaller::InstallUIProceed() {
629 DCHECK_CURRENTLY_ON(BrowserThread::UI);
631 ExtensionService* service = service_weak_.get();
632 if (!service || service->browser_terminating())
635 // If update_from_settings_page_ boolean is true, this functions is
636 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
637 // and if it is false, this function is called in response to
638 // ExtensionInstallPrompt::ConfirmInstall().
639 if (update_from_settings_page_) {
640 service->GrantPermissionsAndEnableExtension(extension());
642 if (!installer_task_runner_->PostTask(
644 base::Bind(&CrxInstaller::CompleteInstall, this)))
648 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
651 void CrxInstaller::InstallUIAbort(bool user_initiated) {
652 // If update_from_settings_page_ boolean is true, this functions is
653 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
654 // and if it is false, this function is called in response to
655 // ExtensionInstallPrompt::ConfirmInstall().
656 if (!update_from_settings_page_) {
657 std::string histogram_name = user_initiated
658 ? "Extensions.Permissions_InstallCancel2"
659 : "Extensions.Permissions_InstallAbort2";
660 ExtensionService::RecordPermissionMessagesHistogram(
661 extension(), histogram_name.c_str());
663 NotifyCrxInstallComplete(false);
666 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
668 // We're done. Since we don't post any more tasks to ourself, our ref count
669 // should go to zero and we die. The destructor will clean up the temp dir.
672 void CrxInstaller::CompleteInstall() {
673 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
675 if (!current_version_.empty()) {
676 Version current_version(current_version_);
677 if (current_version.CompareTo(*(extension()->version())) > 0) {
678 ReportFailureFromFileThread(
680 l10n_util::GetStringUTF16(extension()->is_app() ?
681 IDS_APP_CANT_DOWNGRADE_VERSION :
682 IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
687 // See how long extension install paths are. This is important on
688 // windows, because file operations may fail if the path to a file
689 // exceeds a small constant. See crbug.com/69693 .
690 UMA_HISTOGRAM_CUSTOM_COUNTS(
691 "Extensions.CrxInstallDirPathLength",
692 install_directory_.value().length(), 0, 500, 100);
694 ExtensionAssetsManager* assets_manager =
695 ExtensionAssetsManager::GetInstance();
696 assets_manager->InstallExtension(
698 unpacked_extension_root_,
701 base::Bind(&CrxInstaller::ReloadExtensionAfterInstall, this));
704 void CrxInstaller::ReloadExtensionAfterInstall(
705 const base::FilePath& version_dir) {
706 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
708 if (version_dir.empty()) {
709 ReportFailureFromFileThread(
711 l10n_util::GetStringUTF16(
712 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
716 // This is lame, but we must reload the extension because absolute paths
717 // inside the content scripts are established inside InitFromValue() and we
718 // just moved the extension.
719 // TODO(aa): All paths to resources inside extensions should be created
720 // lazily and based on the Extension's root path at that moment.
721 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
722 // with base::string16
723 std::string extension_id = extension()->id();
725 installer_.set_extension(
726 file_util::LoadExtension(
729 extension()->creation_flags() | Extension::REQUIRE_KEY,
733 ReportSuccessFromFileThread();
735 LOG(ERROR) << error << " " << extension_id << " " << download_url_;
736 ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error)));
740 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
741 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
742 if (!BrowserThread::PostTask(
743 BrowserThread::UI, FROM_HERE,
744 base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
749 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
750 DCHECK_CURRENTLY_ON(BrowserThread::UI);
752 if (!service_weak_.get() || service_weak_->browser_terminating())
755 content::NotificationService* service =
756 content::NotificationService::current();
757 service->Notify(chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
758 content::Source<CrxInstaller>(this),
759 content::Details<const base::string16>(&error.message()));
761 // This isn't really necessary, it is only used because unit tests expect to
762 // see errors get reported via this interface.
764 // TODO(aa): Need to go through unit tests and clean them up too, probably get
766 ExtensionErrorReporter::GetInstance()->ReportError(
771 client_->OnInstallFailure(error);
773 NotifyCrxInstallComplete(false);
775 // Delete temporary files.
779 void CrxInstaller::ReportSuccessFromFileThread() {
780 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
782 // Tracking number of extensions installed by users
783 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
784 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
786 if (!BrowserThread::PostTask(
787 BrowserThread::UI, FROM_HERE,
788 base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this)))
791 // Delete temporary files.
795 void CrxInstaller::ReportSuccessFromUIThread() {
796 DCHECK_CURRENTLY_ON(BrowserThread::UI);
798 if (!service_weak_.get() || service_weak_->browser_terminating())
801 if (!update_from_settings_page_) {
802 // If there is a client, tell the client about installation.
804 client_->OnInstallSuccess(extension(), install_icon_.get());
806 // We update the extension's granted permissions if the user already
807 // approved the install (client_ is non NULL), or we are allowed to install
809 if ((client_ || allow_silent_install_) && grant_permissions_) {
810 PermissionsUpdater perms_updater(profile());
811 perms_updater.GrantActivePermissions(extension());
815 service_weak_->OnExtensionInstalled(
816 extension(), page_ordinal_, install_flags_);
817 NotifyCrxInstallComplete(true);
820 void CrxInstaller::NotifyCrxInstallBegin() {
821 InstallTrackerFactory::GetForProfile(profile())
822 ->OnBeginCrxInstall(expected_id_);
825 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
826 // Some users (such as the download shelf) need to know when a
827 // CRXInstaller is done. Listening for the EXTENSION_* events
828 // is problematic because they don't know anything about the
829 // extension before it is unpacked, so they cannot filter based
831 content::NotificationService::current()->Notify(
832 chrome::NOTIFICATION_CRX_INSTALLER_DONE,
833 content::Source<CrxInstaller>(this),
834 content::Details<const Extension>(
835 success ? extension() : NULL));
837 InstallTrackerFactory::GetForProfile(profile())
838 ->OnFinishCrxInstall(success ? extension()->id() : expected_id_, success);
844 void CrxInstaller::CleanupTempFiles() {
845 if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
846 if (!installer_task_runner_->PostTask(
848 base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
854 // Delete the temp directory and crx file as necessary.
855 if (!temp_dir_.value().empty()) {
856 file_util::DeleteFile(temp_dir_, true);
857 temp_dir_ = base::FilePath();
860 if (delete_source_ && !source_file_.value().empty()) {
861 file_util::DeleteFile(source_file_, false);
862 source_file_ = base::FilePath();
866 void CrxInstaller::CheckUpdateFromSettingsPage() {
867 DCHECK_CURRENTLY_ON(BrowserThread::UI);
869 ExtensionService* service = service_weak_.get();
870 if (!service || service->browser_terminating())
873 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
876 const Extension* installed_extension =
877 service->GetInstalledExtension(extension()->id());
878 if (installed_extension) {
879 // Previous version of the extension exists.
880 update_from_settings_page_ = true;
881 expected_id_ = installed_extension->id();
882 install_source_ = installed_extension->location();
883 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
887 void CrxInstaller::ConfirmReEnable() {
888 DCHECK_CURRENTLY_ON(BrowserThread::UI);
890 ExtensionService* service = service_weak_.get();
891 if (!service || service->browser_terminating())
894 if (!update_from_settings_page_)
897 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
898 if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
902 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
903 client_->ConfirmReEnable(this, extension());
907 } // namespace extensions