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