Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / extensions / webstore_installer.cc
blobd85cfadb480ed483b23289c45a56482b673961f5
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.
5 #include "chrome/browser/extensions/webstore_installer.h"
7 #include <set>
8 #include <vector>
10 #include "base/basictypes.h"
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/files/file_util.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/path_service.h"
18 #include "base/rand_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/time/time.h"
24 #include "chrome/browser/chrome_notification_types.h"
25 #include "chrome/browser/download/download_crx_util.h"
26 #include "chrome/browser/download/download_prefs.h"
27 #include "chrome/browser/download/download_stats.h"
28 #include "chrome/browser/extensions/crx_installer.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/extensions/shared_module_service.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/common/chrome_paths.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/grit/generated_resources.h"
37 #include "components/crx_file/id_util.h"
38 #include "components/update_client/update_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_frame_host.h"
49 #include "content/public/browser/render_process_host.h"
50 #include "content/public/browser/render_view_host.h"
51 #include "content/public/browser/web_contents.h"
52 #include "extensions/browser/extension_registry.h"
53 #include "extensions/browser/extension_system.h"
54 #include "extensions/browser/install/crx_install_error.h"
55 #include "extensions/common/extension.h"
56 #include "extensions/common/extension_urls.h"
57 #include "extensions/common/manifest_constants.h"
58 #include "extensions/common/manifest_handlers/shared_module_info.h"
59 #include "net/base/escape.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "url/gurl.h"
63 #if defined(OS_CHROMEOS)
64 #include "chrome/browser/chromeos/drive/file_system_util.h"
65 #endif
67 using content::BrowserContext;
68 using content::BrowserThread;
69 using content::DownloadItem;
70 using content::DownloadManager;
71 using content::NavigationController;
72 using content::DownloadUrlParameters;
74 namespace {
76 // Key used to attach the Approval to the DownloadItem.
77 const char kApprovalKey[] = "extensions.webstore_installer";
79 const char kInvalidIdError[] = "Invalid id";
80 const char kDownloadDirectoryError[] = "Could not create download directory";
81 const char kDownloadCanceledError[] = "Download canceled";
82 const char kDownloadInterruptedError[] = "Download interrupted";
83 const char kInvalidDownloadError[] =
84 "Download was not a valid extension or user script";
85 const char kDependencyNotFoundError[] = "Dependency not found";
86 const char kDependencyNotSharedModuleError[] =
87 "Dependency is not shared module";
88 const char kInlineInstallSource[] = "inline";
89 const char kDefaultInstallSource[] = "ondemand";
90 const char kAppLauncherInstallSource[] = "applauncher";
92 // TODO(rockot): Share this duplicated constant with the extension updater.
93 // See http://crbug.com/371398.
94 const char kAuthUserQueryKey[] = "authuser";
96 const size_t kTimeRemainingMinutesThreshold = 1u;
98 // Folder for downloading crx files from the webstore. This is used so that the
99 // crx files don't go via the usual downloads folder.
100 const base::FilePath::CharType kWebstoreDownloadFolder[] =
101 FILE_PATH_LITERAL("Webstore Downloads");
103 base::FilePath* g_download_directory_for_tests = NULL;
105 // Must be executed on the FILE thread.
106 void GetDownloadFilePath(
107 const base::FilePath& download_directory,
108 const std::string& id,
109 const base::Callback<void(const base::FilePath&)>& callback) {
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 if (!base::CreateDirectory(download_directory)) {
114 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
115 base::Bind(callback, base::FilePath()));
116 return;
120 // This is to help avoid a race condition between when we generate this
121 // filename and when the download starts writing to it (think concurrently
122 // running sharded browser tests installing the same test file, for
123 // instance).
124 std::string random_number =
125 base::Uint64ToString(base::RandGenerator(kuint16max));
127 base::FilePath file =
128 download_directory.AppendASCII(id + "_" + random_number + ".crx");
130 int uniquifier =
131 base::GetUniquePathNumber(file, base::FilePath::StringType());
132 if (uniquifier > 0) {
133 file = file.InsertBeforeExtensionASCII(
134 base::StringPrintf(" (%d)", uniquifier));
137 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
138 base::Bind(callback, file));
141 void MaybeAppendAuthUserParameter(const std::string& authuser, GURL* url) {
142 if (authuser.empty())
143 return;
144 std::string old_query = url->query();
145 url::Component query(0, old_query.length());
146 url::Component key, value;
147 // Ensure that the URL doesn't already specify an authuser parameter.
148 while (url::ExtractQueryKeyValue(
149 old_query.c_str(), &query, &key, &value)) {
150 std::string key_string = old_query.substr(key.begin, key.len);
151 if (key_string == kAuthUserQueryKey) {
152 return;
155 if (!old_query.empty()) {
156 old_query += "&";
158 std::string authuser_param = base::StringPrintf(
159 "%s=%s",
160 kAuthUserQueryKey,
161 authuser.c_str());
163 // TODO(rockot): Share this duplicated code with the extension updater.
164 // See http://crbug.com/371398.
165 std::string new_query_string = old_query + authuser_param;
166 url::Component new_query(0, new_query_string.length());
167 url::Replacements<char> replacements;
168 replacements.SetQuery(new_query_string.c_str(), new_query);
169 *url = url->ReplaceComponents(replacements);
172 std::string GetErrorMessageForDownloadInterrupt(
173 content::DownloadInterruptReason reason) {
174 switch (reason) {
175 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_UNAUTHORIZED:
176 case content::DOWNLOAD_INTERRUPT_REASON_SERVER_FORBIDDEN:
177 return l10n_util::GetStringUTF8(IDS_WEBSTORE_DOWNLOAD_ACCESS_DENIED);
178 default:
179 break;
181 return kDownloadInterruptedError;
184 } // namespace
186 namespace extensions {
188 // static
189 GURL WebstoreInstaller::GetWebstoreInstallURL(
190 const std::string& extension_id,
191 InstallSource source) {
192 std::string install_source;
193 switch (source) {
194 case INSTALL_SOURCE_INLINE:
195 install_source = kInlineInstallSource;
196 break;
197 case INSTALL_SOURCE_APP_LAUNCHER:
198 install_source = kAppLauncherInstallSource;
199 break;
200 case INSTALL_SOURCE_OTHER:
201 install_source = kDefaultInstallSource;
204 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
205 if (cmd_line->HasSwitch(switches::kAppsGalleryDownloadURL)) {
206 std::string download_url =
207 cmd_line->GetSwitchValueASCII(switches::kAppsGalleryDownloadURL);
208 return GURL(base::StringPrintf(download_url.c_str(),
209 extension_id.c_str()));
211 std::vector<std::string> params;
212 params.push_back("id=" + extension_id);
213 if (!install_source.empty())
214 params.push_back("installsource=" + install_source);
215 params.push_back("uc");
216 std::string url_string = extension_urls::GetWebstoreUpdateUrl().spec();
218 GURL url(url_string + "?response=redirect&" +
219 update_client::UpdateQueryParams::Get(
220 update_client::UpdateQueryParams::CRX) +
221 "&x=" + net::EscapeQueryParamValue(base::JoinString(params, "&"),
222 true));
223 DCHECK(url.is_valid());
225 return url;
228 void WebstoreInstaller::Delegate::OnExtensionDownloadStarted(
229 const std::string& id,
230 content::DownloadItem* item) {
233 void WebstoreInstaller::Delegate::OnExtensionDownloadProgress(
234 const std::string& id,
235 content::DownloadItem* item) {
238 WebstoreInstaller::Approval::Approval()
239 : profile(NULL),
240 use_app_installed_bubble(false),
241 skip_post_install_ui(false),
242 skip_install_dialog(false),
243 enable_launcher(false),
244 manifest_check_level(MANIFEST_CHECK_LEVEL_STRICT),
245 is_ephemeral(false) {
248 scoped_ptr<WebstoreInstaller::Approval>
249 WebstoreInstaller::Approval::CreateWithInstallPrompt(Profile* profile) {
250 scoped_ptr<Approval> result(new Approval());
251 result->profile = profile;
252 return result.Pass();
255 scoped_ptr<WebstoreInstaller::Approval>
256 WebstoreInstaller::Approval::CreateForSharedModule(Profile* profile) {
257 scoped_ptr<Approval> result(new Approval());
258 result->profile = profile;
259 result->skip_install_dialog = true;
260 result->skip_post_install_ui = true;
261 result->manifest_check_level = MANIFEST_CHECK_LEVEL_NONE;
262 return result.Pass();
265 scoped_ptr<WebstoreInstaller::Approval>
266 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
267 Profile* profile,
268 const std::string& extension_id,
269 scoped_ptr<base::DictionaryValue> parsed_manifest,
270 bool strict_manifest_check) {
271 scoped_ptr<Approval> result(new Approval());
272 result->extension_id = extension_id;
273 result->profile = profile;
274 result->manifest = scoped_ptr<Manifest>(
275 new Manifest(Manifest::INVALID_LOCATION,
276 scoped_ptr<base::DictionaryValue>(
277 parsed_manifest->DeepCopy())));
278 result->skip_install_dialog = true;
279 result->manifest_check_level = strict_manifest_check ?
280 MANIFEST_CHECK_LEVEL_STRICT : MANIFEST_CHECK_LEVEL_LOOSE;
281 return result.Pass();
284 WebstoreInstaller::Approval::~Approval() {}
286 const WebstoreInstaller::Approval* WebstoreInstaller::GetAssociatedApproval(
287 const DownloadItem& download) {
288 return static_cast<const Approval*>(download.GetUserData(kApprovalKey));
291 WebstoreInstaller::WebstoreInstaller(Profile* profile,
292 Delegate* delegate,
293 content::WebContents* web_contents,
294 const std::string& id,
295 scoped_ptr<Approval> approval,
296 InstallSource source)
297 : content::WebContentsObserver(web_contents),
298 extension_registry_observer_(this),
299 profile_(profile),
300 delegate_(delegate),
301 id_(id),
302 install_source_(source),
303 download_item_(NULL),
304 approval_(approval.release()),
305 total_modules_(0),
306 download_started_(false) {
307 DCHECK_CURRENTLY_ON(BrowserThread::UI);
308 DCHECK(web_contents);
310 registrar_.Add(this,
311 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
312 content::Source<CrxInstaller>(NULL));
313 extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
316 void WebstoreInstaller::Start() {
317 DCHECK_CURRENTLY_ON(BrowserThread::UI);
318 AddRef(); // Balanced in ReportSuccess and ReportFailure.
320 if (!crx_file::id_util::IdIsValid(id_)) {
321 ReportFailure(kInvalidIdError, FAILURE_REASON_OTHER);
322 return;
325 ExtensionService* extension_service =
326 ExtensionSystem::Get(profile_)->extension_service();
327 if (approval_.get() && approval_->dummy_extension.get()) {
328 extension_service->shared_module_service()->CheckImports(
329 approval_->dummy_extension.get(), &pending_modules_, &pending_modules_);
330 // Do not check the return value of CheckImports, the CRX installer
331 // will report appropriate error messages and fail to install if there
332 // is an import error.
335 // Add the extension main module into the list.
336 SharedModuleInfo::ImportInfo info;
337 info.extension_id = id_;
338 pending_modules_.push_back(info);
340 total_modules_ = pending_modules_.size();
342 std::set<std::string> ids;
343 std::list<SharedModuleInfo::ImportInfo>::const_iterator i;
344 for (i = pending_modules_.begin(); i != pending_modules_.end(); ++i) {
345 ids.insert(i->extension_id);
347 InstallVerifier::Get(profile_)->AddProvisional(ids);
349 std::string name;
350 if (!approval_->manifest->value()->GetString(manifest_keys::kName, &name)) {
351 NOTREACHED();
353 extensions::InstallTracker* tracker =
354 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
355 extensions::InstallObserver::ExtensionInstallParams params(
356 id_,
357 name,
358 approval_->installing_icon,
359 approval_->manifest->is_app(),
360 approval_->manifest->is_platform_app());
361 params.is_ephemeral = approval_->is_ephemeral;
362 tracker->OnBeginExtensionInstall(params);
364 tracker->OnBeginExtensionDownload(id_);
366 // TODO(crbug.com/305343): Query manifest of dependencies before
367 // downloading & installing those dependencies.
368 DownloadNextPendingModule();
371 void WebstoreInstaller::Observe(int type,
372 const content::NotificationSource& source,
373 const content::NotificationDetails& details) {
374 switch (type) {
375 case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
376 CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
377 CHECK(crx_installer);
378 if (crx_installer != crx_installer_.get())
379 return;
381 // TODO(rdevlin.cronin): Continue removing std::string errors and
382 // replacing with base::string16. See crbug.com/71980.
383 const extensions::CrxInstallError* error =
384 content::Details<const extensions::CrxInstallError>(details).ptr();
385 const std::string utf8_error = base::UTF16ToUTF8(error->message());
386 crx_installer_ = NULL;
387 // ReportFailure releases a reference to this object so it must be the
388 // last operation in this method.
389 ReportFailure(utf8_error, FAILURE_REASON_OTHER);
390 break;
393 default:
394 NOTREACHED();
398 void WebstoreInstaller::OnExtensionInstalled(
399 content::BrowserContext* browser_context,
400 const Extension* extension,
401 bool is_update) {
402 CHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
403 if (pending_modules_.empty())
404 return;
405 SharedModuleInfo::ImportInfo info = pending_modules_.front();
406 if (extension->id() != info.extension_id)
407 return;
408 pending_modules_.pop_front();
410 // Clean up local state from the current download.
411 if (download_item_) {
412 download_item_->RemoveObserver(this);
413 download_item_->Remove();
414 download_item_ = NULL;
416 crx_installer_ = NULL;
418 if (pending_modules_.empty()) {
419 CHECK_EQ(extension->id(), id_);
420 ReportSuccess();
421 } else {
422 const Version version_required(info.minimum_version);
423 if (version_required.IsValid() &&
424 extension->version()->CompareTo(version_required) < 0) {
425 // It should not happen, CrxInstaller will make sure the version is
426 // equal or newer than version_required.
427 ReportFailure(kDependencyNotFoundError,
428 FAILURE_REASON_DEPENDENCY_NOT_FOUND);
429 } else if (!SharedModuleInfo::IsSharedModule(extension)) {
430 // It should not happen, CrxInstaller will make sure it is a shared
431 // module.
432 ReportFailure(kDependencyNotSharedModuleError,
433 FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
434 } else {
435 DownloadNextPendingModule();
440 void WebstoreInstaller::InvalidateDelegate() {
441 delegate_ = NULL;
444 void WebstoreInstaller::SetDownloadDirectoryForTests(
445 base::FilePath* directory) {
446 g_download_directory_for_tests = directory;
449 WebstoreInstaller::~WebstoreInstaller() {
450 if (download_item_) {
451 download_item_->RemoveObserver(this);
452 download_item_ = NULL;
456 void WebstoreInstaller::OnDownloadStarted(
457 const std::string& extension_id,
458 DownloadItem* item,
459 content::DownloadInterruptReason interrupt_reason) {
460 if (!item) {
461 DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
462 ReportFailure(content::DownloadInterruptReasonToString(interrupt_reason),
463 FAILURE_REASON_OTHER);
464 return;
467 bool found = false;
468 for (const auto& module : pending_modules_) {
469 if (extension_id == module.extension_id) {
470 found = true;
471 break;
474 if (!found) {
475 // If this extension is not pending, it means another installer has
476 // installed this extension and triggered OnExtensionInstalled(). In this
477 // case, either it was the main module and success has already been
478 // reported, or it was a dependency and either failed (ie. wrong version) or
479 // the next download was triggered. In any case, the only thing that needs
480 // to be done is to stop this download.
481 item->Remove();
482 return;
485 DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
486 DCHECK(!pending_modules_.empty());
487 download_item_ = item;
488 download_item_->AddObserver(this);
489 if (pending_modules_.size() > 1) {
490 // We are downloading a shared module. We need create an approval for it.
491 scoped_ptr<Approval> approval = Approval::CreateForSharedModule(profile_);
492 const SharedModuleInfo::ImportInfo& info = pending_modules_.front();
493 approval->extension_id = info.extension_id;
494 const Version version_required(info.minimum_version);
496 if (version_required.IsValid()) {
497 approval->minimum_version.reset(
498 new Version(version_required));
500 download_item_->SetUserData(kApprovalKey, approval.release());
501 } else {
502 // It is for the main module of the extension. We should use the provided
503 // |approval_|.
504 if (approval_)
505 download_item_->SetUserData(kApprovalKey, approval_.release());
508 if (!download_started_) {
509 if (delegate_)
510 delegate_->OnExtensionDownloadStarted(id_, download_item_);
511 download_started_ = true;
515 void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) {
516 CHECK_EQ(download_item_, download);
518 switch (download->GetState()) {
519 case DownloadItem::CANCELLED:
520 ReportFailure(kDownloadCanceledError, FAILURE_REASON_CANCELLED);
521 break;
522 case DownloadItem::INTERRUPTED:
523 RecordInterrupt(download);
524 ReportFailure(
525 GetErrorMessageForDownloadInterrupt(download->GetLastReason()),
526 FAILURE_REASON_OTHER);
527 break;
528 case DownloadItem::COMPLETE:
529 // Wait for other notifications if the download is really an extension.
530 if (!download_crx_util::IsExtensionDownload(*download)) {
531 ReportFailure(kInvalidDownloadError, FAILURE_REASON_OTHER);
532 } else {
533 if (crx_installer_.get())
534 return; // DownloadItemImpl calls the observer twice, ignore it.
535 StartCrxInstaller(*download);
537 if (pending_modules_.size() == 1) {
538 // The download is the last module - the extension main module.
539 if (delegate_)
540 delegate_->OnExtensionDownloadProgress(id_, download);
541 extensions::InstallTracker* tracker =
542 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
543 tracker->OnDownloadProgress(id_, 100);
546 // Stop the progress timer if it's running.
547 download_progress_timer_.Stop();
548 break;
549 case DownloadItem::IN_PROGRESS: {
550 if (delegate_ && pending_modules_.size() == 1) {
551 // Only report download progress for the main module to |delegrate_|.
552 delegate_->OnExtensionDownloadProgress(id_, download);
554 UpdateDownloadProgress();
555 break;
557 default:
558 // Continue listening if the download is not in one of the above states.
559 break;
563 void WebstoreInstaller::OnDownloadDestroyed(DownloadItem* download) {
564 CHECK_EQ(download_item_, download);
565 download_item_->RemoveObserver(this);
566 download_item_ = NULL;
569 void WebstoreInstaller::DownloadNextPendingModule() {
570 CHECK(!pending_modules_.empty());
571 if (pending_modules_.size() == 1) {
572 DCHECK_EQ(id_, pending_modules_.front().extension_id);
573 DownloadCrx(id_, install_source_);
574 } else {
575 DownloadCrx(pending_modules_.front().extension_id, INSTALL_SOURCE_OTHER);
579 void WebstoreInstaller::DownloadCrx(
580 const std::string& extension_id,
581 InstallSource source) {
582 download_url_ = GetWebstoreInstallURL(extension_id, source);
583 MaybeAppendAuthUserParameter(approval_->authuser, &download_url_);
585 base::FilePath user_data_dir;
586 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
587 base::FilePath download_path = user_data_dir.Append(kWebstoreDownloadFolder);
589 base::FilePath download_directory(g_download_directory_for_tests ?
590 *g_download_directory_for_tests : download_path);
592 #if defined(OS_CHROMEOS)
593 // Do not use drive for extension downloads.
594 if (drive::util::IsUnderDriveMountPoint(download_directory)) {
595 download_directory = DownloadPrefs::FromBrowserContext(
596 profile_)->GetDefaultDownloadDirectoryForProfile();
598 #endif
600 BrowserThread::PostTask(
601 BrowserThread::FILE, FROM_HERE,
602 base::Bind(&GetDownloadFilePath, download_directory, extension_id,
603 base::Bind(&WebstoreInstaller::StartDownload, this, extension_id)));
606 // http://crbug.com/165634
607 // http://crbug.com/126013
608 // The current working theory is that one of the many pointers dereferenced in
609 // here is occasionally deleted before all of its referers are nullified,
610 // probably in a callback race. After this comment is released, the crash
611 // reports should narrow down exactly which pointer it is. Collapsing all the
612 // early-returns into a single branch makes it hard to see exactly which pointer
613 // it is.
614 void WebstoreInstaller::StartDownload(const std::string& extension_id,
615 const base::FilePath& file) {
616 DCHECK_CURRENTLY_ON(BrowserThread::UI);
618 if (file.empty()) {
619 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
620 return;
623 DownloadManager* download_manager =
624 BrowserContext::GetDownloadManager(profile_);
625 if (!download_manager) {
626 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
627 return;
630 content::WebContents* contents = web_contents();
631 if (!contents) {
632 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
633 return;
635 if (!contents->GetRenderProcessHost()) {
636 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
637 return;
639 if (!contents->GetRenderViewHost()) {
640 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
641 return;
644 content::NavigationController& controller = contents->GetController();
645 if (!controller.GetBrowserContext()) {
646 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
647 return;
649 if (!controller.GetBrowserContext()->GetResourceContext()) {
650 ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
651 return;
654 // The download url for the given extension is contained in |download_url_|.
655 // We will navigate the current tab to this url to start the download. The
656 // download system will then pass the crx to the CrxInstaller.
657 RecordDownloadSource(DOWNLOAD_INITIATED_BY_WEBSTORE_INSTALLER);
658 int render_process_host_id = contents->GetRenderProcessHost()->GetID();
659 int render_view_host_routing_id =
660 contents->GetRenderViewHost()->GetRoutingID();
661 content::ResourceContext* resource_context =
662 controller.GetBrowserContext()->GetResourceContext();
663 scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
664 download_url_,
665 render_process_host_id,
666 render_view_host_routing_id,
667 contents->GetMainFrame()->GetRoutingID(),
668 resource_context));
669 params->set_file_path(file);
670 if (controller.GetVisibleEntry())
671 params->set_referrer(content::Referrer::SanitizeForRequest(
672 download_url_, content::Referrer(controller.GetVisibleEntry()->GetURL(),
673 blink::WebReferrerPolicyDefault)));
674 params->set_callback(base::Bind(&WebstoreInstaller::OnDownloadStarted,
675 this,
676 extension_id));
677 download_manager->DownloadUrl(params.Pass());
680 void WebstoreInstaller::UpdateDownloadProgress() {
681 // If the download has gone away, or isn't in progress (in which case we can't
682 // give a good progress estimate), stop any running timers and return.
683 if (!download_item_ ||
684 download_item_->GetState() != DownloadItem::IN_PROGRESS) {
685 download_progress_timer_.Stop();
686 return;
689 int percent = download_item_->PercentComplete();
690 // Only report progress if percent is more than 0 or we have finished
691 // downloading at least one of the pending modules.
692 int finished_modules = total_modules_ - pending_modules_.size();
693 if (finished_modules > 0 && percent < 0)
694 percent = 0;
695 if (percent >= 0) {
696 percent = (percent + (finished_modules * 100)) / total_modules_;
697 extensions::InstallTracker* tracker =
698 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
699 tracker->OnDownloadProgress(id_, percent);
702 // If there's enough time remaining on the download to warrant an update,
703 // set the timer (overwriting any current timers). Otherwise, stop the
704 // timer.
705 base::TimeDelta time_remaining;
706 if (download_item_->TimeRemaining(&time_remaining) &&
707 time_remaining >
708 base::TimeDelta::FromSeconds(kTimeRemainingMinutesThreshold)) {
709 download_progress_timer_.Start(
710 FROM_HERE,
711 base::TimeDelta::FromSeconds(kTimeRemainingMinutesThreshold),
712 this,
713 &WebstoreInstaller::UpdateDownloadProgress);
714 } else {
715 download_progress_timer_.Stop();
719 void WebstoreInstaller::StartCrxInstaller(const DownloadItem& download) {
720 DCHECK_CURRENTLY_ON(BrowserThread::UI);
721 DCHECK(!crx_installer_.get());
723 // The clock may be backward, e.g. daylight savings time just happenned.
724 if (download.GetEndTime() >= download.GetStartTime()) {
725 UMA_HISTOGRAM_TIMES("Extensions.WebstoreDownload.FileDownload",
726 download.GetEndTime() - download.GetStartTime());
728 ExtensionService* service = ExtensionSystem::Get(profile_)->
729 extension_service();
730 CHECK(service);
732 const Approval* approval = GetAssociatedApproval(download);
733 DCHECK(approval);
735 crx_installer_ = download_crx_util::CreateCrxInstaller(profile_, download);
737 crx_installer_->set_expected_id(approval->extension_id);
738 crx_installer_->set_is_gallery_install(true);
739 crx_installer_->set_allow_silent_install(true);
741 crx_installer_->InstallCrx(download.GetFullPath());
744 void WebstoreInstaller::ReportFailure(const std::string& error,
745 FailureReason reason) {
746 if (delegate_) {
747 delegate_->OnExtensionInstallFailure(id_, error, reason);
748 delegate_ = NULL;
751 extensions::InstallTracker* tracker =
752 extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
753 tracker->OnInstallFailure(id_);
755 Release(); // Balanced in Start().
758 void WebstoreInstaller::ReportSuccess() {
759 if (delegate_) {
760 delegate_->OnExtensionInstallSuccess(id_);
761 delegate_ = NULL;
764 Release(); // Balanced in Start().
767 void WebstoreInstaller::RecordInterrupt(const DownloadItem* download) const {
768 UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.WebstoreDownload.InterruptReason",
769 download->GetLastReason());
771 // Use logarithmic bin sizes up to 1 TB.
772 const int kNumBuckets = 30;
773 const int64 kMaxSizeKb = 1 << kNumBuckets;
774 UMA_HISTOGRAM_CUSTOM_COUNTS(
775 "Extensions.WebstoreDownload.InterruptReceivedKBytes",
776 download->GetReceivedBytes() / 1024,
778 kMaxSizeKb,
779 kNumBuckets);
780 int64 total_bytes = download->GetTotalBytes();
781 if (total_bytes >= 0) {
782 UMA_HISTOGRAM_CUSTOM_COUNTS(
783 "Extensions.WebstoreDownload.InterruptTotalKBytes",
784 total_bytes / 1024,
786 kMaxSizeKb,
787 kNumBuckets);
789 UMA_HISTOGRAM_BOOLEAN(
790 "Extensions.WebstoreDownload.InterruptTotalSizeUnknown",
791 total_bytes <= 0);
794 } // namespace extensions