Home | History | Annotate | Download | only in webstore_private
      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 
     50 using content::GpuDataManager;
     51 
     52 namespace extensions {
     53 
     54 namespace BeginInstallWithManifest3 =
     55     api::webstore_private::BeginInstallWithManifest3;
     56 namespace GetEphemeralAppsEnabled =
     57     api::webstore_private::GetEphemeralAppsEnabled;
     58 namespace CompleteInstall = api::webstore_private::CompleteInstall;
     59 namespace GetBrowserLogin = api::webstore_private::GetBrowserLogin;
     60 namespace GetIsLauncherEnabled = api::webstore_private::GetIsLauncherEnabled;
     61 namespace GetStoreLogin = api::webstore_private::GetStoreLogin;
     62 namespace GetWebGLStatus = api::webstore_private::GetWebGLStatus;
     63 namespace InstallBundle = api::webstore_private::InstallBundle;
     64 namespace IsInIncognitoMode = api::webstore_private::IsInIncognitoMode;
     65 namespace LaunchEphemeralApp = api::webstore_private::LaunchEphemeralApp;
     66 namespace LaunchEphemeralAppResult = LaunchEphemeralApp::Results;
     67 namespace SetStoreLogin = api::webstore_private::SetStoreLogin;
     68 
     69 namespace {
     70 
     71 // Holds the Approvals between the time we prompt and start the installs.
     72 class 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 
     88 PendingApprovals::PendingApprovals() {}
     89 PendingApprovals::~PendingApprovals() {}
     90 
     91 void PendingApprovals::PushApproval(
     92     scoped_ptr<WebstoreInstaller::Approval> approval) {
     93   approvals_.push_back(approval.release());
     94 }
     95 
     96 scoped_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 
    109 static 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.
    114 const char kWebstoreLogin[] = "extensions.webstore_login";
    115 const char kAlreadyInstalledError[] = "This item is already installed";
    116 const char kCannotSpecifyIconDataAndUrlError[] =
    117     "You cannot specify both icon data and an icon url";
    118 const char kInvalidIconUrlError[] = "Invalid icon url";
    119 const char kInvalidIdError[] = "Invalid id";
    120 const char kInvalidManifestError[] = "Invalid manifest";
    121 const char kNoPreviousBeginInstallWithManifestError[] =
    122     "* does not match a previous call to beginInstallWithManifest3";
    123 const char kUserCancelledError[] = "User cancelled install";
    124 
    125 WebstoreInstaller::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.
    132 std::string GetWebstoreLogin(Profile* profile) {
    133   if (profile->GetPrefs()->HasPrefPath(kWebstoreLogin))
    134     return profile->GetPrefs()->GetString(kWebstoreLogin);
    135   return std::string();
    136 }
    137 
    138 void SetWebstoreLogin(Profile* profile, const std::string& login) {
    139   profile->GetPrefs()->SetString(kWebstoreLogin, login);
    140 }
    141 
    142 void RecordWebstoreExtensionInstallResult(bool success) {
    143   UMA_HISTOGRAM_BOOLEAN("Webstore.ExtensionInstallResult", success);
    144 }
    145 
    146 }  // namespace
    147 
    148 // static
    149 void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(
    150     WebstoreInstaller::Delegate* delegate) {
    151   test_webstore_installer_delegate = delegate;
    152 }
    153 
    154 // static
    155 scoped_ptr<WebstoreInstaller::Approval>
    156 WebstorePrivateApi::PopApprovalForTesting(
    157     Profile* profile, const std::string& extension_id) {
    158   return g_pending_approvals.Get().PopApproval(profile, extension_id);
    159 }
    160 
    161 WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() {}
    162 WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() {}
    163 
    164 bool 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 
    181 bool 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 
    195 void WebstorePrivateInstallBundleFunction::OnBundleInstallApproved() {
    196   bundle_->CompleteInstall(
    197       dispatcher()->delegate()->GetAssociatedWebContents(),
    198       this);
    199 }
    200 
    201 void 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 
    213 void WebstorePrivateInstallBundleFunction::OnBundleInstallCompleted() {
    214   SendResponse(true);
    215 
    216   Release();  // Balanced in RunAsync().
    217 }
    218 
    219 WebstorePrivateBeginInstallWithManifest3Function::
    220     WebstorePrivateBeginInstallWithManifest3Function() {
    221 }
    222 
    223 WebstorePrivateBeginInstallWithManifest3Function::
    224     ~WebstorePrivateBeginInstallWithManifest3Function() {
    225 }
    226 
    227 bool 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 
    293 const 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 
    319 void WebstorePrivateBeginInstallWithManifest3Function::SetResultCode(
    320     ResultCode code) {
    321   results_ = BeginInstallWithManifest3::Results::Create(
    322       ResultCodeToString(code));
    323 }
    324 
    325 void 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 
    365 void 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 
    392 void 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 
    425 void 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 
    449 WebstorePrivateCompleteInstallFunction::
    450     WebstorePrivateCompleteInstallFunction() {}
    451 
    452 WebstorePrivateCompleteInstallFunction::
    453     ~WebstorePrivateCompleteInstallFunction() {}
    454 
    455 bool 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 
    529 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallSuccess(
    530     const std::string& id) {
    531   OnInstallSuccess(id);
    532   RecordWebstoreExtensionInstallResult(true);
    533 
    534   // Matches the AddRef in RunAsync().
    535   Release();
    536 }
    537 
    538 void 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 
    557 void 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 
    566 WebstorePrivateEnableAppLauncherFunction::
    567     WebstorePrivateEnableAppLauncherFunction() {}
    568 
    569 WebstorePrivateEnableAppLauncherFunction::
    570     ~WebstorePrivateEnableAppLauncherFunction() {}
    571 
    572 bool WebstorePrivateEnableAppLauncherFunction::RunSync() {
    573   AppListService::Get(GetCurrentBrowser()->host_desktop_type())
    574       ->EnableAppList(GetProfile(), AppListService::ENABLE_VIA_WEBSTORE_LINK);
    575   return true;
    576 }
    577 
    578 bool 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 
    586 bool WebstorePrivateGetStoreLoginFunction::RunSync() {
    587   results_ = GetStoreLogin::Results::Create(GetWebstoreLogin(GetProfile()));
    588   return true;
    589 }
    590 
    591 bool 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 
    599 WebstorePrivateGetWebGLStatusFunction::WebstorePrivateGetWebGLStatusFunction() {
    600   feature_checker_ = new GPUFeatureChecker(
    601       gpu::GPU_FEATURE_TYPE_WEBGL,
    602       base::Bind(&WebstorePrivateGetWebGLStatusFunction::OnFeatureCheck,
    603           base::Unretained(this)));
    604 }
    605 
    606 WebstorePrivateGetWebGLStatusFunction::
    607     ~WebstorePrivateGetWebGLStatusFunction() {}
    608 
    609 void WebstorePrivateGetWebGLStatusFunction::CreateResult(bool webgl_allowed) {
    610   results_ = GetWebGLStatus::Results::Create(GetWebGLStatus::Results::
    611       ParseWebgl_status(webgl_allowed ? "webgl_allowed" : "webgl_blocked"));
    612 }
    613 
    614 bool WebstorePrivateGetWebGLStatusFunction::RunAsync() {
    615   feature_checker_->CheckGPUFeatureAvailability();
    616   return true;
    617 }
    618 
    619 void WebstorePrivateGetWebGLStatusFunction::
    620     OnFeatureCheck(bool feature_allowed) {
    621   CreateResult(feature_allowed);
    622   SendResponse(true);
    623 }
    624 
    625 bool WebstorePrivateGetIsLauncherEnabledFunction::RunSync() {
    626   results_ = GetIsLauncherEnabled::Results::Create(IsAppLauncherEnabled());
    627   return true;
    628 }
    629 
    630 bool WebstorePrivateIsInIncognitoModeFunction::RunSync() {
    631   results_ = IsInIncognitoMode::Results::Create(
    632       GetProfile() != GetProfile()->GetOriginalProfile());
    633   return true;
    634 }
    635 
    636 WebstorePrivateLaunchEphemeralAppFunction::
    637     WebstorePrivateLaunchEphemeralAppFunction() {}
    638 
    639 WebstorePrivateLaunchEphemeralAppFunction::
    640     ~WebstorePrivateLaunchEphemeralAppFunction() {}
    641 
    642 bool 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 
    673 void 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 
    731 void 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 
    749 WebstorePrivateGetEphemeralAppsEnabledFunction::
    750     WebstorePrivateGetEphemeralAppsEnabledFunction() {}
    751 
    752 WebstorePrivateGetEphemeralAppsEnabledFunction::
    753     ~WebstorePrivateGetEphemeralAppsEnabledFunction() {}
    754 
    755 bool WebstorePrivateGetEphemeralAppsEnabledFunction::RunSync() {
    756   results_ = GetEphemeralAppsEnabled::Results::Create(
    757       EphemeralAppLauncher::IsFeatureEnabled());
    758   return true;
    759 }
    760 
    761 }  // namespace extensions
    762