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
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h"
7 #include "base/bind_helpers.h"
8 #include "base/command_line.h"
9 #include "base/lazy_instance.h"
10 #include "base/memory/scoped_vector.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/about_flags.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/extensions/crx_installer.h"
19 #include "chrome/browser/extensions/extension_function_dispatcher.h"
20 #include "chrome/browser/extensions/extension_prefs.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/extension_system.h"
23 #include "chrome/browser/extensions/webstore_installer.h"
24 #include "chrome/browser/gpu/gpu_feature_checker.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/browser/signin/signin_manager.h"
27 #include "chrome/browser/signin/signin_manager_factory.h"
28 #include "chrome/browser/sync/profile_sync_service.h"
29 #include "chrome/browser/sync/profile_sync_service_factory.h"
30 #include "chrome/browser/ui/app_list/app_list_service.h"
31 #include "chrome/browser/ui/app_list/app_list_util.h"
32 #include "chrome/browser/ui/browser.h"
33 #include "chrome/common/extensions/extension.h"
34 #include "chrome/common/extensions/extension_constants.h"
35 #include "chrome/common/extensions/extension_l10n_util.h"
36 #include "chrome/common/pref_names.h"
37 #include "content/public/browser/gpu_data_manager.h"
38 #include "content/public/browser/notification_details.h"
39 #include "content/public/browser/notification_source.h"
40 #include "content/public/browser/web_contents.h"
41 #include "extensions/common/error_utils.h"
42 #include "grit/chromium_strings.h"
43 #include "grit/generated_resources.h"
44 #include "ui/base/l10n/l10n_util.h"
46 using content::GpuDataManager;
48 namespace extensions {
50 namespace BeginInstallWithManifest3 =
51 api::webstore_private::BeginInstallWithManifest3;
52 namespace CompleteInstall = api::webstore_private::CompleteInstall;
53 namespace GetBrowserLogin = api::webstore_private::GetBrowserLogin;
54 namespace GetIsLauncherEnabled = api::webstore_private::GetIsLauncherEnabled;
55 namespace GetStoreLogin = api::webstore_private::GetStoreLogin;
56 namespace GetWebGLStatus = api::webstore_private::GetWebGLStatus;
57 namespace InstallBundle = api::webstore_private::InstallBundle;
58 namespace IsInIncognitoMode = api::webstore_private::IsInIncognitoMode;
59 namespace SetStoreLogin = api::webstore_private::SetStoreLogin;
63 // Holds the Approvals between the time we prompt and start the installs.
64 class PendingApprovals {
69 void PushApproval(scoped_ptr<WebstoreInstaller::Approval> approval);
70 scoped_ptr<WebstoreInstaller::Approval> PopApproval(
71 Profile* profile, const std::string& id);
73 typedef ScopedVector<WebstoreInstaller::Approval> ApprovalList;
75 ApprovalList approvals_;
77 DISALLOW_COPY_AND_ASSIGN(PendingApprovals);
80 PendingApprovals::PendingApprovals() {}
81 PendingApprovals::~PendingApprovals() {}
83 void PendingApprovals::PushApproval(
84 scoped_ptr<WebstoreInstaller::Approval> approval) {
85 approvals_.push_back(approval.release());
88 scoped_ptr<WebstoreInstaller::Approval> PendingApprovals::PopApproval(
89 Profile* profile, const std::string& id) {
90 for (size_t i = 0; i < approvals_.size(); ++i) {
91 WebstoreInstaller::Approval* approval = approvals_[i];
92 if (approval->extension_id == id &&
93 profile->IsSameProfile(approval->profile)) {
94 approvals_.weak_erase(approvals_.begin() + i);
95 return scoped_ptr<WebstoreInstaller::Approval>(approval);
98 return scoped_ptr<WebstoreInstaller::Approval>();
101 // Uniquely holds the profile and extension id of an install between the time we
102 // prompt and complete the installs.
103 class PendingInstalls {
108 bool InsertInstall(Profile* profile, const std::string& id);
109 void EraseInstall(Profile* profile, const std::string& id);
111 typedef std::pair<Profile*, std::string> ProfileAndExtensionId;
112 typedef std::vector<ProfileAndExtensionId> InstallList;
114 InstallList::iterator FindInstall(Profile* profile, const std::string& id);
116 InstallList installs_;
118 DISALLOW_COPY_AND_ASSIGN(PendingInstalls);
121 PendingInstalls::PendingInstalls() {}
122 PendingInstalls::~PendingInstalls() {}
124 // Returns true and inserts the profile/id pair if it is not present. Otherwise
126 bool PendingInstalls::InsertInstall(Profile* profile, const std::string& id) {
127 if (FindInstall(profile, id) != installs_.end())
129 installs_.push_back(make_pair(profile, id));
133 // Removes the given profile/id pair.
134 void PendingInstalls::EraseInstall(Profile* profile, const std::string& id) {
135 InstallList::iterator it = FindInstall(profile, id);
136 if (it != installs_.end())
140 PendingInstalls::InstallList::iterator PendingInstalls::FindInstall(
142 const std::string& id) {
143 for (size_t i = 0; i < installs_.size(); ++i) {
144 ProfileAndExtensionId install = installs_[i];
145 if (install.second == id && profile->IsSameProfile(install.first))
146 return (installs_.begin() + i);
148 return installs_.end();
151 static base::LazyInstance<PendingApprovals> g_pending_approvals =
152 LAZY_INSTANCE_INITIALIZER;
153 static base::LazyInstance<PendingInstalls> g_pending_installs =
154 LAZY_INSTANCE_INITIALIZER;
156 // A preference set by the web store to indicate login information for
158 const char kWebstoreLogin[] = "extensions.webstore_login";
159 const char kAlreadyInstalledError[] = "This item is already installed";
160 const char kCannotSpecifyIconDataAndUrlError[] =
161 "You cannot specify both icon data and an icon url";
162 const char kInvalidIconUrlError[] = "Invalid icon url";
163 const char kInvalidIdError[] = "Invalid id";
164 const char kInvalidManifestError[] = "Invalid manifest";
165 const char kNoPreviousBeginInstallWithManifestError[] =
166 "* does not match a previous call to beginInstallWithManifest3";
167 const char kUserCancelledError[] = "User cancelled install";
169 WebstoreInstaller::Delegate* test_webstore_installer_delegate = NULL;
171 // We allow the web store to set a string containing login information when a
172 // purchase is made, so that when a user logs into sync with a different
173 // account we can recognize the situation. The Get function returns the login if
174 // there was previously stored data, or an empty string otherwise. The Set will
175 // overwrite any previous login.
176 std::string GetWebstoreLogin(Profile* profile) {
177 if (profile->GetPrefs()->HasPrefPath(kWebstoreLogin))
178 return profile->GetPrefs()->GetString(kWebstoreLogin);
179 return std::string();
182 void SetWebstoreLogin(Profile* profile, const std::string& login) {
183 profile->GetPrefs()->SetString(kWebstoreLogin, login);
189 void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(
190 WebstoreInstaller::Delegate* delegate) {
191 test_webstore_installer_delegate = delegate;
195 scoped_ptr<WebstoreInstaller::Approval>
196 WebstorePrivateApi::PopApprovalForTesting(
197 Profile* profile, const std::string& extension_id) {
198 return g_pending_approvals.Get().PopApproval(profile, extension_id);
201 WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() {}
202 WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() {}
204 bool WebstorePrivateInstallBundleFunction::RunImpl() {
205 scoped_ptr<InstallBundle::Params> params(
206 InstallBundle::Params::Create(*args_));
207 EXTENSION_FUNCTION_VALIDATE(params);
209 BundleInstaller::ItemList items;
210 if (!ReadBundleInfo(*params, &items))
213 bundle_ = new BundleInstaller(GetCurrentBrowser(), items);
215 AddRef(); // Balanced in OnBundleInstallCompleted / OnBundleInstallCanceled.
217 bundle_->PromptForApproval(this);
221 bool WebstorePrivateInstallBundleFunction::
222 ReadBundleInfo(const InstallBundle::Params& params,
223 BundleInstaller::ItemList* items) {
224 for (size_t i = 0; i < params.details.size(); ++i) {
225 BundleInstaller::Item item;
226 item.id = params.details[i]->id;
227 item.manifest = params.details[i]->manifest;
228 item.localized_name = params.details[i]->localized_name;
229 items->push_back(item);
235 void WebstorePrivateInstallBundleFunction::OnBundleInstallApproved() {
236 bundle_->CompleteInstall(
237 &(dispatcher()->delegate()->GetAssociatedWebContents()->GetController()),
241 void WebstorePrivateInstallBundleFunction::OnBundleInstallCanceled(
242 bool user_initiated) {
244 error_ = "user_canceled";
246 error_ = "unknown_error";
250 Release(); // Balanced in RunImpl().
253 void WebstorePrivateInstallBundleFunction::OnBundleInstallCompleted() {
256 Release(); // Balanced in RunImpl().
259 WebstorePrivateBeginInstallWithManifest3Function::
260 WebstorePrivateBeginInstallWithManifest3Function() {}
262 WebstorePrivateBeginInstallWithManifest3Function::
263 ~WebstorePrivateBeginInstallWithManifest3Function() {}
265 bool WebstorePrivateBeginInstallWithManifest3Function::RunImpl() {
266 params_ = BeginInstallWithManifest3::Params::Create(*args_);
267 EXTENSION_FUNCTION_VALIDATE(params_);
269 if (!extensions::Extension::IdIsValid(params_->details.id)) {
270 SetResultCode(INVALID_ID);
271 error_ = kInvalidIdError;
275 if (params_->details.icon_data && params_->details.icon_url) {
276 SetResultCode(ICON_ERROR);
277 error_ = kCannotSpecifyIconDataAndUrlError;
282 if (params_->details.icon_url) {
284 icon_url = source_url().Resolve(*params_->details.icon_url);
285 if (!icon_url.is_valid()) {
286 SetResultCode(INVALID_ICON_URL);
287 error_ = kInvalidIconUrlError;
292 std::string icon_data = params_->details.icon_data ?
293 *params_->details.icon_data : std::string();
295 ExtensionService* service =
296 extensions::ExtensionSystem::Get(GetProfile())->extension_service();
297 if (service->GetInstalledExtension(params_->details.id) ||
298 !g_pending_installs.Get().InsertInstall(GetProfile(),
299 params_->details.id)) {
300 SetResultCode(ALREADY_INSTALLED);
301 error_ = kAlreadyInstalledError;
305 net::URLRequestContextGetter* context_getter = NULL;
306 if (!icon_url.is_empty())
307 context_getter = GetProfile()->GetRequestContext();
309 scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
310 this, params_->details.id, params_->details.manifest, icon_data, icon_url,
313 // The helper will call us back via OnWebstoreParseSuccess or
314 // OnWebstoreParseFailure.
317 // Matched with a Release in OnWebstoreParseSuccess/OnWebstoreParseFailure.
320 // The response is sent asynchronously in OnWebstoreParseSuccess/
321 // OnWebstoreParseFailure.
325 const char* WebstorePrivateBeginInstallWithManifest3Function::
326 ResultCodeToString(ResultCode code) {
331 return "unknown_error";
333 return "user_cancelled";
335 return "manifest_error";
340 case PERMISSION_DENIED:
341 return "permission_denied";
342 case INVALID_ICON_URL:
343 return "invalid_icon_url";
345 return "signin_failed";
346 case ALREADY_INSTALLED:
347 return "already_installed";
353 void WebstorePrivateBeginInstallWithManifest3Function::SetResultCode(
355 results_ = BeginInstallWithManifest3::Results::Create(
356 ResultCodeToString(code));
359 void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseSuccess(
360 const std::string& id,
361 const SkBitmap& icon,
362 base::DictionaryValue* parsed_manifest) {
363 CHECK_EQ(params_->details.id, id);
364 CHECK(parsed_manifest);
366 parsed_manifest_.reset(parsed_manifest);
368 std::string localized_name = params_->details.localized_name ?
369 *params_->details.localized_name : std::string();
372 dummy_extension_ = ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
373 parsed_manifest_.get(),
374 Extension::FROM_WEBSTORE,
380 if (!dummy_extension_.get()) {
381 OnWebstoreParseFailure(params_->details.id,
382 WebstoreInstallHelper::Delegate::MANIFEST_ERROR,
383 kInvalidManifestError);
387 SigninManagerBase* signin_manager =
388 SigninManagerFactory::GetForProfile(GetProfile());
389 if (dummy_extension_->is_platform_app() &&
391 signin_manager->GetAuthenticatedUsername().empty() &&
392 signin_manager->AuthInProgress()) {
393 signin_tracker_.reset(new SigninTracker(GetProfile(), this));
397 SigninCompletedOrNotNeeded();
400 void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseFailure(
401 const std::string& id,
402 WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
403 const std::string& error_message) {
404 CHECK_EQ(params_->details.id, id);
406 // Map from WebstoreInstallHelper's result codes to ours.
407 switch (result_code) {
408 case WebstoreInstallHelper::Delegate::UNKNOWN_ERROR:
409 SetResultCode(UNKNOWN_ERROR);
411 case WebstoreInstallHelper::Delegate::ICON_ERROR:
412 SetResultCode(ICON_ERROR);
414 case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
415 SetResultCode(MANIFEST_ERROR);
420 error_ = error_message;
421 g_pending_installs.Get().EraseInstall(GetProfile(), id);
424 // Matches the AddRef in RunImpl().
428 void WebstorePrivateBeginInstallWithManifest3Function::SigninFailed(
429 const GoogleServiceAuthError& error) {
430 signin_tracker_.reset();
432 SetResultCode(SIGNIN_FAILED);
433 error_ = error.ToString();
434 g_pending_installs.Get().EraseInstall(GetProfile(), params_->details.id);
437 // Matches the AddRef in RunImpl().
441 void WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess() {
442 signin_tracker_.reset();
444 SigninCompletedOrNotNeeded();
447 void WebstorePrivateBeginInstallWithManifest3Function::
448 SigninCompletedOrNotNeeded() {
449 content::WebContents* web_contents = GetAssociatedWebContents();
450 if (!web_contents) // The browser window has gone away.
452 install_prompt_.reset(new ExtensionInstallPrompt(web_contents));
453 install_prompt_->ConfirmWebstoreInstall(
455 dummy_extension_.get(),
457 ExtensionInstallPrompt::GetDefaultShowDialogCallback());
458 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
461 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIProceed() {
462 // This gets cleared in CrxInstaller::ConfirmInstall(). TODO(asargent) - in
463 // the future we may also want to add time-based expiration, where a whitelist
464 // entry is only valid for some number of minutes.
465 scoped_ptr<WebstoreInstaller::Approval> approval(
466 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
467 GetProfile(), params_->details.id, parsed_manifest_.Pass(), false));
468 approval->use_app_installed_bubble = params_->details.app_install_bubble;
469 approval->enable_launcher = params_->details.enable_launcher;
470 // If we are enabling the launcher, we should not show the app list in order
471 // to train the user to open it themselves at least once.
472 approval->skip_post_install_ui = params_->details.enable_launcher;
473 approval->dummy_extension = dummy_extension_;
474 approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
475 g_pending_approvals.Get().PushApproval(approval.Pass());
477 SetResultCode(ERROR_NONE);
480 // The Permissions_Install histogram is recorded from the ExtensionService
481 // for all extension installs, so we only need to record the web store
482 // specific histogram here.
483 ExtensionService::RecordPermissionMessagesHistogram(
484 dummy_extension_.get(), "Extensions.Permissions_WebStoreInstall");
486 // Matches the AddRef in RunImpl().
490 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIAbort(
491 bool user_initiated) {
492 error_ = kUserCancelledError;
493 SetResultCode(USER_CANCELLED);
494 g_pending_installs.Get().EraseInstall(GetProfile(), params_->details.id);
497 // The web store install histograms are a subset of the install histograms.
498 // We need to record both histograms here since CrxInstaller::InstallUIAbort
499 // is never called for web store install cancellations.
500 std::string histogram_name = user_initiated ?
501 "Extensions.Permissions_WebStoreInstallCancel" :
502 "Extensions.Permissions_WebStoreInstallAbort";
503 ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
504 histogram_name.c_str());
506 histogram_name = user_initiated ?
507 "Extensions.Permissions_InstallCancel" :
508 "Extensions.Permissions_InstallAbort";
509 ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
510 histogram_name.c_str());
512 // Matches the AddRef in RunImpl().
516 WebstorePrivateCompleteInstallFunction::
517 WebstorePrivateCompleteInstallFunction() {}
519 WebstorePrivateCompleteInstallFunction::
520 ~WebstorePrivateCompleteInstallFunction() {}
522 bool WebstorePrivateCompleteInstallFunction::RunImpl() {
523 scoped_ptr<CompleteInstall::Params> params(
524 CompleteInstall::Params::Create(*args_));
525 EXTENSION_FUNCTION_VALIDATE(params);
526 if (!extensions::Extension::IdIsValid(params->expected_id)) {
527 error_ = kInvalidIdError;
531 approval_ = g_pending_approvals.Get()
532 .PopApproval(GetProfile(), params->expected_id)
535 error_ = ErrorUtils::FormatErrorMessage(
536 kNoPreviousBeginInstallWithManifestError, params->expected_id);
540 // Balanced in OnExtensionInstallSuccess() or OnExtensionInstallFailure().
542 AppListService* app_list_service =
543 AppListService::Get(GetCurrentBrowser()->host_desktop_type());
545 if (approval_->enable_launcher)
546 app_list_service->EnableAppList(GetProfile());
548 if (IsAppLauncherEnabled() && approval_->manifest->is_app()) {
549 // Show the app list to show download is progressing. Don't show the app
550 // list on first app install so users can be trained to open it themselves.
551 if (approval_->enable_launcher)
552 app_list_service->CreateForProfile(GetProfile());
554 app_list_service->ShowForProfile(GetProfile());
557 // The extension will install through the normal extension install flow, but
558 // the whitelist entry will bypass the normal permissions install dialog.
559 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
562 &(dispatcher()->delegate()->GetAssociatedWebContents()->GetController()),
565 WebstoreInstaller::INSTALL_SOURCE_OTHER);
571 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallSuccess(
572 const std::string& id) {
573 if (test_webstore_installer_delegate)
574 test_webstore_installer_delegate->OnExtensionInstallSuccess(id);
576 LOG(INFO) << "Install success, sending response";
577 g_pending_installs.Get().EraseInstall(GetProfile(), id);
580 // Matches the AddRef in RunImpl().
584 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallFailure(
585 const std::string& id,
586 const std::string& error,
587 WebstoreInstaller::FailureReason reason) {
588 if (test_webstore_installer_delegate) {
589 test_webstore_installer_delegate->OnExtensionInstallFailure(
594 LOG(INFO) << "Install failed, sending response";
595 g_pending_installs.Get().EraseInstall(GetProfile(), id);
598 // Matches the AddRef in RunImpl().
602 WebstorePrivateEnableAppLauncherFunction::
603 WebstorePrivateEnableAppLauncherFunction() {}
605 WebstorePrivateEnableAppLauncherFunction::
606 ~WebstorePrivateEnableAppLauncherFunction() {}
608 bool WebstorePrivateEnableAppLauncherFunction::RunImpl() {
609 AppListService::Get(GetCurrentBrowser()->host_desktop_type())->
610 EnableAppList(GetProfile());
614 bool WebstorePrivateGetBrowserLoginFunction::RunImpl() {
615 GetBrowserLogin::Results::Info info;
616 info.login = GetProfile()->GetOriginalProfile()->GetPrefs()->GetString(
617 prefs::kGoogleServicesUsername);
618 results_ = GetBrowserLogin::Results::Create(info);
622 bool WebstorePrivateGetStoreLoginFunction::RunImpl() {
623 results_ = GetStoreLogin::Results::Create(GetWebstoreLogin(GetProfile()));
627 bool WebstorePrivateSetStoreLoginFunction::RunImpl() {
628 scoped_ptr<SetStoreLogin::Params> params(
629 SetStoreLogin::Params::Create(*args_));
630 EXTENSION_FUNCTION_VALIDATE(params);
631 SetWebstoreLogin(GetProfile(), params->login);
635 WebstorePrivateGetWebGLStatusFunction::WebstorePrivateGetWebGLStatusFunction() {
636 feature_checker_ = new GPUFeatureChecker(
637 gpu::GPU_FEATURE_TYPE_WEBGL,
638 base::Bind(&WebstorePrivateGetWebGLStatusFunction::OnFeatureCheck,
639 base::Unretained(this)));
642 WebstorePrivateGetWebGLStatusFunction::
643 ~WebstorePrivateGetWebGLStatusFunction() {}
645 void WebstorePrivateGetWebGLStatusFunction::CreateResult(bool webgl_allowed) {
646 results_ = GetWebGLStatus::Results::Create(GetWebGLStatus::Results::
647 ParseWebgl_status(webgl_allowed ? "webgl_allowed" : "webgl_blocked"));
650 bool WebstorePrivateGetWebGLStatusFunction::RunImpl() {
651 feature_checker_->CheckGPUFeatureAvailability();
655 void WebstorePrivateGetWebGLStatusFunction::
656 OnFeatureCheck(bool feature_allowed) {
657 CreateResult(feature_allowed);
661 bool WebstorePrivateGetIsLauncherEnabledFunction::RunImpl() {
662 results_ = GetIsLauncherEnabled::Results::Create(IsAppLauncherEnabled());
666 bool WebstorePrivateIsInIncognitoModeFunction::RunImpl() {
667 results_ = IsInIncognitoMode::Results::Create(
668 GetProfile() != GetProfile()->GetOriginalProfile());
672 } // namespace extensions