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/webstore_installer.h" 6 7 #include <vector> 8 9 #include "base/basictypes.h" 10 #include "base/bind.h" 11 #include "base/command_line.h" 12 #include "base/file_util.h" 13 #include "base/metrics/field_trial.h" 14 #include "base/metrics/histogram.h" 15 #include "base/metrics/sparse_histogram.h" 16 #include "base/path_service.h" 17 #include "base/rand_util.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "chrome/browser/browser_process.h" 23 #include "chrome/browser/chrome_notification_types.h" 24 #include "chrome/browser/download/download_crx_util.h" 25 #include "chrome/browser/download/download_prefs.h" 26 #include "chrome/browser/download/download_stats.h" 27 #include "chrome/browser/extensions/crx_installer.h" 28 #include "chrome/browser/extensions/extension_system.h" 29 #include "chrome/browser/extensions/install_tracker.h" 30 #include "chrome/browser/extensions/install_tracker_factory.h" 31 #include "chrome/browser/extensions/install_verifier.h" 32 #include "chrome/browser/profiles/profile.h" 33 #include "chrome/browser/ui/browser_list.h" 34 #include "chrome/browser/ui/tabs/tab_strip_model.h" 35 #include "chrome/common/chrome_paths.h" 36 #include "chrome/common/chrome_switches.h" 37 #include "chrome/common/extensions/extension_constants.h" 38 #include "chrome/common/omaha_query_params/omaha_query_params.h" 39 #include "content/public/browser/browser_thread.h" 40 #include "content/public/browser/download_manager.h" 41 #include "content/public/browser/download_save_info.h" 42 #include "content/public/browser/download_url_parameters.h" 43 #include "content/public/browser/navigation_controller.h" 44 #include "content/public/browser/navigation_entry.h" 45 #include "content/public/browser/notification_details.h" 46 #include "content/public/browser/notification_service.h" 47 #include "content/public/browser/notification_source.h" 48 #include "content/public/browser/render_process_host.h" 49 #include "content/public/browser/render_view_host.h" 50 #include "content/public/browser/web_contents.h" 51 #include "extensions/common/extension.h" 52 #include "extensions/common/manifest_constants.h" 53 #include "extensions/common/manifest_handlers/shared_module_info.h" 54 #include "net/base/escape.h" 55 #include "url/gurl.h" 56 57 #if defined(OS_CHROMEOS) 58 #include "chrome/browser/chromeos/drive/file_system_util.h" 59 #endif 60 61 using chrome::OmahaQueryParams; 62 using content::BrowserContext; 63 using content::BrowserThread; 64 using content::DownloadItem; 65 using content::DownloadManager; 66 using content::NavigationController; 67 using content::DownloadUrlParameters; 68 69 namespace { 70 71 // Key used to attach the Approval to the DownloadItem. 72 const char kApprovalKey[] = "extensions.webstore_installer"; 73 74 const char kInvalidIdError[] = "Invalid id"; 75 const char kDownloadDirectoryError[] = "Could not create download directory"; 76 const char kDownloadCanceledError[] = "Download canceled"; 77 const char kInstallCanceledError[] = "Install canceled"; 78 const char kDownloadInterruptedError[] = "Download interrupted"; 79 const char kInvalidDownloadError[] = 80 "Download was not a valid extension or user script"; 81 const char kDependencyNotFoundError[] = "Dependency not found"; 82 const char kDependencyNotSharedModuleError[] = 83 "Dependency is not shared module"; 84 const char kInlineInstallSource[] = "inline"; 85 const char kDefaultInstallSource[] = "ondemand"; 86 const char kAppLauncherInstallSource[] = "applauncher"; 87 88 // Folder for downloading crx files from the webstore. This is used so that the 89 // crx files don't go via the usual downloads folder. 90 const base::FilePath::CharType kWebstoreDownloadFolder[] = 91 FILE_PATH_LITERAL("Webstore Downloads"); 92 93 base::FilePath* g_download_directory_for_tests = NULL; 94 95 // Must be executed on the FILE thread. 96 void GetDownloadFilePath( 97 const base::FilePath& download_directory, 98 const std::string& id, 99 const base::Callback<void(const base::FilePath&)>& callback) { 100 base::FilePath directory(g_download_directory_for_tests ? 101 *g_download_directory_for_tests : download_directory); 102 103 #if defined(OS_CHROMEOS) 104 // Do not use drive for extension downloads. 105 if (drive::util::IsUnderDriveMountPoint(directory)) 106 directory = DownloadPrefs::GetDefaultDownloadDirectory(); 107 #endif 108 109 // Ensure the download directory exists. TODO(asargent) - make this use 110 // common code from the downloads system. 111 if (!base::DirectoryExists(directory)) { 112 if (!base::CreateDirectory(directory)) { 113 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 114 base::Bind(callback, base::FilePath())); 115 return; 116 } 117 } 118 119 // This is to help avoid a race condition between when we generate this 120 // filename and when the download starts writing to it (think concurrently 121 // running sharded browser tests installing the same test file, for 122 // instance). 123 std::string random_number = 124 base::Uint64ToString(base::RandGenerator(kuint16max)); 125 126 base::FilePath file = 127 directory.AppendASCII(id + "_" + random_number + ".crx"); 128 129 int uniquifier = 130 file_util::GetUniquePathNumber(file, base::FilePath::StringType()); 131 if (uniquifier > 0) { 132 file = file.InsertBeforeExtensionASCII( 133 base::StringPrintf(" (%d)", uniquifier)); 134 } 135 136 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 137 base::Bind(callback, file)); 138 } 139 140 bool UseSeparateWebstoreDownloadDirectory() { 141 const char kFieldTrial[] = "WebstoreDownloadDirectory"; 142 const char kSeparateDirectoryUnderUDD[] = "SeparateDirectoryUnderUDD"; 143 144 std::string field_trial_group = 145 base::FieldTrialList::FindFullName(kFieldTrial); 146 return field_trial_group == kSeparateDirectoryUnderUDD; 147 } 148 149 } // namespace 150 151 namespace extensions { 152 153 // static 154 GURL WebstoreInstaller::GetWebstoreInstallURL( 155 const std::string& extension_id, 156 InstallSource source) { 157 std::string install_source; 158 switch (source) { 159 case INSTALL_SOURCE_INLINE: 160 install_source = kInlineInstallSource; 161 break; 162 case INSTALL_SOURCE_APP_LAUNCHER: 163 install_source = kAppLauncherInstallSource; 164 break; 165 case INSTALL_SOURCE_OTHER: 166 install_source = kDefaultInstallSource; 167 } 168 169 CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 170 if (cmd_line->HasSwitch(switches::kAppsGalleryDownloadURL)) { 171 std::string download_url = 172 cmd_line->GetSwitchValueASCII(switches::kAppsGalleryDownloadURL); 173 return GURL(base::StringPrintf(download_url.c_str(), 174 extension_id.c_str())); 175 } 176 std::vector<std::string> params; 177 params.push_back("id=" + extension_id); 178 if (!install_source.empty()) 179 params.push_back("installsource=" + install_source); 180 params.push_back("lang=" + g_browser_process->GetApplicationLocale()); 181 params.push_back("uc"); 182 std::string url_string = extension_urls::GetWebstoreUpdateUrl().spec(); 183 184 GURL url(url_string + "?response=redirect&" + 185 OmahaQueryParams::Get(OmahaQueryParams::CRX) + "&x=" + 186 net::EscapeQueryParamValue(JoinString(params, '&'), true)); 187 DCHECK(url.is_valid()); 188 189 return url; 190 } 191 192 void WebstoreInstaller::Delegate::OnExtensionDownloadStarted( 193 const std::string& id, 194 content::DownloadItem* item) { 195 } 196 197 void WebstoreInstaller::Delegate::OnExtensionDownloadProgress( 198 const std::string& id, 199 content::DownloadItem* item) { 200 } 201 202 WebstoreInstaller::Approval::Approval() 203 : profile(NULL), 204 use_app_installed_bubble(false), 205 skip_post_install_ui(false), 206 skip_install_dialog(false), 207 enable_launcher(false), 208 manifest_check_level(MANIFEST_CHECK_LEVEL_STRICT), 209 is_ephemeral(false) { 210 } 211 212 scoped_ptr<WebstoreInstaller::Approval> 213 WebstoreInstaller::Approval::CreateWithInstallPrompt(Profile* profile) { 214 scoped_ptr<Approval> result(new Approval()); 215 result->profile = profile; 216 return result.Pass(); 217 } 218 219 scoped_ptr<WebstoreInstaller::Approval> 220 WebstoreInstaller::Approval::CreateForSharedModule(Profile* profile) { 221 scoped_ptr<Approval> result(new Approval()); 222 result->profile = profile; 223 result->skip_install_dialog = true; 224 result->manifest_check_level = MANIFEST_CHECK_LEVEL_NONE; 225 return result.Pass(); 226 } 227 228 scoped_ptr<WebstoreInstaller::Approval> 229 WebstoreInstaller::Approval::CreateWithNoInstallPrompt( 230 Profile* profile, 231 const std::string& extension_id, 232 scoped_ptr<base::DictionaryValue> parsed_manifest, 233 bool strict_manifest_check) { 234 scoped_ptr<Approval> result(new Approval()); 235 result->extension_id = extension_id; 236 result->profile = profile; 237 result->manifest = scoped_ptr<Manifest>( 238 new Manifest(Manifest::INVALID_LOCATION, 239 scoped_ptr<DictionaryValue>(parsed_manifest->DeepCopy()))); 240 result->skip_install_dialog = true; 241 result->manifest_check_level = strict_manifest_check ? 242 MANIFEST_CHECK_LEVEL_STRICT : MANIFEST_CHECK_LEVEL_LOOSE; 243 return result.Pass(); 244 } 245 246 WebstoreInstaller::Approval::~Approval() {} 247 248 const WebstoreInstaller::Approval* WebstoreInstaller::GetAssociatedApproval( 249 const DownloadItem& download) { 250 return static_cast<const Approval*>(download.GetUserData(kApprovalKey)); 251 } 252 253 WebstoreInstaller::WebstoreInstaller(Profile* profile, 254 Delegate* delegate, 255 NavigationController* controller, 256 const std::string& id, 257 scoped_ptr<Approval> approval, 258 InstallSource source) 259 : profile_(profile), 260 delegate_(delegate), 261 controller_(controller), 262 id_(id), 263 install_source_(source), 264 download_item_(NULL), 265 approval_(approval.release()), 266 total_modules_(0), 267 download_started_(false) { 268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 269 DCHECK(controller_); 270 271 registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE, 272 content::NotificationService::AllSources()); 273 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, 274 content::Source<Profile>(profile->GetOriginalProfile())); 275 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR, 276 content::Source<CrxInstaller>(NULL)); 277 } 278 279 void WebstoreInstaller::Start() { 280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 281 AddRef(); // Balanced in ReportSuccess and ReportFailure. 282 283 if (!Extension::IdIsValid(id_)) { 284 ReportFailure(kInvalidIdError, FAILURE_REASON_OTHER); 285 return; 286 } 287 288 ExtensionService* extension_service = 289 ExtensionSystem::Get(profile_)->extension_service(); 290 if (approval_.get() && approval_->dummy_extension) { 291 ExtensionService::ImportStatus status = 292 extension_service->CheckImports(approval_->dummy_extension, 293 &pending_modules_, &pending_modules_); 294 // For this case, it is because some imports are not shared modules. 295 if (status == ExtensionService::IMPORT_STATUS_UNRECOVERABLE) { 296 ReportFailure(kDependencyNotSharedModuleError, 297 FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE); 298 return; 299 } 300 } 301 302 // Add the extension main module into the list. 303 SharedModuleInfo::ImportInfo info; 304 info.extension_id = id_; 305 pending_modules_.push_back(info); 306 307 total_modules_ = pending_modules_.size(); 308 309 std::set<std::string> ids; 310 std::list<SharedModuleInfo::ImportInfo>::const_iterator i; 311 for (i = pending_modules_.begin(); i != pending_modules_.end(); ++i) { 312 ids.insert(i->extension_id); 313 } 314 ExtensionSystem::Get(profile_)->install_verifier()->AddProvisional(ids); 315 316 // TODO(crbug.com/305343): Query manifest of dependencises before 317 // downloading & installing those dependencies. 318 DownloadNextPendingModule(); 319 320 std::string name; 321 if (!approval_->manifest->value()->GetString(manifest_keys::kName, &name)) { 322 NOTREACHED(); 323 } 324 extensions::InstallTracker* tracker = 325 extensions::InstallTrackerFactory::GetForProfile(profile_); 326 extensions::InstallObserver::ExtensionInstallParams params( 327 id_, 328 name, 329 approval_->installing_icon, 330 approval_->manifest->is_app(), 331 approval_->manifest->is_platform_app()); 332 params.is_ephemeral = approval_->is_ephemeral; 333 tracker->OnBeginExtensionInstall(params); 334 } 335 336 void WebstoreInstaller::Observe(int type, 337 const content::NotificationSource& source, 338 const content::NotificationDetails& details) { 339 switch (type) { 340 case chrome::NOTIFICATION_CRX_INSTALLER_DONE: { 341 const Extension* extension = 342 content::Details<const Extension>(details).ptr(); 343 CrxInstaller* installer = content::Source<CrxInstaller>(source).ptr(); 344 if (extension == NULL && download_item_ != NULL && 345 installer->download_url() == download_item_->GetURL() && 346 installer->profile()->IsSameProfile(profile_)) { 347 ReportFailure(kInstallCanceledError, FAILURE_REASON_CANCELLED); 348 } 349 break; 350 } 351 352 case chrome::NOTIFICATION_EXTENSION_INSTALLED: { 353 CHECK(profile_->IsSameProfile(content::Source<Profile>(source).ptr())); 354 const Extension* extension = 355 content::Details<const InstalledExtensionInfo>(details)->extension; 356 if (pending_modules_.empty()) 357 return; 358 SharedModuleInfo::ImportInfo info = pending_modules_.front(); 359 if (extension->id() != info.extension_id) 360 return; 361 pending_modules_.pop_front(); 362 363 if (pending_modules_.empty()) { 364 CHECK_EQ(extension->id(), id_); 365 ReportSuccess(); 366 } else { 367 const Version version_required(info.minimum_version); 368 if (version_required.IsValid() && 369 extension->version()->CompareTo(version_required) < 0) { 370 // It should not happen, CrxInstaller will make sure the version is 371 // equal or newer than version_required. 372 ReportFailure(kDependencyNotFoundError, 373 FAILURE_REASON_DEPENDENCY_NOT_FOUND); 374 } else if (!SharedModuleInfo::IsSharedModule(extension)) { 375 // It should not happen, CrxInstaller will make sure it is a shared 376 // module. 377 ReportFailure(kDependencyNotSharedModuleError, 378 FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE); 379 } else { 380 DownloadNextPendingModule(); 381 } 382 } 383 break; 384 } 385 386 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: { 387 CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr(); 388 CHECK(crx_installer); 389 if (!profile_->IsSameProfile(crx_installer->profile())) 390 return; 391 392 // TODO(rdevlin.cronin): Continue removing std::string errors and 393 // replacing with base::string16. See crbug.com/71980. 394 const base::string16* error = 395 content::Details<const base::string16>(details).ptr(); 396 const std::string utf8_error = UTF16ToUTF8(*error); 397 if (download_url_ == crx_installer->original_download_url()) 398 ReportFailure(utf8_error, FAILURE_REASON_OTHER); 399 break; 400 } 401 402 default: 403 NOTREACHED(); 404 } 405 } 406 407 void WebstoreInstaller::InvalidateDelegate() { 408 delegate_ = NULL; 409 } 410 411 void WebstoreInstaller::SetDownloadDirectoryForTests( 412 base::FilePath* directory) { 413 g_download_directory_for_tests = directory; 414 } 415 416 WebstoreInstaller::~WebstoreInstaller() { 417 controller_ = NULL; 418 if (download_item_) { 419 download_item_->RemoveObserver(this); 420 download_item_ = NULL; 421 } 422 } 423 424 void WebstoreInstaller::OnDownloadStarted( 425 DownloadItem* item, net::Error error) { 426 if (!item) { 427 DCHECK_NE(net::OK, error); 428 ReportFailure(net::ErrorToString(error), FAILURE_REASON_OTHER); 429 return; 430 } 431 432 DCHECK_EQ(net::OK, error); 433 DCHECK(!pending_modules_.empty()); 434 download_item_ = item; 435 download_item_->AddObserver(this); 436 if (pending_modules_.size() > 1) { 437 // We are downloading a shared module. We need create an approval for it. 438 scoped_ptr<Approval> approval = Approval::CreateForSharedModule(profile_); 439 const SharedModuleInfo::ImportInfo& info = pending_modules_.front(); 440 approval->extension_id = info.extension_id; 441 const Version version_required(info.minimum_version); 442 443 if (version_required.IsValid()) { 444 approval->minimum_version.reset( 445 new Version(version_required)); 446 } 447 download_item_->SetUserData(kApprovalKey, approval.release()); 448 } else { 449 // It is for the main module of the extension. We should use the provided 450 // |approval_|. 451 if (approval_) 452 download_item_->SetUserData(kApprovalKey, approval_.release()); 453 } 454 455 if (!download_started_) { 456 if (delegate_) 457 delegate_->OnExtensionDownloadStarted(id_, download_item_); 458 download_started_ = true; 459 } 460 } 461 462 void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) { 463 CHECK_EQ(download_item_, download); 464 465 switch (download->GetState()) { 466 case DownloadItem::CANCELLED: 467 ReportFailure(kDownloadCanceledError, FAILURE_REASON_CANCELLED); 468 break; 469 case DownloadItem::INTERRUPTED: 470 RecordInterrupt(download); 471 ReportFailure(kDownloadInterruptedError, FAILURE_REASON_OTHER); 472 break; 473 case DownloadItem::COMPLETE: 474 // Wait for other notifications if the download is really an extension. 475 if (!download_crx_util::IsExtensionDownload(*download)) { 476 ReportFailure(kInvalidDownloadError, FAILURE_REASON_OTHER); 477 } else if (pending_modules_.empty()) { 478 // The download is the last module - the extension main module. 479 if (delegate_) 480 delegate_->OnExtensionDownloadProgress(id_, download); 481 extensions::InstallTracker* tracker = 482 extensions::InstallTrackerFactory::GetForProfile(profile_); 483 tracker->OnDownloadProgress(id_, 100); 484 } 485 break; 486 case DownloadItem::IN_PROGRESS: { 487 if (delegate_ && pending_modules_.size() == 1) { 488 // Only report download progress for the main module to |delegrate_|. 489 delegate_->OnExtensionDownloadProgress(id_, download); 490 } 491 int percent = download->PercentComplete(); 492 // Only report progress if precent is more than 0 493 if (percent >= 0) { 494 int finished_modules = total_modules_ - pending_modules_.size(); 495 percent = (percent + finished_modules * 100) / total_modules_; 496 extensions::InstallTracker* tracker = 497 extensions::InstallTrackerFactory::GetForProfile(profile_); 498 tracker->OnDownloadProgress(id_, percent); 499 } 500 break; 501 } 502 default: 503 // Continue listening if the download is not in one of the above states. 504 break; 505 } 506 } 507 508 void WebstoreInstaller::OnDownloadDestroyed(DownloadItem* download) { 509 CHECK_EQ(download_item_, download); 510 download_item_->RemoveObserver(this); 511 download_item_ = NULL; 512 } 513 514 void WebstoreInstaller::DownloadNextPendingModule() { 515 CHECK(!pending_modules_.empty()); 516 if (pending_modules_.size() == 1) { 517 DCHECK_EQ(id_, pending_modules_.front().extension_id); 518 DownloadCrx(id_, install_source_); 519 } else { 520 DownloadCrx(pending_modules_.front().extension_id, INSTALL_SOURCE_OTHER); 521 } 522 } 523 524 void WebstoreInstaller::DownloadCrx( 525 const std::string& extension_id, 526 InstallSource source) { 527 download_url_ = GetWebstoreInstallURL(extension_id, source); 528 529 base::FilePath download_path; 530 if (UseSeparateWebstoreDownloadDirectory()) { 531 base::FilePath user_data_dir; 532 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); 533 download_path = user_data_dir.Append(kWebstoreDownloadFolder); 534 } else { 535 download_path = DownloadPrefs::FromDownloadManager( 536 BrowserContext::GetDownloadManager(profile_))->DownloadPath(); 537 } 538 539 BrowserThread::PostTask( 540 BrowserThread::FILE, FROM_HERE, 541 base::Bind(&GetDownloadFilePath, download_path, id_, 542 base::Bind(&WebstoreInstaller::StartDownload, this))); 543 } 544 545 // http://crbug.com/165634 546 // http://crbug.com/126013 547 // The current working theory is that one of the many pointers dereferenced in 548 // here is occasionally deleted before all of its referers are nullified, 549 // probably in a callback race. After this comment is released, the crash 550 // reports should narrow down exactly which pointer it is. Collapsing all the 551 // early-returns into a single branch makes it hard to see exactly which pointer 552 // it is. 553 void WebstoreInstaller::StartDownload(const base::FilePath& file) { 554 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 555 556 DownloadManager* download_manager = 557 BrowserContext::GetDownloadManager(profile_); 558 if (file.empty()) { 559 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 560 return; 561 } 562 if (!download_manager) { 563 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 564 return; 565 } 566 if (!controller_) { 567 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 568 return; 569 } 570 if (!controller_->GetWebContents()) { 571 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 572 return; 573 } 574 if (!controller_->GetWebContents()->GetRenderProcessHost()) { 575 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 576 return; 577 } 578 if (!controller_->GetWebContents()->GetRenderViewHost()) { 579 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 580 return; 581 } 582 if (!controller_->GetBrowserContext()) { 583 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 584 return; 585 } 586 if (!controller_->GetBrowserContext()->GetResourceContext()) { 587 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER); 588 return; 589 } 590 591 // The download url for the given extension is contained in |download_url_|. 592 // We will navigate the current tab to this url to start the download. The 593 // download system will then pass the crx to the CrxInstaller. 594 RecordDownloadSource(DOWNLOAD_INITIATED_BY_WEBSTORE_INSTALLER); 595 int render_process_host_id = 596 controller_->GetWebContents()->GetRenderProcessHost()->GetID(); 597 int render_view_host_routing_id = 598 controller_->GetWebContents()->GetRenderViewHost()->GetRoutingID(); 599 content::ResourceContext* resource_context = 600 controller_->GetBrowserContext()->GetResourceContext(); 601 scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters( 602 download_url_, 603 render_process_host_id, 604 render_view_host_routing_id , 605 resource_context)); 606 params->set_file_path(file); 607 if (controller_->GetVisibleEntry()) 608 params->set_referrer( 609 content::Referrer(controller_->GetVisibleEntry()->GetURL(), 610 blink::WebReferrerPolicyDefault)); 611 params->set_callback(base::Bind(&WebstoreInstaller::OnDownloadStarted, this)); 612 download_manager->DownloadUrl(params.Pass()); 613 } 614 615 void WebstoreInstaller::ReportFailure(const std::string& error, 616 FailureReason reason) { 617 if (delegate_) { 618 delegate_->OnExtensionInstallFailure(id_, error, reason); 619 delegate_ = NULL; 620 } 621 622 extensions::InstallTracker* tracker = 623 extensions::InstallTrackerFactory::GetForProfile(profile_); 624 tracker->OnInstallFailure(id_); 625 626 Release(); // Balanced in Start(). 627 } 628 629 void WebstoreInstaller::ReportSuccess() { 630 if (delegate_) { 631 delegate_->OnExtensionInstallSuccess(id_); 632 delegate_ = NULL; 633 } 634 635 Release(); // Balanced in Start(). 636 } 637 638 void WebstoreInstaller::RecordInterrupt(const DownloadItem* download) const { 639 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.WebstoreDownload.InterruptReason", 640 download->GetLastReason()); 641 642 // Use logarithmic bin sizes up to 1 TB. 643 const int kNumBuckets = 30; 644 const int64 kMaxSizeKb = 1 << kNumBuckets; 645 UMA_HISTOGRAM_CUSTOM_COUNTS( 646 "Extensions.WebstoreDownload.InterruptReceivedKBytes", 647 download->GetReceivedBytes() / 1024, 648 1, 649 kMaxSizeKb, 650 kNumBuckets); 651 int64 total_bytes = download->GetTotalBytes(); 652 if (total_bytes >= 0) { 653 UMA_HISTOGRAM_CUSTOM_COUNTS( 654 "Extensions.WebstoreDownload.InterruptTotalKBytes", 655 total_bytes / 1024, 656 1, 657 kMaxSizeKb, 658 kNumBuckets); 659 } 660 UMA_HISTOGRAM_BOOLEAN( 661 "Extensions.WebstoreDownload.InterruptTotalSizeUnknown", 662 total_bytes <= 0); 663 } 664 665 } // namespace extensions 666