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