Index: chrome/browser/extensions/api/webstore_private/webstore_private_api.cc |
diff --git a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc |
index ebee14eafb30ae17443bf03f9be3d545f2e3a1a0..c00e7964d81b637a5b47fff6ee81289c31d540c2 100644 |
--- a/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc |
+++ b/chrome/browser/extensions/api/webstore_private/webstore_private_api.cc |
@@ -52,17 +52,20 @@ namespace extensions { |
namespace { |
// Holds the Approvals between the time we prompt and start the installs. |
-struct PendingApprovals { |
- typedef ScopedVector<WebstoreInstaller::Approval> ApprovalList; |
- |
+class PendingApprovals { |
+ public: |
PendingApprovals(); |
~PendingApprovals(); |
void PushApproval(scoped_ptr<WebstoreInstaller::Approval> approval); |
scoped_ptr<WebstoreInstaller::Approval> PopApproval( |
Profile* profile, const std::string& id); |
+ private: |
+ typedef ScopedVector<WebstoreInstaller::Approval> ApprovalList; |
+ |
+ ApprovalList approvals_; |
- ApprovalList approvals; |
+ DISALLOW_COPY_AND_ASSIGN(PendingApprovals); |
}; |
PendingApprovals::PendingApprovals() {} |
@@ -70,24 +73,76 @@ PendingApprovals::~PendingApprovals() {} |
void PendingApprovals::PushApproval( |
scoped_ptr<WebstoreInstaller::Approval> approval) { |
- approvals.push_back(approval.release()); |
+ approvals_.push_back(approval.release()); |
} |
scoped_ptr<WebstoreInstaller::Approval> PendingApprovals::PopApproval( |
Profile* profile, const std::string& id) { |
- for (size_t i = 0; i < approvals.size(); ++i) { |
- WebstoreInstaller::Approval* approval = approvals[i]; |
+ for (size_t i = 0; i < approvals_.size(); ++i) { |
+ WebstoreInstaller::Approval* approval = approvals_[i]; |
if (approval->extension_id == id && |
profile->IsSameProfile(approval->profile)) { |
- approvals.weak_erase(approvals.begin() + i); |
+ approvals_.weak_erase(approvals_.begin() + i); |
return scoped_ptr<WebstoreInstaller::Approval>(approval); |
} |
} |
return scoped_ptr<WebstoreInstaller::Approval>(NULL); |
} |
+// Uniquely holds the profile and extension id of an install between the time we |
+// prompt and complete the installs. |
+class PendingInstalls { |
+ public: |
+ PendingInstalls(); |
+ ~PendingInstalls(); |
+ |
+ bool InsertInstall(Profile* profile, const std::string& id); |
+ void EraseInstall(Profile* profile, const std::string& id); |
+ private: |
+ typedef std::pair<Profile*, std::string> ProfileAndExtensionId; |
+ typedef std::vector<ProfileAndExtensionId> InstallList; |
+ |
+ InstallList::iterator FindInstall(Profile* profile, const std::string& id); |
+ |
+ InstallList installs_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(PendingInstalls); |
+}; |
+ |
+PendingInstalls::PendingInstalls() {} |
+PendingInstalls::~PendingInstalls() {} |
+ |
+// Returns true and inserts the profile/id pair if it is not present. Otherwise |
+// returns false. |
+bool PendingInstalls::InsertInstall(Profile* profile, const std::string& id) { |
+ if (FindInstall(profile, id) != installs_.end()) |
+ return false; |
+ installs_.push_back(make_pair(profile, id)); |
+ return true; |
+} |
+ |
+// Removes the given profile/id pair. |
+void PendingInstalls::EraseInstall(Profile* profile, const std::string& id) { |
+ InstallList::iterator it = FindInstall(profile, id); |
+ if (it != installs_.end()) |
+ installs_.erase(it); |
+} |
+ |
+PendingInstalls::InstallList::iterator PendingInstalls::FindInstall( |
+ Profile* profile, |
+ const std::string& id) { |
+ for (size_t i = 0; i < installs_.size(); ++i) { |
+ ProfileAndExtensionId install = installs_[i]; |
+ if (install.second == id && profile->IsSameProfile(install.first)) |
+ return (installs_.begin() + i); |
+ } |
+ return installs_.end(); |
+} |
+ |
static base::LazyInstance<PendingApprovals> g_pending_approvals = |
LAZY_INSTANCE_INITIALIZER; |
+static base::LazyInstance<PendingInstalls> g_pending_installs = |
+ LAZY_INSTANCE_INITIALIZER; |
const char kAppInstallBubbleKey[] = "appInstallBubble"; |
const char kEnableLauncherKey[] = "enableLauncher"; |
@@ -101,7 +156,7 @@ const char kManifestKey[] = "manifest"; |
// A preference set by the web store to indicate login information for |
// purchased apps. |
const char kWebstoreLogin[] = "extensions.webstore_login"; |
- |
+const char kAlreadyInstalledError[] = "This item is already installed"; |
const char kCannotSpecifyIconDataAndUrlError[] = |
"You cannot specify both icon data and an icon url"; |
const char kInvalidIconUrlError[] = "Invalid icon url"; |
@@ -277,6 +332,15 @@ bool BeginInstallWithManifestFunction::RunImpl() { |
EXTENSION_FUNCTION_VALIDATE(details->GetBoolean( |
kEnableLauncherKey, &enable_launcher_)); |
+ ExtensionService* service = |
+ extensions::ExtensionSystem::Get(profile_)->extension_service(); |
+ if (service->GetInstalledExtension(id_) || |
+ !g_pending_installs.Get().InsertInstall(profile_, id_)) { |
+ SetResultCode(ALREADY_INSTALLED); |
+ error_ = kAlreadyInstalledError; |
+ return false; |
+ } |
+ |
net::URLRequestContextGetter* context_getter = NULL; |
if (!icon_url.is_empty()) |
context_getter = profile()->GetRequestContext(); |
@@ -326,6 +390,9 @@ void BeginInstallWithManifestFunction::SetResultCode(ResultCode code) { |
case SIGNIN_FAILED: |
SetResult(Value::CreateStringValue("signin_failed")); |
break; |
+ case ALREADY_INSTALLED: |
+ SetResult(Value::CreateStringValue("already_installed")); |
+ break; |
default: |
CHECK(false); |
} |
@@ -388,6 +455,7 @@ void BeginInstallWithManifestFunction::OnWebstoreParseFailure( |
CHECK(false); |
} |
error_ = error_message; |
+ g_pending_installs.Get().EraseInstall(profile_, id); |
SendResponse(false); |
// Matches the AddRef in RunImpl(). |
@@ -402,6 +470,7 @@ void BeginInstallWithManifestFunction::SigninFailed( |
SetResultCode(SIGNIN_FAILED); |
error_ = error.ToString(); |
+ g_pending_installs.Get().EraseInstall(profile_, id_); |
SendResponse(false); |
// Matches the AddRef in RunImpl(). |
@@ -455,6 +524,7 @@ void BeginInstallWithManifestFunction::InstallUIProceed() { |
void BeginInstallWithManifestFunction::InstallUIAbort(bool user_initiated) { |
error_ = kUserCancelledError; |
SetResultCode(USER_CANCELLED); |
+ g_pending_installs.Get().EraseInstall(profile_, id_); |
SendResponse(false); |
// The web store install histograms are a subset of the install histograms. |
@@ -552,6 +622,7 @@ void CompleteInstallFunction::OnExtensionInstallSuccess( |
test_webstore_installer_delegate->OnExtensionInstallSuccess(id); |
LOG(INFO) << "Install success, sending response"; |
+ g_pending_installs.Get().EraseInstall(profile_, id); |
SendResponse(true); |
// Matches the AddRef in RunImpl(). |
@@ -572,6 +643,7 @@ void CompleteInstallFunction::OnExtensionInstallFailure( |
error_ = error; |
LOG(INFO) << "Install failed, sending response"; |
+ g_pending_installs.Get().EraseInstall(profile_, id); |
SendResponse(false); |
// Matches the AddRef in RunImpl(). |