crx_installer.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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. 4 5#include "chrome/browser/extensions/crx_installer.h" 6 7#include <map> 8#include <set> 9 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.h" 23#include "base/version.h" 24#include "chrome/browser/extensions/convert_user_script.h" 25#include "chrome/browser/extensions/convert_web_app.h" 26#include "chrome/browser/extensions/crx_installer_error.h" 27#include "chrome/browser/extensions/extension_error_reporter.h" 28#include "chrome/browser/extensions/extension_install_ui.h" 29#include "chrome/browser/extensions/extension_service.h" 30#include "chrome/browser/extensions/extension_system.h" 31#include "chrome/browser/extensions/permissions_updater.h" 32#include "chrome/browser/extensions/webstore_installer.h" 33#include "chrome/browser/profiles/profile.h" 34#include "chrome/browser/web_applications/web_app.h" 35#include "chrome/common/chrome_notification_types.h" 36#include "chrome/common/chrome_paths.h" 37#include "chrome/common/extensions/extension_constants.h" 38#include "chrome/common/extensions/extension_file_util.h" 39#include "chrome/common/extensions/extension_icon_set.h" 40#include "chrome/common/extensions/feature_switch.h" 41#include "chrome/common/extensions/manifest.h" 42#include "chrome/common/extensions/manifest_handlers/icons_handler.h" 43#include "chrome/common/extensions/manifest_handlers/shared_module_info.h" 44#include "chrome/common/extensions/manifest_url_handler.h" 45#include "chrome/common/extensions/user_script.h" 46#include "content/public/browser/browser_thread.h" 47#include "content/public/browser/notification_service.h" 48#include "content/public/browser/resource_dispatcher_host.h" 49#include "content/public/browser/user_metrics.h" 50#include "grit/chromium_strings.h" 51#include "grit/generated_resources.h" 52#include "grit/theme_resources.h" 53#include "third_party/skia/include/core/SkBitmap.h" 54#include "ui/base/l10n/l10n_util.h" 55#include "ui/base/resource/resource_bundle.h" 56 57using content::BrowserThread; 58using content::UserMetricsAction; 59using extensions::SharedModuleInfo; 60 61namespace extensions { 62 63namespace { 64 65// Used in histograms; do not change order. 66enum OffStoreInstallDecision { 67 OnStoreInstall, 68 OffStoreInstallAllowed, 69 OffStoreInstallDisallowed, 70 NumOffStoreInstallDecision 71}; 72 73} // namespace 74 75// static 76scoped_refptr<CrxInstaller> CrxInstaller::Create( 77 ExtensionService* frontend, 78 ExtensionInstallPrompt* client) { 79 return new CrxInstaller(frontend->AsWeakPtr(), client, NULL); 80} 81 82// static 83scoped_refptr<CrxInstaller> CrxInstaller::Create( 84 ExtensionService* service, 85 ExtensionInstallPrompt* client, 86 const WebstoreInstaller::Approval* approval) { 87 return new CrxInstaller(service->AsWeakPtr(), client, approval); 88} 89 90CrxInstaller::CrxInstaller( 91 base::WeakPtr<ExtensionService> service_weak, 92 ExtensionInstallPrompt* client, 93 const WebstoreInstaller::Approval* approval) 94 : install_directory_(service_weak->install_directory()), 95 install_source_(Manifest::INTERNAL), 96 approved_(false), 97 extensions_enabled_(service_weak->extensions_enabled()), 98 delete_source_(false), 99 create_app_shortcut_(false), 100 service_weak_(service_weak), 101 client_(client), 102 apps_require_extension_mime_type_(false), 103 allow_silent_install_(false), 104 bypass_blacklist_for_test_(false), 105 install_cause_(extension_misc::INSTALL_CAUSE_UNSET), 106 creation_flags_(Extension::NO_FLAGS), 107 off_store_install_allow_reason_(OffStoreInstallDisallowed), 108 did_handle_successfully_(true), 109 error_on_unsupported_requirements_(false), 110 has_requirement_errors_(false), 111 install_wait_for_idle_(true), 112 update_from_settings_page_(false), 113 installer_(service_weak->profile()) { 114 installer_task_runner_ = service_weak->GetFileTaskRunner(); 115 if (!approval) 116 return; 117 118 CHECK(profile()->IsSameProfile(approval->profile)); 119 if (client_) { 120 client_->install_ui()->SetUseAppInstalledBubble( 121 approval->use_app_installed_bubble); 122 client_->install_ui()->SetSkipPostInstallUI(approval->skip_post_install_ui); 123 } 124 125 if (approval->skip_install_dialog) { 126 // Mark the extension as approved, but save the expected manifest and ID 127 // so we can check that they match the CRX's. 128 approved_ = true; 129 expected_manifest_.reset(approval->manifest->DeepCopy()); 130 expected_id_ = approval->extension_id; 131 } 132 133 show_dialog_callback_ = approval->show_dialog_callback; 134} 135 136CrxInstaller::~CrxInstaller() { 137 // Make sure the UI is deleted on the ui thread. 138 if (client_) { 139 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, client_); 140 client_ = NULL; 141 } 142} 143 144void CrxInstaller::InstallCrx(const base::FilePath& source_file) { 145 source_file_ = source_file; 146 147 scoped_refptr<SandboxedUnpacker> unpacker( 148 new SandboxedUnpacker(source_file, 149 content::ResourceDispatcherHost::Get() != NULL, 150 install_source_, 151 creation_flags_, 152 install_directory_, 153 installer_task_runner_.get(), 154 this)); 155 156 if (!installer_task_runner_->PostTask( 157 FROM_HERE, 158 base::Bind(&SandboxedUnpacker::Start, unpacker.get()))) 159 NOTREACHED(); 160} 161 162void CrxInstaller::InstallUserScript(const base::FilePath& source_file, 163 const GURL& download_url) { 164 DCHECK(!download_url.is_empty()); 165 166 source_file_ = source_file; 167 download_url_ = download_url; 168 169 if (!installer_task_runner_->PostTask( 170 FROM_HERE, 171 base::Bind(&CrxInstaller::ConvertUserScriptOnFileThread, this))) 172 NOTREACHED(); 173} 174 175void CrxInstaller::ConvertUserScriptOnFileThread() { 176 string16 error; 177 scoped_refptr<Extension> extension = ConvertUserScriptToExtension( 178 source_file_, download_url_, install_directory_, &error); 179 if (!extension.get()) { 180 ReportFailureFromFileThread(CrxInstallerError(error)); 181 return; 182 } 183 184 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get()); 185} 186 187void CrxInstaller::InstallWebApp(const WebApplicationInfo& web_app) { 188 if (!installer_task_runner_->PostTask( 189 FROM_HERE, 190 base::Bind(&CrxInstaller::ConvertWebAppOnFileThread, 191 this, 192 web_app, 193 install_directory_))) 194 NOTREACHED(); 195} 196 197void CrxInstaller::ConvertWebAppOnFileThread( 198 const WebApplicationInfo& web_app, 199 const base::FilePath& install_directory) { 200 string16 error; 201 scoped_refptr<Extension> extension( 202 ConvertWebAppToExtension(web_app, base::Time::Now(), install_directory)); 203 if (!extension.get()) { 204 // Validation should have stopped any potential errors before getting here. 205 NOTREACHED() << "Could not convert web app to extension."; 206 return; 207 } 208 209 // TODO(aa): conversion data gets lost here :( 210 211 OnUnpackSuccess(extension->path(), extension->path(), NULL, extension.get()); 212} 213 214CrxInstallerError CrxInstaller::AllowInstall(const Extension* extension) { 215 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); 216 217 // Make sure the expected ID matches if one was supplied or if we want to 218 // bypass the prompt. 219 if ((approved_ || !expected_id_.empty()) && 220 expected_id_ != extension->id()) { 221 return CrxInstallerError( 222 l10n_util::GetStringFUTF16(IDS_EXTENSION_INSTALL_UNEXPECTED_ID, 223 ASCIIToUTF16(expected_id_), 224 ASCIIToUTF16(extension->id()))); 225 } 226 227 if (expected_version_.get() && 228 !expected_version_->Equals(*extension->version())) { 229 return CrxInstallerError( 230 l10n_util::GetStringFUTF16( 231 IDS_EXTENSION_INSTALL_UNEXPECTED_VERSION, 232 ASCIIToUTF16(expected_version_->GetString()), 233 ASCIIToUTF16(extension->version()->GetString()))); 234 } 235 236 // Make sure the manifests match if we want to bypass the prompt. 237 if (approved_ && 238 (!expected_manifest_.get() || 239 !expected_manifest_->Equals(original_manifest_.get()))) { 240 return CrxInstallerError( 241 l10n_util::GetStringUTF16(IDS_EXTENSION_MANIFEST_INVALID)); 242 } 243 244 // The checks below are skipped for themes and external installs. 245 // TODO(pamg): After ManagementPolicy refactoring is complete, remove this 246 // and other uses of install_source_ that are no longer needed now that the 247 // SandboxedUnpacker sets extension->location. 248 if (extension->is_theme() || Manifest::IsExternalLocation(install_source_)) 249 return CrxInstallerError(); 250 251 if (!extensions_enabled_) { 252 return CrxInstallerError( 253 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_NOT_ENABLED)); 254 } 255 256 if (install_cause_ == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) { 257 if (FeatureSwitch::easy_off_store_install()->IsEnabled()) { 258 const char* kHistogramName = "Extensions.OffStoreInstallDecisionEasy"; 259 if (is_gallery_install()) { 260 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall, 261 NumOffStoreInstallDecision); 262 } else { 263 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed, 264 NumOffStoreInstallDecision); 265 } 266 } else { 267 const char* kHistogramName = "Extensions.OffStoreInstallDecisionHard"; 268 if (is_gallery_install()) { 269 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OnStoreInstall, 270 NumOffStoreInstallDecision); 271 } else if (off_store_install_allow_reason_ != OffStoreInstallDisallowed) { 272 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallAllowed, 273 NumOffStoreInstallDecision); 274 UMA_HISTOGRAM_ENUMERATION("Extensions.OffStoreInstallAllowReason", 275 off_store_install_allow_reason_, 276 NumOffStoreInstallAllowReasons); 277 } else { 278 UMA_HISTOGRAM_ENUMERATION(kHistogramName, OffStoreInstallDisallowed, 279 NumOffStoreInstallDecision); 280 // Don't delete source in this case so that the user can install 281 // manually if they want. 282 delete_source_ = false; 283 did_handle_successfully_ = false; 284 285 return CrxInstallerError( 286 CrxInstallerError::ERROR_OFF_STORE, 287 l10n_util::GetStringUTF16( 288 IDS_EXTENSION_INSTALL_DISALLOWED_ON_SITE)); 289 } 290 } 291 } 292 293 if (installer_.extension()->is_app()) { 294 // If the app was downloaded, apps_require_extension_mime_type_ 295 // will be set. In this case, check that it was served with the 296 // right mime type. Make an exception for file URLs, which come 297 // from the users computer and have no headers. 298 if (!download_url_.SchemeIsFile() && 299 apps_require_extension_mime_type_ && 300 original_mime_type_ != Extension::kMimeType) { 301 return CrxInstallerError( 302 l10n_util::GetStringFUTF16( 303 IDS_EXTENSION_INSTALL_INCORRECT_APP_CONTENT_TYPE, 304 ASCIIToUTF16(Extension::kMimeType))); 305 } 306 307 // If the client_ is NULL, then the app is either being installed via 308 // an internal mechanism like sync, external_extensions, or default apps. 309 // In that case, we don't want to enforce things like the install origin. 310 if (!is_gallery_install() && client_) { 311 // For apps with a gallery update URL, require that they be installed 312 // from the gallery. 313 // TODO(erikkay) Apply this rule for paid extensions and themes as well. 314 if (ManifestURL::UpdatesFromGallery(extension)) { 315 return CrxInstallerError( 316 l10n_util::GetStringFUTF16( 317 IDS_EXTENSION_DISALLOW_NON_DOWNLOADED_GALLERY_INSTALLS, 318 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE))); 319 } 320 321 // For self-hosted apps, verify that the entire extent is on the same 322 // host (or a subdomain of the host) the download happened from. There's 323 // no way for us to verify that the app controls any other hosts. 324 URLPattern pattern(UserScript::ValidUserScriptSchemes()); 325 pattern.SetHost(download_url_.host()); 326 pattern.SetMatchSubdomains(true); 327 328 URLPatternSet patterns = installer_.extension()->web_extent(); 329 for (URLPatternSet::const_iterator i = patterns.begin(); 330 i != patterns.end(); ++i) { 331 if (!pattern.MatchesHost(i->host())) { 332 return CrxInstallerError( 333 l10n_util::GetStringUTF16( 334 IDS_EXTENSION_INSTALL_INCORRECT_INSTALL_HOST)); 335 } 336 } 337 } 338 } 339 340 return CrxInstallerError(); 341} 342 343void CrxInstaller::OnUnpackFailure(const string16& error_message) { 344 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); 345 346 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallSource", 347 install_source(), Manifest::NUM_LOCATIONS); 348 349 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackFailureInstallCause", 350 install_cause(), 351 extension_misc::NUM_INSTALL_CAUSES); 352 353 ReportFailureFromFileThread(CrxInstallerError(error_message)); 354} 355 356void CrxInstaller::OnUnpackSuccess(const base::FilePath& temp_dir, 357 const base::FilePath& extension_dir, 358 const DictionaryValue* original_manifest, 359 const Extension* extension) { 360 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); 361 362 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallSource", 363 install_source(), Manifest::NUM_LOCATIONS); 364 365 366 UMA_HISTOGRAM_ENUMERATION("Extensions.UnpackSuccessInstallCause", 367 install_cause(), 368 extension_misc::NUM_INSTALL_CAUSES); 369 370 installer_.set_extension(extension); 371 temp_dir_ = temp_dir; 372 373 if (original_manifest) 374 original_manifest_.reset(new Manifest( 375 Manifest::INVALID_LOCATION, 376 scoped_ptr<DictionaryValue>(original_manifest->DeepCopy()))); 377 378 // We don't have to delete the unpack dir explicity since it is a child of 379 // the temp dir. 380 unpacked_extension_root_ = extension_dir; 381 382 CrxInstallerError error = AllowInstall(extension); 383 if (error.type() != CrxInstallerError::ERROR_NONE) { 384 ReportFailureFromFileThread(error); 385 return; 386 } 387 388 if (client_) { 389 IconsInfo::DecodeIcon(installer_.extension(), 390 extension_misc::EXTENSION_ICON_LARGE, 391 ExtensionIconSet::MATCH_BIGGER, 392 &install_icon_); 393 } 394 395 if (!BrowserThread::PostTask( 396 BrowserThread::UI, FROM_HERE, 397 base::Bind(&CrxInstaller::CheckImportsAndRequirements, this))) 398 NOTREACHED(); 399} 400 401void CrxInstaller::CheckImportsAndRequirements() { 402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 403 ExtensionService* service = service_weak_.get(); 404 if (!service || service->browser_terminating()) 405 return; 406 407 if (SharedModuleInfo::ImportsModules(extension())) { 408 const std::vector<SharedModuleInfo::ImportInfo>& imports = 409 SharedModuleInfo::GetImports(extension()); 410 std::vector<SharedModuleInfo::ImportInfo>::const_iterator i; 411 for (i = imports.begin(); i != imports.end(); ++i) { 412 Version version_required(i->minimum_version); 413 const Extension* imported_module = 414 service->GetExtensionById(i->extension_id, true); 415 if (!imported_module || 416 (version_required.IsValid() && 417 imported_module->version()->CompareTo(version_required) < 0)) { 418 ReportFailureFromUIThread( 419 CrxInstallerError(l10n_util::GetStringFUTF16( 420 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_FOUND, 421 ASCIIToUTF16(i->extension_id), 422 ASCIIToUTF16(i->minimum_version)))); 423 return; 424 } 425 if (!SharedModuleInfo::IsSharedModule(imported_module)) { 426 ReportFailureFromUIThread( 427 CrxInstallerError(l10n_util::GetStringFUTF16( 428 IDS_EXTENSION_INSTALL_DEPENDENCY_NOT_SHARED_MODULE, 429 ASCIIToUTF16(i->extension_id)))); 430 return; 431 } 432 } 433 } 434 installer_.CheckRequirements(base::Bind(&CrxInstaller::OnRequirementsChecked, 435 this)); 436} 437 438void CrxInstaller::OnRequirementsChecked( 439 std::vector<std::string> requirement_errors) { 440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 441 if (!requirement_errors.empty()) { 442 if (error_on_unsupported_requirements_) { 443 ReportFailureFromUIThread(CrxInstallerError( 444 UTF8ToUTF16(JoinString(requirement_errors, ' ')))); 445 return; 446 } 447 has_requirement_errors_ = true; 448 } 449 ConfirmInstall(); 450} 451 452void CrxInstaller::ConfirmInstall() { 453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 454 ExtensionService* service = service_weak_.get(); 455 if (!service || service->browser_terminating()) 456 return; 457 458 string16 error = installer_.CheckManagementPolicy(); 459 if (!error.empty()) { 460 ReportFailureFromUIThread(CrxInstallerError(error)); 461 return; 462 } 463 464 // Check whether this install is initiated from the settings page to 465 // update an existing extension or app. 466 CheckUpdateFromSettingsPage(); 467 468 GURL overlapping_url; 469 const Extension* overlapping_extension = 470 service->extensions()->GetHostedAppByOverlappingWebExtent( 471 extension()->web_extent()); 472 if (overlapping_extension && 473 overlapping_extension->id() != extension()->id()) { 474 ReportFailureFromUIThread( 475 CrxInstallerError( 476 l10n_util::GetStringFUTF16( 477 IDS_EXTENSION_OVERLAPPING_WEB_EXTENT, 478 UTF8ToUTF16(overlapping_extension->name())))); 479 return; 480 } 481 482 current_version_ = 483 service->extension_prefs()->GetVersionString(extension()->id()); 484 485 if (client_ && 486 (!allow_silent_install_ || !approved_) && 487 !update_from_settings_page_) { 488 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort(). 489 client_->ConfirmInstall(this, extension(), show_dialog_callback_); 490 } else { 491 if (!installer_task_runner_->PostTask( 492 FROM_HERE, 493 base::Bind(&CrxInstaller::CompleteInstall, this))) 494 NOTREACHED(); 495 } 496 return; 497} 498 499void CrxInstaller::InstallUIProceed() { 500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 501 502 ExtensionService* service = service_weak_.get(); 503 if (!service || service->browser_terminating()) 504 return; 505 506 // If update_from_settings_page_ boolean is true, this functions is 507 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable() 508 // and if it is false, this function is called in response to 509 // ExtensionInstallPrompt::ConfirmInstall(). 510 if (update_from_settings_page_) { 511 service->GrantPermissionsAndEnableExtension(extension()); 512 } else { 513 if (!installer_task_runner_->PostTask( 514 FROM_HERE, 515 base::Bind(&CrxInstaller::CompleteInstall, this))) 516 NOTREACHED(); 517 } 518 519 Release(); // balanced in ConfirmInstall() or ConfirmReEnable(). 520} 521 522void CrxInstaller::InstallUIAbort(bool user_initiated) { 523 // If update_from_settings_page_ boolean is true, this functions is 524 // getting called in response to ExtensionInstallPrompt::ConfirmReEnable() 525 // and if it is false, this function is called in response to 526 // ExtensionInstallPrompt::ConfirmInstall(). 527 if (!update_from_settings_page_) { 528 std::string histogram_name = user_initiated ? 529 "Extensions.Permissions_InstallCancel" : 530 "Extensions.Permissions_InstallAbort"; 531 ExtensionService::RecordPermissionMessagesHistogram( 532 extension(), histogram_name.c_str()); 533 534 // Kill the theme loading bubble. 535 content::NotificationService* service = 536 content::NotificationService::current(); 537 service->Notify(chrome::NOTIFICATION_NO_THEME_DETECTED, 538 content::Source<CrxInstaller>(this), 539 content::NotificationService::NoDetails()); 540 541 NotifyCrxInstallComplete(false); 542 } 543 544 Release(); // balanced in ConfirmInstall() or ConfirmReEnable(). 545 546 // We're done. Since we don't post any more tasks to ourself, our ref count 547 // should go to zero and we die. The destructor will clean up the temp dir. 548} 549 550void CrxInstaller::CompleteInstall() { 551 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); 552 553 if (!current_version_.empty()) { 554 Version current_version(current_version_); 555 if (current_version.CompareTo(*(extension()->version())) > 0) { 556 ReportFailureFromFileThread( 557 CrxInstallerError( 558 l10n_util::GetStringUTF16(extension()->is_app() ? 559 IDS_APP_CANT_DOWNGRADE_VERSION : 560 IDS_EXTENSION_CANT_DOWNGRADE_VERSION))); 561 return; 562 } 563 } 564 565 // See how long extension install paths are. This is important on 566 // windows, because file operations may fail if the path to a file 567 // exceeds a small constant. See crbug.com/69693 . 568 UMA_HISTOGRAM_CUSTOM_COUNTS( 569 "Extensions.CrxInstallDirPathLength", 570 install_directory_.value().length(), 0, 500, 100); 571 572 base::FilePath version_dir = extension_file_util::InstallExtension( 573 unpacked_extension_root_, 574 extension()->id(), 575 extension()->VersionString(), 576 install_directory_); 577 if (version_dir.empty()) { 578 ReportFailureFromFileThread( 579 CrxInstallerError( 580 l10n_util::GetStringUTF16( 581 IDS_EXTENSION_MOVE_DIRECTORY_TO_PROFILE_FAILED))); 582 return; 583 } 584 585 // This is lame, but we must reload the extension because absolute paths 586 // inside the content scripts are established inside InitFromValue() and we 587 // just moved the extension. 588 // TODO(aa): All paths to resources inside extensions should be created 589 // lazily and based on the Extension's root path at that moment. 590 // TODO(rdevlin.cronin): Continue removing std::string errors and replacing 591 // with string16 592 std::string extension_id = extension()->id(); 593 std::string error; 594 installer_.set_extension(extension_file_util::LoadExtension( 595 version_dir, 596 install_source_, 597 extension()->creation_flags() | Extension::REQUIRE_KEY, 598 &error)); 599 600 if (extension()) { 601 ReportSuccessFromFileThread(); 602 } else { 603 LOG(ERROR) << error << " " << extension_id << " " << download_url_; 604 ReportFailureFromFileThread(CrxInstallerError(UTF8ToUTF16(error))); 605 } 606 607} 608 609void CrxInstaller::ReportFailureFromFileThread(const CrxInstallerError& error) { 610 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); 611 if (!BrowserThread::PostTask( 612 BrowserThread::UI, FROM_HERE, 613 base::Bind(&CrxInstaller::ReportFailureFromUIThread, this, error))) { 614 NOTREACHED(); 615 } 616} 617 618void CrxInstaller::ReportFailureFromUIThread(const CrxInstallerError& error) { 619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 620 621 content::NotificationService* service = 622 content::NotificationService::current(); 623 service->Notify(chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, 624 content::Source<CrxInstaller>(this), 625 content::Details<const string16>(&error.message())); 626 627 // This isn't really necessary, it is only used because unit tests expect to 628 // see errors get reported via this interface. 629 // 630 // TODO(aa): Need to go through unit tests and clean them up too, probably get 631 // rid of this line. 632 ExtensionErrorReporter::GetInstance()->ReportError( 633 error.message(), false); // quiet 634 635 if (client_) 636 client_->OnInstallFailure(error); 637 638 NotifyCrxInstallComplete(false); 639 640 // Delete temporary files. 641 CleanupTempFiles(); 642} 643 644void CrxInstaller::ReportSuccessFromFileThread() { 645 DCHECK(installer_task_runner_->RunsTasksOnCurrentThread()); 646 647 // Tracking number of extensions installed by users 648 if (install_cause() == extension_misc::INSTALL_CAUSE_USER_DOWNLOAD) 649 UMA_HISTOGRAM_ENUMERATION("Extensions.ExtensionInstalled", 1, 2); 650 651 if (!BrowserThread::PostTask( 652 BrowserThread::UI, FROM_HERE, 653 base::Bind(&CrxInstaller::ReportSuccessFromUIThread, this))) 654 NOTREACHED(); 655 656 // Delete temporary files. 657 CleanupTempFiles(); 658} 659 660void CrxInstaller::ReportSuccessFromUIThread() { 661 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 662 663 if (!service_weak_.get() || service_weak_->browser_terminating()) 664 return; 665 666 if (!update_from_settings_page_) { 667 // If there is a client, tell the client about installation. 668 if (client_) 669 client_->OnInstallSuccess(extension(), install_icon_.get()); 670 671 // We update the extension's granted permissions if the user already 672 // approved the install (client_ is non NULL), or we are allowed to install 673 // this silently. 674 if (client_ || allow_silent_install_) { 675 PermissionsUpdater perms_updater(profile()); 676 perms_updater.GrantActivePermissions(extension()); 677 } 678 } 679 680 // Install the extension if it's not blacklisted, but notify either way. 681 base::Closure on_success = 682 base::Bind(&ExtensionService::OnExtensionInstalled, 683 service_weak_, 684 extension(), 685 page_ordinal_, 686 has_requirement_errors_, 687 install_wait_for_idle_); 688 if (bypass_blacklist_for_test_) { 689 HandleIsBlacklistedResponse(on_success, false); 690 } else { 691 ExtensionSystem::Get(profile())->blacklist()->IsBlacklisted( 692 extension()->id(), 693 base::Bind(&CrxInstaller::HandleIsBlacklistedResponse, 694 this, 695 on_success)); 696 } 697} 698 699void CrxInstaller::HandleIsBlacklistedResponse( 700 const base::Closure& on_success, 701 bool is_blacklisted) { 702 if (is_blacklisted) { 703 string16 error = l10n_util::GetStringFUTF16( 704 IDS_EXTENSION_IS_BLACKLISTED, 705 UTF8ToUTF16(extension()->name())); 706 make_scoped_ptr(ExtensionInstallUI::Create(profile()))->OnInstallFailure( 707 extensions::CrxInstallerError(error)); 708 // Show error via reporter to make tests happy. 709 ExtensionErrorReporter::GetInstance()->ReportError(error, false); // quiet 710 UMA_HISTOGRAM_ENUMERATION("ExtensionBlacklist.BlockCRX", 711 extension()->location(), 712 Manifest::NUM_LOCATIONS); 713 } else { 714 on_success.Run(); 715 } 716 NotifyCrxInstallComplete(!is_blacklisted); 717} 718 719void CrxInstaller::NotifyCrxInstallComplete(bool success) { 720 // Some users (such as the download shelf) need to know when a 721 // CRXInstaller is done. Listening for the EXTENSION_* events 722 // is problematic because they don't know anything about the 723 // extension before it is unpacked, so they cannot filter based 724 // on the extension. 725 content::NotificationService::current()->Notify( 726 chrome::NOTIFICATION_CRX_INSTALLER_DONE, 727 content::Source<CrxInstaller>(this), 728 content::Details<const Extension>( 729 success ? extension() : NULL)); 730 731 if (success) 732 ConfirmReEnable(); 733 734} 735 736void CrxInstaller::CleanupTempFiles() { 737 if (!installer_task_runner_->RunsTasksOnCurrentThread()) { 738 if (!installer_task_runner_->PostTask( 739 FROM_HERE, 740 base::Bind(&CrxInstaller::CleanupTempFiles, this))) { 741 NOTREACHED(); 742 } 743 return; 744 } 745 746 // Delete the temp directory and crx file as necessary. 747 if (!temp_dir_.value().empty()) { 748 extension_file_util::DeleteFile(temp_dir_, true); 749 temp_dir_ = base::FilePath(); 750 } 751 752 if (delete_source_ && !source_file_.value().empty()) { 753 extension_file_util::DeleteFile(source_file_, false); 754 source_file_ = base::FilePath(); 755 } 756} 757 758void CrxInstaller::CheckUpdateFromSettingsPage() { 759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 760 761 ExtensionService* service = service_weak_.get(); 762 if (!service || service->browser_terminating()) 763 return; 764 765 if (off_store_install_allow_reason_ != OffStoreInstallAllowedFromSettingsPage) 766 return; 767 768 const Extension* installed_extension = 769 service->GetInstalledExtension(extension()->id()); 770 if (installed_extension) { 771 // Previous version of the extension exists. 772 update_from_settings_page_ = true; 773 expected_id_ = installed_extension->id(); 774 install_source_ = installed_extension->location(); 775 install_cause_ = extension_misc::INSTALL_CAUSE_UPDATE; 776 } 777} 778 779void CrxInstaller::ConfirmReEnable() { 780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 781 782 ExtensionService* service = service_weak_.get(); 783 if (!service || service->browser_terminating()) 784 return; 785 786 if (!update_from_settings_page_) 787 return; 788 789 ExtensionPrefs* prefs = service->extension_prefs(); 790 if (!prefs->DidExtensionEscalatePermissions(extension()->id())) 791 return; 792 793 if (client_) { 794 AddRef(); // Balanced in InstallUIProceed() and InstallUIAbort(). 795 client_->ConfirmReEnable(this, extension()); 796 } 797} 798 799} // namespace extensions 800