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