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 "base/version.h"
      9 #include "chrome/browser/extensions/crx_installer.h"
     10 #include "chrome/browser/extensions/extension_install_prompt.h"
     11 #include "chrome/browser/extensions/extension_install_ui.h"
     12 #include "chrome/browser/extensions/extension_install_ui_util.h"
     13 #include "chrome/browser/extensions/extension_service.h"
     14 #include "chrome/browser/extensions/install_tracker.h"
     15 #include "chrome/browser/extensions/webstore_data_fetcher.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "components/crx_file/id_util.h"
     18 #include "content/public/browser/web_contents.h"
     19 #include "extensions/browser/extension_prefs.h"
     20 #include "extensions/browser/extension_registry.h"
     21 #include "extensions/browser/extension_system.h"
     22 #include "extensions/browser/extension_util.h"
     23 #include "extensions/common/extension.h"
     24 #include "extensions/common/extension_urls.h"
     25 #include "url/gurl.h"
     26 
     27 using content::WebContents;
     28 
     29 namespace extensions {
     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 const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
     38 const char kInstallInProgressError[] = "An install is already in progress";
     39 const char kLaunchInProgressError[] = "A launch is already in progress";
     40 
     41 WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
     42     const std::string& webstore_item_id,
     43     Profile* profile,
     44     const Callback& callback)
     45     : id_(webstore_item_id),
     46       callback_(callback),
     47       profile_(profile),
     48       install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
     49       show_user_count_(true),
     50       average_rating_(0.0),
     51       rating_count_(0) {
     52 }
     53 
     54 void WebstoreStandaloneInstaller::BeginInstall() {
     55   // Add a ref to keep this alive for WebstoreDataFetcher.
     56   // All code paths from here eventually lead to either CompleteInstall or
     57   // AbortInstall, which both release this ref.
     58   AddRef();
     59 
     60   if (!crx_file::id_util::IdIsValid(id_)) {
     61     CompleteInstall(webstore_install::INVALID_ID, kInvalidWebstoreItemId);
     62     return;
     63   }
     64 
     65   webstore_install::Result result = webstore_install::OTHER_ERROR;
     66   std::string error;
     67   if (!EnsureUniqueInstall(&result, &error)) {
     68     CompleteInstall(result, error);
     69     return;
     70   }
     71 
     72   // Use the requesting page as the referrer both since that is more correct
     73   // (it is the page that caused this request to happen) and so that we can
     74   // track top sites that trigger inline install requests.
     75   webstore_data_fetcher_.reset(new WebstoreDataFetcher(
     76       this,
     77       profile_->GetRequestContext(),
     78       GetRequestorURL(),
     79       id_));
     80   webstore_data_fetcher_->Start();
     81 }
     82 
     83 //
     84 // Private interface implementation.
     85 //
     86 
     87 WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {
     88 }
     89 
     90 void WebstoreStandaloneInstaller::RunCallback(bool success,
     91                                               const std::string& error,
     92                                               webstore_install::Result result) {
     93   callback_.Run(success, error, result);
     94 }
     95 
     96 void WebstoreStandaloneInstaller::AbortInstall() {
     97   callback_.Reset();
     98   // Abort any in-progress fetches.
     99   if (webstore_data_fetcher_) {
    100     webstore_data_fetcher_.reset();
    101     scoped_active_install_.reset();
    102     Release();  // Matches the AddRef in BeginInstall.
    103   }
    104 }
    105 
    106 bool WebstoreStandaloneInstaller::EnsureUniqueInstall(
    107     webstore_install::Result* reason,
    108     std::string* error) {
    109   InstallTracker* tracker = InstallTracker::Get(profile_);
    110   DCHECK(tracker);
    111 
    112   const ActiveInstallData* existing_install_data =
    113       tracker->GetActiveInstall(id_);
    114   if (existing_install_data) {
    115     if (existing_install_data->is_ephemeral) {
    116       *reason = webstore_install::LAUNCH_IN_PROGRESS;
    117       *error = kLaunchInProgressError;
    118     } else {
    119       *reason = webstore_install::INSTALL_IN_PROGRESS;
    120       *error = kInstallInProgressError;
    121     }
    122     return false;
    123   }
    124 
    125   ActiveInstallData install_data(id_);
    126   InitInstallData(&install_data);
    127   scoped_active_install_.reset(new ScopedActiveInstall(tracker, install_data));
    128   return true;
    129 }
    130 
    131 void WebstoreStandaloneInstaller::CompleteInstall(
    132     webstore_install::Result result,
    133     const std::string& error) {
    134   scoped_active_install_.reset();
    135   if (!callback_.is_null())
    136     callback_.Run(result == webstore_install::SUCCESS, error, result);
    137   Release();  // Matches the AddRef in BeginInstall.
    138 }
    139 
    140 void WebstoreStandaloneInstaller::ProceedWithInstallPrompt() {
    141   install_prompt_ = CreateInstallPrompt();
    142   if (install_prompt_.get()) {
    143     ShowInstallUI();
    144     // Control flow finishes up in InstallUIProceed or InstallUIAbort.
    145   } else {
    146     InstallUIProceed();
    147   }
    148 }
    149 
    150 scoped_refptr<const Extension>
    151 WebstoreStandaloneInstaller::GetLocalizedExtensionForDisplay() {
    152   if (!localized_extension_for_display_.get()) {
    153     DCHECK(manifest_.get());
    154     if (!manifest_.get())
    155       return NULL;
    156 
    157     std::string error;
    158     localized_extension_for_display_ =
    159         ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
    160             manifest_.get(),
    161             Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
    162             id_,
    163             localized_name_,
    164             localized_description_,
    165             &error);
    166   }
    167   return localized_extension_for_display_.get();
    168 }
    169 
    170 void WebstoreStandaloneInstaller::InitInstallData(
    171     ActiveInstallData* install_data) const {
    172   // Default implementation sets no properties.
    173 }
    174 
    175 void WebstoreStandaloneInstaller::OnManifestParsed() {
    176   ProceedWithInstallPrompt();
    177 }
    178 
    179 scoped_ptr<ExtensionInstallPrompt>
    180 WebstoreStandaloneInstaller::CreateInstallUI() {
    181   return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
    182 }
    183 
    184 scoped_ptr<WebstoreInstaller::Approval>
    185 WebstoreStandaloneInstaller::CreateApproval() const {
    186   scoped_ptr<WebstoreInstaller::Approval> approval(
    187       WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
    188           profile_,
    189           id_,
    190           scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
    191           true));
    192   approval->skip_post_install_ui = !ShouldShowPostInstallUI();
    193   approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
    194   approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
    195   return approval.Pass();
    196 }
    197 
    198 void WebstoreStandaloneInstaller::InstallUIProceed() {
    199   if (!CheckRequestorAlive()) {
    200     CompleteInstall(webstore_install::ABORTED, std::string());
    201     return;
    202   }
    203 
    204   scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
    205 
    206   ExtensionService* extension_service =
    207       ExtensionSystem::Get(profile_)->extension_service();
    208   const Extension* installed_extension =
    209       extension_service->GetExtensionById(id_, true /* include disabled */);
    210   if (installed_extension) {
    211     std::string install_message;
    212     webstore_install::Result install_result = webstore_install::SUCCESS;
    213     bool done = true;
    214 
    215     if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
    216       // Don't install a blacklisted extension.
    217       install_result = webstore_install::BLACKLISTED;
    218       install_message = kExtensionIsBlacklisted;
    219     } else if (util::IsEphemeralApp(installed_extension->id(), profile_) &&
    220                !approval->is_ephemeral) {
    221       // If the target extension has already been installed ephemerally and is
    222       // up to date, it can be promoted to a regular installed extension and
    223       // downloading from the Web Store is not necessary.
    224       scoped_refptr<const Extension> extension_to_install =
    225           GetLocalizedExtensionForDisplay();
    226       if (!extension_to_install.get()) {
    227         CompleteInstall(webstore_install::INVALID_MANIFEST,
    228                         kInvalidManifestError);
    229         return;
    230       }
    231 
    232       if (installed_extension->version()->CompareTo(
    233               *extension_to_install->version()) < 0) {
    234         // If the existing extension is out of date, proceed with the install
    235         // to update the extension.
    236         done = false;
    237       } else {
    238         install_ui::ShowPostInstallUIForApproval(
    239             profile_, *approval, installed_extension);
    240         extension_service->PromoteEphemeralApp(installed_extension, false);
    241       }
    242     } else if (!extension_service->IsExtensionEnabled(id_)) {
    243       // If the extension is installed but disabled, and not blacklisted,
    244       // enable it.
    245       extension_service->EnableExtension(id_);
    246     }  // else extension is installed and enabled; no work to be done.
    247 
    248     if (done) {
    249       CompleteInstall(install_result, install_message);
    250       return;
    251     }
    252   }
    253 
    254   scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
    255       profile_,
    256       this,
    257       GetWebContents(),
    258       id_,
    259       approval.Pass(),
    260       install_source_);
    261   installer->Start();
    262 }
    263 
    264 void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
    265   CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
    266 }
    267 
    268 void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
    269   OnWebStoreDataFetcherDone();
    270   CompleteInstall(webstore_install::WEBSTORE_REQUEST_ERROR,
    271                   kWebstoreRequestError);
    272 }
    273 
    274 void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
    275     scoped_ptr<base::DictionaryValue> webstore_data) {
    276   OnWebStoreDataFetcherDone();
    277 
    278   if (!CheckRequestorAlive()) {
    279     CompleteInstall(webstore_install::ABORTED, std::string());
    280     return;
    281   }
    282 
    283   std::string error;
    284 
    285   if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
    286     CompleteInstall(webstore_install::NOT_PERMITTED, error);
    287     return;
    288   }
    289 
    290   if (!CheckRequestorPermitted(*webstore_data, &error)) {
    291     CompleteInstall(webstore_install::NOT_PERMITTED, error);
    292     return;
    293   }
    294 
    295   // Manifest, number of users, average rating and rating count are required.
    296   std::string manifest;
    297   if (!webstore_data->GetString(kManifestKey, &manifest) ||
    298       !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
    299       !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
    300       !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
    301     CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
    302                     kInvalidWebstoreResponseError);
    303     return;
    304   }
    305 
    306   // Optional.
    307   show_user_count_ = true;
    308   webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
    309 
    310   if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
    311       average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
    312     CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
    313                     kInvalidWebstoreResponseError);
    314     return;
    315   }
    316 
    317   // Localized name and description are optional.
    318   if ((webstore_data->HasKey(kLocalizedNameKey) &&
    319       !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
    320       (webstore_data->HasKey(kLocalizedDescriptionKey) &&
    321       !webstore_data->GetString(
    322           kLocalizedDescriptionKey, &localized_description_))) {
    323     CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
    324                     kInvalidWebstoreResponseError);
    325     return;
    326   }
    327 
    328   // Icon URL is optional.
    329   GURL icon_url;
    330   if (webstore_data->HasKey(kIconUrlKey)) {
    331     std::string icon_url_string;
    332     if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
    333       CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
    334                       kInvalidWebstoreResponseError);
    335       return;
    336     }
    337     icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
    338         icon_url_string);
    339     if (!icon_url.is_valid()) {
    340       CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
    341                       kInvalidWebstoreResponseError);
    342       return;
    343     }
    344   }
    345 
    346   // Assume ownership of webstore_data.
    347   webstore_data_ = webstore_data.Pass();
    348 
    349   scoped_refptr<WebstoreInstallHelper> helper =
    350       new WebstoreInstallHelper(this,
    351                                 id_,
    352                                 manifest,
    353                                 std::string(),  // We don't have any icon data.
    354                                 icon_url,
    355                                 profile_->GetRequestContext());
    356   // The helper will call us back via OnWebstoreParseSucces or
    357   // OnWebstoreParseFailure.
    358   helper->Start();
    359 }
    360 
    361 void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
    362     const std::string& error) {
    363   OnWebStoreDataFetcherDone();
    364   CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE, error);
    365 }
    366 
    367 void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
    368     const std::string& id,
    369     const SkBitmap& icon,
    370     base::DictionaryValue* manifest) {
    371   CHECK_EQ(id_, id);
    372 
    373   if (!CheckRequestorAlive()) {
    374     CompleteInstall(webstore_install::ABORTED, std::string());
    375     return;
    376   }
    377 
    378   manifest_.reset(manifest);
    379   icon_ = icon;
    380 
    381   OnManifestParsed();
    382 }
    383 
    384 void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
    385     const std::string& id,
    386     InstallHelperResultCode result_code,
    387     const std::string& error_message) {
    388   webstore_install::Result install_result = webstore_install::OTHER_ERROR;
    389   switch (result_code) {
    390     case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
    391       install_result = webstore_install::INVALID_MANIFEST;
    392       break;
    393     case WebstoreInstallHelper::Delegate::ICON_ERROR:
    394       install_result = webstore_install::ICON_ERROR;
    395       break;
    396     default:
    397       break;
    398   }
    399 
    400   CompleteInstall(install_result, error_message);
    401 }
    402 
    403 void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
    404     const std::string& id) {
    405   CHECK_EQ(id_, id);
    406   CompleteInstall(webstore_install::SUCCESS, std::string());
    407 }
    408 
    409 void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
    410     const std::string& id,
    411     const std::string& error,
    412     WebstoreInstaller::FailureReason reason) {
    413   CHECK_EQ(id_, id);
    414 
    415   webstore_install::Result install_result = webstore_install::OTHER_ERROR;
    416   switch (reason) {
    417     case WebstoreInstaller::FAILURE_REASON_CANCELLED:
    418       install_result = webstore_install::USER_CANCELLED;
    419       break;
    420     case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_FOUND:
    421     case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE:
    422       install_result = webstore_install::MISSING_DEPENDENCIES;
    423       break;
    424     default:
    425       break;
    426   }
    427 
    428   CompleteInstall(install_result, error);
    429 }
    430 
    431 void WebstoreStandaloneInstaller::ShowInstallUI() {
    432   scoped_refptr<const Extension> localized_extension =
    433       GetLocalizedExtensionForDisplay();
    434   if (!localized_extension.get()) {
    435     CompleteInstall(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
    436     return;
    437   }
    438 
    439   install_ui_ = CreateInstallUI();
    440   install_ui_->ConfirmStandaloneInstall(
    441       this, localized_extension.get(), &icon_, install_prompt_);
    442 }
    443 
    444 void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
    445   // An instance of this class is passed in as a delegate for the
    446   // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
    447   // therefore needs to remain alive until they are done. Clear the webstore
    448   // data fetcher to avoid calling Release in AbortInstall while any of these
    449   // operations are in progress.
    450   webstore_data_fetcher_.reset();
    451 }
    452 
    453 }  // namespace extensions
    454