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