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