OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/crx_installer.h" | 5 #include "chrome/browser/extensions/crx_installer.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 #include <set> | 8 #include <set> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 apps_require_extension_mime_type_(false), | 126 apps_require_extension_mime_type_(false), |
127 allow_silent_install_(false), | 127 allow_silent_install_(false), |
128 grant_permissions_(true), | 128 grant_permissions_(true), |
129 install_cause_(extension_misc::INSTALL_CAUSE_UNSET), | 129 install_cause_(extension_misc::INSTALL_CAUSE_UNSET), |
130 creation_flags_(Extension::NO_FLAGS), | 130 creation_flags_(Extension::NO_FLAGS), |
131 off_store_install_allow_reason_(OffStoreInstallDisallowed), | 131 off_store_install_allow_reason_(OffStoreInstallDisallowed), |
132 did_handle_successfully_(true), | 132 did_handle_successfully_(true), |
133 error_on_unsupported_requirements_(false), | 133 error_on_unsupported_requirements_(false), |
134 update_from_settings_page_(false), | 134 update_from_settings_page_(false), |
135 install_flags_(kInstallFlagNone), | 135 install_flags_(kInstallFlagNone), |
136 installer_(service_weak->profile()) { | 136 install_checker_(service_weak->profile()) { |
137 installer_task_runner_ = service_weak->GetFileTaskRunner(); | 137 installer_task_runner_ = service_weak->GetFileTaskRunner(); |
138 if (!approval) | 138 if (!approval) |
139 return; | 139 return; |
140 | 140 |
141 CHECK(profile()->IsSameProfile(approval->profile)); | 141 CHECK(profile()->IsSameProfile(approval->profile)); |
142 if (client_) { | 142 if (client_) { |
143 client_->install_ui()->SetUseAppInstalledBubble( | 143 client_->install_ui()->SetUseAppInstalledBubble( |
144 approval->use_app_installed_bubble); | 144 approval->use_app_installed_bubble); |
145 client_->install_ui()->set_skip_post_install_ui( | 145 client_->install_ui()->set_skip_post_install_ui( |
146 approval->skip_post_install_ui); | 146 approval->skip_post_install_ui); |
(...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
362 did_handle_successfully_ = false; | 362 did_handle_successfully_ = false; |
363 | 363 |
364 return CrxInstallerError( | 364 return CrxInstallerError( |
365 CrxInstallerError::ERROR_OFF_STORE, | 365 CrxInstallerError::ERROR_OFF_STORE, |
366 l10n_util::GetStringUTF16( | 366 l10n_util::GetStringUTF16( |
367 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE)); | 367 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE)); |
368 } | 368 } |
369 } | 369 } |
370 } | 370 } |
371 | 371 |
372 if (installer_.extension()->is_app()) { | 372 if (install_checker_.extension()->is_app()) { |
373 // If the app was downloaded, apps_require_extension_mime_type_ | 373 // If the app was downloaded, apps_require_extension_mime_type_ |
374 // will be set. In this case, check that it was served with the | 374 // will be set. In this case, check that it was served with the |
375 // right mime type. Make an exception for file URLs, which come | 375 // right mime type. Make an exception for file URLs, which come |
376 // from the users computer and have no headers. | 376 // from the users computer and have no headers. |
377 if (!download_url_.SchemeIsFile() && | 377 if (!download_url_.SchemeIsFile() && |
378 apps_require_extension_mime_type_ && | 378 apps_require_extension_mime_type_ && |
379 original_mime_type_ != Extension::kMimeType) { | 379 original_mime_type_ != Extension::kMimeType) { |
380 return CrxInstallerError( | 380 return CrxInstallerError( |
381 l10n_util::GetStringFUTF16( | 381 l10n_util::GetStringFUTF16( |
382 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE, | 382 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE, |
(...skipping 14 matching lines...) Expand all Loading... |
397 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE))); | 397 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE))); |
398 } | 398 } |
399 | 399 |
400 // For self-hosted apps, verify that the entire extent is on the same | 400 // For self-hosted apps, verify that the entire extent is on the same |
401 // host (or a subdomain of the host) the download happened from. There's | 401 // host (or a subdomain of the host) the download happened from. There's |
402 // no way for us to verify that the app controls any other hosts. | 402 // no way for us to verify that the app controls any other hosts. |
403 URLPattern pattern(UserScript::ValidUserScriptSchemes()); | 403 URLPattern pattern(UserScript::ValidUserScriptSchemes()); |
404 pattern.SetHost(download_url_.host()); | 404 pattern.SetHost(download_url_.host()); |
405 pattern.SetMatchSubdomains(true); | 405 pattern.SetMatchSubdomains(true); |
406 | 406 |
407 URLPatternSet patterns = installer_.extension()->web_extent(); | 407 URLPatternSet patterns = install_checker_.extension()->web_extent(); |
408 for (URLPatternSet::const_iterator i = patterns.begin(); | 408 for (URLPatternSet::const_iterator i = patterns.begin(); |
409 i != patterns.end(); ++i) { | 409 i != patterns.end(); ++i) { |
410 if (!pattern.MatchesHost(i->host())) { | 410 if (!pattern.MatchesHost(i->host())) { |
411 return CrxInstallerError( | 411 return CrxInstallerError( |
412 l10n_util::GetStringUTF16( | 412 l10n_util::GetStringUTF16( |
413 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST)); | 413 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST)); |
414 } | 414 } |
415 } | 415 } |
416 } | 416 } |
417 } | 417 } |
(...skipping 23 matching lines...) Expand all Loading... |
441 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); | 441 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); |
442 | 442 |
443 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource", | 443 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource", |
444 install_source(), Manifest::NUM_LOCATIONS); | 444 install_source(), Manifest::NUM_LOCATIONS); |
445 | 445 |
446 | 446 |
447 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause", | 447 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause", |
448 install_cause(), | 448 install_cause(), |
449 extension_misc::NUM_INSTALL_CAUSES); | 449 extension_misc::NUM_INSTALL_CAUSES); |
450 | 450 |
451 installer_.set_extension(extension); | 451 install_checker_.set_extension(extension); |
452 temp_dir_ = temp_dir; | 452 temp_dir_ = temp_dir; |
453 if (!install_icon.empty()) | 453 if (!install_icon.empty()) |
454 install_icon_.reset(new SkBitmap(install_icon)); | 454 install_icon_.reset(new SkBitmap(install_icon)); |
455 | 455 |
456 if (original_manifest) | 456 if (original_manifest) |
457 original_manifest_.reset(new Manifest( | 457 original_manifest_.reset(new Manifest( |
458 Manifest::INVALID_LOCATION, | 458 Manifest::INVALID_LOCATION, |
459 scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy()))); | 459 scoped_ptr<base::DictionaryValue>(original_manifest->DeepCopy()))); |
460 | 460 |
461 // We don't have to delete the unpack dir explicity since it is a child of | 461 // We don't have to delete the unpack dir explicity since it is a child of |
462 // the temp dir. | 462 // the temp dir. |
463 unpacked_extension_root_ = extension_dir; | 463 unpacked_extension_root_ = extension_dir; |
464 | 464 |
465 CrxInstallerError error = AllowInstall(extension); | 465 CrxInstallerError error = AllowInstall(extension); |
466 if (error.type() != CrxInstallerError::ERROR_NONE) { | 466 if (error.type() != CrxInstallerError::ERROR_NONE) { |
467 ReportFailureFromFileThread(error); | 467 ReportFailureFromFileThread(error); |
468 return; | 468 return; |
469 } | 469 } |
470 | 470 |
471 if (!BrowserThread::PostTask( | 471 if (!BrowserThread::PostTask(BrowserThread::UI, |
472 BrowserThread::UI, FROM_HERE, | 472 FROM_HERE, |
473 base::Bind(&CrxInstaller::CheckImportsAndRequirements, this))) | 473 base::Bind(&CrxInstaller::CheckInstall, this))) |
474 NOTREACHED(); | 474 NOTREACHED(); |
475 } | 475 } |
476 | 476 |
477 void CrxInstaller::CheckImportsAndRequirements() { | 477 void CrxInstaller::CheckInstall() { |
478 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 478 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
479 ExtensionService* service = service_weak_.get(); | 479 ExtensionService* service = service_weak_.get(); |
480 if (!service || service->browser_terminating()) | 480 if (!service || service->browser_terminating()) |
481 return; | 481 return; |
482 | 482 |
483 if (SharedModuleInfo::ImportsModules(extension())) { | 483 if (SharedModuleInfo::ImportsModules(extension())) { |
484 const std::vector<SharedModuleInfo::ImportInfo>& imports = | 484 const std::vector<SharedModuleInfo::ImportInfo>& imports = |
485 SharedModuleInfo::GetImports(extension()); | 485 SharedModuleInfo::GetImports(extension()); |
486 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i; | 486 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i; |
487 for (i = imports.begin(); i != imports.end(); ++i) { | 487 for (i = imports.begin(); i != imports.end(); ++i) { |
(...skipping 10 matching lines...) Expand all Loading... |
498 !SharedModuleInfo::IsExportAllowedByWhitelist(imported_module, | 498 !SharedModuleInfo::IsExportAllowedByWhitelist(imported_module, |
499 extension()->id())) { | 499 extension()->id())) { |
500 ReportFailureFromUIThread( | 500 ReportFailureFromUIThread( |
501 CrxInstallerError(l10n_util::GetStringFUTF16( | 501 CrxInstallerError(l10n_util::GetStringFUTF16( |
502 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED, | 502 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_WHITELISTED, |
503 base::ASCIIToUTF16(i->extension_id)))); | 503 base::ASCIIToUTF16(i->extension_id)))); |
504 return; | 504 return; |
505 } | 505 } |
506 } | 506 } |
507 } | 507 } |
508 installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked, | 508 |
509 this)); | 509 // Run the policy, requirements and blacklist checks in parallel. |
| 510 install_checker_.Start( |
| 511 ExtensionInstallChecker::CHECK_ALL, |
| 512 false /* fail fast */, |
| 513 base::Bind(&CrxInstaller::OnInstallChecksComplete, this)); |
510 } | 514 } |
511 | 515 |
512 void CrxInstaller::OnRequirementsChecked( | 516 void CrxInstaller::OnInstallChecksComplete(int failed_checks) { |
513 std::vector<std::string> requirement_errors) { | |
514 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 517 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
515 if (!service_weak_) | 518 if (!service_weak_) |
516 return; | 519 return; |
517 | 520 |
518 if (!requirement_errors.empty()) { | 521 // Check for requirement errors. |
| 522 if (!install_checker_.requirement_errors().empty()) { |
519 if (error_on_unsupported_requirements_) { | 523 if (error_on_unsupported_requirements_) { |
520 ReportFailureFromUIThread(CrxInstallerError( | 524 ReportFailureFromUIThread(CrxInstallerError(base::UTF8ToUTF16( |
521 base::UTF8ToUTF16(JoinString(requirement_errors, ' ')))); | 525 JoinString(install_checker_.requirement_errors(), ' ')))); |
522 return; | 526 return; |
523 } | 527 } |
524 install_flags_ |= kInstallFlagHasRequirementErrors; | 528 install_flags_ |= kInstallFlagHasRequirementErrors; |
525 } | 529 } |
526 | 530 |
527 ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted( | 531 // Check the blacklist state. |
528 extension()->id(), | 532 if (install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE) { |
529 base::Bind(&CrxInstaller::OnBlacklistChecked, this)); | |
530 } | |
531 | |
532 void CrxInstaller::OnBlacklistChecked( | |
533 extensions::BlacklistState blacklist_state) { | |
534 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
535 if (!service_weak_) | |
536 return; | |
537 | |
538 if (blacklist_state == extensions::BLACKLISTED_MALWARE) { | |
539 install_flags_ |= kInstallFlagIsBlacklistedForMalware; | 533 install_flags_ |= kInstallFlagIsBlacklistedForMalware; |
540 } | 534 } |
541 | 535 |
542 if ((blacklist_state == extensions::BLACKLISTED_MALWARE || | 536 if ((install_checker_.blacklist_state() == extensions::BLACKLISTED_MALWARE || |
543 blacklist_state == extensions::BLACKLISTED_UNKNOWN) && | 537 install_checker_.blacklist_state() == extensions::BLACKLISTED_UNKNOWN) && |
544 !allow_silent_install_) { | 538 !allow_silent_install_) { |
545 // User tried to install a blacklisted extension. Show an error and | 539 // User tried to install a blacklisted extension. Show an error and |
546 // refuse to install it. | 540 // refuse to install it. |
547 ReportFailureFromUIThread(extensions::CrxInstallerError( | 541 ReportFailureFromUIThread(extensions::CrxInstallerError( |
548 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED, | 542 l10n_util::GetStringFUTF16(IDS_EXTENSION_IS_BLACKLISTED, |
549 base::UTF8ToUTF16(extension()->name())))); | 543 base::UTF8ToUTF16(extension()->name())))); |
550 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX", | 544 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX", |
551 extension()->location(), | 545 extension()->location(), |
552 Manifest::NUM_LOCATIONS); | 546 Manifest::NUM_LOCATIONS); |
553 return; | 547 return; |
554 } | 548 } |
555 | 549 |
556 // NOTE: extension may still be blacklisted, but we're forced to silently | 550 // NOTE: extension may still be blacklisted, but we're forced to silently |
557 // install it. In this case, ExtensionService::OnExtensionInstalled needs to | 551 // install it. In this case, ExtensionService::OnExtensionInstalled needs to |
558 // deal with it. | 552 // deal with it. |
| 553 |
| 554 // Check for policy errors. |
| 555 if (!install_checker_.policy_error().empty()) { |
| 556 // We don't want to show the error infobar for installs from the WebStore, |
| 557 // because the WebStore already shows an error dialog itself. |
| 558 // Note: |client_| can be NULL in unit_tests! |
| 559 if (extension()->from_webstore() && client_) |
| 560 client_->install_ui()->set_skip_post_install_ui(true); |
| 561 ReportFailureFromUIThread( |
| 562 CrxInstallerError(base::UTF8ToUTF16(install_checker_.policy_error()))); |
| 563 return; |
| 564 } |
| 565 |
559 ConfirmInstall(); | 566 ConfirmInstall(); |
560 } | 567 } |
561 | 568 |
562 void CrxInstaller::ConfirmInstall() { | 569 void CrxInstaller::ConfirmInstall() { |
563 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 570 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
564 ExtensionService* service = service_weak_.get(); | 571 ExtensionService* service = service_weak_.get(); |
565 if (!service || service->browser_terminating()) | 572 if (!service || service->browser_terminating()) |
566 return; | 573 return; |
567 | 574 |
568 if (KioskModeInfo::IsKioskOnly(installer_.extension())) { | 575 if (KioskModeInfo::IsKioskOnly(install_checker_.extension())) { |
569 bool in_kiosk_mode = false; | 576 bool in_kiosk_mode = false; |
570 #if defined(OS_CHROMEOS) | 577 #if defined(OS_CHROMEOS) |
571 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); | 578 chromeos::UserManager* user_manager = chromeos::UserManager::Get(); |
572 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp(); | 579 in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp(); |
573 #endif | 580 #endif |
574 if (!in_kiosk_mode) { | 581 if (!in_kiosk_mode) { |
575 ReportFailureFromUIThread(CrxInstallerError( | 582 ReportFailureFromUIThread(CrxInstallerError( |
576 l10n_util::GetStringUTF16( | 583 l10n_util::GetStringUTF16( |
577 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY))); | 584 IDS_EXTENSION_INSTALL_KIOSK_MODE_ONLY))); |
578 return; | 585 return; |
579 } | 586 } |
580 } | 587 } |
581 | 588 |
582 base::string16 error = installer_.CheckManagementPolicy(); | |
583 if (!error.empty()) { | |
584 // We don't want to show the error infobar for installs from the WebStore, | |
585 // because the WebStore already shows an error dialog itself. | |
586 // Note: |client_| can be NULL in unit_tests! | |
587 if (extension()->from_webstore() && client_) | |
588 client_->install_ui()->set_skip_post_install_ui(true); | |
589 ReportFailureFromUIThread(CrxInstallerError(error)); | |
590 return; | |
591 } | |
592 | |
593 // Check whether this install is initiated from the settings page to | 589 // Check whether this install is initiated from the settings page to |
594 // update an existing extension or app. | 590 // update an existing extension or app. |
595 CheckUpdateFromSettingsPage(); | 591 CheckUpdateFromSettingsPage(); |
596 | 592 |
597 GURL overlapping_url; | 593 GURL overlapping_url; |
598 const Extension* overlapping_extension = | 594 const Extension* overlapping_extension = |
599 service->extensions()->GetHostedAppByOverlappingWebExtent( | 595 service->extensions()->GetHostedAppByOverlappingWebExtent( |
600 extension()->web_extent()); | 596 extension()->web_extent()); |
601 if (overlapping_extension && | 597 if (overlapping_extension && |
602 overlapping_extension->id() != extension()->id()) { | 598 overlapping_extension->id() != extension()->id()) { |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
715 | 711 |
716 // This is lame, but we must reload the extension because absolute paths | 712 // This is lame, but we must reload the extension because absolute paths |
717 // inside the content scripts are established inside InitFromValue() and we | 713 // inside the content scripts are established inside InitFromValue() and we |
718 // just moved the extension. | 714 // just moved the extension. |
719 // TODO(aa): All paths to resources inside extensions should be created | 715 // TODO(aa): All paths to resources inside extensions should be created |
720 // lazily and based on the Extension's root path at that moment. | 716 // lazily and based on the Extension's root path at that moment. |
721 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing | 717 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing |
722 // with base::string16 | 718 // with base::string16 |
723 std::string extension_id = extension()->id(); | 719 std::string extension_id = extension()->id(); |
724 std::string error; | 720 std::string error; |
725 installer_.set_extension( | 721 install_checker_.set_extension( |
726 file_util::LoadExtension( | 722 file_util::LoadExtension( |
727 version_dir, | 723 version_dir, |
728 install_source_, | 724 install_source_, |
729 extension()->creation_flags() | Extension::REQUIRE_KEY, | 725 extension()->creation_flags() | Extension::REQUIRE_KEY, |
730 &error).get()); | 726 &error).get()); |
731 | 727 |
732 if (extension()) { | 728 if (extension()) { |
733 ReportSuccessFromFileThread(); | 729 ReportSuccessFromFileThread(); |
734 } else { | 730 } else { |
735 LOG(ERROR) << error << " " << extension_id << " " << download_url_; | 731 LOG(ERROR) << error << " " << extension_id << " " << download_url_; |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
898 if (!prefs->DidExtensionEscalatePermissions(extension()->id())) | 894 if (!prefs->DidExtensionEscalatePermissions(extension()->id())) |
899 return; | 895 return; |
900 | 896 |
901 if (client_) { | 897 if (client_) { |
902 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort(). | 898 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort(). |
903 client_->ConfirmReEnable(this, extension()); | 899 client_->ConfirmReEnable(this, extension()); |
904 } | 900 } |
905 } | 901 } |
906 | 902 |
907 } // namespace extensions | 903 } // namespace extensions |
OLD | NEW |