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/extension_system.h"
32 #include "chrome/browser/extensions/permissions_updater.h"
33 #include "chrome/browser/extensions/webstore_installer.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/web_applications/web_app.h"
36 #include "chrome/common/chrome_paths.h"
37 #include "chrome/common/extensions/extension_constants.h"
38 #include "chrome/common/extensions/extension_file_util.h"
39 #include "chrome/common/extensions/extension_icon_set.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/common/feature_switch.h"
46 #include "extensions/common/manifest.h"
47 #include "extensions/common/manifest_handlers/kiosk_mode_info.h"
48 #include "extensions/common/manifest_handlers/shared_module_info.h"
49 #include "extensions/common/permissions/permission_message_provider.h"
50 #include "extensions/common/permissions/permission_set.h"
51 #include "extensions/common/permissions/permissions_data.h"
52 #include "extensions/common/user_script.h"
53 #include "grit/chromium_strings.h"
54 #include "grit/generated_resources.h"
55 #include "grit/theme_resources.h"
56 #include "third_party/skia/include/core/SkBitmap.h"
57 #include "ui/base/l10n/l10n_util.h"
58 #include "ui/base/resource/resource_bundle.h"
60 #if defined(OS_CHROMEOS)
61 #include "chrome/browser/chromeos/login/user_manager.h"
64 using base::UserMetricsAction
;
65 using content::BrowserThread
;
66 using extensions::SharedModuleInfo
;
68 namespace extensions
{
72 // Used in histograms; do not change order.
73 enum OffStoreInstallDecision
{
75 OffStoreInstallAllowed
,
76 OffStoreInstallDisallowed
,
77 NumOffStoreInstallDecision
83 scoped_refptr
<CrxInstaller
> CrxInstaller::CreateSilent(
84 ExtensionService
* frontend
) {
85 return new CrxInstaller(frontend
->AsWeakPtr(),
86 scoped_ptr
<ExtensionInstallPrompt
>(),
91 scoped_refptr
<CrxInstaller
> CrxInstaller::Create(
92 ExtensionService
* frontend
,
93 scoped_ptr
<ExtensionInstallPrompt
> client
) {
94 return new CrxInstaller(frontend
->AsWeakPtr(), client
.Pass(), NULL
);
98 scoped_refptr
<CrxInstaller
> CrxInstaller::Create(
99 ExtensionService
* service
,
100 scoped_ptr
<ExtensionInstallPrompt
> client
,
101 const WebstoreInstaller::Approval
* approval
) {
102 return new CrxInstaller(service
->AsWeakPtr(), client
.Pass(), approval
);
105 CrxInstaller::CrxInstaller(
106 base::WeakPtr
<ExtensionService
> service_weak
,
107 scoped_ptr
<ExtensionInstallPrompt
> client
,
108 const WebstoreInstaller::Approval
* approval
)
109 : install_directory_(service_weak
->install_directory()),
110 install_source_(Manifest::INTERNAL
),
112 expected_manifest_check_level_(
113 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT
),
114 expected_version_strict_checking_(false),
115 extensions_enabled_(service_weak
->extensions_enabled()),
116 delete_source_(false),
117 create_app_shortcut_(false),
118 service_weak_(service_weak
),
119 // See header file comment on |client_| for why we use a raw pointer here.
120 client_(client
.release()),
121 apps_require_extension_mime_type_(false),
122 allow_silent_install_(false),
123 install_cause_(extension_misc::INSTALL_CAUSE_UNSET
),
124 creation_flags_(Extension::NO_FLAGS
),
125 off_store_install_allow_reason_(OffStoreInstallDisallowed
),
126 did_handle_successfully_(true),
127 error_on_unsupported_requirements_(false),
128 has_requirement_errors_(false),
129 blacklist_state_(extensions::NOT_BLACKLISTED
),
130 install_wait_for_idle_(true),
131 update_from_settings_page_(false),
132 installer_(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()->set_skip_post_install_ui(
142 approval
->skip_post_install_ui
);
145 if (approval
->skip_install_dialog
) {
146 // Mark the extension as approved, but save the expected manifest and ID
147 // so we can check that they match the CRX's.
149 expected_manifest_check_level_
= approval
->manifest_check_level
;
150 if (expected_manifest_check_level_
!=
151 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE
)
152 expected_manifest_
.reset(approval
->manifest
->DeepCopy());
153 expected_id_
= approval
->extension_id
;
155 if (approval
->minimum_version
.get()) {
156 expected_version_
.reset(new Version(*approval
->minimum_version
));
157 expected_version_strict_checking_
= false;
160 show_dialog_callback_
= approval
->show_dialog_callback
;
162 if (approval
->is_ephemeral
)
163 creation_flags_
|= Extension::IS_EPHEMERAL
;
166 CrxInstaller::~CrxInstaller() {
167 // Make sure the UI is deleted on the ui thread.
169 BrowserThread::DeleteSoon(BrowserThread::UI
, FROM_HERE
, client_
);
174 void CrxInstaller::InstallCrx(const base::FilePath
& source_file
) {
175 ExtensionService
* service
= service_weak_
.get();
176 if (!service
|| service
->browser_terminating())
179 source_file_
= source_file
;
181 scoped_refptr
<SandboxedUnpacker
> unpacker(
182 new SandboxedUnpacker(source_file
,
186 installer_task_runner_
.get(),
189 if (!installer_task_runner_
->PostTask(
191 base::Bind(&SandboxedUnpacker::Start
, unpacker
.get())))
195 void CrxInstaller::InstallUserScript(const base::FilePath
& source_file
,
196 const GURL
& download_url
) {
197 DCHECK(!download_url
.is_empty());
199 source_file_
= source_file
;
200 download_url_
= download_url
;
202 if (!installer_task_runner_
->PostTask(
204 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread
, this)))
208 void CrxInstaller::ConvertUserScriptOnFileThread() {
209 base::string16 error
;
210 scoped_refptr
<Extension
> extension
= ConvertUserScriptToExtension(
211 source_file_
, download_url_
, install_directory_
, &error
);
212 if (!extension
.get()) {
213 ReportFailureFromFileThread(CrxInstallerError(error
));
217 OnUnpackSuccess(extension
->path(), extension
->path(), NULL
, extension
.get(),
221 void CrxInstaller::InstallWebApp(const WebApplicationInfo
& web_app
) {
222 if (!installer_task_runner_
->PostTask(
224 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread
,
227 install_directory_
)))
231 void CrxInstaller::ConvertWebAppOnFileThread(
232 const WebApplicationInfo
& web_app
,
233 const base::FilePath
& install_directory
) {
234 base::string16 error
;
235 scoped_refptr
<Extension
> extension(
236 ConvertWebAppToExtension(web_app
, base::Time::Now(), install_directory
));
237 if (!extension
.get()) {
238 // Validation should have stopped any potential errors before getting here.
239 NOTREACHED() << "Could not convert web app to extension.";
243 // TODO(aa): conversion data gets lost here :(
245 OnUnpackSuccess(extension
->path(), extension
->path(), NULL
, extension
.get(),
249 CrxInstallerError
CrxInstaller::AllowInstall(const Extension
* extension
) {
250 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
252 // Make sure the expected ID matches if one was supplied or if we want to
253 // bypass the prompt.
254 if ((approved_
|| !expected_id_
.empty()) &&
255 expected_id_
!= extension
->id()) {
256 return CrxInstallerError(
257 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID
,
258 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 CrxInstallerError(
266 l10n_util::GetStringFUTF16(
267 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION
,
268 base::ASCIIToUTF16(expected_version_
->GetString()),
269 base::ASCIIToUTF16(extension
->version()->GetString())));
272 if (extension
->version()->CompareTo(*expected_version_
) < 0) {
273 return CrxInstallerError(
274 l10n_util::GetStringFUTF16(
275 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION
,
276 base::ASCIIToUTF16(expected_version_
->GetString() + "+"),
277 base::ASCIIToUTF16(extension
->version()->GetString())));
282 // Make sure the manifests match if we want to bypass the prompt.
285 if (expected_manifest_check_level_
==
286 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE
) {
287 // To skip manifest checking, the extension must be a shared module
288 // and not request any permissions.
289 if (SharedModuleInfo::IsSharedModule(extension
) &&
290 PermissionsData::GetActivePermissions(extension
)->IsEmpty()) {
294 valid
= expected_manifest_
->Equals(original_manifest_
.get());
295 if (!valid
&& expected_manifest_check_level_
==
296 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE
) {
298 scoped_refptr
<Extension
> dummy_extension
=
299 Extension::Create(base::FilePath(),
301 *expected_manifest_
->value(),
305 scoped_refptr
<const PermissionSet
> expected_permissions
=
306 PermissionsData::GetActivePermissions(dummy_extension
.get());
307 valid
= !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
308 expected_permissions
,
309 PermissionsData::GetActivePermissions(extension
),
310 extension
->GetType()));
316 return CrxInstallerError(
317 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID
));
320 // The checks below are skipped for themes and external installs.
321 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
322 // and other uses of install_source_ that are no longer needed now that the
323 // SandboxedUnpacker sets extension->location.
324 if (extension
->is_theme() || Manifest::IsExternalLocation(install_source_
))
325 return CrxInstallerError();
327 if (!extensions_enabled_
) {
328 return CrxInstallerError(
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 CrxInstallerError(
362 CrxInstallerError::ERROR_OFF_STORE
,
363 l10n_util::GetStringUTF16(
364 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE
));
369 if (installer_
.extension()->is_app()) {
370 // If the app was downloaded, apps_require_extension_mime_type_
371 // will be set. In this case, check that it was served with the
372 // right mime type. Make an exception for file URLs, which come
373 // from the users computer and have no headers.
374 if (!download_url_
.SchemeIsFile() &&
375 apps_require_extension_mime_type_
&&
376 original_mime_type_
!= Extension::kMimeType
) {
377 return CrxInstallerError(
378 l10n_util::GetStringFUTF16(
379 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE
,
380 base::ASCIIToUTF16(Extension::kMimeType
)));
383 // If the client_ is NULL, then the app is either being installed via
384 // an internal mechanism like sync, external_extensions, or default apps.
385 // In that case, we don't want to enforce things like the install origin.
386 if (!is_gallery_install() && client_
) {
387 // For apps with a gallery update URL, require that they be installed
389 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
390 if (ManifestURL::UpdatesFromGallery(extension
)) {
391 return CrxInstallerError(
392 l10n_util::GetStringFUTF16(
393 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS
,
394 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
)));
397 // For self-hosted apps, verify that the entire extent is on the same
398 // host (or a subdomain of the host) the download happened from. There's
399 // no way for us to verify that the app controls any other hosts.
400 URLPattern
pattern(UserScript::ValidUserScriptSchemes());
401 pattern
.SetHost(download_url_
.host());
402 pattern
.SetMatchSubdomains(true);
404 URLPatternSet patterns
= installer_
.extension()->web_extent();
405 for (URLPatternSet::const_iterator i
= patterns
.begin();
406 i
!= patterns
.end(); ++i
) {
407 if (!pattern
.MatchesHost(i
->host())) {
408 return CrxInstallerError(
409 l10n_util::GetStringUTF16(
410 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST
));
416 return CrxInstallerError();
419 void CrxInstaller::OnUnpackFailure(const base::string16
& error_message
) {
420 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
422 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
423 install_source(), Manifest::NUM_LOCATIONS
);
425 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
427 extension_misc::NUM_INSTALL_CAUSES
);
429 ReportFailureFromFileThread(CrxInstallerError(error_message
));
432 void CrxInstaller::OnUnpackSuccess(
433 const base::FilePath
& temp_dir
,
434 const base::FilePath
& extension_dir
,
435 const base::DictionaryValue
* original_manifest
,
436 const Extension
* extension
,
437 const SkBitmap
& install_icon
) {
438 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
440 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
441 install_source(), Manifest::NUM_LOCATIONS
);
444 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
446 extension_misc::NUM_INSTALL_CAUSES
);
448 installer_
.set_extension(extension
);
449 temp_dir_
= temp_dir
;
450 if (!install_icon
.empty())
451 install_icon_
.reset(new SkBitmap(install_icon
));
453 if (original_manifest
)
454 original_manifest_
.reset(new Manifest(
455 Manifest::INVALID_LOCATION
,
456 scoped_ptr
<base::DictionaryValue
>(original_manifest
->DeepCopy())));
458 // We don't have to delete the unpack dir explicity since it is a child of
460 unpacked_extension_root_
= extension_dir
;
462 CrxInstallerError error
= AllowInstall(extension
);
463 if (error
.type() != CrxInstallerError::ERROR_NONE
) {
464 ReportFailureFromFileThread(error
);
468 if (!BrowserThread::PostTask(
469 BrowserThread::UI
, FROM_HERE
,
470 base::Bind(&CrxInstaller::CheckImportsAndRequirements
, this)))
474 void CrxInstaller::CheckImportsAndRequirements() {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
476 ExtensionService
* service
= service_weak_
.get();
477 if (!service
|| service
->browser_terminating())
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(
491 CrxInstallerError(l10n_util::GetStringFUTF16(
492 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE
,
493 base::ASCIIToUTF16(i
->extension_id
))));
498 installer_
.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked
,
502 void CrxInstaller::OnRequirementsChecked(
503 std::vector
<std::string
> requirement_errors
) {
504 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
508 if (!requirement_errors
.empty()) {
509 if (error_on_unsupported_requirements_
) {
510 ReportFailureFromUIThread(CrxInstallerError(
511 base::UTF8ToUTF16(JoinString(requirement_errors
, ' '))));
514 has_requirement_errors_
= true;
517 ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
519 base::Bind(&CrxInstaller::OnBlacklistChecked
, this));
522 void CrxInstaller::OnBlacklistChecked(
523 extensions::BlacklistState blacklist_state
) {
524 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
528 blacklist_state_
= blacklist_state
;
530 if ((blacklist_state_
== extensions::BLACKLISTED_MALWARE
||
531 blacklist_state_
== extensions::BLACKLISTED_UNKNOWN
) &&
532 !allow_silent_install_
) {
533 // User tried to install a blacklisted extension. Show an error and
534 // refuse to install it.
535 ReportFailureFromUIThread(extensions::CrxInstallerError(
536 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED
,
537 base::UTF8ToUTF16(extension()->name()))));
538 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
539 extension()->location(),
540 Manifest::NUM_LOCATIONS
);
544 // NOTE: extension may still be blacklisted, but we're forced to silently
545 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
550 void CrxInstaller::ConfirmInstall() {
551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
552 ExtensionService
* service
= service_weak_
.get();
553 if (!service
|| service
->browser_terminating())
556 if (KioskModeInfo::IsKioskOnly(installer_
.extension())) {
557 bool in_kiosk_mode
= false;
558 #if defined(OS_CHROMEOS)
559 chromeos::UserManager
* user_manager
= chromeos::UserManager::Get();
560 in_kiosk_mode
= user_manager
&& user_manager
->IsLoggedInAsKioskApp();
562 if (!in_kiosk_mode
) {
563 ReportFailureFromUIThread(CrxInstallerError(
564 l10n_util::GetStringUTF16(
565 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY
)));
569 base::string16 error
= installer_
.CheckManagementPolicy();
570 if (!error
.empty()) {
571 // We don't want to show the error infobar for installs from the WebStore,
572 // because the WebStore already shows an error dialog itself.
573 // Note: |client_| can be NULL in unit_tests!
574 if (extension()->from_webstore() && client_
)
575 client_
->install_ui()->set_skip_post_install_ui(true);
576 ReportFailureFromUIThread(CrxInstallerError(error
));
580 // Check whether this install is initiated from the settings page to
581 // update an existing extension or app.
582 CheckUpdateFromSettingsPage();
584 GURL overlapping_url
;
585 const Extension
* overlapping_extension
=
586 service
->extensions()->GetHostedAppByOverlappingWebExtent(
587 extension()->web_extent());
588 if (overlapping_extension
&&
589 overlapping_extension
->id() != extension()->id()) {
590 ReportFailureFromUIThread(
592 l10n_util::GetStringFUTF16(
593 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT
,
594 base::UTF8ToUTF16(overlapping_extension
->name()))));
599 service
->extension_prefs()->GetVersionString(extension()->id());
602 (!allow_silent_install_
|| !approved_
) &&
603 !update_from_settings_page_
) {
604 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
605 client_
->ConfirmInstall(this, extension(), show_dialog_callback_
);
607 if (!installer_task_runner_
->PostTask(
609 base::Bind(&CrxInstaller::CompleteInstall
, this)))
615 void CrxInstaller::InstallUIProceed() {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
618 ExtensionService
* service
= service_weak_
.get();
619 if (!service
|| service
->browser_terminating())
622 // If update_from_settings_page_ boolean is true, this functions is
623 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
624 // and if it is false, this function is called in response to
625 // ExtensionInstallPrompt::ConfirmInstall().
626 if (update_from_settings_page_
) {
627 service
->GrantPermissionsAndEnableExtension(extension());
629 if (!installer_task_runner_
->PostTask(
631 base::Bind(&CrxInstaller::CompleteInstall
, this)))
635 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
638 void CrxInstaller::InstallUIAbort(bool user_initiated
) {
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 std::string histogram_name
= user_initiated
?
645 "Extensions.Permissions_InstallCancel" :
646 "Extensions.Permissions_InstallAbort";
647 ExtensionService::RecordPermissionMessagesHistogram(
648 extension(), histogram_name
.c_str());
650 NotifyCrxInstallComplete(false);
653 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
655 // We're done. Since we don't post any more tasks to ourself, our ref count
656 // should go to zero and we die. The destructor will clean up the temp dir.
659 void CrxInstaller::CompleteInstall() {
660 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
662 if (!current_version_
.empty()) {
663 Version
current_version(current_version_
);
664 if (current_version
.CompareTo(*(extension()->version())) > 0) {
665 ReportFailureFromFileThread(
667 l10n_util::GetStringUTF16(extension()->is_app() ?
668 IDS_APP_CANT_DOWNGRADE_VERSION
:
669 IDS_EXTENSION_CANT_DOWNGRADE_VERSION
)));
674 // See how long extension install paths are. This is important on
675 // windows, because file operations may fail if the path to a file
676 // exceeds a small constant. See crbug.com/69693 .
677 UMA_HISTOGRAM_CUSTOM_COUNTS(
678 "Extensions.CrxInstallDirPathLength",
679 install_directory_
.value().length(), 0, 500, 100);
681 base::FilePath version_dir
= extension_file_util::InstallExtension(
682 unpacked_extension_root_
,
684 extension()->VersionString(),
686 if (version_dir
.empty()) {
687 ReportFailureFromFileThread(
689 l10n_util::GetStringUTF16(
690 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED
)));
694 // This is lame, but we must reload the extension because absolute paths
695 // inside the content scripts are established inside InitFromValue() and we
696 // just moved the extension.
697 // TODO(aa): All paths to resources inside extensions should be created
698 // lazily and based on the Extension's root path at that moment.
699 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
700 // with base::string16
701 std::string extension_id
= extension()->id();
703 installer_
.set_extension(extension_file_util::LoadExtension(
706 extension()->creation_flags() | Extension::REQUIRE_KEY
,
710 ReportSuccessFromFileThread();
712 LOG(ERROR
) << error
<< " " << extension_id
<< " " << download_url_
;
713 ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error
)));
718 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError
& error
) {
719 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
720 if (!BrowserThread::PostTask(
721 BrowserThread::UI
, FROM_HERE
,
722 base::Bind(&CrxInstaller::ReportFailureFromUIThread
, this, error
))) {
727 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError
& error
) {
728 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
730 content::NotificationService
* service
=
731 content::NotificationService::current();
732 service
->Notify(chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR
,
733 content::Source
<CrxInstaller
>(this),
734 content::Details
<const base::string16
>(&error
.message()));
736 // This isn't really necessary, it is only used because unit tests expect to
737 // see errors get reported via this interface.
739 // TODO(aa): Need to go through unit tests and clean them up too, probably get
741 ExtensionErrorReporter::GetInstance()->ReportError(
742 error
.message(), false); // quiet
745 client_
->OnInstallFailure(error
);
747 NotifyCrxInstallComplete(false);
749 // Delete temporary files.
753 void CrxInstaller::ReportSuccessFromFileThread() {
754 DCHECK(installer_task_runner_
->RunsTasksOnCurrentThread());
756 // Tracking number of extensions installed by users
757 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD
)
758 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
760 if (!BrowserThread::PostTask(
761 BrowserThread::UI
, FROM_HERE
,
762 base::Bind(&CrxInstaller::ReportSuccessFromUIThread
, this)))
765 // Delete temporary files.
769 void CrxInstaller::ReportSuccessFromUIThread() {
770 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
772 if (!service_weak_
.get() || service_weak_
->browser_terminating())
775 if (!update_from_settings_page_
) {
776 // If there is a client, tell the client about installation.
778 client_
->OnInstallSuccess(extension(), install_icon_
.get());
780 // We update the extension's granted permissions if the user already
781 // approved the install (client_ is non NULL), or we are allowed to install
783 if (client_
|| allow_silent_install_
) {
784 PermissionsUpdater
perms_updater(profile());
785 perms_updater
.GrantActivePermissions(extension());
789 service_weak_
->OnExtensionInstalled(extension(),
791 has_requirement_errors_
,
793 install_wait_for_idle_
);
794 NotifyCrxInstallComplete(true);
797 void CrxInstaller::NotifyCrxInstallComplete(bool success
) {
798 // Some users (such as the download shelf) need to know when a
799 // CRXInstaller is done. Listening for the EXTENSION_* events
800 // is problematic because they don't know anything about the
801 // extension before it is unpacked, so they cannot filter based
803 content::NotificationService::current()->Notify(
804 chrome::NOTIFICATION_CRX_INSTALLER_DONE
,
805 content::Source
<CrxInstaller
>(this),
806 content::Details
<const Extension
>(
807 success
? extension() : NULL
));
813 void CrxInstaller::CleanupTempFiles() {
814 if (!installer_task_runner_
->RunsTasksOnCurrentThread()) {
815 if (!installer_task_runner_
->PostTask(
817 base::Bind(&CrxInstaller::CleanupTempFiles
, this))) {
823 // Delete the temp directory and crx file as necessary.
824 if (!temp_dir_
.value().empty()) {
825 extension_file_util::DeleteFile(temp_dir_
, true);
826 temp_dir_
= base::FilePath();
829 if (delete_source_
&& !source_file_
.value().empty()) {
830 extension_file_util::DeleteFile(source_file_
, false);
831 source_file_
= base::FilePath();
835 void CrxInstaller::CheckUpdateFromSettingsPage() {
836 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
838 ExtensionService
* service
= service_weak_
.get();
839 if (!service
|| service
->browser_terminating())
842 if (off_store_install_allow_reason_
!= OffStoreInstallAllowedFromSettingsPage
)
845 const Extension
* installed_extension
=
846 service
->GetInstalledExtension(extension()->id());
847 if (installed_extension
) {
848 // Previous version of the extension exists.
849 update_from_settings_page_
= true;
850 expected_id_
= installed_extension
->id();
851 install_source_
= installed_extension
->location();
852 install_cause_
= extension_misc::INSTALL_CAUSE_UPDATE
;
856 void CrxInstaller::ConfirmReEnable() {
857 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
859 ExtensionService
* service
= service_weak_
.get();
860 if (!service
|| service
->browser_terminating())
863 if (!update_from_settings_page_
)
866 ExtensionPrefs
* prefs
= service
->extension_prefs();
867 if (!prefs
->DidExtensionEscalatePermissions(extension()->id()))
871 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
872 client_
->ConfirmReEnable(this, extension());
876 } // namespace extensions