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