#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 "components/crx_file/id_util.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 "extensions/common/extension_urls.h"
#include "url/gurl.h"
using content::WebContents;
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,
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
// AbortInstall, which both release this ref.
AddRef();
- if (!Extension::IdIsValid(id_)) {
- CompleteInstall(kInvalidWebstoreItemId);
+ if (!crx_file::id_util::IdIsValid(id_)) {
+ 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;
}
webstore_data_fetcher_->Start();
}
-bool WebstoreStandaloneInstaller::CheckInstallValid(
- const base::DictionaryValue& manifest,
+//
+// Private interface implementation.
+//
+
+WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {
+}
+
+void WebstoreStandaloneInstaller::RunCallback(bool success,
+ const std::string& error,
+ webstore_install::Result result) {
+ callback_.Run(success, error, result);
+}
+
+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_.get()) {
+ 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()));
return approval.Pass();
}
+void WebstoreStandaloneInstaller::InstallUIProceed() {
+ if (!CheckRequestorAlive()) {
+ CompleteInstall(webstore_install::ABORTED, std::string());
+ return;
+ }
+
+ scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
+
+ ExtensionService* extension_service =
+ ExtensionSystem::Get(profile_)->extension_service();
+ const Extension* installed_extension =
+ extension_service->GetExtensionById(id_, true /* include disabled */);
+ 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.
+ scoped_refptr<const Extension> extension_to_install =
+ GetLocalizedExtensionForDisplay();
+ if (!extension_to_install.get()) {
+ 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_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;
+ }
+ }
+
+ scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
+ profile_,
+ this,
+ GetWebContents(),
+ id_,
+ approval.Pass(),
+ install_source_);
+ installer->Start();
+}
+
+void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
+ CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
+}
+
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;
}
!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;
}
if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
- CompleteInstall(kInvalidWebstoreResponseError);
+ CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+ kInvalidWebstoreResponseError);
return;
}
(webstore_data->HasKey(kLocalizedDescriptionKey) &&
!webstore_data->GetString(
kLocalizedDescriptionKey, &localized_description_))) {
- CompleteInstall(kInvalidWebstoreResponseError);
+ CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
+ kInvalidWebstoreResponseError);
return;
}
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;
}
}
void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
const std::string& error) {
- CompleteInstall(error);
+ OnWebStoreDataFetcherDone();
+ CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE, error);
}
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);
-}
-
-void WebstoreStandaloneInstaller::InstallUIProceed() {
- if (!CheckRequestorAlive()) {
- CompleteInstall(std::string());
- return;
- }
-
- ExtensionService* extension_service =
- ExtensionSystem::Get(profile_)->extension_service();
- const Extension* 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;
- }
- } else if (!extension->is_ephemeral()) {
- // else extension is installed and enabled; no work to be done.
- CompleteInstall(install_result);
- return;
- }
-
- // TODO(tmdiep): Optimize installation of ephemeral apps. For now we just
- // reinstall the app.
+ 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;
}
- scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
-
- scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
- profile_,
- this,
- GetWebContents(),
- id_,
- approval.Pass(),
- install_source_);
- installer->Start();
-}
-
-void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
- CompleteInstall(kUserCancelledError);
- Release(); // Balanced in ShowInstallUI.
+ CompleteInstall(install_result, error_message);
}
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);
+ CompleteInstall(install_result, error);
}
-void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
- InvokeCallback(error);
- Release(); // Matches the AddRef in BeginInstall.
-}
-
-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() {
+ scoped_refptr<const Extension> localized_extension =
+ GetLocalizedExtensionForDisplay();
+ if (!localized_extension.get()) {
+ 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.get(), &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