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