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