Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(884)

Side by Side Diff: chrome/browser/extensions/api/webstore_private/webstore_private_api.cc

Issue 389613006: Prevent duplicate concurrent installs of the same extension (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h" 5 #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h"
6 6
7 #include "base/bind_helpers.h" 7 #include "base/bind_helpers.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/lazy_instance.h" 9 #include "base/lazy_instance.h"
10 #include "base/memory/scoped_vector.h" 10 #include "base/memory/scoped_vector.h"
11 #include "base/metrics/histogram.h" 11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h" 12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_util.h" 13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h" 14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h" 16 #include "base/values.h"
17 #include "base/version.h" 17 #include "base/version.h"
18 #include "chrome/browser/about_flags.h" 18 #include "chrome/browser/about_flags.h"
19 #include "chrome/browser/apps/ephemeral_app_launcher.h" 19 #include "chrome/browser/apps/ephemeral_app_launcher.h"
20 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h" 21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/extensions/crx_installer.h" 22 #include "chrome/browser/extensions/crx_installer.h"
23 #include "chrome/browser/extensions/extension_service.h" 23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/install_tracker.h"
24 #include "chrome/browser/extensions/webstore_installer.h" 25 #include "chrome/browser/extensions/webstore_installer.h"
25 #include "chrome/browser/gpu/gpu_feature_checker.h" 26 #include "chrome/browser/gpu/gpu_feature_checker.h"
26 #include "chrome/browser/profiles/profile_manager.h" 27 #include "chrome/browser/profiles/profile_manager.h"
27 #include "chrome/browser/signin/signin_manager_factory.h" 28 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/signin/signin_promo.h" 29 #include "chrome/browser/signin/signin_promo.h"
29 #include "chrome/browser/signin/signin_tracker_factory.h" 30 #include "chrome/browser/signin/signin_tracker_factory.h"
30 #include "chrome/browser/sync/profile_sync_service.h" 31 #include "chrome/browser/sync/profile_sync_service.h"
31 #include "chrome/browser/sync/profile_sync_service_factory.h" 32 #include "chrome/browser/sync/profile_sync_service_factory.h"
32 #include "chrome/browser/ui/app_list/app_list_service.h" 33 #include "chrome/browser/ui/app_list/app_list_service.h"
33 #include "chrome/browser/ui/app_list/app_list_util.h" 34 #include "chrome/browser/ui/app_list/app_list_util.h"
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
109 WebstoreInstaller::Approval* approval = approvals_[i]; 110 WebstoreInstaller::Approval* approval = approvals_[i];
110 if (approval->extension_id == id && 111 if (approval->extension_id == id &&
111 profile->IsSameProfile(approval->profile)) { 112 profile->IsSameProfile(approval->profile)) {
112 approvals_.weak_erase(approvals_.begin() + i); 113 approvals_.weak_erase(approvals_.begin() + i);
113 return scoped_ptr<WebstoreInstaller::Approval>(approval); 114 return scoped_ptr<WebstoreInstaller::Approval>(approval);
114 } 115 }
115 } 116 }
116 return scoped_ptr<WebstoreInstaller::Approval>(); 117 return scoped_ptr<WebstoreInstaller::Approval>();
117 } 118 }
118 119
119 // Uniquely holds the profile and extension id of an install between the time we
120 // prompt and complete the installs.
121 class PendingInstalls {
122 public:
123 PendingInstalls();
124 ~PendingInstalls();
125
126 bool InsertInstall(Profile* profile, const std::string& id);
127 void EraseInstall(Profile* profile, const std::string& id);
128 bool ContainsInstall(Profile* profile, const std::string& id);
129 private:
130 typedef std::pair<Profile*, std::string> ProfileAndExtensionId;
131 typedef std::vector<ProfileAndExtensionId> InstallList;
132
133 InstallList::iterator FindInstall(Profile* profile, const std::string& id);
134
135 InstallList installs_;
136
137 DISALLOW_COPY_AND_ASSIGN(PendingInstalls);
138 };
139
140 PendingInstalls::PendingInstalls() {}
141 PendingInstalls::~PendingInstalls() {}
142
143 // Returns true and inserts the profile/id pair if it is not present. Otherwise
144 // returns false.
145 bool PendingInstalls::InsertInstall(Profile* profile, const std::string& id) {
146 if (FindInstall(profile, id) != installs_.end())
147 return false;
148 installs_.push_back(make_pair(profile, id));
149 return true;
150 }
151
152 // Removes the given profile/id pair.
153 void PendingInstalls::EraseInstall(Profile* profile, const std::string& id) {
154 InstallList::iterator it = FindInstall(profile, id);
155 if (it != installs_.end())
156 installs_.erase(it);
157 }
158
159 bool PendingInstalls::ContainsInstall(Profile* profile, const std::string& id) {
160 return FindInstall(profile, id) != installs_.end();
161 }
162
163 PendingInstalls::InstallList::iterator PendingInstalls::FindInstall(
164 Profile* profile,
165 const std::string& id) {
166 for (size_t i = 0; i < installs_.size(); ++i) {
167 ProfileAndExtensionId install = installs_[i];
168 if (install.second == id && profile->IsSameProfile(install.first))
169 return (installs_.begin() + i);
170 }
171 return installs_.end();
172 }
173
174 static base::LazyInstance<PendingApprovals> g_pending_approvals = 120 static base::LazyInstance<PendingApprovals> g_pending_approvals =
175 LAZY_INSTANCE_INITIALIZER; 121 LAZY_INSTANCE_INITIALIZER;
176 static base::LazyInstance<PendingInstalls> g_pending_installs =
177 LAZY_INSTANCE_INITIALIZER;
178 122
179 // A preference set by the web store to indicate login information for 123 // A preference set by the web store to indicate login information for
180 // purchased apps. 124 // purchased apps.
181 const char kWebstoreLogin[] = "extensions.webstore_login"; 125 const char kWebstoreLogin[] = "extensions.webstore_login";
182 const char kAlreadyInstalledError[] = "This item is already installed"; 126 const char kAlreadyInstalledError[] = "This item is already installed";
183 const char kCannotSpecifyIconDataAndUrlError[] = 127 const char kCannotSpecifyIconDataAndUrlError[] =
184 "You cannot specify both icon data and an icon url"; 128 "You cannot specify both icon data and an icon url";
185 const char kInvalidIconUrlError[] = "Invalid icon url"; 129 const char kInvalidIconUrlError[] = "Invalid icon url";
186 const char kInvalidIdError[] = "Invalid id"; 130 const char kInvalidIdError[] = "Invalid id";
187 const char kInvalidManifestError[] = "Invalid manifest"; 131 const char kInvalidManifestError[] = "Invalid manifest";
(...skipping 15 matching lines...) Expand all
203 } 147 }
204 148
205 void SetWebstoreLogin(Profile* profile, const std::string& login) { 149 void SetWebstoreLogin(Profile* profile, const std::string& login) {
206 profile->GetPrefs()->SetString(kWebstoreLogin, login); 150 profile->GetPrefs()->SetString(kWebstoreLogin, login);
207 } 151 }
208 152
209 void RecordWebstoreExtensionInstallResult(bool success) { 153 void RecordWebstoreExtensionInstallResult(bool success) {
210 UMA_HISTOGRAM_BOOLEAN("Webstore.ExtensionInstallResult", success); 154 UMA_HISTOGRAM_BOOLEAN("Webstore.ExtensionInstallResult", success);
211 } 155 }
212 156
157 void RemoveActiveInstall(Profile* profile, const std::string& extension_id) {
158 InstallTracker* tracker = InstallTracker::Get(profile);
159 DCHECK(tracker);
160 tracker->RemoveActiveInstall(extension_id);
161 }
162
213 } // namespace 163 } // namespace
214 164
215 // static 165 // static
216 void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting( 166 void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(
217 WebstoreInstaller::Delegate* delegate) { 167 WebstoreInstaller::Delegate* delegate) {
218 test_webstore_installer_delegate = delegate; 168 test_webstore_installer_delegate = delegate;
219 } 169 }
220 170
221 // static 171 // static
222 scoped_ptr<WebstoreInstaller::Approval> 172 scoped_ptr<WebstoreInstaller::Approval>
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
319 } 269 }
320 270
321 if (params_->details.authuser) { 271 if (params_->details.authuser) {
322 authuser_ = *params_->details.authuser; 272 authuser_ = *params_->details.authuser;
323 } 273 }
324 274
325 std::string icon_data = params_->details.icon_data ? 275 std::string icon_data = params_->details.icon_data ?
326 *params_->details.icon_data : std::string(); 276 *params_->details.icon_data : std::string();
327 277
328 Profile* profile = GetProfile(); 278 Profile* profile = GetProfile();
279 InstallTracker* tracker = InstallTracker::Get(profile);
280 DCHECK(tracker);
329 if (util::IsExtensionInstalledPermanently(params_->details.id, profile) || 281 if (util::IsExtensionInstalledPermanently(params_->details.id, profile) ||
330 !g_pending_installs.Get().InsertInstall(profile, params_->details.id)) { 282 tracker->GetActiveInstall(params_->details.id)) {
331 SetResultCode(ALREADY_INSTALLED); 283 SetResultCode(ALREADY_INSTALLED);
332 error_ = kAlreadyInstalledError; 284 error_ = kAlreadyInstalledError;
333 return false; 285 return false;
334 } 286 }
287 InstallTracker::InstallProgressData install_data(params_->details.id);
288 tracker->AddActiveInstall(install_data);
335 289
336 net::URLRequestContextGetter* context_getter = NULL; 290 net::URLRequestContextGetter* context_getter = NULL;
337 if (!icon_url.is_empty()) 291 if (!icon_url.is_empty())
338 context_getter = GetProfile()->GetRequestContext(); 292 context_getter = GetProfile()->GetRequestContext();
339 293
340 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper( 294 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
341 this, params_->details.id, params_->details.manifest, icon_data, icon_url, 295 this, params_->details.id, params_->details.manifest, icon_data, icon_url,
342 context_getter); 296 context_getter);
343 297
344 // The helper will call us back via OnWebstoreParseSuccess or 298 // The helper will call us back via OnWebstoreParseSuccess or
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
443 case WebstoreInstallHelper::Delegate::ICON_ERROR: 397 case WebstoreInstallHelper::Delegate::ICON_ERROR:
444 SetResultCode(ICON_ERROR); 398 SetResultCode(ICON_ERROR);
445 break; 399 break;
446 case WebstoreInstallHelper::Delegate::MANIFEST_ERROR: 400 case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
447 SetResultCode(MANIFEST_ERROR); 401 SetResultCode(MANIFEST_ERROR);
448 break; 402 break;
449 default: 403 default:
450 CHECK(false); 404 CHECK(false);
451 } 405 }
452 error_ = error_message; 406 error_ = error_message;
453 g_pending_installs.Get().EraseInstall(GetProfile(), id); 407 RemoveActiveInstall(GetProfile(), id);
454 SendResponse(false); 408 SendResponse(false);
455 409
456 // Matches the AddRef in RunAsync(). 410 // Matches the AddRef in RunAsync().
457 Release(); 411 Release();
458 } 412 }
459 413
460 void WebstorePrivateBeginInstallWithManifest3Function::SigninFailed( 414 void WebstorePrivateBeginInstallWithManifest3Function::SigninFailed(
461 const GoogleServiceAuthError& error) { 415 const GoogleServiceAuthError& error) {
462 signin_tracker_.reset(); 416 signin_tracker_.reset();
463 417
464 SetResultCode(SIGNIN_FAILED); 418 SetResultCode(SIGNIN_FAILED);
465 error_ = error.ToString(); 419 error_ = error.ToString();
466 g_pending_installs.Get().EraseInstall(GetProfile(), params_->details.id); 420 RemoveActiveInstall(GetProfile(), params_->details.id);
asargent_no_longer_on_chrome 2014/07/16 18:49:07 It would be cool if we had something like a "Scope
tmdiep 2014/07/17 01:01:51 That's a great idea. Done. The WebstorePrivateAPI
467 SendResponse(false); 421 SendResponse(false);
468 422
469 // Matches the AddRef in RunAsync(). 423 // Matches the AddRef in RunAsync().
470 Release(); 424 Release();
471 } 425 }
472 426
473 void WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess() { 427 void WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess() {
474 signin_tracker_.reset(); 428 signin_tracker_.reset();
475 429
476 SigninCompletedOrNotNeeded(); 430 SigninCompletedOrNotNeeded();
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
524 dummy_extension_.get(), "Extensions.Permissions_WebStoreInstall2"); 478 dummy_extension_.get(), "Extensions.Permissions_WebStoreInstall2");
525 479
526 // Matches the AddRef in RunAsync(). 480 // Matches the AddRef in RunAsync().
527 Release(); 481 Release();
528 } 482 }
529 483
530 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIAbort( 484 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIAbort(
531 bool user_initiated) { 485 bool user_initiated) {
532 error_ = kUserCancelledError; 486 error_ = kUserCancelledError;
533 SetResultCode(USER_CANCELLED); 487 SetResultCode(USER_CANCELLED);
534 g_pending_installs.Get().EraseInstall(GetProfile(), params_->details.id); 488 RemoveActiveInstall(GetProfile(), params_->details.id);
535 SendResponse(false); 489 SendResponse(false);
536 490
537 // The web store install histograms are a subset of the install histograms. 491 // The web store install histograms are a subset of the install histograms.
538 // We need to record both histograms here since CrxInstaller::InstallUIAbort 492 // We need to record both histograms here since CrxInstaller::InstallUIAbort
539 // is never called for web store install cancellations. 493 // is never called for web store install cancellations.
540 std::string histogram_name = 494 std::string histogram_name =
541 user_initiated ? "Extensions.Permissions_WebStoreInstallCancel2" 495 user_initiated ? "Extensions.Permissions_WebStoreInstallCancel2"
542 : "Extensions.Permissions_WebStoreInstallAbort2"; 496 : "Extensions.Permissions_WebStoreInstallAbort2";
543 ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(), 497 ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
544 histogram_name.c_str()); 498 histogram_name.c_str());
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
639 const std::string& id, 593 const std::string& id,
640 const std::string& error, 594 const std::string& error,
641 WebstoreInstaller::FailureReason reason) { 595 WebstoreInstaller::FailureReason reason) {
642 if (test_webstore_installer_delegate) { 596 if (test_webstore_installer_delegate) {
643 test_webstore_installer_delegate->OnExtensionInstallFailure( 597 test_webstore_installer_delegate->OnExtensionInstallFailure(
644 id, error, reason); 598 id, error, reason);
645 } 599 }
646 600
647 error_ = error; 601 error_ = error;
648 VLOG(1) << "Install failed, sending response"; 602 VLOG(1) << "Install failed, sending response";
649 g_pending_installs.Get().EraseInstall(GetProfile(), id); 603 RemoveActiveInstall(GetProfile(), id);
650 SendResponse(false); 604 SendResponse(false);
651 605
652 RecordWebstoreExtensionInstallResult(false); 606 RecordWebstoreExtensionInstallResult(false);
653 607
654 // Matches the AddRef in RunAsync(). 608 // Matches the AddRef in RunAsync().
655 Release(); 609 Release();
656 } 610 }
657 611
658 void WebstorePrivateCompleteInstallFunction::OnInstallSuccess( 612 void WebstorePrivateCompleteInstallFunction::OnInstallSuccess(
659 const std::string& id) { 613 const std::string& id) {
660 if (test_webstore_installer_delegate) 614 if (test_webstore_installer_delegate)
661 test_webstore_installer_delegate->OnExtensionInstallSuccess(id); 615 test_webstore_installer_delegate->OnExtensionInstallSuccess(id);
662 616
663 VLOG(1) << "Install success, sending response"; 617 VLOG(1) << "Install success, sending response";
664 g_pending_installs.Get().EraseInstall(GetProfile(), id); 618 RemoveActiveInstall(GetProfile(), id);
665 SendResponse(true); 619 SendResponse(true);
666 } 620 }
667 621
668 WebstorePrivateEnableAppLauncherFunction:: 622 WebstorePrivateEnableAppLauncherFunction::
669 WebstorePrivateEnableAppLauncherFunction() {} 623 WebstorePrivateEnableAppLauncherFunction() {}
670 624
671 WebstorePrivateEnableAppLauncherFunction:: 625 WebstorePrivateEnableAppLauncherFunction::
672 ~WebstorePrivateEnableAppLauncherFunction() {} 626 ~WebstorePrivateEnableAppLauncherFunction() {}
673 627
674 bool WebstorePrivateEnableAppLauncherFunction::RunSync() { 628 bool WebstorePrivateEnableAppLauncherFunction::RunSync() {
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
849 if (!user_gesture()) { 803 if (!user_gesture()) {
850 SetResult(LaunchEphemeralAppResult::RESULT_USER_GESTURE_REQUIRED, 804 SetResult(LaunchEphemeralAppResult::RESULT_USER_GESTURE_REQUIRED,
851 "User gesture is required"); 805 "User gesture is required");
852 return false; 806 return false;
853 } 807 }
854 808
855 scoped_ptr<LaunchEphemeralApp::Params> params( 809 scoped_ptr<LaunchEphemeralApp::Params> params(
856 LaunchEphemeralApp::Params::Create(*args_)); 810 LaunchEphemeralApp::Params::Create(*args_));
857 EXTENSION_FUNCTION_VALIDATE(params); 811 EXTENSION_FUNCTION_VALIDATE(params);
858 812
859 // If a full install is in progress, do not install ephemerally.
860 if (g_pending_installs.Get().ContainsInstall(GetProfile(), params->id)) {
861 SetResult(LaunchEphemeralAppResult::RESULT_INSTALL_IN_PROGRESS,
862 "An install is already in progress");
863 return false;
864 }
865
866 AddRef(); // Balanced in OnLaunchComplete() 813 AddRef(); // Balanced in OnLaunchComplete()
867 814
868 scoped_refptr<EphemeralAppLauncher> launcher = 815 scoped_refptr<EphemeralAppLauncher> launcher =
869 EphemeralAppLauncher::CreateForWebContents( 816 EphemeralAppLauncher::CreateForWebContents(
870 params->id, 817 params->id,
871 web_contents, 818 web_contents,
872 base::Bind( 819 base::Bind(
873 &WebstorePrivateLaunchEphemeralAppFunction::OnLaunchComplete, 820 &WebstorePrivateLaunchEphemeralAppFunction::OnLaunchComplete,
874 base::Unretained(this))); 821 base::Unretained(this)));
875 launcher->Start(); 822 launcher->Start();
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
912 break; 859 break;
913 case webstore_install::BLOCKED_BY_POLICY: 860 case webstore_install::BLOCKED_BY_POLICY:
914 api_result = LaunchEphemeralAppResult::RESULT_BLOCKED_BY_POLICY; 861 api_result = LaunchEphemeralAppResult::RESULT_BLOCKED_BY_POLICY;
915 break; 862 break;
916 case webstore_install::LAUNCH_FEATURE_DISABLED: 863 case webstore_install::LAUNCH_FEATURE_DISABLED:
917 api_result = LaunchEphemeralAppResult::RESULT_FEATURE_DISABLED; 864 api_result = LaunchEphemeralAppResult::RESULT_FEATURE_DISABLED;
918 break; 865 break;
919 case webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE: 866 case webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE:
920 api_result = LaunchEphemeralAppResult::RESULT_UNSUPPORTED_EXTENSION_TYPE; 867 api_result = LaunchEphemeralAppResult::RESULT_UNSUPPORTED_EXTENSION_TYPE;
921 break; 868 break;
869 case webstore_install::INSTALL_IN_PROGRESS:
870 api_result = LaunchEphemeralAppResult::RESULT_INSTALL_IN_PROGRESS;
871 break;
872 case webstore_install::LAUNCH_IN_PROGRESS:
873 api_result = LaunchEphemeralAppResult::RESULT_LAUNCH_IN_PROGRESS;
874 break;
922 default: 875 default:
923 NOTREACHED(); 876 NOTREACHED();
924 break; 877 break;
925 } 878 }
926 879
927 SetResult(api_result, error); 880 SetResult(api_result, error);
928 Release(); // Matches AddRef() in RunAsync() 881 Release(); // Matches AddRef() in RunAsync()
929 } 882 }
930 883
931 void WebstorePrivateLaunchEphemeralAppFunction::SetResult( 884 void WebstorePrivateLaunchEphemeralAppFunction::SetResult(
(...skipping 20 matching lines...) Expand all
952 WebstorePrivateGetEphemeralAppsEnabledFunction:: 905 WebstorePrivateGetEphemeralAppsEnabledFunction::
953 ~WebstorePrivateGetEphemeralAppsEnabledFunction() {} 906 ~WebstorePrivateGetEphemeralAppsEnabledFunction() {}
954 907
955 bool WebstorePrivateGetEphemeralAppsEnabledFunction::RunSync() { 908 bool WebstorePrivateGetEphemeralAppsEnabledFunction::RunSync() {
956 results_ = GetEphemeralAppsEnabled::Results::Create( 909 results_ = GetEphemeralAppsEnabled::Results::Create(
957 EphemeralAppLauncher::IsFeatureEnabled()); 910 EphemeralAppLauncher::IsFeatureEnabled());
958 return true; 911 return true;
959 } 912 }
960 913
961 } // namespace extensions 914 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698