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