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_inline_installer.h"
      6 
      7 #include "base/strings/stringprintf.h"
      8 #include "chrome/browser/profiles/profile.h"
      9 #include "content/public/browser/web_contents.h"
     10 
     11 using content::WebContents;
     12 
     13 namespace extensions {
     14 
     15 const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
     16 const char kNoVerifiedSitesError[] =
     17     "Inline installs can only be initiated for Chrome Web Store items that "
     18     "have one or more verified sites";
     19 const char kNotFromVerifiedSitesError[] =
     20     "Installs can only be initiated by one of the Chrome Web Store item's "
     21     "verified sites";
     22 const char kInlineInstallSupportedError[] =
     23     "Inline installation is not supported for this item. The user will be "
     24     "redirected to the Chrome Web Store.";
     25 
     26 WebstoreInlineInstaller::WebstoreInlineInstaller(
     27     content::WebContents* web_contents,
     28     const std::string& webstore_item_id,
     29     const GURL& requestor_url,
     30     const Callback& callback)
     31     : WebstoreStandaloneInstaller(
     32           webstore_item_id,
     33           Profile::FromBrowserContext(web_contents->GetBrowserContext()),
     34           callback),
     35       content::WebContentsObserver(web_contents),
     36       requestor_url_(requestor_url) {
     37 }
     38 
     39 WebstoreInlineInstaller::~WebstoreInlineInstaller() {}
     40 
     41 bool WebstoreInlineInstaller::CheckRequestorAlive() const {
     42   // The tab may have gone away - cancel installation in that case.
     43   return web_contents() != NULL;
     44 }
     45 
     46 const GURL& WebstoreInlineInstaller::GetRequestorURL() const {
     47   return requestor_url_;
     48 }
     49 
     50 scoped_refptr<ExtensionInstallPrompt::Prompt>
     51 WebstoreInlineInstaller::CreateInstallPrompt() const {
     52   scoped_refptr<ExtensionInstallPrompt::Prompt> prompt(
     53       new ExtensionInstallPrompt::Prompt(
     54           ExtensionInstallPrompt::INLINE_INSTALL_PROMPT));
     55 
     56   // crbug.com/260742: Don't display the user count if it's zero. The reason
     57   // it's zero is very often that the number isn't actually being counted
     58   // (intentionally), which means that it's unlikely to be correct.
     59   prompt->SetWebstoreData(localized_user_count(),
     60                           show_user_count(),
     61                           average_rating(),
     62                           rating_count());
     63   return prompt;
     64 }
     65 
     66 bool WebstoreInlineInstaller::ShouldShowPostInstallUI() const {
     67   return true;
     68 }
     69 
     70 bool WebstoreInlineInstaller::ShouldShowAppInstalledBubble() const {
     71   return true;
     72 }
     73 
     74 WebContents* WebstoreInlineInstaller::GetWebContents() const {
     75   return web_contents();
     76 }
     77 
     78 bool WebstoreInlineInstaller::CheckInlineInstallPermitted(
     79     const base::DictionaryValue& webstore_data,
     80     std::string* error) const {
     81   // The store may not support inline installs for this item, in which case
     82   // we open the store-provided redirect URL in a new tab and abort the
     83   // installation process.
     84   bool inline_install_not_supported = false;
     85   if (webstore_data.HasKey(kInlineInstallNotSupportedKey)
     86       && !webstore_data.GetBoolean(kInlineInstallNotSupportedKey,
     87                                     &inline_install_not_supported)) {
     88     *error = kInvalidWebstoreResponseError;
     89     return false;
     90   }
     91   if (inline_install_not_supported) {
     92     std::string redirect_url;
     93     if (!webstore_data.GetString(kRedirectUrlKey, &redirect_url)) {
     94       *error = kInvalidWebstoreResponseError;
     95       return false;
     96     }
     97     web_contents()->OpenURL(
     98         content::OpenURLParams(
     99             GURL(redirect_url),
    100             content::Referrer(web_contents()->GetURL(),
    101                               blink::WebReferrerPolicyDefault),
    102             NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, false));
    103     *error = kInlineInstallSupportedError;
    104     return false;
    105   }
    106 
    107   *error = "";
    108   return true;
    109 }
    110 
    111 bool WebstoreInlineInstaller::CheckRequestorPermitted(
    112     const base::DictionaryValue& webstore_data,
    113     std::string* error) const {
    114   // Ensure that there is at least one verified site present.
    115   const bool data_has_single_site = webstore_data.HasKey(kVerifiedSiteKey);
    116   const bool data_has_site_list = webstore_data.HasKey(kVerifiedSitesKey);
    117   if (!data_has_single_site && !data_has_site_list) {
    118     *error = kNoVerifiedSitesError;
    119     return false;
    120   }
    121   bool requestor_is_ok = false;
    122   // Handle the deprecated single-site case.
    123   if (!data_has_site_list) {
    124     std::string verified_site;
    125     if (!webstore_data.GetString(kVerifiedSiteKey, &verified_site)) {
    126       *error = kInvalidWebstoreResponseError;
    127       return false;
    128     }
    129     requestor_is_ok = IsRequestorURLInVerifiedSite(requestor_url_,
    130                                                    verified_site);
    131   } else {
    132     const base::ListValue* verified_sites = NULL;
    133     if (!webstore_data.GetList(kVerifiedSitesKey, &verified_sites)) {
    134       *error = kInvalidWebstoreResponseError;
    135       return false;
    136     }
    137     for (base::ListValue::const_iterator it = verified_sites->begin();
    138          it != verified_sites->end() && !requestor_is_ok; ++it) {
    139       std::string verified_site;
    140       if (!(*it)->GetAsString(&verified_site)) {
    141         *error = kInvalidWebstoreResponseError;
    142         return false;
    143       }
    144       if (IsRequestorURLInVerifiedSite(requestor_url_, verified_site)) {
    145         requestor_is_ok = true;
    146       }
    147     }
    148   }
    149   if (!requestor_is_ok) {
    150     *error = kNotFromVerifiedSitesError;
    151     return false;
    152   }
    153   *error = "";
    154   return true;
    155 }
    156 
    157 //
    158 // Private implementation.
    159 //
    160 
    161 void WebstoreInlineInstaller::WebContentsDestroyed() {
    162   AbortInstall();
    163 }
    164 
    165 // static
    166 bool WebstoreInlineInstaller::IsRequestorURLInVerifiedSite(
    167     const GURL& requestor_url,
    168     const std::string& verified_site) {
    169   // Turn the verified site into a URL that can be parsed by URLPattern.
    170   // |verified_site| must follow the format:
    171   //
    172   // [scheme://]host[:port][/path/specifier]
    173   //
    174   // If scheme is omitted, URLPattern will match against either an
    175   // HTTP or HTTPS requestor. If scheme is specified, it must be either HTTP
    176   // or HTTPS, and URLPattern will only match the scheme specified.
    177   GURL verified_site_url(verified_site);
    178   int valid_schemes = URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
    179   if (!verified_site_url.is_valid() || !verified_site_url.IsStandard())
    180     // If no scheme is specified, GURL will fail to parse the string correctly.
    181     // It will either determine that the URL is invalid, or parse a
    182     // host:port/path as scheme:host/path.
    183     verified_site_url = GURL("http://" + verified_site);
    184   else if (verified_site_url.SchemeIs("http"))
    185     valid_schemes = URLPattern::SCHEME_HTTP;
    186   else if (verified_site_url.SchemeIs("https"))
    187     valid_schemes = URLPattern::SCHEME_HTTPS;
    188   else
    189     return false;
    190 
    191   std::string port_spec =
    192       verified_site_url.has_port() ? ":" + verified_site_url.port() : "";
    193   std::string path_spec = verified_site_url.path() + "*";
    194   std::string verified_site_pattern_spec =
    195       base::StringPrintf(
    196           "%s://*.%s%s%s",
    197           verified_site_url.scheme().c_str(),
    198           verified_site_url.host().c_str(),
    199           port_spec.c_str(),
    200           path_spec.c_str());
    201 
    202   URLPattern verified_site_pattern(valid_schemes);
    203   URLPattern::ParseResult parse_result =
    204       verified_site_pattern.Parse(verified_site_pattern_spec);
    205   if (parse_result != URLPattern::PARSE_SUCCESS) {
    206     DLOG(WARNING) << "Could not parse " << verified_site_pattern_spec <<
    207         " as URL pattern " << parse_result;
    208     return false;
    209   }
    210   verified_site_pattern.SetScheme("*");
    211 
    212   return verified_site_pattern.MatchesURL(requestor_url);
    213 }
    214 
    215 }  // namespace extensions
    216