Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / webstore_standalone_installer.cc
index c4f621a..859cf48 100644 (file)
@@ -5,15 +5,20 @@
 #include "chrome/browser/extensions/webstore_standalone_installer.h"
 
 #include "base/values.h"
+#include "base/version.h"
 #include "chrome/browser/extensions/crx_installer.h"
 #include "chrome/browser/extensions/extension_install_prompt.h"
 #include "chrome/browser/extensions/extension_install_ui.h"
+#include "chrome/browser/extensions/extension_install_ui_util.h"
 #include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/install_tracker.h"
 #include "chrome/browser/extensions/webstore_data_fetcher.h"
 #include "chrome/browser/profiles/profile.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_registry.h"
 #include "extensions/browser/extension_system.h"
+#include "extensions/browser/extension_util.h"
 #include "extensions/common/extension.h"
 #include "url/gurl.h"
 
@@ -28,6 +33,8 @@ const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
 const char kInvalidManifestError[] = "Invalid manifest";
 const char kUserCancelledError[] = "User cancelled install";
 const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
+const char kInstallInProgressError[] = "An install is already in progress";
+const char kLaunchInProgressError[] = "A launch is already in progress";
 
 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
     const std::string& webstore_item_id,
@@ -42,12 +49,6 @@ WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
       rating_count_(0) {
 }
 
-WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
-
-//
-// Private interface implementation.
-//
-
 void WebstoreStandaloneInstaller::BeginInstall() {
   // Add a ref to keep this alive for WebstoreDataFetcher.
   // All code paths from here eventually lead to either CompleteInstall or
@@ -55,7 +56,14 @@ void WebstoreStandaloneInstaller::BeginInstall() {
   AddRef();
 
   if (!Extension::IdIsValid(id_)) {
-    CompleteInstall(kInvalidWebstoreItemId);
+    CompleteInstall(webstore_install::INVALID_ID, kInvalidWebstoreItemId);
+    return;
+  }
+
+  webstore_install::Result result = webstore_install::OTHER_ERROR;
+  std::string error;
+  if (!EnsureUniqueInstall(&result, &error)) {
+    CompleteInstall(result, error);
     return;
   }
 
@@ -70,12 +78,96 @@ void WebstoreStandaloneInstaller::BeginInstall() {
   webstore_data_fetcher_->Start();
 }
 
-bool WebstoreStandaloneInstaller::CheckInstallValid(
-    const base::DictionaryValue& manifest,
+//
+// Private interface implementation.
+//
+
+WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {
+}
+
+void WebstoreStandaloneInstaller::AbortInstall() {
+  callback_.Reset();
+  // Abort any in-progress fetches.
+  if (webstore_data_fetcher_) {
+    webstore_data_fetcher_.reset();
+    scoped_active_install_.reset();
+    Release();  // Matches the AddRef in BeginInstall.
+  }
+}
+
+bool WebstoreStandaloneInstaller::EnsureUniqueInstall(
+    webstore_install::Result* reason,
     std::string* error) {
+  InstallTracker* tracker = InstallTracker::Get(profile_);
+  DCHECK(tracker);
+
+  const ActiveInstallData* existing_install_data =
+      tracker->GetActiveInstall(id_);
+  if (existing_install_data) {
+    if (existing_install_data->is_ephemeral) {
+      *reason = webstore_install::LAUNCH_IN_PROGRESS;
+      *error = kLaunchInProgressError;
+    } else {
+      *reason = webstore_install::INSTALL_IN_PROGRESS;
+      *error = kInstallInProgressError;
+    }
+    return false;
+  }
+
+  ActiveInstallData install_data(id_);
+  InitInstallData(&install_data);
+  scoped_active_install_.reset(new ScopedActiveInstall(tracker, install_data));
   return true;
 }
 
+void WebstoreStandaloneInstaller::CompleteInstall(
+    webstore_install::Result result,
+    const std::string& error) {
+  scoped_active_install_.reset();
+  if (!callback_.is_null())
+    callback_.Run(result == webstore_install::SUCCESS, error, result);
+  Release();  // Matches the AddRef in BeginInstall.
+}
+
+void WebstoreStandaloneInstaller::ProceedWithInstallPrompt() {
+  install_prompt_ = CreateInstallPrompt();
+  if (install_prompt_) {
+    ShowInstallUI();
+    // Control flow finishes up in InstallUIProceed or InstallUIAbort.
+  } else {
+    InstallUIProceed();
+  }
+}
+
+scoped_refptr<const Extension>
+WebstoreStandaloneInstaller::GetLocalizedExtensionForDisplay() {
+  if (!localized_extension_for_display_.get()) {
+    DCHECK(manifest_.get());
+    if (!manifest_.get())
+      return NULL;
+
+    std::string error;
+    localized_extension_for_display_ =
+        ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
+            manifest_.get(),
+            Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
+            id_,
+            localized_name_,
+            localized_description_,
+            &error);
+  }
+  return localized_extension_for_display_.get();
+}
+
+void WebstoreStandaloneInstaller::InitInstallData(
+    ActiveInstallData* install_data) const {
+  // Default implementation sets no properties.
+}
+
+void WebstoreStandaloneInstaller::OnManifestParsed() {
+  ProceedWithInstallPrompt();
+}
+
 scoped_ptr<ExtensionInstallPrompt>
 WebstoreStandaloneInstaller::CreateInstallUI() {
   return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
@@ -96,25 +188,29 @@ WebstoreStandaloneInstaller::CreateApproval() const {
 }
 
 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
-  CompleteInstall(kWebstoreRequestError);
+  OnWebStoreDataFetcherDone();
+  CompleteInstall(webstore_install::WEBSTORE_REQUEST_ERROR,
+                  kWebstoreRequestError);
 }
 
 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
     scoped_ptr<base::DictionaryValue> webstore_data) {
+  OnWebStoreDataFetcherDone();
+
   if (!CheckRequestorAlive()) {
-    CompleteInstall(std::string());
+    CompleteInstall(webstore_install::ABORTED, std::string());
     return;
   }
 
   std::string error;
 
   if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
-    CompleteInstall(error);
+    CompleteInstall(webstore_install::NOT_PERMITTED, error);
     return;
   }
 
   if (!CheckRequestorPermitted(*webstore_data, &error)) {
-    CompleteInstall(error);
+    CompleteInstall(webstore_install::NOT_PERMITTED, error);
     return;
   }
 
@@ -124,7 +220,8 @@ void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
       !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
       !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
       !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
-    CompleteInstall(kInvalidWebstoreResponseError);
+    CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+                    kInvalidWebstoreResponseError);
     return;
   }
 
@@ -134,7 +231,8 @@ void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
 
   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
-    CompleteInstall(kInvalidWebstoreResponseError);
+    CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+                    kInvalidWebstoreResponseError);
     return;
   }
 
@@ -144,7 +242,8 @@ void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
       (webstore_data->HasKey(kLocalizedDescriptionKey) &&
       !webstore_data->GetString(
           kLocalizedDescriptionKey, &localized_description_))) {
-    CompleteInstall(kInvalidWebstoreResponseError);
+    CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+                    kInvalidWebstoreResponseError);
     return;
   }
 
@@ -153,13 +252,15 @@ void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
   if (webstore_data->HasKey(kIconUrlKey)) {
     std::string icon_url_string;
     if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
-      CompleteInstall(kInvalidWebstoreResponseError);
+      CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+                      kInvalidWebstoreResponseError);
       return;
     }
     icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
         icon_url_string);
     if (!icon_url.is_valid()) {
-      CompleteInstall(kInvalidWebstoreResponseError);
+      CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+                      kInvalidWebstoreResponseError);
       return;
     }
   }
@@ -181,7 +282,8 @@ void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
 
 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
     const std::string& error) {
-  CompleteInstall(error);
+  OnWebStoreDataFetcherDone();
+  CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE, error);
 }
 
 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
@@ -191,71 +293,90 @@ void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
   CHECK_EQ(id_, id);
 
   if (!CheckRequestorAlive()) {
-    CompleteInstall(std::string());
+    CompleteInstall(webstore_install::ABORTED, std::string());
     return;
   }
 
   manifest_.reset(manifest);
   icon_ = icon;
 
-  std::string error;
-  if (!CheckInstallValid(*manifest, &error)) {
-    DCHECK(!error.empty());
-    CompleteInstall(error);
-    return;
-  }
-
-  install_prompt_ = CreateInstallPrompt();
-  if (install_prompt_) {
-    ShowInstallUI();
-    // Control flow finishes up in InstallUIProceed or InstallUIAbort.
-  } else {
-    // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
-    // OnExtensionInstallSuccess or OnExtensionInstallFailure.
-    AddRef();
-    InstallUIProceed();
-  }
+  OnManifestParsed();
 }
 
 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
     const std::string& id,
     InstallHelperResultCode result_code,
     const std::string& error_message) {
-  CompleteInstall(error_message);
+  webstore_install::Result install_result = webstore_install::OTHER_ERROR;
+  switch (result_code) {
+    case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
+      install_result = webstore_install::INVALID_MANIFEST;
+      break;
+    case WebstoreInstallHelper::Delegate::ICON_ERROR:
+      install_result = webstore_install::ICON_ERROR;
+      break;
+    default:
+      break;
+  }
+
+  CompleteInstall(install_result, error_message);
 }
 
 void WebstoreStandaloneInstaller::InstallUIProceed() {
   if (!CheckRequestorAlive()) {
-    CompleteInstall(std::string());
+    CompleteInstall(webstore_install::ABORTED, std::string());
     return;
   }
 
+  scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
+
   ExtensionService* extension_service =
       ExtensionSystem::Get(profile_)->extension_service();
-  const Extension* extension =
+  const Extension* installed_extension =
       extension_service->GetExtensionById(id_, true /* include disabled */);
-  if (extension) {
-    std::string install_result;  // Empty string for install success.
-    if (!extension_service->IsExtensionEnabled(id_)) {
-      if (!ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
-        // If the extension is installed but disabled, and not blacklisted,
-        // enable it.
-        extension_service->EnableExtension(id_);
-      } else {  // Don't install a blacklisted extension.
-        install_result = kExtensionIsBlacklisted;
+  if (installed_extension) {
+    std::string install_message;
+    webstore_install::Result install_result = webstore_install::SUCCESS;
+    bool done = true;
+
+    if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
+      // Don't install a blacklisted extension.
+      install_result = webstore_install::BLACKLISTED;
+      install_message = kExtensionIsBlacklisted;
+    } else if (util::IsEphemeralApp(installed_extension->id(), profile_) &&
+               !approval->is_ephemeral) {
+      // If the target extension has already been installed ephemerally and is
+      // up to date, it can be promoted to a regular installed extension and
+      // downloading from the Web Store is not necessary.
+      const Extension* extension_to_install = GetLocalizedExtensionForDisplay();
+      if (!extension_to_install) {
+        CompleteInstall(webstore_install::INVALID_MANIFEST,
+                        kInvalidManifestError);
+        return;
+      }
+
+      if (installed_extension->version()->CompareTo(
+              *extension_to_install->version()) < 0) {
+        // If the existing extension is out of date, proceed with the install
+        // to update the extension.
+        done = false;
+      } else {
+        install_ui::ShowPostInstallUIForApproval(
+            profile_, *approval, installed_extension);
+        extension_service->PromoteEphemeralApp(installed_extension, false);
       }
-    } else if (!extension->is_ephemeral()) {
-      // else extension is installed and enabled; no work to be done.
-      CompleteInstall(install_result);
+    } else if (!extension_service->IsExtensionEnabled(id_)) {
+      // If the extension is installed but disabled, and not blacklisted,
+      // enable it.
+      extension_service->EnableExtension(id_);
+    }  // else extension is installed and enabled; no work to be done.
+
+    if (done) {
+      CompleteInstall(install_result, install_message);
       return;
     }
-
-    // TODO(tmdiep): Optimize installation of ephemeral apps. For now we just
-    // reinstall the app.
   }
 
-  scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
-
   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
       profile_,
       this,
@@ -267,69 +388,56 @@ void WebstoreStandaloneInstaller::InstallUIProceed() {
 }
 
 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
-  CompleteInstall(kUserCancelledError);
-  Release();  // Balanced in ShowInstallUI.
+  CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
 }
 
 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
     const std::string& id) {
   CHECK_EQ(id_, id);
-  CompleteInstall(std::string());
-  Release();  // Balanced in ShowInstallUI.
+  CompleteInstall(webstore_install::SUCCESS, std::string());
 }
 
 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
     const std::string& id,
     const std::string& error,
-    WebstoreInstaller::FailureReason cancelled) {
+    WebstoreInstaller::FailureReason reason) {
   CHECK_EQ(id_, id);
-  CompleteInstall(error);
-  Release();  // Balanced in ShowInstallUI.
-}
 
-void WebstoreStandaloneInstaller::AbortInstall() {
-  callback_.Reset();
-  // Abort any in-progress fetches.
-  if (webstore_data_fetcher_) {
-    webstore_data_fetcher_.reset();
-    Release();  // Matches the AddRef in BeginInstall.
+  webstore_install::Result install_result = webstore_install::OTHER_ERROR;
+  switch (reason) {
+    case WebstoreInstaller::FAILURE_REASON_CANCELLED:
+      install_result = webstore_install::USER_CANCELLED;
+      break;
+    case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_FOUND:
+    case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE:
+      install_result = webstore_install::MISSING_DEPENDENCIES;
+      break;
+    default:
+      break;
   }
-}
 
-void WebstoreStandaloneInstaller::InvokeCallback(const std::string& error) {
-  if (!callback_.is_null())
-    callback_.Run(error.empty(), error);
-}
-
-void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
-  InvokeCallback(error);
-  Release();  // Matches the AddRef in BeginInstall.
+  CompleteInstall(install_result, error);
 }
 
-void
-WebstoreStandaloneInstaller::ShowInstallUI() {
-  std::string error;
-  localized_extension_for_display_ =
-      ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
-          manifest_.get(),
-          Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
-          id_,
-          localized_name_,
-          localized_description_,
-          &error);
-  if (!localized_extension_for_display_.get()) {
-    CompleteInstall(kInvalidManifestError);
+void WebstoreStandaloneInstaller::ShowInstallUI() {
+  const Extension* localized_extension = GetLocalizedExtensionForDisplay();
+  if (!localized_extension) {
+    CompleteInstall(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
     return;
   }
 
-  // Keep this alive as long as the install prompt lives.
-  // Balanced in InstallUIAbort or indirectly in InstallUIProceed via
-  // OnExtensionInstallSuccess or OnExtensionInstallFailure.
-  AddRef();
-
   install_ui_ = CreateInstallUI();
   install_ui_->ConfirmStandaloneInstall(
-      this, localized_extension_for_display_.get(), &icon_, *install_prompt_);
+      this, localized_extension, &icon_, install_prompt_);
+}
+
+void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
+  // An instance of this class is passed in as a delegate for the
+  // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
+  // therefore needs to remain alive until they are done. Clear the webstore
+  // data fetcher to avoid calling Release in AbortInstall while any of these
+  // operations are in progress.
+  webstore_data_fetcher_.reset();
 }
 
 }  // namespace extensions