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