Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / webstore_installer.cc
index 5ef853c..7c069ce 100644 (file)
@@ -9,29 +9,33 @@
 #include "base/basictypes.h"
 #include "base/bind.h"
 #include "base/command_line.h"
-#include "base/file_util.h"
+#include "base/files/file_util.h"
+#include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/path_service.h"
 #include "base/rand_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/browser_process.h"
+#include "base/time/time.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/download/download_crx_util.h"
 #include "chrome/browser/download/download_prefs.h"
 #include "chrome/browser/download/download_stats.h"
 #include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/extension_system.h"
 #include "chrome/browser/extensions/install_tracker.h"
 #include "chrome/browser/extensions/install_tracker_factory.h"
+#include "chrome/browser/extensions/install_verifier.h"
+#include "chrome/browser/extensions/shared_module_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_paths.h"
 #include "chrome/common/chrome_switches.h"
-#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/extensions/manifest_handlers/shared_module_info.h"
-#include "chrome/common/omaha_query_params/omaha_query_params.h"
+#include "components/crx_file/id_util.h"
+#include "components/omaha_query_params/omaha_query_params.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_manager.h"
 #include "content/public/browser/download_save_info.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_urls.h"
 #include "extensions/common/manifest_constants.h"
+#include "extensions/common/manifest_handlers/shared_module_info.h"
 #include "net/base/escape.h"
 #include "url/gurl.h"
 
@@ -52,7 +61,6 @@
 #include "chrome/browser/chromeos/drive/file_system_util.h"
 #endif
 
-using chrome::OmahaQueryParams;
 using content::BrowserContext;
 using content::BrowserThread;
 using content::DownloadItem;
@@ -68,36 +76,38 @@ const char kApprovalKey[] = "extensions.webstore_installer";
 const char kInvalidIdError[] = "Invalid id";
 const char kDownloadDirectoryError[] = "Could not create download directory";
 const char kDownloadCanceledError[] = "Download canceled";
-const char kInstallCanceledError[] = "Install canceled";
 const char kDownloadInterruptedError[] = "Download interrupted";
 const char kInvalidDownloadError[] =
     "Download was not a valid extension or user script";
 const char kDependencyNotFoundError[] = "Dependency not found";
 const char kDependencyNotSharedModuleError[] =
-  "Dependency is not shared module";
+    "Dependency is not shared module";
 const char kInlineInstallSource[] = "inline";
 const char kDefaultInstallSource[] = "ondemand";
 const char kAppLauncherInstallSource[] = "applauncher";
 
+// TODO(rockot): Share this duplicated constant with the extension updater.
+// See http://crbug.com/371398.
+const char kAuthUserQueryKey[] = "authuser";
+
+const size_t kTimeRemainingMinutesThreshold = 1u;
+
+// Folder for downloading crx files from the webstore. This is used so that the
+// crx files don't go via the usual downloads folder.
+const base::FilePath::CharType kWebstoreDownloadFolder[] =
+    FILE_PATH_LITERAL("Webstore Downloads");
+
 base::FilePath* g_download_directory_for_tests = NULL;
 
 // Must be executed on the FILE thread.
 void GetDownloadFilePath(
-    const base::FilePath& download_directory, const std::string& id,
+    const base::FilePath& download_directory,
+    const std::string& id,
     const base::Callback<void(const base::FilePath&)>& callback) {
-  base::FilePath directory(g_download_directory_for_tests ?
-                     *g_download_directory_for_tests : download_directory);
-
-#if defined(OS_CHROMEOS)
-  // Do not use drive for extension downloads.
-  if (drive::util::IsUnderDriveMountPoint(directory))
-    directory = DownloadPrefs::GetDefaultDownloadDirectory();
-#endif
-
   // Ensure the download directory exists. TODO(asargent) - make this use
   // common code from the downloads system.
-  if (!base::DirectoryExists(directory)) {
-    if (!file_util::CreateDirectory(directory)) {
+  if (!base::DirectoryExists(download_directory)) {
+    if (!base::CreateDirectory(download_directory)) {
       BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
                               base::Bind(callback, base::FilePath()));
       return;
@@ -112,10 +122,10 @@ void GetDownloadFilePath(
       base::Uint64ToString(base::RandGenerator(kuint16max));
 
   base::FilePath file =
-      directory.AppendASCII(id + "_" + random_number + ".crx");
+      download_directory.AppendASCII(id + "_" + random_number + ".crx");
 
   int uniquifier =
-      file_util::GetUniquePathNumber(file, base::FilePath::StringType());
+      base::GetUniquePathNumber(file, base::FilePath::StringType());
   if (uniquifier > 0) {
     file = file.InsertBeforeExtensionASCII(
         base::StringPrintf(" (%d)", uniquifier));
@@ -125,13 +135,45 @@ void GetDownloadFilePath(
                           base::Bind(callback, file));
 }
 
+void MaybeAppendAuthUserParameter(const std::string& authuser, GURL* url) {
+  if (authuser.empty())
+    return;
+  std::string old_query = url->query();
+  url::Component query(0, old_query.length());
+  url::Component key, value;
+  // Ensure that the URL doesn't already specify an authuser parameter.
+  while (url::ExtractQueryKeyValue(
+             old_query.c_str(), &query, &key, &value)) {
+    std::string key_string = old_query.substr(key.begin, key.len);
+    if (key_string == kAuthUserQueryKey) {
+      return;
+    }
+  }
+  if (!old_query.empty()) {
+    old_query += "&";
+  }
+  std::string authuser_param = base::StringPrintf(
+      "%s=%s",
+      kAuthUserQueryKey,
+      authuser.c_str());
+
+  // TODO(rockot): Share this duplicated code with the extension updater.
+  // See http://crbug.com/371398.
+  std::string new_query_string = old_query + authuser_param;
+  url::Component new_query(0, new_query_string.length());
+  url::Replacements<char> replacements;
+  replacements.SetQuery(new_query_string.c_str(), new_query);
+  *url = url->ReplaceComponents(replacements);
+}
+
 }  // namespace
 
 namespace extensions {
 
 // static
 GURL WebstoreInstaller::GetWebstoreInstallURL(
-    const std::string& extension_id, InstallSource source) {
+    const std::string& extension_id,
+    InstallSource source) {
   std::string install_source;
   switch (source) {
     case INSTALL_SOURCE_INLINE:
@@ -155,13 +197,13 @@ GURL WebstoreInstaller::GetWebstoreInstallURL(
   params.push_back("id=" + extension_id);
   if (!install_source.empty())
     params.push_back("installsource=" + install_source);
-  params.push_back("lang=" + g_browser_process->GetApplicationLocale());
   params.push_back("uc");
   std::string url_string = extension_urls::GetWebstoreUpdateUrl().spec();
 
   GURL url(url_string + "?response=redirect&" +
-           OmahaQueryParams::Get(OmahaQueryParams::CRX) + "&x=" +
-           net::EscapeQueryParamValue(JoinString(params, '&'), true));
+           omaha_query_params::OmahaQueryParams::Get(
+               omaha_query_params::OmahaQueryParams::CRX) +
+           "&x=" + net::EscapeQueryParamValue(JoinString(params, '&'), true));
   DCHECK(url.is_valid());
 
   return url;
@@ -183,7 +225,8 @@ WebstoreInstaller::Approval::Approval()
       skip_post_install_ui(false),
       skip_install_dialog(false),
       enable_launcher(false),
-      manifest_check_level(MANIFEST_CHECK_LEVEL_STRICT) {
+      manifest_check_level(MANIFEST_CHECK_LEVEL_STRICT),
+      is_ephemeral(false) {
 }
 
 scoped_ptr<WebstoreInstaller::Approval>
@@ -198,6 +241,7 @@ WebstoreInstaller::Approval::CreateForSharedModule(Profile* profile) {
   scoped_ptr<Approval> result(new Approval());
   result->profile = profile;
   result->skip_install_dialog = true;
+  result->skip_post_install_ui = true;
   result->manifest_check_level = MANIFEST_CHECK_LEVEL_NONE;
   return result.Pass();
 }
@@ -213,7 +257,8 @@ WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
   result->profile = profile;
   result->manifest = scoped_ptr<Manifest>(
       new Manifest(Manifest::INVALID_LOCATION,
-                   scoped_ptr<DictionaryValue>(parsed_manifest->DeepCopy())));
+                   scoped_ptr<base::DictionaryValue>(
+                       parsed_manifest->DeepCopy())));
   result->skip_install_dialog = true;
   result->manifest_check_level = strict_manifest_check ?
     MANIFEST_CHECK_LEVEL_STRICT : MANIFEST_CHECK_LEVEL_LOOSE;
@@ -229,51 +274,46 @@ const WebstoreInstaller::Approval* WebstoreInstaller::GetAssociatedApproval(
 
 WebstoreInstaller::WebstoreInstaller(Profile* profile,
                                      Delegate* delegate,
-                                     NavigationController* controller,
+                                     content::WebContents* web_contents,
                                      const std::string& id,
                                      scoped_ptr<Approval> approval,
                                      InstallSource source)
-    : profile_(profile),
+    : content::WebContentsObserver(web_contents),
+      extension_registry_observer_(this),
+      profile_(profile),
       delegate_(delegate),
-      controller_(controller),
       id_(id),
       install_source_(source),
       download_item_(NULL),
       approval_(approval.release()),
       total_modules_(0),
       download_started_(false) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-  DCHECK(controller_);
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  DCHECK(web_contents);
 
-  registrar_.Add(this, chrome::NOTIFICATION_CRX_INSTALLER_DONE,
-                 content::NotificationService::AllSources());
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED,
-                 content::Source<Profile>(profile->GetOriginalProfile()));
-  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR,
+  registrar_.Add(this,
+                 extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR,
                  content::Source<CrxInstaller>(NULL));
+  extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
 }
 
 void WebstoreInstaller::Start() {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   AddRef();  // Balanced in ReportSuccess and ReportFailure.
 
-  if (!Extension::IdIsValid(id_)) {
+  if (!crx_file::id_util::IdIsValid(id_)) {
     ReportFailure(kInvalidIdError, FAILURE_REASON_OTHER);
     return;
   }
 
   ExtensionService* extension_service =
     ExtensionSystem::Get(profile_)->extension_service();
-  if (approval_.get() && approval_->dummy_extension) {
-    ExtensionService::ImportStatus status =
-      extension_service->CheckImports(approval_->dummy_extension,
-                                      &pending_modules_, &pending_modules_);
-    // For this case, it is because some imports are not shared modules.
-    if (status == ExtensionService::IMPORT_STATUS_UNRECOVERABLE) {
-      ReportFailure(kDependencyNotSharedModuleError,
-          FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
-      return;
-    }
+  if (approval_.get() && approval_->dummy_extension.get()) {
+    extension_service->shared_module_service()->CheckImports(
+        approval_->dummy_extension.get(), &pending_modules_, &pending_modules_);
+    // Do not check the return value of CheckImports, the CRX installer
+    // will report appropriate error messages and fail to install if there
+    // is an import error.
   }
 
   // Add the extension main module into the list.
@@ -283,83 +323,54 @@ void WebstoreInstaller::Start() {
 
   total_modules_ = pending_modules_.size();
 
-  // TODO(crbug.com/305343): Query manifest of dependencises before
-  // downloading & installing those dependencies.
-  DownloadNextPendingModule();
+  std::set<std::string> ids;
+  std::list<SharedModuleInfo::ImportInfo>::const_iterator i;
+  for (i = pending_modules_.begin(); i != pending_modules_.end(); ++i) {
+    ids.insert(i->extension_id);
+  }
+  ExtensionSystem::Get(profile_)->install_verifier()->AddProvisional(ids);
 
   std::string name;
   if (!approval_->manifest->value()->GetString(manifest_keys::kName, &name)) {
     NOTREACHED();
   }
   extensions::InstallTracker* tracker =
-      extensions::InstallTrackerFactory::GetForProfile(profile_);
-  tracker->OnBeginExtensionInstall(
-      id_, name, approval_->installing_icon, approval_->manifest->is_app(),
+      extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+  extensions::InstallObserver::ExtensionInstallParams params(
+      id_,
+      name,
+      approval_->installing_icon,
+      approval_->manifest->is_app(),
       approval_->manifest->is_platform_app());
+  params.is_ephemeral = approval_->is_ephemeral;
+  tracker->OnBeginExtensionInstall(params);
+
+  tracker->OnBeginExtensionDownload(id_);
+
+  // TODO(crbug.com/305343): Query manifest of dependencies before
+  // downloading & installing those dependencies.
+  DownloadNextPendingModule();
 }
 
 void WebstoreInstaller::Observe(int type,
                                 const content::NotificationSource& source,
                                 const content::NotificationDetails& details) {
   switch (type) {
-    case chrome::NOTIFICATION_CRX_INSTALLER_DONE: {
-      const Extension* extension =
-          content::Details<const Extension>(details).ptr();
-      CrxInstaller* installer = content::Source<CrxInstaller>(source).ptr();
-      if (extension == NULL && download_item_ != NULL &&
-          installer->download_url() == download_item_->GetURL() &&
-          installer->profile()->IsSameProfile(profile_)) {
-        ReportFailure(kInstallCanceledError, FAILURE_REASON_CANCELLED);
-      }
-      break;
-    }
-
-    case chrome::NOTIFICATION_EXTENSION_INSTALLED: {
-      CHECK(profile_->IsSameProfile(content::Source<Profile>(source).ptr()));
-      const Extension* extension =
-          content::Details<const InstalledExtensionInfo>(details)->extension;
-      if (pending_modules_.empty())
-        return;
-      SharedModuleInfo::ImportInfo info = pending_modules_.front();
-      if (extension->id() != info.extension_id)
-        return;
-      pending_modules_.pop_front();
-
-      if (pending_modules_.empty()) {
-        CHECK_EQ(extension->id(), id_);
-        ReportSuccess();
-      } else {
-        const Version version_required(info.minimum_version);
-        if (version_required.IsValid() &&
-            extension->version()->CompareTo(version_required) < 0) {
-          // It should not happen, CrxInstaller will make sure the version is
-          // equal or newer than version_required.
-          ReportFailure(kDependencyNotFoundError,
-              FAILURE_REASON_DEPENDENCY_NOT_FOUND);
-        } else if (!SharedModuleInfo::IsSharedModule(extension)) {
-          // It should not happen, CrxInstaller will make sure it is a shared
-          // module.
-          ReportFailure(kDependencyNotSharedModuleError,
-              FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
-        } else {
-          DownloadNextPendingModule();
-        }
-      }
-      break;
-    }
-
-    case chrome::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
+    case extensions::NOTIFICATION_EXTENSION_INSTALL_ERROR: {
       CrxInstaller* crx_installer = content::Source<CrxInstaller>(source).ptr();
       CHECK(crx_installer);
-      if (!profile_->IsSameProfile(crx_installer->profile()))
+      if (crx_installer != crx_installer_.get())
         return;
 
       // TODO(rdevlin.cronin): Continue removing std::string errors and
-      // replacing with string16. See crbug.com/71980.
-      const string16* error = content::Details<const string16>(details).ptr();
-      const std::string utf8_error = UTF16ToUTF8(*error);
-      if (download_url_ == crx_installer->original_download_url())
-        ReportFailure(utf8_error, FAILURE_REASON_OTHER);
+      // replacing with base::string16. See crbug.com/71980.
+      const base::string16* error =
+          content::Details<const base::string16>(details).ptr();
+      const std::string utf8_error = base::UTF16ToUTF8(*error);
+      crx_installer_ = NULL;
+      // ReportFailure releases a reference to this object so it must be the
+      // last operation in this method.
+      ReportFailure(utf8_error, FAILURE_REASON_OTHER);
       break;
     }
 
@@ -368,6 +379,48 @@ void WebstoreInstaller::Observe(int type,
   }
 }
 
+void WebstoreInstaller::OnExtensionInstalled(
+    content::BrowserContext* browser_context,
+    const Extension* extension,
+    bool is_update) {
+  CHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context)));
+  if (pending_modules_.empty())
+    return;
+  SharedModuleInfo::ImportInfo info = pending_modules_.front();
+  if (extension->id() != info.extension_id)
+    return;
+  pending_modules_.pop_front();
+
+  // Clean up local state from the current download.
+  if (download_item_) {
+    download_item_->RemoveObserver(this);
+    download_item_->Remove();
+    download_item_ = NULL;
+  }
+  crx_installer_ = NULL;
+
+  if (pending_modules_.empty()) {
+    CHECK_EQ(extension->id(), id_);
+    ReportSuccess();
+  } else {
+    const Version version_required(info.minimum_version);
+    if (version_required.IsValid() &&
+        extension->version()->CompareTo(version_required) < 0) {
+      // It should not happen, CrxInstaller will make sure the version is
+      // equal or newer than version_required.
+      ReportFailure(kDependencyNotFoundError,
+                    FAILURE_REASON_DEPENDENCY_NOT_FOUND);
+    } else if (!SharedModuleInfo::IsSharedModule(extension)) {
+      // It should not happen, CrxInstaller will make sure it is a shared
+      // module.
+      ReportFailure(kDependencyNotSharedModuleError,
+                    FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE);
+    } else {
+      DownloadNextPendingModule();
+    }
+  }
+}
+
 void WebstoreInstaller::InvalidateDelegate() {
   delegate_ = NULL;
 }
@@ -378,7 +431,6 @@ void WebstoreInstaller::SetDownloadDirectoryForTests(
 }
 
 WebstoreInstaller::~WebstoreInstaller() {
-  controller_ = NULL;
   if (download_item_) {
     download_item_->RemoveObserver(this);
     download_item_ = NULL;
@@ -386,14 +438,16 @@ WebstoreInstaller::~WebstoreInstaller() {
 }
 
 void WebstoreInstaller::OnDownloadStarted(
-    DownloadItem* item, net::Error error) {
+    DownloadItem* item,
+    content::DownloadInterruptReason interrupt_reason) {
   if (!item) {
-    DCHECK_NE(net::OK, error);
-    ReportFailure(net::ErrorToString(error), FAILURE_REASON_OTHER);
+    DCHECK_NE(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
+    ReportFailure(content::DownloadInterruptReasonToString(interrupt_reason),
+                  FAILURE_REASON_OTHER);
     return;
   }
 
-  DCHECK_EQ(net::OK, error);
+  DCHECK_EQ(content::DOWNLOAD_INTERRUPT_REASON_NONE, interrupt_reason);
   DCHECK(!pending_modules_.empty());
   download_item_ = item;
   download_item_->AddObserver(this);
@@ -431,35 +485,36 @@ void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) {
       ReportFailure(kDownloadCanceledError, FAILURE_REASON_CANCELLED);
       break;
     case DownloadItem::INTERRUPTED:
+      RecordInterrupt(download);
       ReportFailure(kDownloadInterruptedError, FAILURE_REASON_OTHER);
       break;
     case DownloadItem::COMPLETE:
       // Wait for other notifications if the download is really an extension.
       if (!download_crx_util::IsExtensionDownload(*download)) {
         ReportFailure(kInvalidDownloadError, FAILURE_REASON_OTHER);
-      } else if (pending_modules_.empty()) {
-        // The download is the last module - the extension main module.
-        if (delegate_)
-          delegate_->OnExtensionDownloadProgress(id_, download);
-        extensions::InstallTracker* tracker =
-            extensions::InstallTrackerFactory::GetForProfile(profile_);
-        tracker->OnDownloadProgress(id_, 100);
+      } else {
+        if (crx_installer_.get())
+          return;  // DownloadItemImpl calls the observer twice, ignore it.
+        StartCrxInstaller(*download);
+
+        if (pending_modules_.size() == 1) {
+          // The download is the last module - the extension main module.
+          if (delegate_)
+            delegate_->OnExtensionDownloadProgress(id_, download);
+          extensions::InstallTracker* tracker =
+              extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+          tracker->OnDownloadProgress(id_, 100);
+        }
       }
+      // Stop the progress timer if it's running.
+      download_progress_timer_.Stop();
       break;
     case DownloadItem::IN_PROGRESS: {
       if (delegate_ && pending_modules_.size() == 1) {
         // Only report download progress for the main module to |delegrate_|.
         delegate_->OnExtensionDownloadProgress(id_, download);
       }
-      int percent = download->PercentComplete();
-      // Only report progress if precent is more than 0
-      if (percent >= 0) {
-        int finished_modules = total_modules_ - pending_modules_.size();
-        percent = (percent + finished_modules * 100) / total_modules_;
-        extensions::InstallTracker* tracker =
-          extensions::InstallTrackerFactory::GetForProfile(profile_);
-        tracker->OnDownloadProgress(id_, percent);
-      }
+      UpdateDownloadProgress();
       break;
     }
     default:
@@ -485,13 +540,29 @@ void WebstoreInstaller::DownloadNextPendingModule() {
 }
 
 void WebstoreInstaller::DownloadCrx(
-    const std::string& extension_id, InstallSource source) {
+    const std::string& extension_id,
+    InstallSource source) {
   download_url_ = GetWebstoreInstallURL(extension_id, source);
-  base::FilePath download_path = DownloadPrefs::FromDownloadManager(
-      BrowserContext::GetDownloadManager(profile_))->DownloadPath();
+  MaybeAppendAuthUserParameter(approval_->authuser, &download_url_);
+
+  base::FilePath user_data_dir;
+  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  base::FilePath download_path = user_data_dir.Append(kWebstoreDownloadFolder);
+
+  base::FilePath download_directory(g_download_directory_for_tests ?
+      *g_download_directory_for_tests : download_path);
+
+#if defined(OS_CHROMEOS)
+  // Do not use drive for extension downloads.
+  if (drive::util::IsUnderDriveMountPoint(download_directory)) {
+    download_directory = DownloadPrefs::FromBrowserContext(
+        profile_)->GetDefaultDownloadDirectoryForProfile();
+  }
+#endif
+
   BrowserThread::PostTask(
       BrowserThread::FILE, FROM_HERE,
-      base::Bind(&GetDownloadFilePath, download_path, id_,
+      base::Bind(&GetDownloadFilePath, download_directory, extension_id,
         base::Bind(&WebstoreInstaller::StartDownload, this)));
 }
 
@@ -504,39 +575,40 @@ void WebstoreInstaller::DownloadCrx(
 // early-returns into a single branch makes it hard to see exactly which pointer
 // it is.
 void WebstoreInstaller::StartDownload(const base::FilePath& file) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
-  DownloadManager* download_manager =
-    BrowserContext::GetDownloadManager(profile_);
   if (file.empty()) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
+
+  DownloadManager* download_manager =
+      BrowserContext::GetDownloadManager(profile_);
   if (!download_manager) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
-  if (!controller_) {
-    ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
-    return;
-  }
-  if (!controller_->GetWebContents()) {
+
+  content::WebContents* contents = web_contents();
+  if (!contents) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
-  if (!controller_->GetWebContents()->GetRenderProcessHost()) {
+  if (!contents->GetRenderProcessHost()) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
-  if (!controller_->GetWebContents()->GetRenderViewHost()) {
+  if (!contents->GetRenderViewHost()) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
-  if (!controller_->GetBrowserContext()) {
+
+  content::NavigationController& controller = contents->GetController();
+  if (!controller.GetBrowserContext()) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
-  if (!controller_->GetBrowserContext()->GetResourceContext()) {
+  if (!controller.GetBrowserContext()->GetResourceContext()) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
     return;
   }
@@ -545,26 +617,84 @@ void WebstoreInstaller::StartDownload(const base::FilePath& file) {
   // We will navigate the current tab to this url to start the download. The
   // download system will then pass the crx to the CrxInstaller.
   RecordDownloadSource(DOWNLOAD_INITIATED_BY_WEBSTORE_INSTALLER);
-  int render_process_host_id =
-    controller_->GetWebContents()->GetRenderProcessHost()->GetID();
+  int render_process_host_id = contents->GetRenderProcessHost()->GetID();
   int render_view_host_routing_id =
-    controller_->GetWebContents()->GetRenderViewHost()->GetRoutingID();
+      contents->GetRenderViewHost()->GetRoutingID();
   content::ResourceContext* resource_context =
-    controller_->GetBrowserContext()->GetResourceContext();
+      controller.GetBrowserContext()->GetResourceContext();
   scoped_ptr<DownloadUrlParameters> params(new DownloadUrlParameters(
       download_url_,
       render_process_host_id,
       render_view_host_routing_id ,
       resource_context));
   params->set_file_path(file);
-  if (controller_->GetVisibleEntry())
+  if (controller.GetVisibleEntry())
     params->set_referrer(
-        content::Referrer(controller_->GetVisibleEntry()->GetURL(),
-                          WebKit::WebReferrerPolicyDefault));
+        content::Referrer(controller.GetVisibleEntry()->GetURL(),
+                          blink::WebReferrerPolicyDefault));
   params->set_callback(base::Bind(&WebstoreInstaller::OnDownloadStarted, this));
   download_manager->DownloadUrl(params.Pass());
 }
 
+void WebstoreInstaller::UpdateDownloadProgress() {
+  // If the download has gone away, or isn't in progress (in which case we can't
+  // give a good progress estimate), stop any running timers and return.
+  if (!download_item_ ||
+      download_item_->GetState() != DownloadItem::IN_PROGRESS) {
+    download_progress_timer_.Stop();
+    return;
+  }
+
+  int percent = download_item_->PercentComplete();
+  // Only report progress if percent is more than 0 or we have finished
+  // downloading at least one of the pending modules.
+  int finished_modules = total_modules_ - pending_modules_.size();
+  if (finished_modules > 0 && percent < 0)
+    percent = 0;
+  if (percent >= 0) {
+    percent = (percent + (finished_modules * 100)) / total_modules_;
+    extensions::InstallTracker* tracker =
+        extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
+    tracker->OnDownloadProgress(id_, percent);
+  }
+
+  // If there's enough time remaining on the download to warrant an update,
+  // set the timer (overwriting any current timers). Otherwise, stop the
+  // timer.
+  base::TimeDelta time_remaining;
+  if (download_item_->TimeRemaining(&time_remaining) &&
+      time_remaining >
+          base::TimeDelta::FromSeconds(kTimeRemainingMinutesThreshold)) {
+    download_progress_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromSeconds(kTimeRemainingMinutesThreshold),
+        this,
+        &WebstoreInstaller::UpdateDownloadProgress);
+  } else {
+    download_progress_timer_.Stop();
+  }
+}
+
+void WebstoreInstaller::StartCrxInstaller(const DownloadItem& download) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK(!crx_installer_.get());
+
+  ExtensionService* service = ExtensionSystem::Get(profile_)->
+      extension_service();
+  CHECK(service);
+
+  const Approval* approval = GetAssociatedApproval(download);
+  DCHECK(approval);
+
+  crx_installer_ = download_crx_util::CreateCrxInstaller(profile_, download);
+
+  crx_installer_->set_expected_id(approval->extension_id);
+  crx_installer_->set_is_gallery_install(true);
+  crx_installer_->set_allow_silent_install(true);
+
+  crx_installer_->InstallCrx(download.GetFullPath());
+}
+
 void WebstoreInstaller::ReportFailure(const std::string& error,
                                       FailureReason reason) {
   if (delegate_) {
@@ -573,7 +703,7 @@ void WebstoreInstaller::ReportFailure(const std::string& error,
   }
 
   extensions::InstallTracker* tracker =
-      extensions::InstallTrackerFactory::GetForProfile(profile_);
+      extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
   tracker->OnInstallFailure(id_);
 
   Release();  // Balanced in Start().
@@ -588,4 +718,31 @@ void WebstoreInstaller::ReportSuccess() {
   Release();  // Balanced in Start().
 }
 
+void WebstoreInstaller::RecordInterrupt(const DownloadItem* download) const {
+  UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.WebstoreDownload.InterruptReason",
+                              download->GetLastReason());
+
+  // Use logarithmic bin sizes up to 1 TB.
+  const int kNumBuckets = 30;
+  const int64 kMaxSizeKb = 1 << kNumBuckets;
+  UMA_HISTOGRAM_CUSTOM_COUNTS(
+      "Extensions.WebstoreDownload.InterruptReceivedKBytes",
+      download->GetReceivedBytes() / 1024,
+      1,
+      kMaxSizeKb,
+      kNumBuckets);
+  int64 total_bytes = download->GetTotalBytes();
+  if (total_bytes >= 0) {
+    UMA_HISTOGRAM_CUSTOM_COUNTS(
+        "Extensions.WebstoreDownload.InterruptTotalKBytes",
+        total_bytes / 1024,
+        1,
+        kMaxSizeKb,
+        kNumBuckets);
+  }
+  UMA_HISTOGRAM_BOOLEAN(
+      "Extensions.WebstoreDownload.InterruptTotalSizeUnknown",
+      total_bytes <= 0);
+}
+
 }  // namespace extensions