Extract code handling PrinterProviderAPI from PrintPreviewHandler
[chromium-blink-merge.git] / chrome / browser / extensions / crx_installer.cc
blob17cc1a9538eb4ce3ed9a958272fa56407e90c8ca
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_installer_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 expected_manifest_check_level_(
115 WebstoreInstaller::MANIFEST_CHECK_LEVEL_STRICT),
116 expected_version_strict_checking_(false),
117 extensions_enabled_(service_weak->extensions_enabled()),
118 delete_source_(false),
119 create_app_shortcut_(false),
120 service_weak_(service_weak),
121 // See header file comment on |client_| for why we use a raw pointer here.
122 client_(client.release()),
123 apps_require_extension_mime_type_(false),
124 allow_silent_install_(false),
125 grant_permissions_(true),
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 update_from_settings_page_(false),
132 install_flags_(kInstallFlagNone),
133 install_checker_(service_weak->profile()) {
134 installer_task_runner_ = service_weak->GetFileTaskRunner();
135 if (!approval)
136 return;
138 CHECK(profile()->IsSameProfile(approval->profile));
139 if (client_) {
140 client_->install_ui()->SetUseAppInstalledBubble(
141 approval->use_app_installed_bubble);
142 client_->install_ui()->SetSkipPostInstallUI(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.
148 approved_ = true;
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;
161 set_is_ephemeral(approval->is_ephemeral);
164 CrxInstaller::~CrxInstaller() {
165 // Make sure the UI is deleted on the ui thread.
166 if (client_) {
167 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_);
168 client_ = NULL;
172 void CrxInstaller::InstallCrx(const base::FilePath& source_file) {
173 InstallCrxFile(CRXFileInfo(source_file));
176 void CrxInstaller::InstallCrxFile(const CRXFileInfo& source_file) {
177 ExtensionService* service = service_weak_.get();
178 if (!service || service->browser_terminating())
179 return;
181 NotifyCrxInstallBegin();
183 source_file_ = source_file.path;
185 scoped_refptr<SandboxedUnpacker> unpacker(
186 new SandboxedUnpacker(source_file,
187 install_source_,
188 creation_flags_,
189 install_directory_,
190 installer_task_runner_.get(),
191 this));
193 if (!installer_task_runner_->PostTask(
194 FROM_HERE,
195 base::Bind(&SandboxedUnpacker::Start, unpacker.get())))
196 NOTREACHED();
199 void CrxInstaller::InstallUserScript(const base::FilePath& source_file,
200 const GURL& download_url) {
201 DCHECK(!download_url.is_empty());
203 NotifyCrxInstallBegin();
205 source_file_ = source_file;
206 download_url_ = download_url;
208 if (!installer_task_runner_->PostTask(
209 FROM_HERE,
210 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this)))
211 NOTREACHED();
214 void CrxInstaller::ConvertUserScriptOnFileThread() {
215 base::string16 error;
216 scoped_refptr<Extension> extension = ConvertUserScriptToExtension(
217 source_file_, download_url_, install_directory_, &error);
218 if (!extension.get()) {
219 ReportFailureFromFileThread(CrxInstallerError(error));
220 return;
223 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
224 SkBitmap());
227 void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) {
228 NotifyCrxInstallBegin();
230 if (!installer_task_runner_->PostTask(
231 FROM_HERE,
232 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread, this, web_app)))
233 NOTREACHED();
236 void CrxInstaller::ConvertWebAppOnFileThread(
237 const WebApplicationInfo& web_app) {
238 scoped_refptr<Extension> extension(ConvertWebAppToExtension(
239 web_app, base::Time::Now(), install_directory_));
240 if (!extension.get()) {
241 // Validation should have stopped any potential errors before getting here.
242 NOTREACHED() << "Could not convert web app to extension.";
243 return;
246 // TODO(aa): conversion data gets lost here :(
248 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get(),
249 SkBitmap());
252 CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) {
253 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
255 // Make sure the expected ID matches if one was supplied or if we want to
256 // bypass the prompt.
257 if ((approved_ || !expected_id_.empty()) &&
258 expected_id_ != extension->id()) {
259 return CrxInstallerError(
260 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID,
261 base::ASCIIToUTF16(expected_id_),
262 base::ASCIIToUTF16(extension->id())));
265 if (expected_version_.get()) {
266 if (expected_version_strict_checking_) {
267 if (!expected_version_->Equals(*extension->version())) {
268 return CrxInstallerError(
269 l10n_util::GetStringFUTF16(
270 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
271 base::ASCIIToUTF16(expected_version_->GetString()),
272 base::ASCIIToUTF16(extension->version()->GetString())));
274 } else {
275 if (extension->version()->CompareTo(*expected_version_) < 0) {
276 return CrxInstallerError(
277 l10n_util::GetStringFUTF16(
278 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION,
279 base::ASCIIToUTF16(expected_version_->GetString() + "+"),
280 base::ASCIIToUTF16(extension->version()->GetString())));
285 // Make sure the manifests match if we want to bypass the prompt.
286 if (approved_) {
287 bool valid = false;
288 if (expected_manifest_check_level_ ==
289 WebstoreInstaller::MANIFEST_CHECK_LEVEL_NONE) {
290 // To skip manifest checking, the extension must be a shared module
291 // and not request any permissions.
292 if (SharedModuleInfo::IsSharedModule(extension) &&
293 extension->permissions_data()->active_permissions()->IsEmpty()) {
294 valid = true;
296 } else {
297 valid = expected_manifest_->Equals(original_manifest_.get());
298 if (!valid && expected_manifest_check_level_ ==
299 WebstoreInstaller::MANIFEST_CHECK_LEVEL_LOOSE) {
300 std::string error;
301 scoped_refptr<Extension> dummy_extension =
302 Extension::Create(base::FilePath(),
303 install_source_,
304 *expected_manifest_->value(),
305 creation_flags_,
306 extension->id(),
307 &error);
308 if (error.empty()) {
309 scoped_refptr<const PermissionSet> expected_permissions =
310 dummy_extension->permissions_data()->active_permissions();
311 valid = !(PermissionMessageProvider::Get()->IsPrivilegeIncrease(
312 expected_permissions.get(),
313 extension->permissions_data()->active_permissions().get(),
314 extension->GetType()));
319 if (!valid)
320 return CrxInstallerError(
321 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID));
324 // The checks below are skipped for themes and external installs.
325 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this
326 // and other uses of install_source_ that are no longer needed now that the
327 // SandboxedUnpacker sets extension->location.
328 if (extension->is_theme() || Manifest::IsExternalLocation(install_source_))
329 return CrxInstallerError();
331 if (!extensions_enabled_) {
332 return CrxInstallerError(
333 CrxInstallerError::ERROR_DECLINED,
334 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED));
337 if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) {
338 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) {
339 const char kHistogramName[] = "Extensions.OffStoreInstallDecisionEasy";
340 if (is_gallery_install()) {
341 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
342 NumOffStoreInstallDecision);
343 } else {
344 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
345 NumOffStoreInstallDecision);
347 } else {
348 const char kHistogramName[] = "Extensions.OffStoreInstallDecisionHard";
349 if (is_gallery_install()) {
350 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall,
351 NumOffStoreInstallDecision);
352 } else if (off_store_install_allow_reason_ != OffStoreInstallDisallowed) {
353 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed,
354 NumOffStoreInstallDecision);
355 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason",
356 off_store_install_allow_reason_,
357 NumOffStoreInstallAllowReasons);
358 } else {
359 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallDisallowed,
360 NumOffStoreInstallDecision);
361 // Don't delete source in this case so that the user can install
362 // manually if they want.
363 delete_source_ = false;
364 did_handle_successfully_ = false;
366 return CrxInstallerError(
367 CrxInstallerError::ERROR_OFF_STORE,
368 l10n_util::GetStringUTF16(
369 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE));
374 if (install_checker_.extension()->is_app()) {
375 // If the app was downloaded, apps_require_extension_mime_type_
376 // will be set. In this case, check that it was served with the
377 // right mime type. Make an exception for file URLs, which come
378 // from the users computer and have no headers.
379 if (!download_url_.SchemeIsFile() &&
380 apps_require_extension_mime_type_ &&
381 original_mime_type_ != Extension::kMimeType) {
382 return CrxInstallerError(
383 l10n_util::GetStringFUTF16(
384 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE,
385 base::ASCIIToUTF16(Extension::kMimeType)));
388 // If the client_ is NULL, then the app is either being installed via
389 // an internal mechanism like sync, external_extensions, or default apps.
390 // In that case, we don't want to enforce things like the install origin.
391 if (!is_gallery_install() && client_) {
392 // For apps with a gallery update URL, require that they be installed
393 // from the gallery.
394 // TODO(erikkay) Apply this rule for paid extensions and themes as well.
395 if (ManifestURL::UpdatesFromGallery(extension)) {
396 return CrxInstallerError(
397 l10n_util::GetStringFUTF16(
398 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS,
399 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)));
402 // For self-hosted apps, verify that the entire extent is on the same
403 // host (or a subdomain of the host) the download happened from. There's
404 // no way for us to verify that the app controls any other hosts.
405 URLPattern pattern(UserScript::ValidUserScriptSchemes());
406 pattern.SetHost(download_url_.host());
407 pattern.SetMatchSubdomains(true);
409 URLPatternSet patterns = install_checker_.extension()->web_extent();
410 for (URLPatternSet::const_iterator i = patterns.begin();
411 i != patterns.end(); ++i) {
412 if (!pattern.MatchesHost(i->host())) {
413 return CrxInstallerError(
414 l10n_util::GetStringUTF16(
415 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST));
421 return CrxInstallerError();
424 void CrxInstaller::OnUnpackFailure(const base::string16& error_message) {
425 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
427 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource",
428 install_source(), Manifest::NUM_LOCATIONS);
430 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause",
431 install_cause(),
432 extension_misc::NUM_INSTALL_CAUSES);
434 ReportFailureFromFileThread(CrxInstallerError(error_message));
437 void CrxInstaller::OnUnpackSuccess(
438 const base::FilePath& temp_dir,
439 const base::FilePath& extension_dir,
440 const base::DictionaryValue* original_manifest,
441 const Extension* extension,
442 const SkBitmap& install_icon) {
443 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
445 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource",
446 install_source(), Manifest::NUM_LOCATIONS);
449 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause",
450 install_cause(),
451 extension_misc::NUM_INSTALL_CAUSES);
453 install_checker_.set_extension(extension);
454 temp_dir_ = temp_dir;
455 if (!install_icon.empty())
456 install_icon_.reset(new SkBitmap(install_icon));
458 if (original_manifest)
459 original_manifest_.reset(new Manifest(
460 Manifest::INVALID_LOCATION,
461 scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy())));
463 // We don't have to delete the unpack dir explicity since it is a child of
464 // the temp dir.
465 unpacked_extension_root_ = extension_dir;
467 CrxInstallerError error = AllowInstall(extension);
468 if (error.type() != CrxInstallerError::ERROR_NONE) {
469 ReportFailureFromFileThread(error);
470 return;
473 if (!BrowserThread::PostTask(BrowserThread::UI,
474 FROM_HERE,
475 base::Bind(&CrxInstaller::CheckInstall, this)))
476 NOTREACHED();
479 void CrxInstaller::CheckInstall() {
480 DCHECK_CURRENTLY_ON(BrowserThread::UI);
481 ExtensionService* service = service_weak_.get();
482 if (!service || service->browser_terminating())
483 return;
485 // TODO(crbug.com/420147): Move this code to a utility class to avoid
486 // duplication of SharedModuleService::CheckImports code.
487 if (SharedModuleInfo::ImportsModules(extension())) {
488 const std::vector<SharedModuleInfo::ImportInfo>& imports =
489 SharedModuleInfo::GetImports(extension());
490 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i;
491 for (i = imports.begin(); i != imports.end(); ++i) {
492 Version version_required(i->minimum_version);
493 const Extension* imported_module =
494 service->GetExtensionById(i->extension_id, true);
495 if (imported_module &&
496 !SharedModuleInfo::IsSharedModule(imported_module)) {
497 ReportFailureFromUIThread(CrxInstallerError(
498 CrxInstallerError::ERROR_DECLINED,
499 l10n_util::GetStringFUTF16(
500 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE,
501 base::UTF8ToUTF16(imported_module->name()))));
502 return;
503 } else if (imported_module && (version_required.IsValid() &&
504 imported_module->version()->CompareTo(
505 version_required) < 0)) {
506 ReportFailureFromUIThread(CrxInstallerError(
507 CrxInstallerError::ERROR_DECLINED,
508 l10n_util::GetStringFUTF16(
509 IDS_EXTENSION_INSTALL_DEPENDENCY_OLD_VERSION,
510 base::UTF8ToUTF16(imported_module->name()),
511 base::ASCIIToUTF16(i->minimum_version),
512 base::ASCIIToUTF16(imported_module->version()->GetString()))));
513 return;
514 } else if (imported_module &&
515 !SharedModuleInfo::IsExportAllowedByWhitelist(
516 imported_module, extension()->id())) {
517 ReportFailureFromUIThread(CrxInstallerError(
518 CrxInstallerError::ERROR_DECLINED,
519 l10n_util::GetStringFUTF16(
520 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED,
521 base::UTF8ToUTF16(extension()->name()),
522 base::UTF8ToUTF16(imported_module->name()))));
523 return;
528 // Run the policy, requirements and blacklist checks in parallel.
529 install_checker_.Start(
530 ExtensionInstallChecker::CHECK_ALL,
531 false /* fail fast */,
532 base::Bind(&CrxInstaller::OnInstallChecksComplete, this));
535 void CrxInstaller::OnInstallChecksComplete(int failed_checks) {
536 DCHECK_CURRENTLY_ON(BrowserThread::UI);
537 if (!service_weak_)
538 return;
540 // Check for requirement errors.
541 if (!install_checker_.requirement_errors().empty()) {
542 if (error_on_unsupported_requirements_) {
543 ReportFailureFromUIThread(
544 CrxInstallerError(CrxInstallerError::ERROR_DECLINED,
545 base::UTF8ToUTF16(JoinString(
546 install_checker_.requirement_errors(), ' '))));
547 return;
549 install_flags_ |= kInstallFlagHasRequirementErrors;
552 // Check the blacklist state.
553 if (install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE) {
554 install_flags_ |= kInstallFlagIsBlacklistedForMalware;
557 if ((install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE ||
558 install_checker_.blacklist_state() == extensions::BLACKLISTED_UNKNOWN) &&
559 !allow_silent_install_) {
560 // User tried to install a blacklisted extension. Show an error and
561 // refuse to install it.
562 ReportFailureFromUIThread(extensions::CrxInstallerError(
563 CrxInstallerError::ERROR_DECLINED,
564 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED,
565 base::UTF8ToUTF16(extension()->name()))));
566 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX",
567 extension()->location(),
568 Manifest::NUM_LOCATIONS);
569 return;
572 // NOTE: extension may still be blacklisted, but we're forced to silently
573 // install it. In this case, ExtensionService::OnExtensionInstalled needs to
574 // deal with it.
576 // Check for policy errors.
577 if (!install_checker_.policy_error().empty()) {
578 // We don't want to show the error infobar for installs from the WebStore,
579 // because the WebStore already shows an error dialog itself.
580 // Note: |client_| can be NULL in unit_tests!
581 if (extension()->from_webstore() && client_)
582 client_->install_ui()->SetSkipPostInstallUI(true);
583 ReportFailureFromUIThread(
584 CrxInstallerError(CrxInstallerError::ERROR_DECLINED,
585 base::UTF8ToUTF16(install_checker_.policy_error())));
586 return;
589 ConfirmInstall();
592 void CrxInstaller::ConfirmInstall() {
593 DCHECK_CURRENTLY_ON(BrowserThread::UI);
594 ExtensionService* service = service_weak_.get();
595 if (!service || service->browser_terminating())
596 return;
598 if (KioskModeInfo::IsKioskOnly(install_checker_.extension().get())) {
599 bool in_kiosk_mode = false;
600 #if defined(OS_CHROMEOS)
601 user_manager::UserManager* user_manager = user_manager::UserManager::Get();
602 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp();
603 #endif
604 if (!in_kiosk_mode) {
605 ReportFailureFromUIThread(CrxInstallerError(
606 CrxInstallerError::ERROR_DECLINED,
607 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY)));
608 return;
612 // Check whether this install is initiated from the settings page to
613 // update an existing extension or app.
614 CheckUpdateFromSettingsPage();
616 GURL overlapping_url;
617 ExtensionRegistry* registry = ExtensionRegistry::Get(service->profile());
618 const Extension* overlapping_extension =
619 registry->enabled_extensions().GetHostedAppByOverlappingWebExtent(
620 extension()->web_extent());
621 if (overlapping_extension &&
622 overlapping_extension->id() != extension()->id()) {
623 ReportFailureFromUIThread(
624 CrxInstallerError(
625 l10n_util::GetStringFUTF16(
626 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT,
627 base::UTF8ToUTF16(overlapping_extension->name()))));
628 return;
631 current_version_ = ExtensionPrefs::Get(service->profile())
632 ->GetVersionString(extension()->id());
634 if (client_ &&
635 (!allow_silent_install_ || !approved_) &&
636 !update_from_settings_page_) {
637 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
638 client_->ConfirmInstall(this, extension(), show_dialog_callback_);
639 } else {
640 if (!installer_task_runner_->PostTask(
641 FROM_HERE,
642 base::Bind(&CrxInstaller::CompleteInstall, this)))
643 NOTREACHED();
645 return;
648 void CrxInstaller::InstallUIProceed() {
649 DCHECK_CURRENTLY_ON(BrowserThread::UI);
651 ExtensionService* service = service_weak_.get();
652 if (!service || service->browser_terminating())
653 return;
655 // If update_from_settings_page_ boolean is true, this functions is
656 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
657 // and if it is false, this function is called in response to
658 // ExtensionInstallPrompt::ConfirmInstall().
659 if (update_from_settings_page_) {
660 service->GrantPermissionsAndEnableExtension(extension());
661 } else {
662 if (!installer_task_runner_->PostTask(
663 FROM_HERE,
664 base::Bind(&CrxInstaller::CompleteInstall, this)))
665 NOTREACHED();
668 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
671 void CrxInstaller::InstallUIAbort(bool user_initiated) {
672 // If update_from_settings_page_ boolean is true, this functions is
673 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable()
674 // and if it is false, this function is called in response to
675 // ExtensionInstallPrompt::ConfirmInstall().
676 if (!update_from_settings_page_) {
677 std::string histogram_name = user_initiated
678 ? "Extensions.Permissions_InstallCancel2"
679 : "Extensions.Permissions_InstallAbort2";
680 ExtensionService::RecordPermissionMessagesHistogram(
681 extension(), histogram_name.c_str());
683 NotifyCrxInstallComplete(false);
686 Release(); // balanced in ConfirmInstall() or ConfirmReEnable().
688 // We're done. Since we don't post any more tasks to ourself, our ref count
689 // should go to zero and we die. The destructor will clean up the temp dir.
692 void CrxInstaller::CompleteInstall() {
693 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
695 if (!current_version_.empty()) {
696 Version current_version(current_version_);
697 if (current_version.CompareTo(*(extension()->version())) > 0) {
698 ReportFailureFromFileThread(CrxInstallerError(
699 CrxInstallerError::ERROR_DECLINED,
700 l10n_util::GetStringUTF16(
701 extension()->is_app() ? IDS_APP_CANT_DOWNGRADE_VERSION
702 : IDS_EXTENSION_CANT_DOWNGRADE_VERSION)));
703 return;
707 // See how long extension install paths are. This is important on
708 // windows, because file operations may fail if the path to a file
709 // exceeds a small constant. See crbug.com/69693 .
710 UMA_HISTOGRAM_CUSTOM_COUNTS(
711 "Extensions.CrxInstallDirPathLength",
712 install_directory_.value().length(), 0, 500, 100);
714 ExtensionAssetsManager* assets_manager =
715 ExtensionAssetsManager::GetInstance();
716 assets_manager->InstallExtension(
717 extension(),
718 unpacked_extension_root_,
719 install_directory_,
720 profile(),
721 base::Bind(&CrxInstaller::ReloadExtensionAfterInstall, this));
724 void CrxInstaller::ReloadExtensionAfterInstall(
725 const base::FilePath& version_dir) {
726 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
728 if (version_dir.empty()) {
729 ReportFailureFromFileThread(
730 CrxInstallerError(
731 l10n_util::GetStringUTF16(
732 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED)));
733 return;
736 // This is lame, but we must reload the extension because absolute paths
737 // inside the content scripts are established inside InitFromValue() and we
738 // just moved the extension.
739 // TODO(aa): All paths to resources inside extensions should be created
740 // lazily and based on the Extension's root path at that moment.
741 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing
742 // with base::string16
743 std::string extension_id = extension()->id();
744 std::string error;
745 install_checker_.set_extension(
746 file_util::LoadExtension(
747 version_dir,
748 install_source_,
749 extension()->creation_flags() | Extension::REQUIRE_KEY,
750 &error).get());
752 if (extension()) {
753 ReportSuccessFromFileThread();
754 } else {
755 LOG(ERROR) << error << " " << extension_id << " " << download_url_;
756 ReportFailureFromFileThread(CrxInstallerError(base::UTF8ToUTF16(error)));
760 void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) {
761 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
762 if (!BrowserThread::PostTask(
763 BrowserThread::UI, FROM_HERE,
764 base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) {
765 NOTREACHED();
769 void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) {
770 DCHECK_CURRENTLY_ON(BrowserThread::UI);
772 if (!service_weak_.get() || service_weak_->browser_terminating())
773 return;
775 content::NotificationService* service =
776 content::NotificationService::current();
777 service->Notify(extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
778 content::Source<CrxInstaller>(this),
779 content::Details<const extensions::CrxInstallerError>(&error));
781 // This isn't really necessary, it is only used because unit tests expect to
782 // see errors get reported via this interface.
784 // TODO(aa): Need to go through unit tests and clean them up too, probably get
785 // rid of this line.
786 ExtensionErrorReporter::GetInstance()->ReportError(
787 error.message(),
788 false); // Be quiet.
790 if (client_)
791 client_->OnInstallFailure(error);
793 NotifyCrxInstallComplete(false);
795 // Delete temporary files.
796 CleanupTempFiles();
799 void CrxInstaller::ReportSuccessFromFileThread() {
800 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread());
802 // Tracking number of extensions installed by users
803 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD)
804 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2);
806 if (!BrowserThread::PostTask(
807 BrowserThread::UI, FROM_HERE,
808 base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this)))
809 NOTREACHED();
811 // Delete temporary files.
812 CleanupTempFiles();
815 void CrxInstaller::ReportSuccessFromUIThread() {
816 DCHECK_CURRENTLY_ON(BrowserThread::UI);
818 if (!service_weak_.get() || service_weak_->browser_terminating())
819 return;
821 if (!update_from_settings_page_) {
822 // If there is a client, tell the client about installation.
823 if (client_)
824 client_->OnInstallSuccess(extension(), install_icon_.get());
826 // We update the extension's granted permissions if the user already
827 // approved the install (client_ is non NULL), or we are allowed to install
828 // this silently.
829 if ((client_ || allow_silent_install_) && grant_permissions_) {
830 PermissionsUpdater perms_updater(profile());
831 perms_updater.InitializePermissions(extension());
832 perms_updater.GrantActivePermissions(extension());
836 service_weak_->OnExtensionInstalled(
837 extension(), page_ordinal_, install_flags_);
838 NotifyCrxInstallComplete(true);
841 void CrxInstaller::NotifyCrxInstallBegin() {
842 InstallTrackerFactory::GetForBrowserContext(profile())
843 ->OnBeginCrxInstall(expected_id_);
846 void CrxInstaller::NotifyCrxInstallComplete(bool success) {
847 // Some users (such as the download shelf) need to know when a
848 // CRXInstaller is done. Listening for the EXTENSION_* events
849 // is problematic because they don't know anything about the
850 // extension before it is unpacked, so they cannot filter based
851 // on the extension.
852 content::NotificationService::current()->Notify(
853 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
854 content::Source<CrxInstaller>(this),
855 content::Details<const Extension>(success ? extension() : NULL));
857 InstallTrackerFactory::GetForBrowserContext(profile())
858 ->OnFinishCrxInstall(success ? extension()->id() : expected_id_, success);
860 if (success)
861 ConfirmReEnable();
864 void CrxInstaller::CleanupTempFiles() {
865 if (!installer_task_runner_->RunsTasksOnCurrentThread()) {
866 if (!installer_task_runner_->PostTask(
867 FROM_HERE,
868 base::Bind(&CrxInstaller::CleanupTempFiles, this))) {
869 NOTREACHED();
871 return;
874 // Delete the temp directory and crx file as necessary.
875 if (!temp_dir_.value().empty()) {
876 file_util::DeleteFile(temp_dir_, true);
877 temp_dir_ = base::FilePath();
880 if (delete_source_ && !source_file_.value().empty()) {
881 file_util::DeleteFile(source_file_, false);
882 source_file_ = base::FilePath();
886 void CrxInstaller::CheckUpdateFromSettingsPage() {
887 DCHECK_CURRENTLY_ON(BrowserThread::UI);
889 ExtensionService* service = service_weak_.get();
890 if (!service || service->browser_terminating())
891 return;
893 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage)
894 return;
896 const Extension* installed_extension =
897 service->GetInstalledExtension(extension()->id());
898 if (installed_extension) {
899 // Previous version of the extension exists.
900 update_from_settings_page_ = true;
901 expected_id_ = installed_extension->id();
902 install_source_ = installed_extension->location();
903 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE;
907 void CrxInstaller::ConfirmReEnable() {
908 DCHECK_CURRENTLY_ON(BrowserThread::UI);
910 ExtensionService* service = service_weak_.get();
911 if (!service || service->browser_terminating())
912 return;
914 if (!update_from_settings_page_)
915 return;
917 ExtensionPrefs* prefs = ExtensionPrefs::Get(service->profile());
918 if (!prefs->DidExtensionEscalatePermissions(extension()->id()))
919 return;
921 if (client_) {
922 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort().
923 client_->ConfirmReEnable(this, extension());
927 } // namespace extensions