Home | History | Annotate | Download | only in extensions
      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/webstore_standalone_installer.h"
      6 
      7 #include "base/values.h"
      8 #include "chrome/browser/extensions/crx_installer.h"
      9 #include "chrome/browser/extensions/extension_install_prompt.h"
     10 #include "chrome/browser/extensions/extension_install_ui.h"
     11 #include "chrome/browser/extensions/extension_service.h"
     12 #include "chrome/browser/extensions/webstore_data_fetcher.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "content/public/browser/web_contents.h"
     15 #include "extensions/browser/extension_prefs.h"
     16 #include "extensions/browser/extension_registry.h"
     17 #include "extensions/browser/extension_system.h"
     18 #include "extensions/browser/extension_util.h"
     19 #include "extensions/common/extension.h"
     20 #include "url/gurl.h"
     21 
     22 using content::WebContents;
     23 
     24 namespace extensions {
     25 
     26 const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
     27 const char kWebstoreRequestError[] =
     28     "Could not fetch data from the Chrome Web Store";
     29 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
     30 const char kInvalidManifestError[] = "Invalid manifest";
     31 const char kUserCancelledError[] = "User cancelled install";
     32 const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
     33 
     34 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
     35     const std::string& webstore_item_id,
     36     Profile* profile,
     37     const Callback& callback)
     38     : id_(webstore_item_id),
     39       callback_(callback),
     40       profile_(profile),
     41       install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
     42       show_user_count_(true),
     43       average_rating_(0.0),
     44       rating_count_(0) {
     45 }
     46 
     47 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
     48 
     49 //
     50 // Private interface implementation.
     51 //
     52 
     53 void WebstoreStandaloneInstaller::BeginInstall() {
     54   // Add a ref to keep this alive for WebstoreDataFetcher.
     55   // All code paths from here eventually lead to either CompleteInstall or
     56   // AbortInstall, which both release this ref.
     57   AddRef();
     58 
     59   if (!Extension::IdIsValid(id_)) {
     60     CompleteInstall(kInvalidWebstoreItemId);
     61     return;
     62   }
     63 
     64   // Use the requesting page as the referrer both since that is more correct
     65   // (it is the page that caused this request to happen) and so that we can
     66   // track top sites that trigger inline install requests.
     67   webstore_data_fetcher_.reset(new WebstoreDataFetcher(
     68       this,
     69       profile_->GetRequestContext(),
     70       GetRequestorURL(),
     71       id_));
     72   webstore_data_fetcher_->Start();
     73 }
     74 
     75 bool WebstoreStandaloneInstaller::CheckInstallValid(
     76     const base::DictionaryValue& manifest,
     77     std::string* error) {
     78   return true;
     79 }
     80 
     81 scoped_ptr<ExtensionInstallPrompt>
     82 WebstoreStandaloneInstaller::CreateInstallUI() {
     83   return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
     84 }
     85 
     86 scoped_ptr<WebstoreInstaller::Approval>
     87 WebstoreStandaloneInstaller::CreateApproval() const {
     88   scoped_ptr<WebstoreInstaller::Approval> approval(
     89       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
     90           profile_,
     91           id_,
     92           scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
     93           true));
     94   approval->skip_post_install_ui = !ShouldShowPostInstallUI();
     95   approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
     96   approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
     97   return approval.Pass();
     98 }
     99 
    100 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
    101   OnWebStoreDataFetcherDone();
    102   CompleteInstall(kWebstoreRequestError);
    103 }
    104 
    105 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
    106     scoped_ptr<base::DictionaryValue> webstore_data) {
    107   OnWebStoreDataFetcherDone();
    108 
    109   if (!CheckRequestorAlive()) {
    110     CompleteInstall(std::string());
    111     return;
    112   }
    113 
    114   std::string error;
    115 
    116   if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
    117     CompleteInstall(error);
    118     return;
    119   }
    120 
    121   if (!CheckRequestorPermitted(*webstore_data, &error)) {
    122     CompleteInstall(error);
    123     return;
    124   }
    125 
    126   // Manifest, number of users, average rating and rating count are required.
    127   std::string manifest;
    128   if (!webstore_data->GetString(kManifestKey, &manifest) ||
    129       !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
    130       !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
    131       !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
    132     CompleteInstall(kInvalidWebstoreResponseError);
    133     return;
    134   }
    135 
    136   // Optional.
    137   show_user_count_ = true;
    138   webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
    139 
    140   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
    141       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
    142     CompleteInstall(kInvalidWebstoreResponseError);
    143     return;
    144   }
    145 
    146   // Localized name and description are optional.
    147   if ((webstore_data->HasKey(kLocalizedNameKey) &&
    148       !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
    149       (webstore_data->HasKey(kLocalizedDescriptionKey) &&
    150       !webstore_data->GetString(
    151           kLocalizedDescriptionKey, &localized_description_))) {
    152     CompleteInstall(kInvalidWebstoreResponseError);
    153     return;
    154   }
    155 
    156   // Icon URL is optional.
    157   GURL icon_url;
    158   if (webstore_data->HasKey(kIconUrlKey)) {
    159     std::string icon_url_string;
    160     if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
    161       CompleteInstall(kInvalidWebstoreResponseError);
    162       return;
    163     }
    164     icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
    165         icon_url_string);
    166     if (!icon_url.is_valid()) {
    167       CompleteInstall(kInvalidWebstoreResponseError);
    168       return;
    169     }
    170   }
    171 
    172   // Assume ownership of webstore_data.
    173   webstore_data_ = webstore_data.Pass();
    174 
    175   scoped_refptr<WebstoreInstallHelper> helper =
    176       new WebstoreInstallHelper(this,
    177                                 id_,
    178                                 manifest,
    179                                 std::string(),  // We don't have any icon data.
    180                                 icon_url,
    181                                 profile_->GetRequestContext());
    182   // The helper will call us back via OnWebstoreParseSucces or
    183   // OnWebstoreParseFailure.
    184   helper->Start();
    185 }
    186 
    187 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
    188     const std::string& error) {
    189   OnWebStoreDataFetcherDone();
    190   CompleteInstall(error);
    191 }
    192 
    193 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
    194     const std::string& id,
    195     const SkBitmap& icon,
    196     base::DictionaryValue* manifest) {
    197   CHECK_EQ(id_, id);
    198 
    199   if (!CheckRequestorAlive()) {
    200     CompleteInstall(std::string());
    201     return;
    202   }
    203 
    204   manifest_.reset(manifest);
    205   icon_ = icon;
    206 
    207   std::string error;
    208   if (!CheckInstallValid(*manifest, &error)) {
    209     DCHECK(!error.empty());
    210     CompleteInstall(error);
    211     return;
    212   }
    213 
    214   install_prompt_ = CreateInstallPrompt();
    215   if (install_prompt_) {
    216     ShowInstallUI();
    217     // Control flow finishes up in InstallUIProceed or InstallUIAbort.
    218   } else {
    219     InstallUIProceed();
    220   }
    221 }
    222 
    223 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
    224     const std::string& id,
    225     InstallHelperResultCode result_code,
    226     const std::string& error_message) {
    227   CompleteInstall(error_message);
    228 }
    229 
    230 void WebstoreStandaloneInstaller::InstallUIProceed() {
    231   if (!CheckRequestorAlive()) {
    232     CompleteInstall(std::string());
    233     return;
    234   }
    235 
    236   scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
    237 
    238   ExtensionService* extension_service =
    239       ExtensionSystem::Get(profile_)->extension_service();
    240   const Extension* extension =
    241       extension_service->GetExtensionById(id_, true /* include disabled */);
    242   if (extension) {
    243     std::string install_result;  // Empty string for install success.
    244 
    245     if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
    246       // Don't install a blacklisted extension.
    247       install_result = kExtensionIsBlacklisted;
    248     } else if (util::IsEphemeralApp(extension->id(), profile_) &&
    249                !approval->is_ephemeral) {
    250       // If the target extension has already been installed ephemerally, it can
    251       // be promoted to a regular installed extension and downloading from the
    252       // Web Store is not necessary.
    253       extension_service->PromoteEphemeralApp(extension, false);
    254     } else if (!extension_service->IsExtensionEnabled(id_)) {
    255       // If the extension is installed but disabled, and not blacklisted,
    256       // enable it.
    257       extension_service->EnableExtension(id_);
    258     }  // else extension is installed and enabled; no work to be done.
    259 
    260     CompleteInstall(install_result);
    261     return;
    262   }
    263 
    264   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
    265       profile_,
    266       this,
    267       GetWebContents(),
    268       id_,
    269       approval.Pass(),
    270       install_source_);
    271   installer->Start();
    272 }
    273 
    274 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
    275   CompleteInstall(kUserCancelledError);
    276 }
    277 
    278 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
    279     const std::string& id) {
    280   CHECK_EQ(id_, id);
    281   CompleteInstall(std::string());
    282 }
    283 
    284 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
    285     const std::string& id,
    286     const std::string& error,
    287     WebstoreInstaller::FailureReason cancelled) {
    288   CHECK_EQ(id_, id);
    289   CompleteInstall(error);
    290 }
    291 
    292 void WebstoreStandaloneInstaller::AbortInstall() {
    293   callback_.Reset();
    294   // Abort any in-progress fetches.
    295   if (webstore_data_fetcher_) {
    296     webstore_data_fetcher_.reset();
    297     Release();  // Matches the AddRef in BeginInstall.
    298   }
    299 }
    300 
    301 void WebstoreStandaloneInstaller::InvokeCallback(const std::string& error) {
    302   if (!callback_.is_null())
    303     callback_.Run(error.empty(), error);
    304 }
    305 
    306 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
    307   InvokeCallback(error);
    308   Release();  // Matches the AddRef in BeginInstall.
    309 }
    310 
    311 void WebstoreStandaloneInstaller::ShowInstallUI() {
    312   std::string error;
    313   localized_extension_for_display_ =
    314       ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
    315           manifest_.get(),
    316           Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
    317           id_,
    318           localized_name_,
    319           localized_description_,
    320           &error);
    321   if (!localized_extension_for_display_.get()) {
    322     CompleteInstall(kInvalidManifestError);
    323     return;
    324   }
    325 
    326   install_ui_ = CreateInstallUI();
    327   install_ui_->ConfirmStandaloneInstall(
    328       this, localized_extension_for_display_.get(), &icon_, install_prompt_);
    329 }
    330 
    331 void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
    332   // An instance of this class is passed in as a delegate for the
    333   // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
    334   // therefore needs to remain alive until they are done. Clear the webstore
    335   // data fetcher to avoid calling Release in AbortInstall while any of these
    336   // operations are in progress.
    337   webstore_data_fetcher_.reset();
    338 }
    339 
    340 }  // namespace extensions
    341