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