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 11using content::WebContents; 12 13namespace extensions { 14 15const char kVerifiedSiteKey[] = "verified_site"; 16const char kVerifiedSitesKey[] = "verified_sites"; 17const char kInlineInstallNotSupportedKey[] = "inline_install_not_supported"; 18const char kRedirectUrlKey[] = "redirect_url"; 19 20const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse"; 21const char kNoVerifiedSitesError[] = 22 "Inline installs can only be initiated for Chrome Web Store items that " 23 "have one or more verified sites"; 24const char kNotFromVerifiedSitesError[] = 25 "Installs can only be initiated by one of the Chrome Web Store item's " 26 "verified sites"; 27const char kInlineInstallSupportedError[] = 28 "Inline installation is not supported for this item. The user will be " 29 "redirected to the Chrome Web Store."; 30 31WebstoreInlineInstaller::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 44WebstoreInlineInstaller::~WebstoreInlineInstaller() {} 45 46bool WebstoreInlineInstaller::CheckRequestorAlive() const { 47 // The tab may have gone away - cancel installation in that case. 48 return web_contents() != NULL; 49} 50 51const GURL& WebstoreInlineInstaller::GetRequestorURL() const { 52 return requestor_url_; 53} 54 55scoped_ptr<ExtensionInstallPrompt::Prompt> 56WebstoreInlineInstaller::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 71bool WebstoreInlineInstaller::ShouldShowPostInstallUI() const { 72 return true; 73} 74 75bool WebstoreInlineInstaller::ShouldShowAppInstalledBubble() const { 76 return true; 77} 78 79WebContents* WebstoreInlineInstaller::GetWebContents() const { 80 return web_contents(); 81} 82 83bool 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 116bool 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 166void WebstoreInlineInstaller::WebContentsDestroyed( 167 content::WebContents* web_contents) { 168 AbortInstall(); 169} 170 171// static 172bool 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