Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / extensions / webstore_installer.cc
blobb7d679821e986675cf1397a52787b53a94c02185
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.
5 #include "chrome/browser/extensions/webstore_installer.h"
7 #include <vector>
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"
57 #if defined(OS_CHROMEOS)
58 #include "chrome/browser/chromeos/drive/file_system_util.h"
59 #endif
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;
69 namespace {
71 // Key used to attach the Approval to the DownloadItem.
72 const char kApprovalKey[] = "extensions.webstore_installer";
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";
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");
93 base::FilePath* g_download_directory_for_tests = NULL;
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 // Ensure the download directory exists. TODO(asargent) - make this use
101 // common code from the downloads system.
102 if (!base::DirectoryExists(download_directory)) {
103 if (!base::CreateDirectory(download_directory)) {
104 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
105 base::Bind(callback, base::FilePath()));
106 return;
110 // This is to help avoid a race condition between when we generate this
111 // filename and when the download starts writing to it (think concurrently
112 // running sharded browser tests installing the same test file, for
113 // instance).
114 std::string random_number =
115 base::Uint64ToString(base::RandGenerator(kuint16max));
117 base::FilePath file =
118 download_directory.AppendASCII(id + "_" + random_number + ".crx");
120 int uniquifier =
121 file_util::GetUniquePathNumber(file, base::FilePath::StringType());
122 if (uniquifier > 0) {
123 file = file.InsertBeforeExtensionASCII(
124 base::StringPrintf(" (%d)", uniquifier));
127 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
128 base::Bind(callback, file));
131 bool UseSeparateWebstoreDownloadDirectory() {
132 const char kFieldTrial[] = "WebstoreDownloadDirectory";
133 const char kSeparateDirectoryUnderUDD[] = "SeparateDirectoryUnderUDD";
135 std::string field_trial_group =
136 base::FieldTrialList::FindFullName(kFieldTrial);
137 return field_trial_group == kSeparateDirectoryUnderUDD;
140 } // namespace
142 namespace extensions {
144 // static
145 GURL WebstoreInstaller::GetWebstoreInstallURL(
146 const std::string& extension_id,
147 InstallSource source) {
148 std::string install_source;
149 switch (source) {
150 case INSTALL_SOURCE_INLINE:
151 install_source = kInlineInstallSource;
152 break;
153 case INSTALL_SOURCE_APP_LAUNCHER:
154 install_source = kAppLauncherInstallSource;
155 break;
156 case INSTALL_SOURCE_OTHER:
157 install_source = kDefaultInstallSource;
160 CommandLine* cmd_line = CommandLine::ForCurrentProcess();
161 if (cmd_line->HasSwitch(switches::kAppsGalleryDownloadURL)) {
162 std::string download_url =
163 cmd_line->GetSwitchValueASCII(switches::kAppsGalleryDownloadURL);
164 return GURL(base::StringPrintf(download_url.c_str(),
165 extension_id.c_str()));
167 std::vector<std::string> params;
168 params.push_back("id=" + extension_id);
169 if (!install_source.empty())
170 params.push_back("installsource=" + install_source);
171 params.push_back("lang=" + g_browser_process->GetApplicationLocale());
172 params.push_back("uc");
173 std::string url_string = extension_urls::GetWebstoreUpdateUrl().spec();
175 GURL url(url_string + "?response=redirect&" +
176 OmahaQueryParams::Get(OmahaQueryParams::CRX) + "&x=" +
177 net::EscapeQueryParamValue(JoinString(params, '&'), true));
178 DCHECK(url.is_valid());
180 return url;
183 void WebstoreInstaller::Delegate::OnExtensionDownloadStarted(
184 const std::string& id,
185 content::DownloadItem* item) {
188 void WebstoreInstaller::Delegate::OnExtensionDownloadProgress(
189 const std::string& id,
190 content::DownloadItem* item) {
193 WebstoreInstaller::Approval::Approval()
194 : profile(NULL),
195 use_app_installed_bubble(false),
196 skip_post_install_ui(false),
197 skip_install_dialog(false),
198 enable_launcher(false),
199 manifest_check_level(MANIFEST_CHECK_LEVEL_STRICT),
200 is_ephemeral(false) {
203 scoped_ptr<WebstoreInstaller::Approval>
204 WebstoreInstaller::Approval::CreateWithInstallPrompt(Profile* profile) {
205 scoped_ptr<Approval> result(new Approval());
206 result->profile = profile;
207 return result.Pass();
210 scoped_ptr<WebstoreInstaller::Approval>
211 WebstoreInstaller::Approval::CreateForSharedModule(Profile* profile) {
212 scoped_ptr<Approval> result(new Approval());
213 result->profile = profile;
214 result->skip_install_dialog = true;
215 result->manifest_check_level = MANIFEST_CHECK_LEVEL_NONE;
216 return result.Pass();
219 scoped_ptr<WebstoreInstaller::Approval>
220 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
221 Profile* profile,
222 const std::string& extension_id,
223 scoped_ptr<base::DictionaryValue> parsed_manifest,
224 bool strict_manifest_check) {
225 scoped_ptr<Approval> result(new Approval());
226 result->extension_id = extension_id;
227 result->profile = profile;
228 result->manifest = scoped_ptr<Manifest>(
229 new Manifest(Manifest::INVALID_LOCATION,
230 scoped_ptr<base::DictionaryValue>(
231 parsed_manifest->DeepCopy())));
232 result->skip_install_dialog = true;
233 result->manifest_check_level = strict_manifest_check ?
234 MANIFEST_CHECK_LEVEL_STRICT : MANIFEST_CHECK_LEVEL_LOOSE;
235 return result.Pass();
238 WebstoreInstaller::Approval::~Approval() {}
240 const WebstoreInstaller::Approval* WebstoreInstaller::GetAssociatedApproval(
241 const DownloadItem& download) {
242 return static_cast<const Approval*>(download.GetUserData(kApprovalKey));
245 WebstoreInstaller::WebstoreInstaller(Profile* profile,
246 Delegate* delegate,
247 NavigationController* controller,
248 const std::string& id,
249 scoped_ptr<Approval> approval,
250 InstallSource source)
251 : profile_(profile),
252 delegate_(delegate),
253 controller_(controller),
254 id_(id),
255 install_source_(source),
256 download_item_(NULL),
257 approval_(approval.release()),
258 total_modules_(0),
259 download_started_(false) {
260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261 DCHECK(controller_);
263 registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
264 content::NotificationService::AllSources());
265 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
266 content::Source<Profile>(profile->GetOriginalProfile()));
267 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
268 content::Source<CrxInstaller>(NULL));
271 void WebstoreInstaller::Start() {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 AddRef(); // Balanced in ReportSuccess and ReportFailure.
275 if (!Extension::IdIsValid(id_)) {
276 ReportFailure(kInvalidIdError, FAILURE_REASON_OTHER);
277 return;
280 ExtensionService* extension_service =
281 ExtensionSystem::Get(profile_)->extension_service();
282 if (approval_.get() && approval_->dummy_extension) {
283 ExtensionService::ImportStatus status =
284 extension_service->CheckImports(approval_->dummy_extension,
285 &pending_modules_, &pending_modules_);
286 // For this case, it is because some imports are not shared modules.
287 if (status == ExtensionService::IMPORT_STATUS_UNRECOVERABLE) {
288 ReportFailure(kDependencyNotSharedModuleError,
289 FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
290 return;
294 // Add the extension main module into the list.
295 SharedModuleInfo::ImportInfo info;
296 info.extension_id = id_;
297 pending_modules_.push_back(info);
299 total_modules_ = pending_modules_.size();
301 std::set<std::string> ids;
302 std::list<SharedModuleInfo::ImportInfo>::const_iterator i;
303 for (i = pending_modules_.begin(); i != pending_modules_.end(); ++i) {
304 ids.insert(i->extension_id);
306 ExtensionSystem::Get(profile_)->install_verifier()->AddProvisional(ids);
308 // TODO(crbug.com/305343): Query manifest of dependencises before
309 // downloading & installing those dependencies.
310 DownloadNextPendingModule();
312 std::string name;
313 if (!approval_->manifest->value()->GetString(manifest_keys::kName, &name)) {
314 NOTREACHED();
316 extensions::InstallTracker* tracker =
317 extensions::InstallTrackerFactory::GetForProfile(profile_);
318 extensions::InstallObserver::ExtensionInstallParams params(
319 id_,
320 name,
321 approval_->installing_icon,
322 approval_->manifest->is_app(),
323 approval_->manifest->is_platform_app());
324 params.is_ephemeral = approval_->is_ephemeral;
325 tracker->OnBeginExtensionInstall(params);
328 void WebstoreInstaller::Observe(int type,
329 const content::NotificationSource& source,
330 const content::NotificationDetails& details) {
331 switch (type) {
332 case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
333 const Extension* extension =
334 content::Details<const Extension>(details).ptr();
335 CrxInstaller* installer = content::Source<CrxInstaller>(source).ptr();
336 if (extension == NULL && download_item_ != NULL &&
337 installer->download_url() == download_item_->GetURL() &&
338 installer->profile()->IsSameProfile(profile_)) {
339 ReportFailure(kInstallCanceledError, FAILURE_REASON_CANCELLED);
341 break;
344 case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
345 CHECK(profile_->IsSameProfile(content::Source<Profile>(source).ptr()));
346 const Extension* extension =
347 content::Details<const InstalledExtensionInfo>(details)->extension;
348 if (pending_modules_.empty())
349 return;
350 SharedModuleInfo::ImportInfo info = pending_modules_.front();
351 if (extension->id() != info.extension_id)
352 return;
353 pending_modules_.pop_front();
355 if (pending_modules_.empty()) {
356 CHECK_EQ(extension->id(), id_);
357 ReportSuccess();
358 } else {
359 const Version version_required(info.minimum_version);
360 if (version_required.IsValid() &&
361 extension->version()->CompareTo(version_required) < 0) {
362 // It should not happen, CrxInstaller will make sure the version is
363 // equal or newer than version_required.
364 ReportFailure(kDependencyNotFoundError,
365 FAILURE_REASON_DEPENDENCY_NOT_FOUND);
366 } else if (!SharedModuleInfo::IsSharedModule(extension)) {
367 // It should not happen, CrxInstaller will make sure it is a shared
368 // module.
369 ReportFailure(kDependencyNotSharedModuleError,
370 FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
371 } else {
372 DownloadNextPendingModule();
375 break;
378 case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
379 CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
380 CHECK(crx_installer);
381 if (!profile_->IsSameProfile(crx_installer->profile()))
382 return;
384 // TODO(rdevlin.cronin): Continue removing std::string errors and
385 // replacing with base::string16. See crbug.com/71980.
386 const base::string16* error =
387 content::Details<const base::string16>(details).ptr();
388 const std::string utf8_error = base::UTF16ToUTF8(*error);
389 if (download_url_ == crx_installer->original_download_url())
390 ReportFailure(utf8_error, FAILURE_REASON_OTHER);
391 break;
394 default:
395 NOTREACHED();
399 void WebstoreInstaller::InvalidateDelegate() {
400 delegate_ = NULL;
403 void WebstoreInstaller::SetDownloadDirectoryForTests(
404 base::FilePath* directory) {
405 g_download_directory_for_tests = directory;
408 WebstoreInstaller::~WebstoreInstaller() {
409 controller_ = NULL;
410 if (download_item_) {
411 download_item_->RemoveObserver(this);
412 download_item_ = NULL;
416 void WebstoreInstaller::OnDownloadStarted(
417 DownloadItem* item,
418 content::DownloadInterruptReason interrupt_reason) {
419 if (!item) {
420 DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
421 ReportFailure(content::DownloadInterruptReasonToString(interrupt_reason),
422 FAILURE_REASON_OTHER);
423 return;
426 DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
427 DCHECK(!pending_modules_.empty());
428 download_item_ = item;
429 download_item_->AddObserver(this);
430 if (pending_modules_.size() > 1) {
431 // We are downloading a shared module. We need create an approval for it.
432 scoped_ptr<Approval> approval = Approval::CreateForSharedModule(profile_);
433 const SharedModuleInfo::ImportInfo& info = pending_modules_.front();
434 approval->extension_id = info.extension_id;
435 const Version version_required(info.minimum_version);
437 if (version_required.IsValid()) {
438 approval->minimum_version.reset(
439 new Version(version_required));
441 download_item_->SetUserData(kApprovalKey, approval.release());
442 } else {
443 // It is for the main module of the extension. We should use the provided
444 // |approval_|.
445 if (approval_)
446 download_item_->SetUserData(kApprovalKey, approval_.release());
449 if (!download_started_) {
450 if (delegate_)
451 delegate_->OnExtensionDownloadStarted(id_, download_item_);
452 download_started_ = true;
456 void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) {
457 CHECK_EQ(download_item_, download);
459 switch (download->GetState()) {
460 case DownloadItem::CANCELLED:
461 ReportFailure(kDownloadCanceledError, FAILURE_REASON_CANCELLED);
462 break;
463 case DownloadItem::INTERRUPTED:
464 RecordInterrupt(download);
465 ReportFailure(kDownloadInterruptedError, FAILURE_REASON_OTHER);
466 break;
467 case DownloadItem::COMPLETE:
468 // Wait for other notifications if the download is really an extension.
469 if (!download_crx_util::IsExtensionDownload(*download)) {
470 ReportFailure(kInvalidDownloadError, FAILURE_REASON_OTHER);
471 } else if (pending_modules_.empty()) {
472 // The download is the last module - the extension main module.
473 if (delegate_)
474 delegate_->OnExtensionDownloadProgress(id_, download);
475 extensions::InstallTracker* tracker =
476 extensions::InstallTrackerFactory::GetForProfile(profile_);
477 tracker->OnDownloadProgress(id_, 100);
479 break;
480 case DownloadItem::IN_PROGRESS: {
481 if (delegate_ && pending_modules_.size() == 1) {
482 // Only report download progress for the main module to |delegrate_|.
483 delegate_->OnExtensionDownloadProgress(id_, download);
485 int percent = download->PercentComplete();
486 // Only report progress if precent is more than 0
487 if (percent >= 0) {
488 int finished_modules = total_modules_ - pending_modules_.size();
489 percent = (percent + finished_modules * 100) / total_modules_;
490 extensions::InstallTracker* tracker =
491 extensions::InstallTrackerFactory::GetForProfile(profile_);
492 tracker->OnDownloadProgress(id_, percent);
494 break;
496 default:
497 // Continue listening if the download is not in one of the above states.
498 break;
502 void WebstoreInstaller::OnDownloadDestroyed(DownloadItem* download) {
503 CHECK_EQ(download_item_, download);
504 download_item_->RemoveObserver(this);
505 download_item_ = NULL;
508 void WebstoreInstaller::DownloadNextPendingModule() {
509 CHECK(!pending_modules_.empty());
510 if (pending_modules_.size() == 1) {
511 DCHECK_EQ(id_, pending_modules_.front().extension_id);
512 DownloadCrx(id_, install_source_);
513 } else {
514 DownloadCrx(pending_modules_.front().extension_id, INSTALL_SOURCE_OTHER);
518 void WebstoreInstaller::DownloadCrx(
519 const std::string& extension_id,
520 InstallSource source) {
521 download_url_ = GetWebstoreInstallURL(extension_id, source);
523 base::FilePath download_path;
524 if (UseSeparateWebstoreDownloadDirectory()) {
525 base::FilePath user_data_dir;
526 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
527 download_path = user_data_dir.Append(kWebstoreDownloadFolder);
528 } else {
529 download_path = DownloadPrefs::FromDownloadManager(
530 BrowserContext::GetDownloadManager(profile_))->DownloadPath();
533 base::FilePath download_directory(g_download_directory_for_tests ?
534 *g_download_directory_for_tests : download_path);
536 #if defined(OS_CHROMEOS)
537 // Do not use drive for extension downloads.
538 if (drive::util::IsUnderDriveMountPoint(download_directory)) {
539 download_directory = DownloadPrefs::FromBrowserContext(
540 profile_)->GetDefaultDownloadDirectoryForProfile();
542 #endif
544 BrowserThread::PostTask(
545 BrowserThread::FILE, FROM_HERE,
546 base::Bind(&GetDownloadFilePath, download_directory, id_,
547 base::Bind(&WebstoreInstaller::StartDownload, this)));
550 // http://crbug.com/165634
551 // http://crbug.com/126013
552 // The current working theory is that one of the many pointers dereferenced in
553 // here is occasionally deleted before all of its referers are nullified,
554 // probably in a callback race. After this comment is released, the crash
555 // reports should narrow down exactly which pointer it is. Collapsing all the
556 // early-returns into a single branch makes it hard to see exactly which pointer
557 // it is.
558 void WebstoreInstaller::StartDownload(const base::FilePath& file) {
559 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
561 DownloadManager* download_manager =
562 BrowserContext::GetDownloadManager(profile_);
563 if (file.empty()) {
564 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
565 return;
567 if (!download_manager) {
568 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
569 return;
571 if (!controller_) {
572 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
573 return;
575 if (!controller_->GetWebContents()) {
576 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
577 return;
579 if (!controller_->GetWebContents()->GetRenderProcessHost()) {
580 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
581 return;
583 if (!controller_->GetWebContents()->GetRenderViewHost()) {
584 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
585 return;
587 if (!controller_->GetBrowserContext()) {
588 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
589 return;
591 if (!controller_->GetBrowserContext()->GetResourceContext()) {
592 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
593 return;
596 // The download url for the given extension is contained in |download_url_|.
597 // We will navigate the current tab to this url to start the download. The
598 // download system will then pass the crx to the CrxInstaller.
599 RecordDownloadSource(DOWNLOAD_INITIATED_BY_WEBSTORE_INSTALLER);
600 int render_process_host_id =
601 controller_->GetWebContents()->GetRenderProcessHost()->GetID();
602 int render_view_host_routing_id =
603 controller_->GetWebContents()->GetRenderViewHost()->GetRoutingID();
604 content::ResourceContext* resource_context =
605 controller_->GetBrowserContext()->GetResourceContext();
606 scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
607 download_url_,
608 render_process_host_id,
609 render_view_host_routing_id ,
610 resource_context));
611 params->set_file_path(file);
612 if (controller_->GetVisibleEntry())
613 params->set_referrer(
614 content::Referrer(controller_->GetVisibleEntry()->GetURL(),
615 blink::WebReferrerPolicyDefault));
616 params->set_callback(base::Bind(&WebstoreInstaller::OnDownloadStarted, this));
617 download_manager->DownloadUrl(params.Pass());
620 void WebstoreInstaller::ReportFailure(const std::string& error,
621 FailureReason reason) {
622 if (delegate_) {
623 delegate_->OnExtensionInstallFailure(id_, error, reason);
624 delegate_ = NULL;
627 extensions::InstallTracker* tracker =
628 extensions::InstallTrackerFactory::GetForProfile(profile_);
629 tracker->OnInstallFailure(id_);
631 Release(); // Balanced in Start().
634 void WebstoreInstaller::ReportSuccess() {
635 if (delegate_) {
636 delegate_->OnExtensionInstallSuccess(id_);
637 delegate_ = NULL;
640 Release(); // Balanced in Start().
643 void WebstoreInstaller::RecordInterrupt(const DownloadItem* download) const {
644 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.WebstoreDownload.InterruptReason",
645 download->GetLastReason());
647 // Use logarithmic bin sizes up to 1 TB.
648 const int kNumBuckets = 30;
649 const int64 kMaxSizeKb = 1 << kNumBuckets;
650 UMA_HISTOGRAM_CUSTOM_COUNTS(
651 "Extensions.WebstoreDownload.InterruptReceivedKBytes",
652 download->GetReceivedBytes() / 1024,
654 kMaxSizeKb,
655 kNumBuckets);
656 int64 total_bytes = download->GetTotalBytes();
657 if (total_bytes >= 0) {
658 UMA_HISTOGRAM_CUSTOM_COUNTS(
659 "Extensions.WebstoreDownload.InterruptTotalKBytes",
660 total_bytes / 1024,
662 kMaxSizeKb,
663 kNumBuckets);
665 UMA_HISTOGRAM_BOOLEAN(
666 "Extensions.WebstoreDownload.InterruptTotalSizeUnknown",
667 total_bytes <= 0);
670 } // namespace extensions