Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / webstore_private / webstore_private_api.cc
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.
4
5 #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h"
6
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/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/about_flags.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/crx_installer.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_util.h"
22 #include "chrome/browser/extensions/webstore_installer.h"
23 #include "chrome/browser/gpu/gpu_feature_checker.h"
24 #include "chrome/browser/profiles/profile_manager.h"
25 #include "chrome/browser/signin/signin_manager_factory.h"
26 #include "chrome/browser/signin/signin_promo.h"
27 #include "chrome/browser/signin/signin_tracker_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_constants.h"
34 #include "chrome/common/pref_names.h"
35 #include "components/signin/core/browser/signin_manager.h"
36 #include "components/signin/core/common/profile_management_switches.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 "content/public/common/page_transition_types.h"
42 #include "content/public/common/referrer.h"
43 #include "extensions/browser/extension_function_dispatcher.h"
44 #include "extensions/browser/extension_prefs.h"
45 #include "extensions/browser/extension_system.h"
46 #include "extensions/common/error_utils.h"
47 #include "extensions/common/extension.h"
48 #include "extensions/common/extension_l10n_util.h"
49 #include "google_apis/gaia/google_service_auth_error.h"
50 #include "grit/chromium_strings.h"
51 #include "grit/generated_resources.h"
52 #include "ui/base/l10n/l10n_util.h"
53 #include "url/gurl.h"
54
55 using content::GpuDataManager;
56
57 namespace extensions {
58
59 namespace BeginInstallWithManifest3 =
60     api::webstore_private::BeginInstallWithManifest3;
61 namespace CompleteInstall = api::webstore_private::CompleteInstall;
62 namespace GetBrowserLogin = api::webstore_private::GetBrowserLogin;
63 namespace GetIsLauncherEnabled = api::webstore_private::GetIsLauncherEnabled;
64 namespace GetStoreLogin = api::webstore_private::GetStoreLogin;
65 namespace GetWebGLStatus = api::webstore_private::GetWebGLStatus;
66 namespace InstallBundle = api::webstore_private::InstallBundle;
67 namespace IsInIncognitoMode = api::webstore_private::IsInIncognitoMode;
68 namespace SignIn = api::webstore_private::SignIn;
69 namespace SetStoreLogin = api::webstore_private::SetStoreLogin;
70
71 namespace {
72
73 // Holds the Approvals between the time we prompt and start the installs.
74 class PendingApprovals {
75  public:
76   PendingApprovals();
77   ~PendingApprovals();
78
79   void PushApproval(scoped_ptr<WebstoreInstaller::Approval> approval);
80   scoped_ptr<WebstoreInstaller::Approval> PopApproval(
81       Profile* profile, const std::string& id);
82  private:
83   typedef ScopedVector<WebstoreInstaller::Approval> ApprovalList;
84
85   ApprovalList approvals_;
86
87   DISALLOW_COPY_AND_ASSIGN(PendingApprovals);
88 };
89
90 PendingApprovals::PendingApprovals() {}
91 PendingApprovals::~PendingApprovals() {}
92
93 void PendingApprovals::PushApproval(
94     scoped_ptr<WebstoreInstaller::Approval> approval) {
95   approvals_.push_back(approval.release());
96 }
97
98 scoped_ptr<WebstoreInstaller::Approval> PendingApprovals::PopApproval(
99     Profile* profile, const std::string& id) {
100   for (size_t i = 0; i < approvals_.size(); ++i) {
101     WebstoreInstaller::Approval* approval = approvals_[i];
102     if (approval->extension_id == id &&
103         profile->IsSameProfile(approval->profile)) {
104       approvals_.weak_erase(approvals_.begin() + i);
105       return scoped_ptr<WebstoreInstaller::Approval>(approval);
106     }
107   }
108   return scoped_ptr<WebstoreInstaller::Approval>();
109 }
110
111 // Uniquely holds the profile and extension id of an install between the time we
112 // prompt and complete the installs.
113 class PendingInstalls {
114  public:
115   PendingInstalls();
116   ~PendingInstalls();
117
118   bool InsertInstall(Profile* profile, const std::string& id);
119   void EraseInstall(Profile* profile, const std::string& id);
120  private:
121   typedef std::pair<Profile*, std::string> ProfileAndExtensionId;
122   typedef std::vector<ProfileAndExtensionId> InstallList;
123
124   InstallList::iterator FindInstall(Profile* profile, const std::string& id);
125
126   InstallList installs_;
127
128   DISALLOW_COPY_AND_ASSIGN(PendingInstalls);
129 };
130
131 PendingInstalls::PendingInstalls() {}
132 PendingInstalls::~PendingInstalls() {}
133
134 // Returns true and inserts the profile/id pair if it is not present. Otherwise
135 // returns false.
136 bool PendingInstalls::InsertInstall(Profile* profile, const std::string& id) {
137   if (FindInstall(profile, id) != installs_.end())
138     return false;
139   installs_.push_back(make_pair(profile, id));
140   return true;
141 }
142
143 // Removes the given profile/id pair.
144 void PendingInstalls::EraseInstall(Profile* profile, const std::string& id) {
145   InstallList::iterator it = FindInstall(profile, id);
146   if (it != installs_.end())
147     installs_.erase(it);
148 }
149
150 PendingInstalls::InstallList::iterator PendingInstalls::FindInstall(
151     Profile* profile,
152     const std::string& id) {
153   for (size_t i = 0; i < installs_.size(); ++i) {
154     ProfileAndExtensionId install = installs_[i];
155     if (install.second == id && profile->IsSameProfile(install.first))
156       return (installs_.begin() + i);
157   }
158   return installs_.end();
159 }
160
161 static base::LazyInstance<PendingApprovals> g_pending_approvals =
162     LAZY_INSTANCE_INITIALIZER;
163 static base::LazyInstance<PendingInstalls> g_pending_installs =
164     LAZY_INSTANCE_INITIALIZER;
165
166 // A preference set by the web store to indicate login information for
167 // purchased apps.
168 const char kWebstoreLogin[] = "extensions.webstore_login";
169 const char kAlreadyInstalledError[] = "This item is already installed";
170 const char kCannotSpecifyIconDataAndUrlError[] =
171     "You cannot specify both icon data and an icon url";
172 const char kInvalidIconUrlError[] = "Invalid icon url";
173 const char kInvalidIdError[] = "Invalid id";
174 const char kInvalidManifestError[] = "Invalid manifest";
175 const char kNoPreviousBeginInstallWithManifestError[] =
176     "* does not match a previous call to beginInstallWithManifest3";
177 const char kUserCancelledError[] = "User cancelled install";
178
179 WebstoreInstaller::Delegate* test_webstore_installer_delegate = NULL;
180
181 // We allow the web store to set a string containing login information when a
182 // purchase is made, so that when a user logs into sync with a different
183 // account we can recognize the situation. The Get function returns the login if
184 // there was previously stored data, or an empty string otherwise. The Set will
185 // overwrite any previous login.
186 std::string GetWebstoreLogin(Profile* profile) {
187   if (profile->GetPrefs()->HasPrefPath(kWebstoreLogin))
188     return profile->GetPrefs()->GetString(kWebstoreLogin);
189   return std::string();
190 }
191
192 void SetWebstoreLogin(Profile* profile, const std::string& login) {
193   profile->GetPrefs()->SetString(kWebstoreLogin, login);
194 }
195
196 void RecordWebstoreExtensionInstallResult(bool success) {
197   UMA_HISTOGRAM_BOOLEAN("Webstore.ExtensionInstallResult", success);
198 }
199
200 }  // namespace
201
202 // static
203 void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(
204     WebstoreInstaller::Delegate* delegate) {
205   test_webstore_installer_delegate = delegate;
206 }
207
208 // static
209 scoped_ptr<WebstoreInstaller::Approval>
210 WebstorePrivateApi::PopApprovalForTesting(
211     Profile* profile, const std::string& extension_id) {
212   return g_pending_approvals.Get().PopApproval(profile, extension_id);
213 }
214
215 WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() {}
216 WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() {}
217
218 bool WebstorePrivateInstallBundleFunction::RunAsync() {
219   scoped_ptr<InstallBundle::Params> params(
220       InstallBundle::Params::Create(*args_));
221   EXTENSION_FUNCTION_VALIDATE(params);
222
223   BundleInstaller::ItemList items;
224   if (!ReadBundleInfo(*params, &items))
225     return false;
226
227   bundle_ = new BundleInstaller(GetCurrentBrowser(), items);
228
229   AddRef();  // Balanced in OnBundleInstallCompleted / OnBundleInstallCanceled.
230
231   bundle_->PromptForApproval(this);
232   return true;
233 }
234
235 bool WebstorePrivateInstallBundleFunction::
236     ReadBundleInfo(const InstallBundle::Params& params,
237     BundleInstaller::ItemList* items) {
238   for (size_t i = 0; i < params.details.size(); ++i) {
239     BundleInstaller::Item item;
240     item.id = params.details[i]->id;
241     item.manifest = params.details[i]->manifest;
242     item.localized_name = params.details[i]->localized_name;
243     items->push_back(item);
244   }
245
246   return true;
247 }
248
249 void WebstorePrivateInstallBundleFunction::OnBundleInstallApproved() {
250   bundle_->CompleteInstall(
251       dispatcher()->delegate()->GetAssociatedWebContents(),
252       this);
253 }
254
255 void WebstorePrivateInstallBundleFunction::OnBundleInstallCanceled(
256     bool user_initiated) {
257   if (user_initiated)
258     error_ = "user_canceled";
259   else
260     error_ = "unknown_error";
261
262   SendResponse(false);
263
264   Release();  // Balanced in RunAsync().
265 }
266
267 void WebstorePrivateInstallBundleFunction::OnBundleInstallCompleted() {
268   SendResponse(true);
269
270   Release();  // Balanced in RunAsync().
271 }
272
273 WebstorePrivateBeginInstallWithManifest3Function::
274     WebstorePrivateBeginInstallWithManifest3Function() {
275 }
276
277 WebstorePrivateBeginInstallWithManifest3Function::
278     ~WebstorePrivateBeginInstallWithManifest3Function() {
279 }
280
281 bool WebstorePrivateBeginInstallWithManifest3Function::RunAsync() {
282   params_ = BeginInstallWithManifest3::Params::Create(*args_);
283   EXTENSION_FUNCTION_VALIDATE(params_);
284
285   if (!extensions::Extension::IdIsValid(params_->details.id)) {
286     SetResultCode(INVALID_ID);
287     error_ = kInvalidIdError;
288     return false;
289   }
290
291   if (params_->details.icon_data && params_->details.icon_url) {
292     SetResultCode(ICON_ERROR);
293     error_ = kCannotSpecifyIconDataAndUrlError;
294     return false;
295   }
296
297   GURL icon_url;
298   if (params_->details.icon_url) {
299     std::string tmp_url;
300     icon_url = source_url().Resolve(*params_->details.icon_url);
301     if (!icon_url.is_valid()) {
302       SetResultCode(INVALID_ICON_URL);
303       error_ = kInvalidIconUrlError;
304       return false;
305     }
306   }
307
308   if (params_->details.authuser) {
309     authuser_ = *params_->details.authuser;
310   }
311
312   std::string icon_data = params_->details.icon_data ?
313       *params_->details.icon_data : std::string();
314
315   Profile* profile = GetProfile();
316   if (util::IsExtensionInstalledPermanently(params_->details.id, profile) ||
317       !g_pending_installs.Get().InsertInstall(profile, params_->details.id)) {
318     SetResultCode(ALREADY_INSTALLED);
319     error_ = kAlreadyInstalledError;
320     return false;
321   }
322
323   net::URLRequestContextGetter* context_getter = NULL;
324   if (!icon_url.is_empty())
325     context_getter = GetProfile()->GetRequestContext();
326
327   scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
328       this, params_->details.id, params_->details.manifest, icon_data, icon_url,
329           context_getter);
330
331   // The helper will call us back via OnWebstoreParseSuccess or
332   // OnWebstoreParseFailure.
333   helper->Start();
334
335   // Matched with a Release in OnWebstoreParseSuccess/OnWebstoreParseFailure.
336   AddRef();
337
338   // The response is sent asynchronously in OnWebstoreParseSuccess/
339   // OnWebstoreParseFailure.
340   return true;
341 }
342
343 const char* WebstorePrivateBeginInstallWithManifest3Function::
344     ResultCodeToString(ResultCode code) {
345   switch (code) {
346     case ERROR_NONE:
347       return "";
348     case UNKNOWN_ERROR:
349       return "unknown_error";
350     case USER_CANCELLED:
351       return "user_cancelled";
352     case MANIFEST_ERROR:
353       return "manifest_error";
354     case ICON_ERROR:
355       return "icon_error";
356     case INVALID_ID:
357       return "invalid_id";
358     case PERMISSION_DENIED:
359       return "permission_denied";
360     case INVALID_ICON_URL:
361       return "invalid_icon_url";
362     case SIGNIN_FAILED:
363       return "signin_failed";
364     case ALREADY_INSTALLED:
365       return "already_installed";
366   }
367   NOTREACHED();
368   return "";
369 }
370
371 void WebstorePrivateBeginInstallWithManifest3Function::SetResultCode(
372     ResultCode code) {
373   results_ = BeginInstallWithManifest3::Results::Create(
374       ResultCodeToString(code));
375 }
376
377 void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseSuccess(
378     const std::string& id,
379     const SkBitmap& icon,
380     base::DictionaryValue* parsed_manifest) {
381   CHECK_EQ(params_->details.id, id);
382   CHECK(parsed_manifest);
383   icon_ = icon;
384   parsed_manifest_.reset(parsed_manifest);
385
386   std::string localized_name = params_->details.localized_name ?
387       *params_->details.localized_name : std::string();
388
389   std::string error;
390   dummy_extension_ = ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
391       parsed_manifest_.get(),
392       Extension::FROM_WEBSTORE,
393       id,
394       localized_name,
395       std::string(),
396       &error);
397
398   if (!dummy_extension_.get()) {
399     OnWebstoreParseFailure(params_->details.id,
400                            WebstoreInstallHelper::Delegate::MANIFEST_ERROR,
401                            kInvalidManifestError);
402     return;
403   }
404
405   SigninManagerBase* signin_manager =
406       SigninManagerFactory::GetForProfile(GetProfile());
407   if (dummy_extension_->is_platform_app() &&
408       signin_manager &&
409       signin_manager->GetAuthenticatedUsername().empty() &&
410       signin_manager->AuthInProgress()) {
411     signin_tracker_ =
412         SigninTrackerFactory::CreateForProfile(GetProfile(), this);
413     return;
414   }
415
416   SigninCompletedOrNotNeeded();
417 }
418
419 void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseFailure(
420     const std::string& id,
421     WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
422     const std::string& error_message) {
423   CHECK_EQ(params_->details.id, id);
424
425   // Map from WebstoreInstallHelper's result codes to ours.
426   switch (result_code) {
427     case WebstoreInstallHelper::Delegate::UNKNOWN_ERROR:
428       SetResultCode(UNKNOWN_ERROR);
429       break;
430     case WebstoreInstallHelper::Delegate::ICON_ERROR:
431       SetResultCode(ICON_ERROR);
432       break;
433     case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
434       SetResultCode(MANIFEST_ERROR);
435       break;
436     default:
437       CHECK(false);
438   }
439   error_ = error_message;
440   g_pending_installs.Get().EraseInstall(GetProfile(), id);
441   SendResponse(false);
442
443   // Matches the AddRef in RunAsync().
444   Release();
445 }
446
447 void WebstorePrivateBeginInstallWithManifest3Function::SigninFailed(
448     const GoogleServiceAuthError& error) {
449   signin_tracker_.reset();
450
451   SetResultCode(SIGNIN_FAILED);
452   error_ = error.ToString();
453   g_pending_installs.Get().EraseInstall(GetProfile(), params_->details.id);
454   SendResponse(false);
455
456   // Matches the AddRef in RunAsync().
457   Release();
458 }
459
460 void WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess() {
461   signin_tracker_.reset();
462
463   SigninCompletedOrNotNeeded();
464 }
465
466 void WebstorePrivateBeginInstallWithManifest3Function::MergeSessionComplete(
467     const GoogleServiceAuthError& error) {
468   // TODO(rogerta): once the embeded inline flow is enabled, the code in
469   // WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess()
470   // should move to here.
471 }
472
473 void WebstorePrivateBeginInstallWithManifest3Function::
474     SigninCompletedOrNotNeeded() {
475   content::WebContents* web_contents = GetAssociatedWebContents();
476   if (!web_contents)  // The browser window has gone away.
477     return;
478   install_prompt_.reset(new ExtensionInstallPrompt(web_contents));
479   install_prompt_->ConfirmWebstoreInstall(
480       this,
481       dummy_extension_.get(),
482       &icon_,
483       ExtensionInstallPrompt::GetDefaultShowDialogCallback());
484   // Control flow finishes up in InstallUIProceed or InstallUIAbort.
485 }
486
487 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIProceed() {
488   // This gets cleared in CrxInstaller::ConfirmInstall(). TODO(asargent) - in
489   // the future we may also want to add time-based expiration, where a whitelist
490   // entry is only valid for some number of minutes.
491   scoped_ptr<WebstoreInstaller::Approval> approval(
492       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
493           GetProfile(), params_->details.id, parsed_manifest_.Pass(), false));
494   approval->use_app_installed_bubble = params_->details.app_install_bubble;
495   approval->enable_launcher = params_->details.enable_launcher;
496   // If we are enabling the launcher, we should not show the app list in order
497   // to train the user to open it themselves at least once.
498   approval->skip_post_install_ui = params_->details.enable_launcher;
499   approval->dummy_extension = dummy_extension_;
500   approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
501   approval->authuser = authuser_;
502   g_pending_approvals.Get().PushApproval(approval.Pass());
503
504   SetResultCode(ERROR_NONE);
505   SendResponse(true);
506
507   // The Permissions_Install histogram is recorded from the ExtensionService
508   // for all extension installs, so we only need to record the web store
509   // specific histogram here.
510   ExtensionService::RecordPermissionMessagesHistogram(
511       dummy_extension_.get(), "Extensions.Permissions_WebStoreInstall");
512
513   // Matches the AddRef in RunAsync().
514   Release();
515 }
516
517 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIAbort(
518     bool user_initiated) {
519   error_ = kUserCancelledError;
520   SetResultCode(USER_CANCELLED);
521   g_pending_installs.Get().EraseInstall(GetProfile(), params_->details.id);
522   SendResponse(false);
523
524   // The web store install histograms are a subset of the install histograms.
525   // We need to record both histograms here since CrxInstaller::InstallUIAbort
526   // is never called for web store install cancellations.
527   std::string histogram_name = user_initiated ?
528       "Extensions.Permissions_WebStoreInstallCancel" :
529       "Extensions.Permissions_WebStoreInstallAbort";
530   ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
531                                                       histogram_name.c_str());
532
533   histogram_name = user_initiated ?
534       "Extensions.Permissions_InstallCancel" :
535       "Extensions.Permissions_InstallAbort";
536   ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
537                                                       histogram_name.c_str());
538
539   // Matches the AddRef in RunAsync().
540   Release();
541 }
542
543 WebstorePrivateCompleteInstallFunction::
544     WebstorePrivateCompleteInstallFunction() {}
545
546 WebstorePrivateCompleteInstallFunction::
547     ~WebstorePrivateCompleteInstallFunction() {}
548
549 bool WebstorePrivateCompleteInstallFunction::RunAsync() {
550   scoped_ptr<CompleteInstall::Params> params(
551       CompleteInstall::Params::Create(*args_));
552   EXTENSION_FUNCTION_VALIDATE(params);
553   if (!extensions::Extension::IdIsValid(params->expected_id)) {
554     error_ = kInvalidIdError;
555     return false;
556   }
557
558   approval_ = g_pending_approvals.Get()
559                   .PopApproval(GetProfile(), params->expected_id)
560                   .Pass();
561   if (!approval_) {
562     error_ = ErrorUtils::FormatErrorMessage(
563         kNoPreviousBeginInstallWithManifestError, params->expected_id);
564     return false;
565   }
566
567   // Balanced in OnExtensionInstallSuccess() or OnExtensionInstallFailure().
568   AddRef();
569   AppListService* app_list_service =
570       AppListService::Get(GetCurrentBrowser()->host_desktop_type());
571
572   if (approval_->enable_launcher) {
573     app_list_service->EnableAppList(GetProfile(),
574                                     AppListService::ENABLE_FOR_APP_INSTALL);
575   }
576
577   if (IsAppLauncherEnabled() && approval_->manifest->is_app()) {
578     // Show the app list to show download is progressing. Don't show the app
579     // list on first app install so users can be trained to open it themselves.
580     if (approval_->enable_launcher)
581       app_list_service->CreateForProfile(GetProfile());
582     else
583       app_list_service->AutoShowForProfile(GetProfile());
584   }
585
586   // The extension will install through the normal extension install flow, but
587   // the whitelist entry will bypass the normal permissions install dialog.
588   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
589       GetProfile(),
590       this,
591       dispatcher()->delegate()->GetAssociatedWebContents(),
592       params->expected_id,
593       approval_.Pass(),
594       WebstoreInstaller::INSTALL_SOURCE_OTHER);
595   installer->Start();
596
597   return true;
598 }
599
600 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallSuccess(
601     const std::string& id) {
602   if (test_webstore_installer_delegate)
603     test_webstore_installer_delegate->OnExtensionInstallSuccess(id);
604
605   VLOG(1) << "Install success, sending response";
606   g_pending_installs.Get().EraseInstall(GetProfile(), id);
607   SendResponse(true);
608
609   RecordWebstoreExtensionInstallResult(true);
610
611   // Matches the AddRef in RunAsync().
612   Release();
613 }
614
615 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallFailure(
616     const std::string& id,
617     const std::string& error,
618     WebstoreInstaller::FailureReason reason) {
619   if (test_webstore_installer_delegate) {
620     test_webstore_installer_delegate->OnExtensionInstallFailure(
621         id, error, reason);
622   }
623
624   error_ = error;
625   VLOG(1) << "Install failed, sending response";
626   g_pending_installs.Get().EraseInstall(GetProfile(), id);
627   SendResponse(false);
628
629   RecordWebstoreExtensionInstallResult(false);
630
631   // Matches the AddRef in RunAsync().
632   Release();
633 }
634
635 WebstorePrivateEnableAppLauncherFunction::
636     WebstorePrivateEnableAppLauncherFunction() {}
637
638 WebstorePrivateEnableAppLauncherFunction::
639     ~WebstorePrivateEnableAppLauncherFunction() {}
640
641 bool WebstorePrivateEnableAppLauncherFunction::RunSync() {
642   AppListService::Get(GetCurrentBrowser()->host_desktop_type())
643       ->EnableAppList(GetProfile(), AppListService::ENABLE_VIA_WEBSTORE_LINK);
644   return true;
645 }
646
647 bool WebstorePrivateGetBrowserLoginFunction::RunSync() {
648   GetBrowserLogin::Results::Info info;
649   info.login = GetProfile()->GetOriginalProfile()->GetPrefs()->GetString(
650       prefs::kGoogleServicesUsername);
651   results_ = GetBrowserLogin::Results::Create(info);
652   return true;
653 }
654
655 bool WebstorePrivateGetStoreLoginFunction::RunSync() {
656   results_ = GetStoreLogin::Results::Create(GetWebstoreLogin(GetProfile()));
657   return true;
658 }
659
660 bool WebstorePrivateSetStoreLoginFunction::RunSync() {
661   scoped_ptr<SetStoreLogin::Params> params(
662       SetStoreLogin::Params::Create(*args_));
663   EXTENSION_FUNCTION_VALIDATE(params);
664   SetWebstoreLogin(GetProfile(), params->login);
665   return true;
666 }
667
668 WebstorePrivateGetWebGLStatusFunction::WebstorePrivateGetWebGLStatusFunction() {
669   feature_checker_ = new GPUFeatureChecker(
670       gpu::GPU_FEATURE_TYPE_WEBGL,
671       base::Bind(&WebstorePrivateGetWebGLStatusFunction::OnFeatureCheck,
672           base::Unretained(this)));
673 }
674
675 WebstorePrivateGetWebGLStatusFunction::
676     ~WebstorePrivateGetWebGLStatusFunction() {}
677
678 void WebstorePrivateGetWebGLStatusFunction::CreateResult(bool webgl_allowed) {
679   results_ = GetWebGLStatus::Results::Create(GetWebGLStatus::Results::
680       ParseWebgl_status(webgl_allowed ? "webgl_allowed" : "webgl_blocked"));
681 }
682
683 bool WebstorePrivateGetWebGLStatusFunction::RunAsync() {
684   feature_checker_->CheckGPUFeatureAvailability();
685   return true;
686 }
687
688 void WebstorePrivateGetWebGLStatusFunction::
689     OnFeatureCheck(bool feature_allowed) {
690   CreateResult(feature_allowed);
691   SendResponse(true);
692 }
693
694 bool WebstorePrivateGetIsLauncherEnabledFunction::RunSync() {
695   results_ = GetIsLauncherEnabled::Results::Create(IsAppLauncherEnabled());
696   return true;
697 }
698
699 bool WebstorePrivateIsInIncognitoModeFunction::RunSync() {
700   results_ = IsInIncognitoMode::Results::Create(
701       GetProfile() != GetProfile()->GetOriginalProfile());
702   return true;
703 }
704
705 WebstorePrivateSignInFunction::WebstorePrivateSignInFunction()
706     : signin_manager_(NULL) {}
707 WebstorePrivateSignInFunction::~WebstorePrivateSignInFunction() {}
708
709 bool WebstorePrivateSignInFunction::RunAsync() {
710   scoped_ptr<SignIn::Params> params = SignIn::Params::Create(*args_);
711   EXTENSION_FUNCTION_VALIDATE(params);
712
713   // This API must be called only in response to a user gesture.
714   if (!user_gesture()) {
715     error_ = "user_gesture_required";
716     SendResponse(false);
717     return false;
718   }
719
720   // The |continue_url| is required, and must be hosted on the same origin as
721   // the calling page.
722   GURL continue_url(params->continue_url);
723   content::WebContents* web_contents = GetAssociatedWebContents();
724   if (!continue_url.is_valid() ||
725       continue_url.GetOrigin() !=
726           web_contents->GetLastCommittedURL().GetOrigin()) {
727     error_ = "invalid_continue_url";
728     SendResponse(false);
729     return false;
730   }
731
732   // If sign-in is disallowed, give up.
733   signin_manager_ = SigninManagerFactory::GetForProfile(GetProfile());
734   if (!signin_manager_ || !signin_manager_->IsSigninAllowed() ||
735       switches::IsEnableWebBasedSignin()) {
736     error_ = "signin_is_disallowed";
737     SendResponse(false);
738     return false;
739   }
740
741   // If the user is already signed in, there's nothing else to do.
742   if (!signin_manager_->GetAuthenticatedUsername().empty()) {
743     SendResponse(true);
744     return true;
745   }
746
747   // If an authentication is currently in progress, wait for it to complete.
748   if (signin_manager_->AuthInProgress()) {
749     SigninManagerFactory::GetInstance()->AddObserver(this);
750     signin_tracker_ =
751         SigninTrackerFactory::CreateForProfile(GetProfile(), this).Pass();
752     AddRef();  // Balanced in the sign-in observer methods below.
753     return true;
754   }
755
756   GURL signin_url =
757       signin::GetPromoURLWithContinueURL(signin::SOURCE_WEBSTORE_INSTALL,
758                                          false /* auto_close */,
759                                          false /* is_constrained */,
760                                          continue_url);
761   web_contents->GetController().LoadURL(signin_url,
762                                         content::Referrer(),
763                                         content::PAGE_TRANSITION_AUTO_TOPLEVEL,
764                                         std::string());
765
766   SendResponse(true);
767   return true;
768 }
769
770 void WebstorePrivateSignInFunction::SigninManagerShutdown(
771     SigninManagerBase* manager) {
772   if (manager == signin_manager_)
773     SigninFailed(GoogleServiceAuthError::AuthErrorNone());
774 }
775
776 void WebstorePrivateSignInFunction::SigninFailed(
777     const GoogleServiceAuthError& error) {
778   error_ = "signin_failed";
779   SendResponse(false);
780
781   SigninManagerFactory::GetInstance()->RemoveObserver(this);
782   Release();  // Balanced in RunAsync().
783 }
784
785 void WebstorePrivateSignInFunction::SigninSuccess() {
786   // Nothing to do yet. Keep waiting until MergeSessionComplete() is called.
787 }
788
789 void WebstorePrivateSignInFunction::MergeSessionComplete(
790     const GoogleServiceAuthError& error) {
791   if (error.state() == GoogleServiceAuthError::NONE) {
792     SendResponse(true);
793   } else {
794     error_ = "merge_session_failed";
795     SendResponse(false);
796   }
797
798   SigninManagerFactory::GetInstance()->RemoveObserver(this);
799   Release();  // Balanced in RunAsync().
800 }
801
802 }  // namespace extensions