Roll src/third_party/WebKit f298044:aa8346d (svn 202628:202629)
[chromium-blink-merge.git] / chrome / browser / extensions / crx_installer.cc
blobc8cee2bda9dd81dc1f266cde77ba227544bb1dc1
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/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_registry.h"
43 #include "extensions/browser/extension_system.h"
44 #include "extensions/browser/install/crx_install_error.h"
45 #include "extensions/browser/install/extension_install_ui.h"
46 #include "extensions/browser/install_flag.h"
47 #include "extensions/browser/notification_types.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/manifest_url_handlers.h"
55 #include "extensions/common/permissions/permission_message_provider.h"
56 #include "extensions/common/permissions/permission_set.h"
57 #include "extensions/common/permissions/permissions_data.h"
58 #include "extensions/common/user_script.h"
59 #include "grit/extensions_strings.h"
60 #include "third_party/skia/include/core/SkBitmap.h"
61 #include "ui/base/l10n/l10n_util.h"
63 #if defined(OS_CHROMEOS)
64 #include "components/user_manager/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(base::WeakPtr<ExtensionService> service_weak,
109 scoped_ptr<ExtensionInstallPrompt> client,
110 const WebstoreInstaller::Approval* approval)
111 : install_directory_(service_weak->install_directory()),
112 install_source_(Manifest::INTERNAL),
113 approved_(false),
114 hash_check_failed_(false),
115 expected_manifest_check_level_(
116 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
117 fail_install_if_unexpected_version_(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 grant_permissions_(true),
127 install_cause_(extension_misc::INSTALL_CAUSE_UNSET),
128 creation_flags_(Extension::NO_FLAGS),
129 off_store_install_allow_reason_(OffStoreInstallDisallowed),
130 did_handle_successfully_(true),
131 error_on_unsupported_requirements_(false),
132 update_from_settings_page_(false),
133 install_flags_(kInstallFlagNone),
134 install_checker_(service_weak->profile()) {
135 installer_task_runner_ = service_weak->GetFileTaskRunner();
136 if (!approval)
137 return;
139 CHECK(profile()->IsSameProfile(approval->profile));
140 if (client_) {
141 client_->install_ui()->SetUseAppInstalledBubble(
142 approval->use_app_installed_bubble);
143 client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui);
146 if (approval->skip_install_dialog) {
147 // Mark the extension as approved, but save the expected manifest and ID
148 // so we can check that they match the CRX's.
149 approved_ = true;
150 expected_manifest_check_level_ = approval->manifest_check_level;
151 if (expected_manifest_check_level_ !=
152 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE)
153 expected_manifest_.reset(approval->manifest->DeepCopy());
154 expected_id_ = approval->extension_id;
156 if (approval->minimum_version.get())
157 minimum_version_ = base::Version(*approval->minimum_version);
159 show_dialog_callback_ = approval->show_dialog_callback;
160 set_is_ephemeral(approval->is_ephemeral);
163 CrxInstaller::~CrxInstaller() {
164 DCHECK_CURRENTLY_ON(BrowserThread::UI);
165 // Ensure |client_| and |install_checker_| data members are destroyed on the
166 // UI thread. The |client_| dialog has a weak reference as |this| is its
167 // delegate, and |install_checker_| owns WeakPtrs, so must be destroyed on the
168 // same thread that created it.
171 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
172 InstallCrxFile(CRXFileInfo(source_file));
175 void CrxInstaller::InstallCrxFile(const CRXFileInfo& source_file) {
176 ExtensionService* service = service_weak_.get();
177 if (!service || service->browser_terminating())
178 return;
180 NotifyCrxInstallBegin();
182 source_file_ = source_file.path;
184 scoped_refptr<SandboxedUnpacker> unpacker(new SandboxedUnpacker(
185 install_source_, creation_flags_, install_directory_,
186 installer_task_runner_.get(), this));
188 if (!installer_task_runner_->PostTask(
189 FROM_HERE, base::Bind(&SandboxedUnpacker::StartWithCrx,
190 unpacker.get(), source_file))) {
191 NOTREACHED();
195 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
196 const GURL& download_url) {
197 DCHECK(!download_url.is_empty());
199 NotifyCrxInstallBegin();
201 source_file_ = source_file;
202 download_url_ = download_url;
204 if (!installer_task_runner_->PostTask(
205 FROM_HERE,
206 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
207 NOTREACHED();
210 void CrxInstaller::ConvertUserScriptOnFileThread() {
211 base::string16 error;
212 scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
213 source_file_, download_url_, install_directory_, &error);
214 if (!extension.get()) {
215 ReportFailureFromFileThread(CrxInstallError(error));
216 return;
219 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
220 SkBitmap());
223 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
224 NotifyCrxInstallBegin();
226 if (!installer_task_runner_->PostTask(
227 FROM_HERE,
228 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread, this, web_app)))
229 NOTREACHED();
232 void CrxInstaller::ConvertWebAppOnFileThread(
233 const WebApplicationInfo& web_app) {
234 scoped_refptr<Extension> extension(ConvertWebAppToExtension(
235 web_app, base::Time::Now(), install_directory_));
236 if (!extension.get()) {
237 // Validation should have stopped any potential errors before getting here.
238 NOTREACHED() << "Could not convert web app to extension.";
239 return;
242 // TODO(aa): conversion data gets lost here :(
244 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
245 SkBitmap());
248 CrxInstallError CrxInstaller::AllowInstall(const Extension* extension) {
249 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
251 // Make sure the expected ID matches if one was supplied or if we want to
252 // bypass the prompt.
253 if ((approved_ || !expected_id_.empty()) &&
254 expected_id_ != extension->id()) {
255 return CrxInstallError(l10n_util::GetStringFUTF16(
256 IDS_EXTENSION_INSTALL_UNEXPECTED_ID, base::ASCIIToUTF16(expected_id_),
257 base::ASCIIToUTF16(extension->id())));
260 if (minimum_version_.IsValid() &&
261 extension->version()->CompareTo(minimum_version_) < 0) {
262 return CrxInstallError(l10n_util::GetStringFUTF16(
263 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
264 base::ASCIIToUTF16(minimum_version_.GetString() + "+"),
265 base::ASCIIToUTF16(extension->version()->GetString())));
268 if (expected_version_.IsValid() && fail_install_if_unexpected_version_ &&
269 !expected_version_.Equals(*extension->version())) {
270 return CrxInstallError(l10n_util::GetStringFUTF16(
271 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
272 base::ASCIIToUTF16(expected_version_.GetString()),
273 base::ASCIIToUTF16(extension->version()->GetString())));
276 // Make sure the manifests match if we want to bypass the prompt.
277 if (approved_) {
278 bool valid = false;
279 if (expected_manifest_check_level_ ==
280 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) {
281 // To skip manifest checking, the extension must be a shared module
282 // and not request any permissions.
283 if (SharedModuleInfo::IsSharedModule(extension) &&
284 extension->permissions_data()->active_permissions()->IsEmpty()) {
285 valid = true;
287 } else {
288 valid = expected_manifest_->Equals(original_manifest_.get());
289 if (!valid && expected_manifest_check_level_ ==
290 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) {
291 std::string error;
292 scoped_refptr<Extension> dummy_extension =
293 Extension::Create(base::FilePath(),
294 install_source_,
295 *expected_manifest_->value(),
296 creation_flags_,
297 extension->id(),
298 &error);
299 if (error.empty()) {
300 scoped_refptr<const PermissionSet> expected_permissions =
301 dummy_extension->permissions_data()->active_permissions();
302 valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
303 expected_permissions.get(),
304 extension->permissions_data()->active_permissions().get(),
305 extension->GetType()));
310 if (!valid)
311 return CrxInstallError(
312 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
315 // The checks below are skipped for themes and external installs.
316 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
317 // and other uses of install_source_ that are no longer needed now that the
318 // SandboxedUnpacker sets extension->location.
319 if (extension->is_theme() || Manifest::IsExternalLocation(install_source_))
320 return CrxInstallError();
322 if (!extensions_enabled_) {
323 return CrxInstallError(
324 CrxInstallError::ERROR_DECLINED,
325 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED));
328 if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) {
329 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
330 const char kHistogramName[] = "Extensions.OffStoreInstallDecisionEasy";
331 if (is_gallery_install()) {
332 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
333 NumOffStoreInstallDecision);
334 } else {
335 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
336 NumOffStoreInstallDecision);
338 } else {
339 const char kHistogramName[] = "Extensions.OffStoreInstallDecisionHard";
340 if (is_gallery_install()) {
341 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
342 NumOffStoreInstallDecision);
343 } else if (off_store_install_allow_reason_ != OffStoreInstallDisallowed) {
344 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
345 NumOffStoreInstallDecision);
346 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
347 off_store_install_allow_reason_,
348 NumOffStoreInstallAllowReasons);
349 } else {
350 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallDisallowed,
351 NumOffStoreInstallDecision);
352 // Don't delete source in this case so that the user can install
353 // manually if they want.
354 delete_source_ = false;
355 did_handle_successfully_ = false;
357 return CrxInstallError(CrxInstallError::ERROR_OFF_STORE,
358 l10n_util::GetStringUTF16(
359 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE));
364 if (install_checker_.extension()->is_app()) {
365 // If the app was downloaded, apps_require_extension_mime_type_
366 // will be set. In this case, check that it was served with the
367 // right mime type. Make an exception for file URLs, which come
368 // from the users computer and have no headers.
369 if (!download_url_.SchemeIsFile() &&
370 apps_require_extension_mime_type_ &&
371 original_mime_type_ != Extension::kMimeType) {
372 return CrxInstallError(l10n_util::GetStringFUTF16(
373 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE,
374 base::ASCIIToUTF16(Extension::kMimeType)));
377 // If the client_ is NULL, then the app is either being installed via
378 // an internal mechanism like sync, external_extensions, or default apps.
379 // In that case, we don't want to enforce things like the install origin.
380 if (!is_gallery_install() && client_) {
381 // For apps with a gallery update URL, require that they be installed
382 // from the gallery.
383 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
384 if (ManifestURL::UpdatesFromGallery(extension)) {
385 return CrxInstallError(l10n_util::GetStringFUTF16(
386 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS,
387 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
390 // For self-hosted apps, verify that the entire extent is on the same
391 // host (or a subdomain of the host) the download happened from. There's
392 // no way for us to verify that the app controls any other hosts.
393 URLPattern pattern(UserScript::ValidUserScriptSchemes());
394 pattern.SetHost(download_url_.host());
395 pattern.SetMatchSubdomains(true);
397 URLPatternSet patterns = install_checker_.extension()->web_extent();
398 for (URLPatternSet::const_iterator i = patterns.begin();
399 i != patterns.end(); ++i) {
400 if (!pattern.MatchesHost(i->host())) {
401 return CrxInstallError(l10n_util::GetStringUTF16(
402 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST));
408 return CrxInstallError();
411 void CrxInstaller::OnUnpackFailure(const CrxInstallError& error) {
412 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
414 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
415 install_source(), Manifest::NUM_LOCATIONS);
417 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
418 install_cause(),
419 extension_misc::NUM_INSTALL_CAUSES);
421 ReportFailureFromFileThread(error);
424 void CrxInstaller::OnUnpackSuccess(
425 const base::FilePath& temp_dir,
426 const base::FilePath& extension_dir,
427 const base::DictionaryValue* original_manifest,
428 const Extension* extension,
429 const SkBitmap& install_icon) {
430 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
432 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
433 install_source(), Manifest::NUM_LOCATIONS);
436 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
437 install_cause(),
438 extension_misc::NUM_INSTALL_CAUSES);
440 install_checker_.set_extension(extension);
441 temp_dir_ = temp_dir;
442 if (!install_icon.empty())
443 install_icon_.reset(new SkBitmap(install_icon));
445 if (original_manifest)
446 original_manifest_.reset(new Manifest(
447 Manifest::INVALID_LOCATION,
448 scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy())));
450 // We don't have to delete the unpack dir explicity since it is a child of
451 // the temp dir.
452 unpacked_extension_root_ = extension_dir;
454 CrxInstallError error = AllowInstall(extension);
455 if (error.type() != CrxInstallError::ERROR_NONE) {
456 ReportFailureFromFileThread(error);
457 return;
460 if (!BrowserThread::PostTask(BrowserThread::UI,
461 FROM_HERE,
462 base::Bind(&CrxInstaller::CheckInstall, this)))
463 NOTREACHED();
466 void CrxInstaller::CheckInstall() {
467 DCHECK_CURRENTLY_ON(BrowserThread::UI);
468 ExtensionService* service = service_weak_.get();
469 if (!service || service->browser_terminating())
470 return;
472 // TODO(crbug.com/420147): Move this code to a utility class to avoid
473 // duplication of SharedModuleService::CheckImports code.
474 if (SharedModuleInfo::ImportsModules(extension())) {
475 const std::vector<SharedModuleInfo::ImportInfo>& imports =
476 SharedModuleInfo::GetImports(extension());
477 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
478 for (i = imports.begin(); i != imports.end(); ++i) {
479 base::Version version_required(i->minimum_version);
480 const Extension* imported_module =
481 service->GetExtensionById(i->extension_id, true);
482 if (imported_module &&
483 !SharedModuleInfo::IsSharedModule(imported_module)) {
484 ReportFailureFromUIThread(CrxInstallError(
485 CrxInstallError::ERROR_DECLINED,
486 l10n_util::GetStringFUTF16(
487 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE,
488 base::UTF8ToUTF16(imported_module->name()))));
489 return;
490 } else if (imported_module && (version_required.IsValid() &&
491 imported_module->version()->CompareTo(
492 version_required) < 0)) {
493 ReportFailureFromUIThread(CrxInstallError(
494 CrxInstallError::ERROR_DECLINED,
495 l10n_util::GetStringFUTF16(
496 IDS_EXTENSION_INSTALL_DEPENDENCY_OLD_VERSION,
497 base::UTF8ToUTF16(imported_module->name()),
498 base::ASCIIToUTF16(i->minimum_version),
499 base::ASCIIToUTF16(imported_module->version()->GetString()))));
500 return;
501 } else if (imported_module &&
502 !SharedModuleInfo::IsExportAllowedByWhitelist(
503 imported_module, extension()->id())) {
504 ReportFailureFromUIThread(CrxInstallError(
505 CrxInstallError::ERROR_DECLINED,
506 l10n_util::GetStringFUTF16(
507 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED,
508 base::UTF8ToUTF16(extension()->name()),
509 base::UTF8ToUTF16(imported_module->name()))));
510 return;
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);
524 if (!service_weak_)
525 return;
527 // Check for requirement errors.
528 if (!install_checker_.requirement_errors().empty()) {
529 if (error_on_unsupported_requirements_) {
530 ReportFailureFromUIThread(
531 CrxInstallError(CrxInstallError::ERROR_DECLINED,
532 base::UTF8ToUTF16(base::JoinString(
533 install_checker_.requirement_errors(), " "))));
534 return;
536 install_flags_ |= kInstallFlagHasRequirementErrors;
539 // Check the blacklist state.
540 if (install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE) {
541 install_flags_ |= kInstallFlagIsBlacklistedForMalware;
544 if ((install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE ||
545 install_checker_.blacklist_state() == extensions::BLACKLISTED_UNKNOWN) &&
546 !allow_silent_install_) {
547 // User tried to install a blacklisted extension. Show an error and
548 // refuse to install it.
549 ReportFailureFromUIThread(extensions::CrxInstallError(
550 CrxInstallError::ERROR_DECLINED,
551 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
552 base::UTF8ToUTF16(extension()->name()))));
553 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
554 extension()->location(),
555 Manifest::NUM_LOCATIONS);
556 return;
559 // NOTE: extension may still be blacklisted, but we're forced to silently
560 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
561 // deal with it.
563 // Check for policy errors.
564 if (!install_checker_.policy_error().empty()) {
565 // We don't want to show the error infobar for installs from the WebStore,
566 // because the WebStore already shows an error dialog itself.
567 // Note: |client_| can be NULL in unit_tests!
568 if (extension()->from_webstore() && client_)
569 client_->install_ui()->SetSkipPostInstallUI(true);
570 ReportFailureFromUIThread(
571 CrxInstallError(CrxInstallError::ERROR_DECLINED,
572 base::UTF8ToUTF16(install_checker_.policy_error())));
573 return;
576 ConfirmInstall();
579 void CrxInstaller::ConfirmInstall() {
580 DCHECK_CURRENTLY_ON(BrowserThread::UI);
581 ExtensionService* service = service_weak_.get();
582 if (!service || service->browser_terminating())
583 return;
585 if (KioskModeInfo::IsKioskOnly(install_checker_.extension().get())) {
586 bool in_kiosk_mode = false;
587 #if defined(OS_CHROMEOS)
588 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
589 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
590 #endif
591 if (!in_kiosk_mode) {
592 ReportFailureFromUIThread(CrxInstallError(
593 CrxInstallError::ERROR_DECLINED,
594 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
595 return;
599 // Check whether this install is initiated from the settings page to
600 // update an existing extension or app.
601 CheckUpdateFromSettingsPage();
603 GURL overlapping_url;
604 ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
605 const Extension* overlapping_extension =
606 registry->enabled_extensions().GetHostedAppByOverlappingWebExtent(
607 extension()->web_extent());
608 if (overlapping_extension &&
609 overlapping_extension->id() != extension()->id()) {
610 ReportFailureFromUIThread(CrxInstallError(l10n_util::GetStringFUTF16(
611 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
612 base::UTF8ToUTF16(overlapping_extension->name()))));
613 return;
616 current_version_ = base::Version(ExtensionPrefs::Get(service->profile())
617 ->GetVersionString(extension()->id()));
619 if (client_ &&
620 (!allow_silent_install_ || !approved_) &&
621 !update_from_settings_page_) {
622 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
623 client_->ConfirmInstall(this, extension(), show_dialog_callback_);
624 } else {
625 if (!installer_task_runner_->PostTask(
626 FROM_HERE,
627 base::Bind(&CrxInstaller::CompleteInstall, this)))
628 NOTREACHED();
630 return;
633 void CrxInstaller::InstallUIProceed() {
634 DCHECK_CURRENTLY_ON(BrowserThread::UI);
636 ExtensionService* service = service_weak_.get();
637 if (!service || service->browser_terminating())
638 return;
640 // If update_from_settings_page_ boolean is true, this functions is
641 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
642 // and if it is false, this function is called in response to
643 // ExtensionInstallPrompt::ConfirmInstall().
644 if (update_from_settings_page_) {
645 service->GrantPermissionsAndEnableExtension(extension());
646 } else {
647 if (!installer_task_runner_->PostTask(
648 FROM_HERE,
649 base::Bind(&CrxInstaller::CompleteInstall, this)))
650 NOTREACHED();
653 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
656 void CrxInstaller::InstallUIAbort(bool user_initiated) {
657 // If update_from_settings_page_ boolean is true, this functions is
658 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
659 // and if it is false, this function is called in response to
660 // ExtensionInstallPrompt::ConfirmInstall().
661 if (!update_from_settings_page_) {
662 const char* histogram_name = user_initiated ? "InstallCancel"
663 : "InstallAbort";
664 ExtensionService::RecordPermissionMessagesHistogram(
665 extension(), histogram_name);
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_.IsValid() &&
680 current_version_.CompareTo(*(extension()->version())) > 0) {
681 ReportFailureFromFileThread(CrxInstallError(
682 CrxInstallError::ERROR_DECLINED,
683 l10n_util::GetStringUTF16(
684 extension()->is_app() ? IDS_APP_CANT_DOWNGRADE_VERSION
685 : IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
686 return;
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(
699 extension(),
700 unpacked_extension_root_,
701 install_directory_,
702 profile(),
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(CrxInstallError(l10n_util::GetStringUTF16(
712 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
713 return;
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();
724 std::string error;
725 install_checker_.set_extension(
726 file_util::LoadExtension(
727 version_dir,
728 install_source_,
729 extension()->creation_flags() | Extension::REQUIRE_KEY,
730 &error).get());
732 if (extension()) {
733 ReportSuccessFromFileThread();
734 } else {
735 LOG(ERROR) << error << " " << extension_id << " " << download_url_;
736 ReportFailureFromFileThread(CrxInstallError(base::UTF8ToUTF16(error)));
740 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallError& error) {
741 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
742 if (!BrowserThread::PostTask(
743 BrowserThread::UI, FROM_HERE,
744 base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
745 NOTREACHED();
749 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallError& error) {
750 DCHECK_CURRENTLY_ON(BrowserThread::UI);
752 if (!service_weak_.get() || service_weak_->browser_terminating())
753 return;
755 content::NotificationService* service =
756 content::NotificationService::current();
757 service->Notify(extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
758 content::Source<CrxInstaller>(this),
759 content::Details<const extensions::CrxInstallError>(&error));
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
765 // rid of this line.
766 ExtensionErrorReporter::GetInstance()->ReportError(
767 error.message(),
768 false); // Be quiet.
770 if (client_)
771 client_->OnInstallFailure(error);
773 NotifyCrxInstallComplete(false);
775 // Delete temporary files.
776 CleanupTempFiles();
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)))
789 NOTREACHED();
791 // Delete temporary files.
792 CleanupTempFiles();
795 void CrxInstaller::ReportSuccessFromUIThread() {
796 DCHECK_CURRENTLY_ON(BrowserThread::UI);
798 if (!service_weak_.get() || service_weak_->browser_terminating())
799 return;
801 if (!update_from_settings_page_) {
802 // If there is a client, tell the client about installation.
803 if (client_)
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
808 // this silently.
809 if ((client_ || allow_silent_install_) &&
810 grant_permissions_ &&
811 (!expected_version_.IsValid() ||
812 expected_version_.Equals(*extension()->version()))) {
813 PermissionsUpdater perms_updater(profile());
814 perms_updater.InitializePermissions(extension());
815 perms_updater.GrantActivePermissions(extension());
819 service_weak_->OnExtensionInstalled(
820 extension(), page_ordinal_, install_flags_);
821 NotifyCrxInstallComplete(true);
824 void CrxInstaller::NotifyCrxInstallBegin() {
825 InstallTrackerFactory::GetForBrowserContext(profile())
826 ->OnBeginCrxInstall(expected_id_);
829 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
830 // Some users (such as the download shelf) need to know when a
831 // CRXInstaller is done. Listening for the EXTENSION_* events
832 // is problematic because they don't know anything about the
833 // extension before it is unpacked, so they cannot filter based
834 // on the extension.
835 content::NotificationService::current()->Notify(
836 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
837 content::Source<CrxInstaller>(this),
838 content::Details<const Extension>(success ? extension() : NULL));
840 InstallTrackerFactory::GetForBrowserContext(profile())
841 ->OnFinishCrxInstall(success ? extension()->id() : expected_id_, success);
843 if (success)
844 ConfirmReEnable();
847 void CrxInstaller::CleanupTempFiles() {
848 if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
849 if (!installer_task_runner_->PostTask(
850 FROM_HERE,
851 base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
852 NOTREACHED();
854 return;
857 // Delete the temp directory and crx file as necessary.
858 if (!temp_dir_.value().empty()) {
859 file_util::DeleteFile(temp_dir_, true);
860 temp_dir_ = base::FilePath();
863 if (delete_source_ && !source_file_.value().empty()) {
864 file_util::DeleteFile(source_file_, false);
865 source_file_ = base::FilePath();
869 void CrxInstaller::CheckUpdateFromSettingsPage() {
870 DCHECK_CURRENTLY_ON(BrowserThread::UI);
872 ExtensionService* service = service_weak_.get();
873 if (!service || service->browser_terminating())
874 return;
876 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
877 return;
879 const Extension* installed_extension =
880 service->GetInstalledExtension(extension()->id());
881 if (installed_extension) {
882 // Previous version of the extension exists.
883 update_from_settings_page_ = true;
884 expected_id_ = installed_extension->id();
885 install_source_ = installed_extension->location();
886 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
890 void CrxInstaller::ConfirmReEnable() {
891 DCHECK_CURRENTLY_ON(BrowserThread::UI);
893 ExtensionService* service = service_weak_.get();
894 if (!service || service->browser_terminating())
895 return;
897 if (!update_from_settings_page_)
898 return;
900 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
901 if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
902 return;
904 if (client_) {
905 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
906 client_->ConfirmReEnable(this, extension());
910 } // namespace extensions