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 <stddef.h>
8 #include <stdint.h>
9 #include <limits>
10 #include <set>
11 #include <utility>
12 #include <vector>
13 
14 #include "base/bind.h"
15 #include "base/command_line.h"
16 #include "base/files/file_util.h"
17 #include "base/metrics/field_trial.h"
18 #include "base/metrics/histogram_functions.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/path_service.h"
21 #include "base/rand_util.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_piece.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/task/post_task.h"
28 #include "base/time/time.h"
29 #include "build/build_config.h"
30 #include "chrome/browser/chrome_notification_types.h"
31 #include "chrome/browser/download/download_crx_util.h"
32 #include "chrome/browser/download/download_prefs.h"
33 #include "chrome/browser/download/download_stats.h"
34 #include "chrome/browser/extensions/crx_installer.h"
35 #include "chrome/browser/extensions/install_tracker.h"
36 #include "chrome/browser/extensions/install_tracker_factory.h"
37 #include "chrome/browser/extensions/install_verifier.h"
38 #include "chrome/browser/extensions/shared_module_service.h"
39 #include "chrome/browser/profiles/profile.h"
40 #include "chrome/common/chrome_paths.h"
41 #include "chrome/common/chrome_switches.h"
42 #include "chrome/grit/generated_resources.h"
43 #include "components/crx_file/id_util.h"
44 #include "components/download/public/common/download_url_parameters.h"
45 #include "components/update_client/update_query_params.h"
46 #include "content/public/browser/browser_thread.h"
47 #include "content/public/browser/download_manager.h"
48 #include "content/public/browser/navigation_controller.h"
49 #include "content/public/browser/navigation_entry.h"
50 #include "content/public/browser/notification_details.h"
51 #include "content/public/browser/notification_service.h"
52 #include "content/public/browser/notification_source.h"
53 #include "content/public/browser/render_frame_host.h"
54 #include "content/public/browser/render_process_host.h"
55 #include "content/public/browser/render_view_host.h"
56 #include "content/public/browser/web_contents.h"
57 #include "extensions/browser/extension_file_task_runner.h"
58 #include "extensions/browser/extension_system.h"
59 #include "extensions/browser/install/crx_install_error.h"
60 #include "extensions/common/extension.h"
61 #include "extensions/common/extension_urls.h"
62 #include "extensions/common/manifest_constants.h"
63 #include "extensions/common/manifest_handlers/shared_module_info.h"
64 #include "net/base/escape.h"
65 #include "net/traffic_annotation/network_traffic_annotation.h"
66 #include "ui/base/l10n/l10n_util.h"
67 #include "url/gurl.h"
68 
69 using content::BrowserContext;
70 using content::BrowserThread;
71 using content::DownloadManager;
72 using content::NavigationController;
73 using download::DownloadItem;
74 using download::DownloadUrlParameters;
75 
76 namespace {
77 
78 // Key used to attach the Approval to the DownloadItem.
79 const char kApprovalKey[] = "extensions.webstore_installer";
80 
81 const char kInvalidIdError[] = "Invalid id";
82 const char kDownloadDirectoryError[] = "Could not create download directory";
83 const char kDownloadCanceledError[] = "Download canceled";
84 const char kDownloadInterruptedError[] = "Download interrupted";
85 const char kInvalidDownloadError[] =
86     "Download was not a valid extension or user script";
87 const char kDependencyNotFoundError[] = "Dependency not found";
88 const char kDependencyNotSharedModuleError[] =
89     "Dependency is not shared module";
90 const char kInlineInstallSource[] = "inline";
91 const char kDefaultInstallSource[] = "ondemand";
92 const char kAppLauncherInstallSource[] = "applauncher";
93 
94 // TODO(rockot): Share this duplicated constant with the extension updater.
95 // See http://crbug.com/371398.
96 const char kAuthUserQueryKey[] = "authuser";
97 
98 constexpr base::TimeDelta kTimeRemainingThreshold =
99     base::TimeDelta::FromSeconds(1);
100 
101 // Folder for downloading crx files from the webstore. This is used so that the
102 // crx files don't go via the usual downloads folder.
103 const base::FilePath::CharType kWebstoreDownloadFolder[] =
104     FILE_PATH_LITERAL("Webstore Downloads");
105 
106 base::FilePath* g_download_directory_for_tests = nullptr;
107 
GetDownloadFilePath(const base::FilePath & download_directory,const std::string & id)108 base::FilePath GetDownloadFilePath(const base::FilePath& download_directory,
109                                    const std::string& id) {
110   // Ensure the download directory exists. TODO(asargent) - make this use
111   // common code from the downloads system.
112   if (!base::DirectoryExists(download_directory) &&
113       !base::CreateDirectory(download_directory)) {
114     return base::FilePath();
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 = base::NumberToString(
122       base::RandGenerator(std::numeric_limits<uint16_t>::max()));
123 
124   base::FilePath file =
125       download_directory.AppendASCII(id + "_" + random_number + ".crx");
126 
127   return base::GetUniquePath(file);
128 }
129 
MaybeAppendAuthUserParameter(const std::string & authuser,GURL * url)130 void MaybeAppendAuthUserParameter(const std::string& authuser, GURL* url) {
131   if (authuser.empty())
132     return;
133   std::string old_query = url->query();
134   url::Component query(0, old_query.length());
135   url::Component key, value;
136   // Ensure that the URL doesn't already specify an authuser parameter.
137   while (url::ExtractQueryKeyValue(
138              old_query.c_str(), &query, &key, &value)) {
139     std::string key_string = old_query.substr(key.begin, key.len);
140     if (key_string == kAuthUserQueryKey) {
141       return;
142     }
143   }
144   if (!old_query.empty()) {
145     old_query += "&";
146   }
147   std::string authuser_param = base::StringPrintf(
148       "%s=%s",
149       kAuthUserQueryKey,
150       authuser.c_str());
151 
152   // TODO(rockot): Share this duplicated code with the extension updater.
153   // See http://crbug.com/371398.
154   std::string new_query_string = old_query + authuser_param;
155   url::Component new_query(0, new_query_string.length());
156   url::Replacements<char> replacements;
157   replacements.SetQuery(new_query_string.c_str(), new_query);
158   *url = url->ReplaceComponents(replacements);
159 }
160 
GetErrorMessageForDownloadInterrupt(download::DownloadInterruptReason reason)161 std::string GetErrorMessageForDownloadInterrupt(
162     download::DownloadInterruptReason reason) {
163   switch (reason) {
164     case download::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
165     case download::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN:
166       return l10n_util::GetStringUTF8(IDS_WEBSTORE_DOWNLOAD_ACCESS_DENIED);
167     default:
168       break;
169   }
170   return kDownloadInterruptedError;
171 }
172 
173 }  // namespace
174 
175 namespace extensions {
176 
177 // static
GetWebstoreInstallURL(const std::string & extension_id,InstallSource source)178 GURL WebstoreInstaller::GetWebstoreInstallURL(
179     const std::string& extension_id,
180     InstallSource source) {
181   std::string install_source;
182   switch (source) {
183     case INSTALL_SOURCE_INLINE:
184       install_source = kInlineInstallSource;
185       break;
186     case INSTALL_SOURCE_APP_LAUNCHER:
187       install_source = kAppLauncherInstallSource;
188       break;
189     case INSTALL_SOURCE_OTHER:
190       install_source = kDefaultInstallSource;
191   }
192 
193   base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
194   if (cmd_line->HasSwitch(::switches::kAppsGalleryDownloadURL)) {
195     std::string download_url =
196         cmd_line->GetSwitchValueASCII(::switches::kAppsGalleryDownloadURL);
197     return GURL(base::StringPrintf(download_url.c_str(),
198                                    extension_id.c_str()));
199   }
200   std::vector<base::StringPiece> params;
201   std::string extension_param = "id=" + extension_id;
202   std::string installsource_param = "installsource=" + install_source;
203   params.push_back(extension_param);
204   if (!install_source.empty())
205     params.push_back(installsource_param);
206   params.push_back("uc");
207   std::string url_string = extension_urls::GetWebstoreUpdateUrl().spec();
208 
209   GURL url(url_string + "?response=redirect&" +
210            update_client::UpdateQueryParams::Get(
211                update_client::UpdateQueryParams::CRX) +
212            "&x=" + net::EscapeQueryParamValue(base::JoinString(params, "&"),
213                                               true));
214   DCHECK(url.is_valid());
215 
216   return url;
217 }
218 
OnExtensionDownloadStarted(const std::string & id,download::DownloadItem * item)219 void WebstoreInstaller::Delegate::OnExtensionDownloadStarted(
220     const std::string& id,
221     download::DownloadItem* item) {}
222 
OnExtensionDownloadProgress(const std::string & id,download::DownloadItem * item)223 void WebstoreInstaller::Delegate::OnExtensionDownloadProgress(
224     const std::string& id,
225     download::DownloadItem* item) {}
226 
227 WebstoreInstaller::Approval::Approval() = default;
228 
229 std::unique_ptr<WebstoreInstaller::Approval>
CreateWithInstallPrompt(Profile * profile)230 WebstoreInstaller::Approval::CreateWithInstallPrompt(Profile* profile) {
231   std::unique_ptr<Approval> result(new Approval());
232   result->profile = profile;
233   return result;
234 }
235 
236 std::unique_ptr<WebstoreInstaller::Approval>
CreateForSharedModule(Profile * profile)237 WebstoreInstaller::Approval::CreateForSharedModule(Profile* profile) {
238   std::unique_ptr<Approval> result(new Approval());
239   result->profile = profile;
240   result->skip_install_dialog = true;
241   result->skip_post_install_ui = true;
242   result->manifest_check_level = MANIFEST_CHECK_LEVEL_NONE;
243   return result;
244 }
245 
246 std::unique_ptr<WebstoreInstaller::Approval>
CreateWithNoInstallPrompt(Profile * profile,const std::string & extension_id,std::unique_ptr<base::DictionaryValue> parsed_manifest,bool strict_manifest_check)247 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
248     Profile* profile,
249     const std::string& extension_id,
250     std::unique_ptr<base::DictionaryValue> parsed_manifest,
251     bool strict_manifest_check) {
252   std::unique_ptr<Approval> result(new Approval());
253   result->extension_id = extension_id;
254   result->profile = profile;
255   result->manifest = std::unique_ptr<Manifest>(new Manifest(
256       Manifest::INVALID_LOCATION,
257       std::unique_ptr<base::DictionaryValue>(parsed_manifest->DeepCopy())));
258   result->skip_install_dialog = true;
259   result->manifest_check_level = strict_manifest_check ?
260     MANIFEST_CHECK_LEVEL_STRICT : MANIFEST_CHECK_LEVEL_LOOSE;
261   return result;
262 }
263 
~Approval()264 WebstoreInstaller::Approval::~Approval() {}
265 
GetAssociatedApproval(const DownloadItem & download)266 const WebstoreInstaller::Approval* WebstoreInstaller::GetAssociatedApproval(
267     const DownloadItem& download) {
268   return static_cast<const Approval*>(download.GetUserData(kApprovalKey));
269 }
270 
WebstoreInstaller(Profile * profile,Delegate * delegate,content::WebContents * web_contents,const std::string & id,std::unique_ptr<Approval> approval,InstallSource source)271 WebstoreInstaller::WebstoreInstaller(Profile* profile,
272                                      Delegate* delegate,
273                                      content::WebContents* web_contents,
274                                      const std::string& id,
275                                      std::unique_ptr<Approval> approval,
276                                      InstallSource source)
277     : content::WebContentsObserver(web_contents),
278       profile_(profile),
279       delegate_(delegate),
280       id_(id),
281       install_source_(source),
282       approval_(approval.release()) {
283   DCHECK_CURRENTLY_ON(BrowserThread::UI);
284   DCHECK(web_contents);
285 
286   registrar_.Add(this, extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
287                  content::Source<CrxInstaller>(nullptr));
288   extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
289 }
290 
Start()291 void WebstoreInstaller::Start() {
292   DCHECK_CURRENTLY_ON(BrowserThread::UI);
293   AddRef();  // Balanced in ReportSuccess and ReportFailure.
294 
295   if (!crx_file::id_util::IdIsValid(id_)) {
296     ReportFailure(kInvalidIdError, FAILURE_REASON_OTHER);
297     return;
298   }
299 
300   ExtensionService* extension_service =
301     ExtensionSystem::Get(profile_)->extension_service();
302   if (approval_.get() && approval_->dummy_extension.get()) {
303     extension_service->shared_module_service()->CheckImports(
304         approval_->dummy_extension.get(), &pending_modules_, &pending_modules_);
305     // Do not check the return value of CheckImports, the CRX installer
306     // will report appropriate error messages and fail to install if there
307     // is an import error.
308   }
309 
310   // Add the extension main module into the list.
311   SharedModuleInfo::ImportInfo info;
312   info.extension_id = id_;
313   pending_modules_.push_back(info);
314 
315   total_modules_ = pending_modules_.size();
316 
317   std::set<std::string> ids;
318   std::list<SharedModuleInfo::ImportInfo>::const_iterator i;
319   for (i = pending_modules_.begin(); i != pending_modules_.end(); ++i) {
320     ids.insert(i->extension_id);
321   }
322   InstallVerifier::Get(profile_)->AddProvisional(ids);
323 
324   std::string name;
325   if (!approval_->manifest->value()->GetString(manifest_keys::kName, &name)) {
326     NOTREACHED();
327   }
328   extensions::InstallTracker* tracker =
329       extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
330   extensions::InstallObserver::ExtensionInstallParams params(
331       id_,
332       name,
333       approval_->installing_icon,
334       approval_->manifest->is_app(),
335       approval_->manifest->is_platform_app());
336   tracker->OnBeginExtensionInstall(params);
337 
338   tracker->OnBeginExtensionDownload(id_);
339 
340   // TODO(crbug.com/305343): Query manifest of dependencies before
341   // downloading & installing those dependencies.
342   DownloadNextPendingModule();
343 }
344 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)345 void WebstoreInstaller::Observe(int type,
346                                 const content::NotificationSource& source,
347                                 const content::NotificationDetails& details) {
348   DCHECK_EQ(extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR, type);
349 
350   CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
351   CHECK(crx_installer);
352   if (crx_installer != crx_installer_.get())
353     return;
354 
355   // TODO(rdevlin.cronin): Continue removing std::string errors and
356   // replacing with base::string16. See crbug.com/71980.
357   const extensions::CrxInstallError* error =
358       content::Details<const extensions::CrxInstallError>(details).ptr();
359   const std::string utf8_error = base::UTF16ToUTF8(error->message());
360   crx_installer_ = nullptr;
361   // ReportFailure releases a reference to this object so it must be the
362   // last operation in this method.
363   ReportFailure(utf8_error, FAILURE_REASON_OTHER);
364 }
365 
OnExtensionInstalled(content::BrowserContext * browser_context,const Extension * extension,bool is_update)366 void WebstoreInstaller::OnExtensionInstalled(
367     content::BrowserContext* browser_context,
368     const Extension* extension,
369     bool is_update) {
370   CHECK(profile_->IsSameOrParent(Profile::FromBrowserContext(browser_context)));
371   if (pending_modules_.empty())
372     return;
373   SharedModuleInfo::ImportInfo info = pending_modules_.front();
374   if (extension->id() != info.extension_id)
375     return;
376   pending_modules_.pop_front();
377 
378   // Clean up local state from the current download.
379   if (download_item_) {
380     download_item_->RemoveObserver(this);
381     download_item_->Remove();
382     download_item_ = nullptr;
383   }
384   crx_installer_.reset();
385 
386   if (pending_modules_.empty()) {
387     CHECK_EQ(extension->id(), id_);
388     ReportSuccess();
389   } else {
390     const base::Version version_required(info.minimum_version);
391     if (version_required.IsValid() &&
392         extension->version().CompareTo(version_required) < 0) {
393       // It should not happen, CrxInstaller will make sure the version is
394       // equal or newer than version_required.
395       ReportFailure(kDependencyNotFoundError,
396                     FAILURE_REASON_DEPENDENCY_NOT_FOUND);
397     } else if (!SharedModuleInfo::IsSharedModule(extension)) {
398       // It should not happen, CrxInstaller will make sure it is a shared
399       // module.
400       ReportFailure(kDependencyNotSharedModuleError,
401                     FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
402     } else {
403       DownloadNextPendingModule();
404     }
405   }
406 }
407 
InvalidateDelegate()408 void WebstoreInstaller::InvalidateDelegate() {
409   delegate_ = nullptr;
410 }
411 
SetDownloadDirectoryForTests(base::FilePath * directory)412 void WebstoreInstaller::SetDownloadDirectoryForTests(
413     base::FilePath* directory) {
414   g_download_directory_for_tests = directory;
415 }
416 
~WebstoreInstaller()417 WebstoreInstaller::~WebstoreInstaller() {
418   if (download_item_) {
419     download_item_->RemoveObserver(this);
420     download_item_ = nullptr;
421   }
422 }
423 
OnDownloadStarted(const std::string & extension_id,DownloadItem * item,download::DownloadInterruptReason interrupt_reason)424 void WebstoreInstaller::OnDownloadStarted(
425     const std::string& extension_id,
426     DownloadItem* item,
427     download::DownloadInterruptReason interrupt_reason) {
428   if (!item || interrupt_reason != download::DOWNLOAD_INTERRUPT_REASON_NONE) {
429     if (item)
430       item->Remove();
431     ReportFailure(download::DownloadInterruptReasonToString(interrupt_reason),
432                   FAILURE_REASON_OTHER);
433     return;
434   }
435 
436   bool found = false;
437   for (const auto& module : pending_modules_) {
438     if (extension_id == module.extension_id) {
439       found = true;
440       break;
441     }
442   }
443   if (!found) {
444     // If this extension is not pending, it means another installer has
445     // installed this extension and triggered OnExtensionInstalled(). In this
446     // case, either it was the main module and success has already been
447     // reported, or it was a dependency and either failed (ie. wrong version) or
448     // the next download was triggered. In any case, the only thing that needs
449     // to be done is to stop this download.
450     item->Remove();
451     return;
452   }
453 
454   DCHECK_EQ(download::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
455   DCHECK(!pending_modules_.empty());
456   download_item_ = item;
457   download_item_->AddObserver(this);
458   if (pending_modules_.size() > 1) {
459     // We are downloading a shared module. We need create an approval for it.
460     std::unique_ptr<Approval> approval =
461         Approval::CreateForSharedModule(profile_);
462     const SharedModuleInfo::ImportInfo& info = pending_modules_.front();
463     approval->extension_id = info.extension_id;
464     const base::Version version_required(info.minimum_version);
465 
466     if (version_required.IsValid()) {
467       approval->minimum_version.reset(new base::Version(version_required));
468     }
469     download_item_->SetUserData(kApprovalKey, std::move(approval));
470   } else {
471     // It is for the main module of the extension. We should use the provided
472     // |approval_|.
473     if (approval_)
474       download_item_->SetUserData(kApprovalKey, std::move(approval_));
475   }
476 
477   if (!download_started_) {
478     if (delegate_)
479       delegate_->OnExtensionDownloadStarted(id_, download_item_);
480     download_started_ = true;
481   }
482 }
483 
OnDownloadUpdated(DownloadItem * download)484 void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) {
485   CHECK_EQ(download_item_, download);
486 
487   switch (download->GetState()) {
488     case DownloadItem::CANCELLED:
489       ReportFailure(kDownloadCanceledError, FAILURE_REASON_CANCELLED);
490       break;
491     case DownloadItem::INTERRUPTED:
492       RecordInterrupt(download);
493       ReportFailure(
494           GetErrorMessageForDownloadInterrupt(download->GetLastReason()),
495           FAILURE_REASON_OTHER);
496       break;
497     case DownloadItem::COMPLETE:
498       // Stop the progress timer if it's running.
499       download_progress_timer_.Stop();
500 
501       // Only wait for other notifications if the download is really
502       // an extension.
503       if (!download_crx_util::IsExtensionDownload(*download)) {
504         ReportFailure(kInvalidDownloadError, FAILURE_REASON_OTHER);
505         return;
506       }
507 
508       if (crx_installer_.get())
509         return;  // DownloadItemImpl calls the observer twice, ignore it.
510 
511       StartCrxInstaller(*download);
512 
513       if (pending_modules_.size() == 1) {
514         // The download is the last module - the extension main module.
515         if (delegate_)
516           delegate_->OnExtensionDownloadProgress(id_, download);
517         extensions::InstallTracker* tracker =
518             extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
519         tracker->OnDownloadProgress(id_, 100);
520       }
521       break;
522     case DownloadItem::IN_PROGRESS: {
523       if (delegate_ && pending_modules_.size() == 1) {
524         // Only report download progress for the main module to |delegrate_|.
525         delegate_->OnExtensionDownloadProgress(id_, download);
526       }
527       UpdateDownloadProgress();
528       break;
529     }
530     default:
531       // Continue listening if the download is not in one of the above states.
532       break;
533   }
534 }
535 
OnDownloadDestroyed(DownloadItem * download)536 void WebstoreInstaller::OnDownloadDestroyed(DownloadItem* download) {
537   CHECK_EQ(download_item_, download);
538   download_item_->RemoveObserver(this);
539   download_item_ = nullptr;
540 }
541 
DownloadNextPendingModule()542 void WebstoreInstaller::DownloadNextPendingModule() {
543   CHECK(!pending_modules_.empty());
544   if (pending_modules_.size() == 1) {
545     DCHECK_EQ(id_, pending_modules_.front().extension_id);
546     DownloadCrx(id_, install_source_);
547   } else {
548     DownloadCrx(pending_modules_.front().extension_id, INSTALL_SOURCE_OTHER);
549   }
550 }
551 
DownloadCrx(const std::string & extension_id,InstallSource source)552 void WebstoreInstaller::DownloadCrx(
553     const std::string& extension_id,
554     InstallSource source) {
555   download_url_ = GetWebstoreInstallURL(extension_id, source);
556   MaybeAppendAuthUserParameter(approval_->authuser, &download_url_);
557 
558   base::FilePath user_data_dir;
559   base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
560   base::FilePath download_path = user_data_dir.Append(kWebstoreDownloadFolder);
561 
562   base::FilePath download_directory(g_download_directory_for_tests ?
563       *g_download_directory_for_tests : download_path);
564 
565   base::PostTaskAndReplyWithResult(
566       GetExtensionFileTaskRunner().get(), FROM_HERE,
567       base::BindOnce(&GetDownloadFilePath, download_directory, extension_id),
568       base::BindOnce(&WebstoreInstaller::StartDownload, this, extension_id));
569 }
570 
571 // http://crbug.com/165634
572 // http://crbug.com/126013
573 // The current working theory is that one of the many pointers dereferenced in
574 // here is occasionally deleted before all of its referers are nullified,
575 // probably in a callback race. After this comment is released, the crash
576 // reports should narrow down exactly which pointer it is.  Collapsing all the
577 // early-returns into a single branch makes it hard to see exactly which pointer
578 // it is.
StartDownload(const std::string & extension_id,const base::FilePath & file)579 void WebstoreInstaller::StartDownload(const std::string& extension_id,
580                                       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->GetMainFrame()->GetRenderViewHost()) {
601     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
602     return;
603   }
604   if (!contents->GetMainFrame()->GetRenderViewHost()->GetProcess()) {
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   int render_process_host_id =
623       contents->GetMainFrame()->GetRenderViewHost()->GetProcess()->GetID();
624 
625   content::RenderFrameHost* render_frame_host = contents->GetMainFrame();
626   net::NetworkTrafficAnnotationTag traffic_annotation =
627       net::DefineNetworkTrafficAnnotation("webstore_installer", R"(
628         semantics {
629           sender: "Webstore Installer"
630           description: "Downloads an extension for installation."
631           trigger:
632             "User initiates a webstore extension installation flow, including "
633             "installing from the webstore, inline installation from a site, "
634             "re-installing a corrupted extension, and others."
635           data:
636             "The id of the extension to be installed and information about the "
637             "user's installation, including version, language, distribution "
638             "(Chrome vs Chromium), NaCl architecture, installation source (as "
639             "an enum), and accepted crx formats."
640           destination: GOOGLE_OWNED_SERVICE
641         }
642         policy {
643           cookies_allowed: YES
644           cookies_store: "user"
645           setting:
646             "This feature cannot be disabled. It is only activated if the user "
647             "triggers an extension installation."
648           chrome_policy {
649             ExtensionInstallBlocklist {
650               ExtensionInstallBlocklist: {
651                 entries: '*'
652               }
653             }
654           }
655         })");
656   std::unique_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
657       download_url_, render_process_host_id, render_frame_host->GetRoutingID(),
658       traffic_annotation));
659   params->set_file_path(file);
660   if (controller.GetVisibleEntry()) {
661     content::Referrer referrer = content::Referrer::SanitizeForRequest(
662         download_url_,
663         content::Referrer(controller.GetVisibleEntry()->GetURL(),
664                           network::mojom::ReferrerPolicy::kDefault));
665     params->set_referrer(referrer.url);
666     params->set_referrer_policy(
667         content::Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
668   }
669   params->set_callback(base::BindOnce(&WebstoreInstaller::OnDownloadStarted,
670                                       this, extension_id));
671   params->set_download_source(download::DownloadSource::EXTENSION_INSTALLER);
672   download_manager->DownloadUrl(std::move(params));
673 }
674 
675 void WebstoreInstaller::UpdateDownloadProgress() {
676   // If the download has gone away, or isn't in progress (in which case we can't
677   // give a good progress estimate), stop any running timers and return.
678   if (!download_item_ ||
679       download_item_->GetState() != DownloadItem::IN_PROGRESS) {
680     download_progress_timer_.Stop();
681     return;
682   }
683 
684   int percent = download_item_->PercentComplete();
685   // Only report progress if percent is more than 0 or we have finished
686   // downloading at least one of the pending modules.
687   int finished_modules = total_modules_ - pending_modules_.size();
688   if (finished_modules > 0 && percent < 0)
689     percent = 0;
690   if (percent >= 0) {
691     percent = (percent + (finished_modules * 100)) / total_modules_;
692     extensions::InstallTracker* tracker =
693         extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
694     tracker->OnDownloadProgress(id_, percent);
695   }
696 
697   // If there's enough time remaining on the download to warrant an update,
698   // set the timer (overwriting any current timers). Otherwise, stop the
699   // timer.
700   base::TimeDelta time_remaining;
701   if (download_item_->TimeRemaining(&time_remaining) &&
702       time_remaining > kTimeRemainingThreshold) {
703     download_progress_timer_.Start(FROM_HERE, kTimeRemainingThreshold, this,
704                                    &WebstoreInstaller::UpdateDownloadProgress);
705   } else {
706     download_progress_timer_.Stop();
707   }
708 }
709 
StartCrxInstaller(const DownloadItem & download)710 void WebstoreInstaller::StartCrxInstaller(const DownloadItem& download) {
711   DCHECK_CURRENTLY_ON(BrowserThread::UI);
712   DCHECK(!crx_installer_.get());
713 
714   ExtensionService* service = ExtensionSystem::Get(profile_)->
715       extension_service();
716   CHECK(service);
717 
718   const Approval* approval = GetAssociatedApproval(download);
719   DCHECK(approval);
720 
721   crx_installer_ = download_crx_util::CreateCrxInstaller(profile_, download);
722 
723   crx_installer_->set_expected_id(approval->extension_id);
724   crx_installer_->set_is_gallery_install(true);
725   crx_installer_->set_allow_silent_install(true);
726 
727   crx_installer_->InstallCrx(download.GetFullPath());
728 }
729 
ReportFailure(const std::string & error,FailureReason reason)730 void WebstoreInstaller::ReportFailure(const std::string& error,
731                                       FailureReason reason) {
732   if (delegate_) {
733     delegate_->OnExtensionInstallFailure(id_, error, reason);
734     delegate_ = nullptr;
735   }
736 
737   extensions::InstallTracker* tracker =
738       extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
739   tracker->OnInstallFailure(id_);
740 
741   Release();  // Balanced in Start().
742 }
743 
ReportSuccess()744 void WebstoreInstaller::ReportSuccess() {
745   if (delegate_) {
746     delegate_->OnExtensionInstallSuccess(id_);
747     delegate_ = nullptr;
748   }
749 
750   Release();  // Balanced in Start().
751 }
752 
RecordInterrupt(const DownloadItem * download) const753 void WebstoreInstaller::RecordInterrupt(const DownloadItem* download) const {
754   base::UmaHistogramSparse("Extensions.WebstoreDownload.InterruptReason",
755                            download->GetLastReason());
756 
757   // Use logarithmic bin sizes up to 1 TB.
758   const int kNumBuckets = 30;
759   const int64_t kMaxSizeKb = 1 << kNumBuckets;
760   UMA_HISTOGRAM_CUSTOM_COUNTS(
761       "Extensions.WebstoreDownload.InterruptReceivedKBytes",
762       download->GetReceivedBytes() / 1024,
763       1,
764       kMaxSizeKb,
765       kNumBuckets);
766   int64_t total_bytes = download->GetTotalBytes();
767   if (total_bytes >= 0) {
768     UMA_HISTOGRAM_CUSTOM_COUNTS(
769         "Extensions.WebstoreDownload.InterruptTotalKBytes",
770         total_bytes / 1024,
771         1,
772         kMaxSizeKb,
773         kNumBuckets);
774   }
775   UMA_HISTOGRAM_BOOLEAN(
776       "Extensions.WebstoreDownload.InterruptTotalSizeUnknown",
777       total_bytes <= 0);
778 }
779 
780 }  // namespace extensions
781