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 "chrome/common/extensions/extension.h"
     15 #include "content/public/browser/web_contents.h"
     16 #include "url/gurl.h"
     17 
     18 using content::WebContents;
     19 
     20 namespace extensions {
     21 
     22 const char kManifestKey[] = "manifest";
     23 const char kIconUrlKey[] = "icon_url";
     24 const char kLocalizedNameKey[] = "localized_name";
     25 const char kLocalizedDescriptionKey[] = "localized_description";
     26 const char kUsersKey[] = "users";
     27 const char kShowUserCountKey[] = "show_user_count";
     28 const char kAverageRatingKey[] = "average_rating";
     29 const char kRatingCountKey[] = "rating_count";
     30 const char kRedirectUrlKey[] = "redirect_url";
     31 
     32 const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
     33 const char kWebstoreRequestError[] =
     34     "Could not fetch data from the Chrome Web Store";
     35 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
     36 const char kInvalidManifestError[] = "Invalid manifest";
     37 const char kUserCancelledError[] = "User cancelled install";
     38 
     39 
     40 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
     41     const std::string& webstore_item_id,
     42     Profile* profile,
     43     const Callback& callback)
     44     : id_(webstore_item_id),
     45       callback_(callback),
     46       profile_(profile),
     47       show_user_count_(true),
     48       average_rating_(0.0),
     49       rating_count_(0) {
     50   CHECK(!callback_.is_null());
     51 }
     52 
     53 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {}
     54 
     55 //
     56 // Private interface implementation.
     57 //
     58 
     59 void WebstoreStandaloneInstaller::BeginInstall() {
     60   AddRef();  // Balanced in CompleteInstall or WebContentsDestroyed.
     61 
     62   if (!Extension::IdIsValid(id_)) {
     63     CompleteInstall(kInvalidWebstoreItemId);
     64     return;
     65   }
     66 
     67   // Use the requesting page as the referrer both since that is more correct
     68   // (it is the page that caused this request to happen) and so that we can
     69   // track top sites that trigger inline install requests.
     70   webstore_data_fetcher_.reset(new WebstoreDataFetcher(
     71       this,
     72       profile_->GetRequestContext(),
     73       GetRequestorURL(),
     74       id_));
     75   webstore_data_fetcher_->Start();
     76 }
     77 
     78 scoped_ptr<ExtensionInstallPrompt>
     79 WebstoreStandaloneInstaller::CreateInstallUI() {
     80   return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
     81 }
     82 
     83 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
     84   CompleteInstall(kWebstoreRequestError);
     85 }
     86 
     87 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
     88     DictionaryValue* webstore_data) {
     89   if (!CheckRequestorAlive()) {
     90     CompleteInstall(std::string());
     91     return;
     92   }
     93 
     94   std::string error;
     95 
     96   if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
     97     CompleteInstall(error);
     98     return;
     99   }
    100 
    101   if (!CheckRequestorPermitted(*webstore_data, &error)) {
    102     CompleteInstall(error);
    103     return;
    104   }
    105 
    106   // Manifest, number of users, average rating and rating count are required.
    107   std::string manifest;
    108   if (!webstore_data->GetString(kManifestKey, &manifest) ||
    109       !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
    110       !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
    111       !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
    112     CompleteInstall(kInvalidWebstoreResponseError);
    113     return;
    114   }
    115 
    116   // Optional.
    117   show_user_count_ = true;
    118   webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
    119 
    120   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
    121       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
    122     CompleteInstall(kInvalidWebstoreResponseError);
    123     return;
    124   }
    125 
    126   // Localized name and description are optional.
    127   if ((webstore_data->HasKey(kLocalizedNameKey) &&
    128       !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
    129       (webstore_data->HasKey(kLocalizedDescriptionKey) &&
    130       !webstore_data->GetString(
    131           kLocalizedDescriptionKey, &localized_description_))) {
    132     CompleteInstall(kInvalidWebstoreResponseError);
    133     return;
    134   }
    135 
    136   // Icon URL is optional.
    137   GURL icon_url;
    138   if (webstore_data->HasKey(kIconUrlKey)) {
    139     std::string icon_url_string;
    140     if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
    141       CompleteInstall(kInvalidWebstoreResponseError);
    142       return;
    143     }
    144     icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
    145         icon_url_string);
    146     if (!icon_url.is_valid()) {
    147       CompleteInstall(kInvalidWebstoreResponseError);
    148       return;
    149     }
    150   }
    151 
    152   // Assume ownership of webstore_data.
    153   webstore_data_.reset(webstore_data);
    154 
    155   scoped_refptr<WebstoreInstallHelper> helper =
    156       new WebstoreInstallHelper(this,
    157                                 id_,
    158                                 manifest,
    159                                 std::string(),  // We don't have any icon data.
    160                                 icon_url,
    161                                 profile_->GetRequestContext());
    162   // The helper will call us back via OnWebstoreParseSucces or
    163   // OnWebstoreParseFailure.
    164   helper->Start();
    165 }
    166 
    167 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
    168     const std::string& error) {
    169   CompleteInstall(error);
    170 }
    171 
    172 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
    173     const std::string& id,
    174     const SkBitmap& icon,
    175     base::DictionaryValue* manifest) {
    176   CHECK_EQ(id_, id);
    177 
    178   if (!CheckRequestorAlive()) {
    179     CompleteInstall(std::string());
    180     return;
    181   }
    182 
    183   manifest_.reset(manifest);
    184   icon_ = icon;
    185 
    186   install_prompt_ = CreateInstallPrompt();
    187   if (install_prompt_) {
    188     ShowInstallUI();
    189     // Control flow finishes up in InstallUIProceed or InstallUIAbort.
    190   } else {
    191     InstallUIProceed();
    192   }
    193 }
    194 
    195 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
    196     const std::string& id,
    197     InstallHelperResultCode result_code,
    198     const std::string& error_message) {
    199   CompleteInstall(error_message);
    200 }
    201 
    202 void WebstoreStandaloneInstaller::InstallUIProceed() {
    203   if (!CheckRequestorAlive()) {
    204     CompleteInstall(std::string());
    205     return;
    206   }
    207 
    208   scoped_ptr<WebstoreInstaller::Approval> approval(
    209       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
    210           profile_,
    211           id_,
    212           scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy())));
    213   approval->skip_post_install_ui = !ShouldShowPostInstallUI();
    214   approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
    215 
    216   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
    217       profile_,
    218       this,
    219       &(GetWebContents()->GetController()),
    220       id_,
    221       approval.Pass(),
    222       WebstoreInstaller::FLAG_INLINE_INSTALL);
    223   installer->Start();
    224 }
    225 
    226 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
    227   CompleteInstall(kUserCancelledError);
    228 }
    229 
    230 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
    231     const std::string& id) {
    232   CHECK_EQ(id_, id);
    233   CompleteInstall(std::string());
    234 }
    235 
    236 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
    237     const std::string& id,
    238     const std::string& error,
    239     WebstoreInstaller::FailureReason cancelled) {
    240   CHECK_EQ(id_, id);
    241   CompleteInstall(error);
    242 }
    243 
    244 void WebstoreStandaloneInstaller::AbortInstall() {
    245   callback_.Reset();
    246   // Abort any in-progress fetches.
    247   if (webstore_data_fetcher_) {
    248     webstore_data_fetcher_.reset();
    249     Release();  // Matches the AddRef in BeginInstall.
    250   }
    251 }
    252 
    253 void WebstoreStandaloneInstaller::CompleteInstall(const std::string& error) {
    254   // Clear webstore_data_fetcher_ so that WebContentsDestroyed will no longer
    255   // call Release in case the WebContents is destroyed before this object.
    256   scoped_ptr<WebstoreDataFetcher> webstore_data_fetcher(
    257       webstore_data_fetcher_.Pass());
    258   if (!callback_.is_null())
    259     callback_.Run(error.empty(), error);
    260 
    261   Release();  // Matches the AddRef in BeginInstall.
    262 }
    263 
    264 void
    265 WebstoreStandaloneInstaller::ShowInstallUI() {
    266   std::string error;
    267   localized_extension_for_display_ =
    268       ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
    269           manifest_.get(),
    270           Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
    271           id_,
    272           localized_name_,
    273           localized_description_,
    274           &error);
    275   if (!localized_extension_for_display_.get()) {
    276     CompleteInstall(kInvalidManifestError);
    277     return;
    278   }
    279 
    280   install_ui_ = CreateInstallUI();
    281   install_ui_->ConfirmStandaloneInstall(
    282       this, localized_extension_for_display_.get(), &icon_, *install_prompt_);
    283 }
    284 
    285 }  // namespace extensions
    286