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