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