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