Home | History | Annotate | Download | only in extensions
      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.
     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 
    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
    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 
    192 void WebstoreInstaller::Delegate::OnExtensionDownloadStarted(
    193     const std::string& id,
    194     content::DownloadItem* item) {
    195 }
    196 
    197 void WebstoreInstaller::Delegate::OnExtensionDownloadProgress(
    198     const std::string& id,
    199     content::DownloadItem* item) {
    200 }
    201 
    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>
    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>
    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>
    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 
    246 WebstoreInstaller::Approval::~Approval() {}
    247 
    248 const WebstoreInstaller::Approval* WebstoreInstaller::GetAssociatedApproval(
    249     const DownloadItem& download) {
    250   return static_cast<const Approval*>(download.GetUserData(kApprovalKey));
    251 }
    252 
    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 
    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 
    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 
    407 void WebstoreInstaller::InvalidateDelegate() {
    408   delegate_ = NULL;
    409 }
    410 
    411 void WebstoreInstaller::SetDownloadDirectoryForTests(
    412     base::FilePath* directory) {
    413   g_download_directory_for_tests = directory;
    414 }
    415 
    416 WebstoreInstaller::~WebstoreInstaller() {
    417   controller_ = NULL;
    418   if (download_item_) {
    419     download_item_->RemoveObserver(this);
    420     download_item_ = NULL;
    421   }
    422 }
    423 
    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 
    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 
    508 void WebstoreInstaller::OnDownloadDestroyed(DownloadItem* download) {
    509   CHECK_EQ(download_item_, download);
    510   download_item_->RemoveObserver(this);
    511   download_item_ = NULL;
    512 }
    513 
    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 
    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.
    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 
    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 
    629 void WebstoreInstaller::ReportSuccess() {
    630   if (delegate_) {
    631     delegate_->OnExtensionInstallSuccess(id_);
    632     delegate_ = NULL;
    633   }
    634 
    635   Release();  // Balanced in Start().
    636 }
    637 
    638 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