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 "apps/app_launcher.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/command_line.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/memory/scoped_vector.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_function_dispatcher.h"
     21 #include "chrome/browser/extensions/extension_prefs.h"
     22 #include "chrome/browser/extensions/extension_service.h"
     23 #include "chrome/browser/extensions/extension_system.h"
     24 #include "chrome/browser/extensions/webstore_installer.h"
     25 #include "chrome/browser/gpu/gpu_feature_checker.h"
     26 #include "chrome/browser/profiles/profile_manager.h"
     27 #include "chrome/browser/signin/signin_manager.h"
     28 #include "chrome/browser/signin/signin_manager_factory.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/common/extensions/extension.h"
     33 #include "chrome/common/extensions/extension_constants.h"
     34 #include "chrome/common/extensions/extension_l10n_util.h"
     35 #include "chrome/common/extensions/extension_manifest_constants.h"
     36 #include "chrome/common/pref_names.h"
     37 #include "content/public/browser/gpu_data_manager.h"
     38 #include "content/public/browser/notification_details.h"
     39 #include "content/public/browser/notification_source.h"
     40 #include "content/public/browser/web_contents.h"
     41 #include "extensions/common/error_utils.h"
     42 #include "grit/chromium_strings.h"
     43 #include "grit/generated_resources.h"
     44 #include "ui/base/l10n/l10n_util.h"
     45 
     46 using content::GpuDataManager;
     47 
     48 namespace extensions {
     49 
     50 namespace {
     51 
     52 // Holds the Approvals between the time we prompt and start the installs.
     53 class PendingApprovals {
     54  public:
     55   PendingApprovals();
     56   ~PendingApprovals();
     57 
     58   void PushApproval(scoped_ptr<WebstoreInstaller::Approval> approval);
     59   scoped_ptr<WebstoreInstaller::Approval> PopApproval(
     60       Profile* profile, const std::string& id);
     61  private:
     62   typedef ScopedVector<WebstoreInstaller::Approval> ApprovalList;
     63 
     64   ApprovalList approvals_;
     65 
     66   DISALLOW_COPY_AND_ASSIGN(PendingApprovals);
     67 };
     68 
     69 PendingApprovals::PendingApprovals() {}
     70 PendingApprovals::~PendingApprovals() {}
     71 
     72 void PendingApprovals::PushApproval(
     73     scoped_ptr<WebstoreInstaller::Approval> approval) {
     74   approvals_.push_back(approval.release());
     75 }
     76 
     77 scoped_ptr<WebstoreInstaller::Approval> PendingApprovals::PopApproval(
     78     Profile* profile, const std::string& id) {
     79   for (size_t i = 0; i < approvals_.size(); ++i) {
     80     WebstoreInstaller::Approval* approval = approvals_[i];
     81     if (approval->extension_id == id &&
     82         profile->IsSameProfile(approval->profile)) {
     83       approvals_.weak_erase(approvals_.begin() + i);
     84       return scoped_ptr<WebstoreInstaller::Approval>(approval);
     85     }
     86   }
     87   return scoped_ptr<WebstoreInstaller::Approval>();
     88 }
     89 
     90 // Uniquely holds the profile and extension id of an install between the time we
     91 // prompt and complete the installs.
     92 class PendingInstalls {
     93  public:
     94   PendingInstalls();
     95   ~PendingInstalls();
     96 
     97   bool InsertInstall(Profile* profile, const std::string& id);
     98   void EraseInstall(Profile* profile, const std::string& id);
     99  private:
    100   typedef std::pair<Profile*, std::string> ProfileAndExtensionId;
    101   typedef std::vector<ProfileAndExtensionId> InstallList;
    102 
    103   InstallList::iterator FindInstall(Profile* profile, const std::string& id);
    104 
    105   InstallList installs_;
    106 
    107   DISALLOW_COPY_AND_ASSIGN(PendingInstalls);
    108 };
    109 
    110 PendingInstalls::PendingInstalls() {}
    111 PendingInstalls::~PendingInstalls() {}
    112 
    113 // Returns true and inserts the profile/id pair if it is not present. Otherwise
    114 // returns false.
    115 bool PendingInstalls::InsertInstall(Profile* profile, const std::string& id) {
    116   if (FindInstall(profile, id) != installs_.end())
    117     return false;
    118   installs_.push_back(make_pair(profile, id));
    119   return true;
    120 }
    121 
    122 // Removes the given profile/id pair.
    123 void PendingInstalls::EraseInstall(Profile* profile, const std::string& id) {
    124   InstallList::iterator it = FindInstall(profile, id);
    125   if (it != installs_.end())
    126     installs_.erase(it);
    127 }
    128 
    129 PendingInstalls::InstallList::iterator PendingInstalls::FindInstall(
    130     Profile* profile,
    131     const std::string& id) {
    132   for (size_t i = 0; i < installs_.size(); ++i) {
    133     ProfileAndExtensionId install = installs_[i];
    134     if (install.second == id && profile->IsSameProfile(install.first))
    135       return (installs_.begin() + i);
    136   }
    137   return installs_.end();
    138 }
    139 
    140 static base::LazyInstance<PendingApprovals> g_pending_approvals =
    141     LAZY_INSTANCE_INITIALIZER;
    142 static base::LazyInstance<PendingInstalls> g_pending_installs =
    143     LAZY_INSTANCE_INITIALIZER;
    144 
    145 const char kAppInstallBubbleKey[] = "appInstallBubble";
    146 const char kEnableLauncherKey[] = "enableLauncher";
    147 const char kIconDataKey[] = "iconData";
    148 const char kIconUrlKey[] = "iconUrl";
    149 const char kIdKey[] = "id";
    150 const char kLocalizedNameKey[] = "localizedName";
    151 const char kLoginKey[] = "login";
    152 const char kManifestKey[] = "manifest";
    153 
    154 // A preference set by the web store to indicate login information for
    155 // purchased apps.
    156 const char kWebstoreLogin[] = "extensions.webstore_login";
    157 const char kAlreadyInstalledError[] = "This item is already installed";
    158 const char kCannotSpecifyIconDataAndUrlError[] =
    159     "You cannot specify both icon data and an icon url";
    160 const char kInvalidIconUrlError[] = "Invalid icon url";
    161 const char kInvalidIdError[] = "Invalid id";
    162 const char kInvalidManifestError[] = "Invalid manifest";
    163 const char kNoPreviousBeginInstallWithManifestError[] =
    164     "* does not match a previous call to beginInstallWithManifest3";
    165 const char kUserCancelledError[] = "User cancelled install";
    166 
    167 // Helper to create a dictionary with login properties set from the appropriate
    168 // values in the passed-in |profile|.
    169 base::DictionaryValue* CreateLoginResult(Profile* profile) {
    170   base::DictionaryValue* dictionary = new base::DictionaryValue();
    171   std::string username = profile->GetPrefs()->GetString(
    172       prefs::kGoogleServicesUsername);
    173   dictionary->SetString(kLoginKey, username);
    174   return dictionary;
    175 }
    176 
    177 WebstoreInstaller::Delegate* test_webstore_installer_delegate = NULL;
    178 
    179 // We allow the web store to set a string containing login information when a
    180 // purchase is made, so that when a user logs into sync with a different
    181 // account we can recognize the situation. The Get function returns the login if
    182 // there was previously stored data, or an empty string otherwise. The Set will
    183 // overwrite any previous login.
    184 std::string GetWebstoreLogin(Profile* profile) {
    185   if (profile->GetPrefs()->HasPrefPath(kWebstoreLogin))
    186     return profile->GetPrefs()->GetString(kWebstoreLogin);
    187   return std::string();
    188 }
    189 
    190 void SetWebstoreLogin(Profile* profile, const std::string& login) {
    191   profile->GetPrefs()->SetString(kWebstoreLogin, login);
    192 }
    193 
    194 }  // namespace
    195 
    196 // static
    197 void WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(
    198     WebstoreInstaller::Delegate* delegate) {
    199   test_webstore_installer_delegate = delegate;
    200 }
    201 
    202 // static
    203 scoped_ptr<WebstoreInstaller::Approval>
    204 WebstorePrivateApi::PopApprovalForTesting(
    205     Profile* profile, const std::string& extension_id) {
    206   return g_pending_approvals.Get().PopApproval(profile, extension_id);
    207 }
    208 
    209 WebstorePrivateInstallBundleFunction::WebstorePrivateInstallBundleFunction() {}
    210 WebstorePrivateInstallBundleFunction::~WebstorePrivateInstallBundleFunction() {}
    211 
    212 bool WebstorePrivateInstallBundleFunction::RunImpl() {
    213   base::ListValue* extensions = NULL;
    214   EXTENSION_FUNCTION_VALIDATE(args_->GetList(0, &extensions));
    215 
    216   BundleInstaller::ItemList items;
    217   if (!ReadBundleInfo(extensions, &items))
    218     return false;
    219 
    220   bundle_ = new BundleInstaller(GetCurrentBrowser(), items);
    221 
    222   AddRef();  // Balanced in OnBundleInstallCompleted / OnBundleInstallCanceled.
    223 
    224   bundle_->PromptForApproval(this);
    225   return true;
    226 }
    227 
    228 bool WebstorePrivateInstallBundleFunction::
    229     ReadBundleInfo(base::ListValue* extensions,
    230     BundleInstaller::ItemList* items) {
    231   for (size_t i = 0; i < extensions->GetSize(); ++i) {
    232     base::DictionaryValue* details = NULL;
    233     EXTENSION_FUNCTION_VALIDATE(extensions->GetDictionary(i, &details));
    234 
    235     BundleInstaller::Item item;
    236     EXTENSION_FUNCTION_VALIDATE(details->GetString(
    237         kIdKey, &item.id));
    238     EXTENSION_FUNCTION_VALIDATE(details->GetString(
    239         kManifestKey, &item.manifest));
    240     EXTENSION_FUNCTION_VALIDATE(details->GetString(
    241         kLocalizedNameKey, &item.localized_name));
    242 
    243     items->push_back(item);
    244   }
    245 
    246   return true;
    247 }
    248 
    249 void WebstorePrivateInstallBundleFunction::OnBundleInstallApproved() {
    250   bundle_->CompleteInstall(
    251       &(dispatcher()->delegate()->GetAssociatedWebContents()->GetController()),
    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 RunImpl().
    265 }
    266 
    267 void WebstorePrivateInstallBundleFunction::OnBundleInstallCompleted() {
    268   SendResponse(true);
    269 
    270   Release();  // Balanced in RunImpl().
    271 }
    272 
    273 WebstorePrivateBeginInstallWithManifest3Function::
    274     WebstorePrivateBeginInstallWithManifest3Function()
    275     : use_app_installed_bubble_(false), enable_launcher_(false) {}
    276 
    277 WebstorePrivateBeginInstallWithManifest3Function::
    278     ~WebstorePrivateBeginInstallWithManifest3Function() {}
    279 
    280 bool WebstorePrivateBeginInstallWithManifest3Function::RunImpl() {
    281   base::DictionaryValue* details = NULL;
    282   EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(0, &details));
    283   CHECK(details);
    284 
    285   EXTENSION_FUNCTION_VALIDATE(details->GetString(kIdKey, &id_));
    286   if (!extensions::Extension::IdIsValid(id_)) {
    287     SetResultCode(INVALID_ID);
    288     error_ = kInvalidIdError;
    289     return false;
    290   }
    291 
    292   EXTENSION_FUNCTION_VALIDATE(details->GetString(kManifestKey, &manifest_));
    293 
    294   if (details->HasKey(kIconDataKey) && details->HasKey(kIconUrlKey)) {
    295     SetResultCode(ICON_ERROR);
    296     error_ = kCannotSpecifyIconDataAndUrlError;
    297     return false;
    298   }
    299 
    300   if (details->HasKey(kIconDataKey))
    301     EXTENSION_FUNCTION_VALIDATE(details->GetString(kIconDataKey, &icon_data_));
    302 
    303   GURL icon_url;
    304   if (details->HasKey(kIconUrlKey)) {
    305     std::string tmp_url;
    306     EXTENSION_FUNCTION_VALIDATE(details->GetString(kIconUrlKey, &tmp_url));
    307     icon_url = source_url().Resolve(tmp_url);
    308     if (!icon_url.is_valid()) {
    309       SetResultCode(INVALID_ICON_URL);
    310       error_ = kInvalidIconUrlError;
    311       return false;
    312     }
    313   }
    314 
    315   if (details->HasKey(kLocalizedNameKey))
    316     EXTENSION_FUNCTION_VALIDATE(details->GetString(kLocalizedNameKey,
    317                                                    &localized_name_));
    318 
    319   if (details->HasKey(kAppInstallBubbleKey))
    320     EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(
    321         kAppInstallBubbleKey, &use_app_installed_bubble_));
    322 
    323   if (details->HasKey(kEnableLauncherKey))
    324     EXTENSION_FUNCTION_VALIDATE(details->GetBoolean(
    325         kEnableLauncherKey, &enable_launcher_));
    326 
    327   ExtensionService* service =
    328     extensions::ExtensionSystem::Get(profile_)->extension_service();
    329   if (service->GetInstalledExtension(id_) ||
    330       !g_pending_installs.Get().InsertInstall(profile_, id_)) {
    331     SetResultCode(ALREADY_INSTALLED);
    332     error_ = kAlreadyInstalledError;
    333     return false;
    334   }
    335 
    336   net::URLRequestContextGetter* context_getter = NULL;
    337   if (!icon_url.is_empty())
    338     context_getter = profile()->GetRequestContext();
    339 
    340   scoped_refptr<WebstoreInstallHelper> helper = new WebstoreInstallHelper(
    341       this, id_, manifest_, icon_data_, icon_url, context_getter);
    342 
    343   // The helper will call us back via OnWebstoreParseSuccess or
    344   // OnWebstoreParseFailure.
    345   helper->Start();
    346 
    347   // Matched with a Release in OnWebstoreParseSuccess/OnWebstoreParseFailure.
    348   AddRef();
    349 
    350   // The response is sent asynchronously in OnWebstoreParseSuccess/
    351   // OnWebstoreParseFailure.
    352   return true;
    353 }
    354 
    355 
    356 void WebstorePrivateBeginInstallWithManifest3Function::SetResultCode(
    357     ResultCode code) {
    358   switch (code) {
    359     case ERROR_NONE:
    360       SetResult(Value::CreateStringValue(std::string()));
    361       break;
    362     case UNKNOWN_ERROR:
    363       SetResult(Value::CreateStringValue("unknown_error"));
    364       break;
    365     case USER_CANCELLED:
    366       SetResult(Value::CreateStringValue("user_cancelled"));
    367       break;
    368     case MANIFEST_ERROR:
    369       SetResult(Value::CreateStringValue("manifest_error"));
    370       break;
    371     case ICON_ERROR:
    372       SetResult(Value::CreateStringValue("icon_error"));
    373       break;
    374     case INVALID_ID:
    375       SetResult(Value::CreateStringValue("invalid_id"));
    376       break;
    377     case PERMISSION_DENIED:
    378       SetResult(Value::CreateStringValue("permission_denied"));
    379       break;
    380     case INVALID_ICON_URL:
    381       SetResult(Value::CreateStringValue("invalid_icon_url"));
    382       break;
    383     case SIGNIN_FAILED:
    384       SetResult(Value::CreateStringValue("signin_failed"));
    385       break;
    386     case ALREADY_INSTALLED:
    387       SetResult(Value::CreateStringValue("already_installed"));
    388       break;
    389     default:
    390       CHECK(false);
    391   }
    392 }
    393 
    394 void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseSuccess(
    395     const std::string& id,
    396     const SkBitmap& icon,
    397     base::DictionaryValue* parsed_manifest) {
    398   CHECK_EQ(id_, id);
    399   CHECK(parsed_manifest);
    400   icon_ = icon;
    401   parsed_manifest_.reset(parsed_manifest);
    402 
    403   std::string error;
    404   dummy_extension_ = ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
    405       parsed_manifest_.get(),
    406       Extension::FROM_WEBSTORE,
    407       id,
    408       localized_name_,
    409       std::string(),
    410       &error);
    411 
    412   if (!dummy_extension_.get()) {
    413     OnWebstoreParseFailure(id_,
    414                            WebstoreInstallHelper::Delegate::MANIFEST_ERROR,
    415                            kInvalidManifestError);
    416     return;
    417   }
    418 
    419   SigninManagerBase* signin_manager =
    420       SigninManagerFactory::GetForProfile(profile());
    421   if (dummy_extension_->is_platform_app() &&
    422       signin_manager &&
    423       signin_manager->GetAuthenticatedUsername().empty() &&
    424       signin_manager->AuthInProgress()) {
    425     signin_tracker_.reset(new SigninTracker(profile(), this));
    426     return;
    427   }
    428 
    429   SigninCompletedOrNotNeeded();
    430 }
    431 
    432 void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseFailure(
    433     const std::string& id,
    434     WebstoreInstallHelper::Delegate::InstallHelperResultCode result_code,
    435     const std::string& error_message) {
    436   CHECK_EQ(id_, id);
    437 
    438   // Map from WebstoreInstallHelper's result codes to ours.
    439   switch (result_code) {
    440     case WebstoreInstallHelper::Delegate::UNKNOWN_ERROR:
    441       SetResultCode(UNKNOWN_ERROR);
    442       break;
    443     case WebstoreInstallHelper::Delegate::ICON_ERROR:
    444       SetResultCode(ICON_ERROR);
    445       break;
    446     case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
    447       SetResultCode(MANIFEST_ERROR);
    448       break;
    449     default:
    450       CHECK(false);
    451   }
    452   error_ = error_message;
    453   g_pending_installs.Get().EraseInstall(profile_, id);
    454   SendResponse(false);
    455 
    456   // Matches the AddRef in RunImpl().
    457   Release();
    458 }
    459 
    460 void WebstorePrivateBeginInstallWithManifest3Function::SigninFailed(
    461     const GoogleServiceAuthError& error) {
    462   signin_tracker_.reset();
    463 
    464   SetResultCode(SIGNIN_FAILED);
    465   error_ = error.ToString();
    466   g_pending_installs.Get().EraseInstall(profile_, id_);
    467   SendResponse(false);
    468 
    469   // Matches the AddRef in RunImpl().
    470   Release();
    471 }
    472 
    473 void WebstorePrivateBeginInstallWithManifest3Function::SigninSuccess() {
    474   signin_tracker_.reset();
    475 
    476   SigninCompletedOrNotNeeded();
    477 }
    478 
    479 void WebstorePrivateBeginInstallWithManifest3Function::
    480     SigninCompletedOrNotNeeded() {
    481   content::WebContents* web_contents = GetAssociatedWebContents();
    482   if (!web_contents)  // The browser window has gone away.
    483     return;
    484   install_prompt_.reset(new ExtensionInstallPrompt(web_contents));
    485   install_prompt_->ConfirmWebstoreInstall(
    486       this,
    487       dummy_extension_.get(),
    488       &icon_,
    489       ExtensionInstallPrompt::GetDefaultShowDialogCallback());
    490   // Control flow finishes up in InstallUIProceed or InstallUIAbort.
    491 }
    492 
    493 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIProceed() {
    494   // This gets cleared in CrxInstaller::ConfirmInstall(). TODO(asargent) - in
    495   // the future we may also want to add time-based expiration, where a whitelist
    496   // entry is only valid for some number of minutes.
    497   scoped_ptr<WebstoreInstaller::Approval> approval(
    498       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
    499           profile(), id_, parsed_manifest_.Pass()));
    500   approval->use_app_installed_bubble = use_app_installed_bubble_;
    501   approval->enable_launcher = enable_launcher_;
    502   // If we are enabling the launcher, we should not show the app list in order
    503   // to train the user to open it themselves at least once.
    504   approval->skip_post_install_ui = enable_launcher_;
    505   approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
    506   g_pending_approvals.Get().PushApproval(approval.Pass());
    507 
    508   SetResultCode(ERROR_NONE);
    509   SendResponse(true);
    510 
    511   // The Permissions_Install histogram is recorded from the ExtensionService
    512   // for all extension installs, so we only need to record the web store
    513   // specific histogram here.
    514   ExtensionService::RecordPermissionMessagesHistogram(
    515       dummy_extension_.get(), "Extensions.Permissions_WebStoreInstall");
    516 
    517   // Matches the AddRef in RunImpl().
    518   Release();
    519 }
    520 
    521 void WebstorePrivateBeginInstallWithManifest3Function::InstallUIAbort(
    522     bool user_initiated) {
    523   error_ = kUserCancelledError;
    524   SetResultCode(USER_CANCELLED);
    525   g_pending_installs.Get().EraseInstall(profile_, id_);
    526   SendResponse(false);
    527 
    528   // The web store install histograms are a subset of the install histograms.
    529   // We need to record both histograms here since CrxInstaller::InstallUIAbort
    530   // is never called for web store install cancellations.
    531   std::string histogram_name = user_initiated ?
    532       "Extensions.Permissions_WebStoreInstallCancel" :
    533       "Extensions.Permissions_WebStoreInstallAbort";
    534   ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
    535                                                       histogram_name.c_str());
    536 
    537   histogram_name = user_initiated ?
    538       "Extensions.Permissions_InstallCancel" :
    539       "Extensions.Permissions_InstallAbort";
    540   ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(),
    541                                                       histogram_name.c_str());
    542 
    543   // Matches the AddRef in RunImpl().
    544   Release();
    545 }
    546 
    547 WebstorePrivateCompleteInstallFunction::
    548     WebstorePrivateCompleteInstallFunction() {}
    549 
    550 WebstorePrivateCompleteInstallFunction::
    551     ~WebstorePrivateCompleteInstallFunction() {}
    552 
    553 bool WebstorePrivateCompleteInstallFunction::RunImpl() {
    554   std::string id;
    555   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &id));
    556   if (!extensions::Extension::IdIsValid(id)) {
    557     error_ = kInvalidIdError;
    558     return false;
    559   }
    560 
    561   approval_ = g_pending_approvals.Get().PopApproval(profile(), id).Pass();
    562   if (!approval_) {
    563     error_ = ErrorUtils::FormatErrorMessage(
    564         kNoPreviousBeginInstallWithManifestError, id);
    565     return false;
    566   }
    567 
    568   // Balanced in OnExtensionInstallSuccess() or OnExtensionInstallFailure().
    569   AddRef();
    570 
    571   if (approval_->enable_launcher)
    572     AppListService::Get()->EnableAppList(profile());
    573 
    574   if (apps::IsAppLauncherEnabled() && approval_->manifest->is_app()) {
    575     // Show the app list to show download is progressing. Don't show the app
    576     // list on first app install so users can be trained to open it themselves.
    577     if (approval_->enable_launcher)
    578       AppListService::Get()->CreateForProfile(profile());
    579     else
    580       AppListService::Get()->ShowForProfile(profile());
    581   }
    582 
    583   // The extension will install through the normal extension install flow, but
    584   // the whitelist entry will bypass the normal permissions install dialog.
    585   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
    586       profile(), this,
    587       &(dispatcher()->delegate()->GetAssociatedWebContents()->GetController()),
    588       id, approval_.Pass(), WebstoreInstaller::FLAG_NONE);
    589   installer->Start();
    590 
    591   return true;
    592 }
    593 
    594 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallSuccess(
    595     const std::string& id) {
    596   if (test_webstore_installer_delegate)
    597     test_webstore_installer_delegate->OnExtensionInstallSuccess(id);
    598 
    599   LOG(INFO) << "Install success, sending response";
    600   g_pending_installs.Get().EraseInstall(profile_, id);
    601   SendResponse(true);
    602 
    603   // Matches the AddRef in RunImpl().
    604   Release();
    605 }
    606 
    607 void WebstorePrivateCompleteInstallFunction::OnExtensionInstallFailure(
    608     const std::string& id,
    609     const std::string& error,
    610     WebstoreInstaller::FailureReason reason) {
    611   if (test_webstore_installer_delegate) {
    612     test_webstore_installer_delegate->OnExtensionInstallFailure(
    613         id, error, reason);
    614   }
    615 
    616   error_ = error;
    617   LOG(INFO) << "Install failed, sending response";
    618   g_pending_installs.Get().EraseInstall(profile_, id);
    619   SendResponse(false);
    620 
    621   // Matches the AddRef in RunImpl().
    622   Release();
    623 }
    624 
    625 WebstorePrivateEnableAppLauncherFunction::
    626     WebstorePrivateEnableAppLauncherFunction() {}
    627 
    628 WebstorePrivateEnableAppLauncherFunction::
    629     ~WebstorePrivateEnableAppLauncherFunction() {}
    630 
    631 bool WebstorePrivateEnableAppLauncherFunction::RunImpl() {
    632   AppListService::Get()->EnableAppList(profile());
    633   SendResponse(true);
    634   return true;
    635 }
    636 
    637 bool WebstorePrivateGetBrowserLoginFunction::RunImpl() {
    638   SetResult(CreateLoginResult(profile_->GetOriginalProfile()));
    639   return true;
    640 }
    641 
    642 bool WebstorePrivateGetStoreLoginFunction::RunImpl() {
    643   SetResult(Value::CreateStringValue(GetWebstoreLogin(profile_)));
    644   return true;
    645 }
    646 
    647 bool WebstorePrivateSetStoreLoginFunction::RunImpl() {
    648   std::string login;
    649   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &login));
    650   SetWebstoreLogin(profile_, login);
    651   return true;
    652 }
    653 
    654 WebstorePrivateGetWebGLStatusFunction::WebstorePrivateGetWebGLStatusFunction() {
    655   feature_checker_ = new GPUFeatureChecker(
    656       gpu::GPU_FEATURE_TYPE_WEBGL,
    657       base::Bind(&WebstorePrivateGetWebGLStatusFunction::OnFeatureCheck,
    658           base::Unretained(this)));
    659 }
    660 
    661 WebstorePrivateGetWebGLStatusFunction::
    662     ~WebstorePrivateGetWebGLStatusFunction() {}
    663 
    664 void WebstorePrivateGetWebGLStatusFunction::CreateResult(bool webgl_allowed) {
    665   SetResult(Value::CreateStringValue(
    666       webgl_allowed ? "webgl_allowed" : "webgl_blocked"));
    667 }
    668 
    669 bool WebstorePrivateGetWebGLStatusFunction::RunImpl() {
    670   feature_checker_->CheckGPUFeatureAvailability();
    671   return true;
    672 }
    673 
    674 void WebstorePrivateGetWebGLStatusFunction::
    675     OnFeatureCheck(bool feature_allowed) {
    676   CreateResult(feature_allowed);
    677   SendResponse(true);
    678 }
    679 
    680 bool WebstorePrivateGetIsLauncherEnabledFunction::RunImpl() {
    681   SetResult(Value::CreateBooleanValue(apps::IsAppLauncherEnabled()));
    682   SendResponse(true);
    683   return true;
    684 }
    685 
    686 bool WebstorePrivateIsInIncognitoModeFunction::RunImpl() {
    687   SetResult(
    688       Value::CreateBooleanValue(profile_ != profile_->GetOriginalProfile()));
    689   SendResponse(true);
    690   return true;
    691 }
    692 
    693 }  // namespace extensions
    694