Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / webstore_installer.cc
index a780425..7c069ce 100644 (file)
@@ -9,7 +9,7 @@
 #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 "chrome/browser/extensions/install_tracker.h"
 #include "chrome/browser/extensions/install_tracker_factory.h"
 #include "chrome/browser/extensions/install_verifier.h"
-#include "chrome/browser/omaha_query_params/omaha_query_params.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_constants.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"
@@ -58,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;
@@ -74,7 +76,6 @@ 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";
@@ -85,6 +86,10 @@ 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
@@ -130,13 +135,35 @@ void GetDownloadFilePath(
                           base::Bind(callback, file));
 }
 
-bool UseSeparateWebstoreDownloadDirectory() {
-  const char kFieldTrial[] = "WebstoreDownloadDirectory";
-  const char kSeparateDirectoryUnderUDD[] = "SeparateDirectoryUnderUDD";
-
-  std::string field_trial_group =
-      base::FieldTrialList::FindFullName(kFieldTrial);
-  return field_trial_group == kSeparateDirectoryUnderUDD;
+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
@@ -174,8 +201,9 @@ GURL WebstoreInstaller::GetWebstoreInstallURL(
   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;
@@ -213,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();
 }
@@ -250,6 +279,7 @@ WebstoreInstaller::WebstoreInstaller(Profile* profile,
                                      scoped_ptr<Approval> approval,
                                      InstallSource source)
     : content::WebContentsObserver(web_contents),
+      extension_registry_observer_(this),
       profile_(profile),
       delegate_(delegate),
       id_(id),
@@ -258,38 +288,32 @@ WebstoreInstaller::WebstoreInstaller(Profile* profile,
       approval_(approval.release()),
       total_modules_(0),
       download_started_(false) {
-  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+  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.
@@ -311,7 +335,7 @@ void WebstoreInstaller::Start() {
     NOTREACHED();
   }
   extensions::InstallTracker* tracker =
-      extensions::InstallTrackerFactory::GetForProfile(profile_);
+      extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
   extensions::InstallObserver::ExtensionInstallParams params(
       id_,
       name,
@@ -332,56 +356,10 @@ 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
@@ -389,8 +367,10 @@ void WebstoreInstaller::Observe(int type,
       const base::string16* error =
           content::Details<const base::string16>(details).ptr();
       const std::string utf8_error = base::UTF16ToUTF8(*error);
-      if (download_url_ == crx_installer->original_download_url())
-        ReportFailure(utf8_error, FAILURE_REASON_OTHER);
+      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;
     }
 
@@ -399,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;
 }
@@ -470,13 +492,19 @@ void WebstoreInstaller::OnDownloadUpdated(DownloadItem* download) {
       // 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();
@@ -515,16 +543,11 @@ void WebstoreInstaller::DownloadCrx(
     const std::string& extension_id,
     InstallSource source) {
   download_url_ = GetWebstoreInstallURL(extension_id, source);
+  MaybeAppendAuthUserParameter(approval_->authuser, &download_url_);
 
-  base::FilePath download_path;
-  if (UseSeparateWebstoreDownloadDirectory()) {
-    base::FilePath user_data_dir;
-    PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-    download_path = user_data_dir.Append(kWebstoreDownloadFolder);
-  } else {
-    download_path = DownloadPrefs::FromDownloadManager(
-        BrowserContext::GetDownloadManager(profile_))->DownloadPath();
-  }
+  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);
@@ -539,7 +562,7 @@ void WebstoreInstaller::DownloadCrx(
 
   BrowserThread::PostTask(
       BrowserThread::FILE, FROM_HERE,
-      base::Bind(&GetDownloadFilePath, download_directory, id_,
+      base::Bind(&GetDownloadFilePath, download_directory, extension_id,
         base::Bind(&WebstoreInstaller::StartDownload, this)));
 }
 
@@ -552,7 +575,7 @@ 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);
 
   if (file.empty()) {
     ReportFailure(kDownloadDirectoryError, FAILURE_REASON_OTHER);
@@ -623,12 +646,15 @@ void WebstoreInstaller::UpdateDownloadProgress() {
   }
 
   int percent = download_item_->PercentComplete();
-  // Only report progress if precent is more than 0
+  // 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) {
-    int finished_modules = total_modules_ - pending_modules_.size();
     percent = (percent + (finished_modules * 100)) / total_modules_;
     extensions::InstallTracker* tracker =
-        extensions::InstallTrackerFactory::GetForProfile(profile_);
+        extensions::InstallTrackerFactory::GetForBrowserContext(profile_);
     tracker->OnDownloadProgress(id_, percent);
   }
 
@@ -649,6 +675,26 @@ void WebstoreInstaller::UpdateDownloadProgress() {
   }
 }
 
+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_) {
@@ -657,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().