Cast: Stop logging kVideoFrameSentToEncoder and rename a couple events.
[chromium-blink-merge.git] / chrome / browser / extensions / crx_installer.cc
blob9a5e4ef6c0adddbe057327a420eb99c1bd36a0f0
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"
7 #include <map>
8 #include <set>
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"
65 #endif
67 using base::UserMetricsAction;
68 using content::BrowserThread;
69 using extensions::SharedModuleInfo;
71 namespace extensions {
73 namespace {
75 // Used in histograms; do not change order.
76 enum OffStoreInstallDecision {
77 OnStoreInstall,
78 OffStoreInstallAllowed,
79 OffStoreInstallDisallowed,
80 NumOffStoreInstallDecision
83 } // namespace
85 // static
86 scoped_refptr<CrxInstaller> CrxInstaller::CreateSilent(
87 ExtensionService* frontend) {
88 return new CrxInstaller(frontend->AsWeakPtr(),
89 scoped_ptr<ExtensionInstallPrompt>(),
90 NULL);
93 // static
94 scoped_refptr<CrxInstaller> CrxInstaller::Create(
95 ExtensionService* frontend,
96 scoped_ptr<ExtensionInstallPrompt> client) {
97 return new CrxInstaller(frontend->AsWeakPtr(), client.Pass(), NULL);
100 // static
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),
114 approved_(false),
115 expected_manifest_check_level_(
116 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
117 expected_version_strict_checking_(false),
118 extensions_enabled_(service_weak->extensions_enabled()),
119 delete_source_(false),
120 create_app_shortcut_(false),
121 service_weak_(service_weak),
122 // See header file comment on |client_| for why we use a raw pointer here.
123 client_(client.release()),
124 apps_require_extension_mime_type_(false),
125 allow_silent_install_(false),
126 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();
137 if (!approval)
138 return;
140 CHECK(profile()->IsSameProfile(approval->profile));
141 if (client_) {
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.
151 approved_ = true;
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.
171 if (client_) {
172 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
173 client_ = NULL;
177 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
178 ExtensionService* service = service_weak_.get();
179 if (!service || service->browser_terminating())
180 return;
182 NotifyCrxInstallBegin();
184 source_file_ = source_file;
186 scoped_refptr<SandboxedUnpacker> unpacker(
187 new SandboxedUnpacker(source_file,
188 install_source_,
189 creation_flags_,
190 install_directory_,
191 installer_task_runner_.get(),
192 this));
194 if (!installer_task_runner_->PostTask(
195 FROM_HERE,
196 base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
197 NOTREACHED();
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(
210 FROM_HERE,
211 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
212 NOTREACHED();
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));
221 return;
224 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
225 SkBitmap());
228 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
229 NotifyCrxInstallBegin();
231 if (!installer_task_runner_->PostTask(
232 FROM_HERE,
233 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread,
234 this,
235 web_app,
236 install_directory_)))
237 NOTREACHED();
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.";
249 return;
252 // TODO(aa): conversion data gets lost here :(
254 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
255 SkBitmap());
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())));
280 } else {
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.
292 if (approved_) {
293 bool valid = false;
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()) {
300 valid = true;
302 } else {
303 valid = expected_manifest_->Equals(original_manifest_.get());
304 if (!valid && expected_manifest_check_level_ ==
305 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) {
306 std::string error;
307 scoped_refptr<Extension> dummy_extension =
308 Extension::Create(base::FilePath(),
309 install_source_,
310 *expected_manifest_->value(),
311 creation_flags_,
312 &error);
313 if (error.empty()) {
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()));
324 if (!valid)
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);
347 } else {
348 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
349 NumOffStoreInstallDecision);
351 } else {
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);
362 } else {
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
397 // from the gallery.
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",
435 install_cause(),
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",
454 install_cause(),
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
468 // the temp dir.
469 unpacked_extension_root_ = extension_dir;
471 CrxInstallerError error = AllowInstall(extension);
472 if (error.type() != CrxInstallerError::ERROR_NONE) {
473 ReportFailureFromFileThread(error);
474 return;
477 if (!BrowserThread::PostTask(
478 BrowserThread::UI, FROM_HERE,
479 base::Bind(&CrxInstaller::CheckImportsAndRequirements, this)))
480 NOTREACHED();
483 void CrxInstaller::CheckImportsAndRequirements() {
484 DCHECK_CURRENTLY_ON(BrowserThread::UI);
485 ExtensionService* service = service_weak_.get();
486 if (!service || service->browser_terminating())
487 return;
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))));
502 return;
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))));
510 return;
514 installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked,
515 this));
518 void CrxInstaller::OnRequirementsChecked(
519 std::vector<std::string> requirement_errors) {
520 DCHECK_CURRENTLY_ON(BrowserThread::UI);
521 if (!service_weak_)
522 return;
524 if (!requirement_errors.empty()) {
525 if (error_on_unsupported_requirements_) {
526 ReportFailureFromUIThread(CrxInstallerError(
527 base::UTF8ToUTF16(JoinString(requirement_errors, ' '))));
528 return;
530 has_requirement_errors_ = true;
533 ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted(
534 extension()->id(),
535 base::Bind(&CrxInstaller::OnBlacklistChecked, this));
538 void CrxInstaller::OnBlacklistChecked(
539 extensions::BlacklistState blacklist_state) {
540 DCHECK_CURRENTLY_ON(BrowserThread::UI);
541 if (!service_weak_)
542 return;
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);
557 return;
560 // NOTE: extension may still be blacklisted, but we're forced to silently
561 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
562 // deal with it.
563 ConfirmInstall();
566 void CrxInstaller::ConfirmInstall() {
567 DCHECK_CURRENTLY_ON(BrowserThread::UI);
568 ExtensionService* service = service_weak_.get();
569 if (!service || service->browser_terminating())
570 return;
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();
577 #endif
578 if (!in_kiosk_mode) {
579 ReportFailureFromUIThread(CrxInstallerError(
580 l10n_util::GetStringUTF16(
581 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
582 return;
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));
594 return;
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(
608 CrxInstallerError(
609 l10n_util::GetStringFUTF16(
610 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
611 base::UTF8ToUTF16(overlapping_extension->name()))));
612 return;
615 current_version_ = ExtensionPrefs::Get(service->profile())
616 ->GetVersionString(extension()->id());
618 if (client_ &&
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_);
623 } else {
624 if (!installer_task_runner_->PostTask(
625 FROM_HERE,
626 base::Bind(&CrxInstaller::CompleteInstall, this)))
627 NOTREACHED();
629 return;
632 void CrxInstaller::InstallUIProceed() {
633 DCHECK_CURRENTLY_ON(BrowserThread::UI);
635 ExtensionService* service = service_weak_.get();
636 if (!service || service->browser_terminating())
637 return;
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());
645 } else {
646 if (!installer_task_runner_->PostTask(
647 FROM_HERE,
648 base::Bind(&CrxInstaller::CompleteInstall, this)))
649 NOTREACHED();
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(
683 CrxInstallerError(
684 l10n_util::GetStringUTF16(extension()->is_app() ?
685 IDS_APP_CANT_DOWNGRADE_VERSION :
686 IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
687 return;
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_,
700 extension()->id(),
701 extension()->VersionString(),
702 install_directory_);
703 if (version_dir.empty()) {
704 ReportFailureFromFileThread(
705 CrxInstallerError(
706 l10n_util::GetStringUTF16(
707 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
708 return;
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();
719 std::string error;
720 installer_.set_extension(
721 file_util::LoadExtension(
722 version_dir,
723 install_source_,
724 extension()->creation_flags() | Extension::REQUIRE_KEY,
725 &error).get());
727 if (extension()) {
728 ReportSuccessFromFileThread();
729 } else {
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))) {
741 NOTREACHED();
745 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
746 DCHECK_CURRENTLY_ON(BrowserThread::UI);
748 if (!service_weak_.get() || service_weak_->browser_terminating())
749 return;
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
761 // rid of this line.
762 ExtensionErrorReporter::GetInstance()->ReportError(
763 error.message(),
764 false); // Be quiet.
766 if (client_)
767 client_->OnInstallFailure(error);
769 NotifyCrxInstallComplete(false);
771 // Delete temporary files.
772 CleanupTempFiles();
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)))
785 NOTREACHED();
787 // Delete temporary files.
788 CleanupTempFiles();
791 void CrxInstaller::ReportSuccessFromUIThread() {
792 DCHECK_CURRENTLY_ON(BrowserThread::UI);
794 if (!service_weak_.get() || service_weak_->browser_terminating())
795 return;
797 if (!update_from_settings_page_) {
798 // If there is a client, tell the client about installation.
799 if (client_)
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
804 // this silently.
805 if (client_ || allow_silent_install_) {
806 PermissionsUpdater perms_updater(profile());
807 perms_updater.GrantActivePermissions(extension());
811 service_weak_->OnExtensionInstalled(extension(),
812 page_ordinal_,
813 has_requirement_errors_,
814 blacklist_state_,
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
829 // on the extension.
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);
839 if (success)
840 ConfirmReEnable();
843 void CrxInstaller::CleanupTempFiles() {
844 if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
845 if (!installer_task_runner_->PostTask(
846 FROM_HERE,
847 base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
848 NOTREACHED();
850 return;
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())
870 return;
872 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
873 return;
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())
891 return;
893 if (!update_from_settings_page_)
894 return;
896 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
897 if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
898 return;
900 if (client_) {
901 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
902 client_->ConfirmReEnable(this, extension());
906 } // namespace extensions