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