| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/extensions/webstore_inline_installer.h" |
| |
| #include "base/strings/stringprintf.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "content/public/browser/web_contents.h" |
| |
| using content::WebContents; |
| |
| namespace extensions { |
| |
| const char kVerifiedSiteKey[] = "verified_site"; |
| const char kVerifiedSitesKey[] = "verified_sites"; |
| const char kInlineInstallNotSupportedKey[] = "inline_install_not_supported"; |
| const char kRedirectUrlKey[] = "redirect_url"; |
| |
| const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse"; |
| const char kNoVerifiedSitesError[] = |
| "Inline installs can only be initiated for Chrome Web Store items that " |
| "have one or more verified sites"; |
| const char kNotFromVerifiedSitesError[] = |
| "Installs can only be initiated by one of the Chrome Web Store item's " |
| "verified sites"; |
| const char kInlineInstallSupportedError[] = |
| "Inline installation is not supported for this item. The user will be " |
| "redirected to the Chrome Web Store."; |
| |
| WebstoreInlineInstaller::WebstoreInlineInstaller( |
| content::WebContents* web_contents, |
| const std::string& webstore_item_id, |
| const GURL& requestor_url, |
| const Callback& callback) |
| : WebstoreStandaloneInstaller( |
| webstore_item_id, |
| Profile::FromBrowserContext(web_contents->GetBrowserContext()), |
| callback), |
| content::WebContentsObserver(web_contents), |
| requestor_url_(requestor_url) { |
| } |
| |
| WebstoreInlineInstaller::~WebstoreInlineInstaller() {} |
| |
| bool WebstoreInlineInstaller::CheckRequestorAlive() const { |
| // The tab may have gone away - cancel installation in that case. |
| return web_contents() != NULL; |
| } |
| |
| const GURL& WebstoreInlineInstaller::GetRequestorURL() const { |
| return requestor_url_; |
| } |
| |
| scoped_ptr<ExtensionInstallPrompt::Prompt> |
| WebstoreInlineInstaller::CreateInstallPrompt() const { |
| scoped_ptr<ExtensionInstallPrompt::Prompt> prompt( |
| new ExtensionInstallPrompt::Prompt( |
| ExtensionInstallPrompt::INLINE_INSTALL_PROMPT)); |
| |
| // crbug.com/260742: Don't display the user count if it's zero. The reason |
| // it's zero is very often that the number isn't actually being counted |
| // (intentionally), which means that it's unlikely to be correct. |
| prompt->SetInlineInstallWebstoreData(localized_user_count(), |
| show_user_count(), |
| average_rating(), |
| rating_count()); |
| return prompt.Pass(); |
| } |
| |
| bool WebstoreInlineInstaller::ShouldShowPostInstallUI() const { |
| return true; |
| } |
| |
| bool WebstoreInlineInstaller::ShouldShowAppInstalledBubble() const { |
| return true; |
| } |
| |
| WebContents* WebstoreInlineInstaller::GetWebContents() const { |
| return web_contents(); |
| } |
| |
| bool WebstoreInlineInstaller::CheckInlineInstallPermitted( |
| const base::DictionaryValue& webstore_data, |
| std::string* error) const { |
| // The store may not support inline installs for this item, in which case |
| // we open the store-provided redirect URL in a new tab and abort the |
| // installation process. |
| bool inline_install_not_supported = false; |
| if (webstore_data.HasKey(kInlineInstallNotSupportedKey) |
| && !webstore_data.GetBoolean(kInlineInstallNotSupportedKey, |
| &inline_install_not_supported)) { |
| *error = kInvalidWebstoreResponseError; |
| return false; |
| } |
| if (inline_install_not_supported) { |
| std::string redirect_url; |
| if (!webstore_data.GetString(kRedirectUrlKey, &redirect_url)) { |
| *error = kInvalidWebstoreResponseError; |
| return false; |
| } |
| web_contents()->OpenURL( |
| content::OpenURLParams( |
| GURL(redirect_url), |
| content::Referrer(web_contents()->GetURL(), |
| WebKit::WebReferrerPolicyDefault), |
| NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_AUTO_BOOKMARK, false)); |
| *error = kInlineInstallSupportedError; |
| return false; |
| } |
| |
| *error = ""; |
| return true; |
| } |
| |
| bool WebstoreInlineInstaller::CheckRequestorPermitted( |
| const base::DictionaryValue& webstore_data, |
| std::string* error) const { |
| // Ensure that there is at least one verified site present. |
| const bool data_has_single_site = webstore_data.HasKey(kVerifiedSiteKey); |
| const bool data_has_site_list = webstore_data.HasKey(kVerifiedSitesKey); |
| if (!data_has_single_site && !data_has_site_list) { |
| *error = kNoVerifiedSitesError; |
| return false; |
| } |
| bool requestor_is_ok = false; |
| // Handle the deprecated single-site case. |
| if (!data_has_site_list) { |
| std::string verified_site; |
| if (!webstore_data.GetString(kVerifiedSiteKey, &verified_site)) { |
| *error = kInvalidWebstoreResponseError; |
| return false; |
| } |
| requestor_is_ok = IsRequestorURLInVerifiedSite(requestor_url_, |
| verified_site); |
| } else { |
| const base::ListValue* verified_sites = NULL; |
| if (!webstore_data.GetList(kVerifiedSitesKey, &verified_sites)) { |
| *error = kInvalidWebstoreResponseError; |
| return false; |
| } |
| for (base::ListValue::const_iterator it = verified_sites->begin(); |
| it != verified_sites->end() && !requestor_is_ok; ++it) { |
| std::string verified_site; |
| if (!(*it)->GetAsString(&verified_site)) { |
| *error = kInvalidWebstoreResponseError; |
| return false; |
| } |
| if (IsRequestorURLInVerifiedSite(requestor_url_, verified_site)) { |
| requestor_is_ok = true; |
| } |
| } |
| } |
| if (!requestor_is_ok) { |
| *error = kNotFromVerifiedSitesError; |
| return false; |
| } |
| *error = ""; |
| return true; |
| } |
| |
| // |
| // Private implementation. |
| // |
| |
| void WebstoreInlineInstaller::WebContentsDestroyed( |
| content::WebContents* web_contents) { |
| AbortInstall(); |
| } |
| |
| // static |
| bool WebstoreInlineInstaller::IsRequestorURLInVerifiedSite( |
| const GURL& requestor_url, |
| const std::string& verified_site) { |
| // Turn the verified site into a URL that can be parsed by URLPattern. |
| // |verified_site| must follow the format: |
| // |
| // [scheme://]host[:port][/path/specifier] |
| // |
| // If scheme is omitted, URLPattern will match against either an |
| // HTTP or HTTPS requestor. If scheme is specified, it must be either HTTP |
| // or HTTPS, and URLPattern will only match the scheme specified. |
| GURL verified_site_url(verified_site); |
| int valid_schemes = URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; |
| if (!verified_site_url.is_valid() || !verified_site_url.IsStandard()) |
| // If no scheme is specified, GURL will fail to parse the string correctly. |
| // It will either determine that the URL is invalid, or parse a |
| // host:port/path as scheme:host/path. |
| verified_site_url = GURL("http://" + verified_site); |
| else if (verified_site_url.SchemeIs("http")) |
| valid_schemes = URLPattern::SCHEME_HTTP; |
| else if (verified_site_url.SchemeIs("https")) |
| valid_schemes = URLPattern::SCHEME_HTTPS; |
| else |
| return false; |
| |
| std::string port_spec = |
| verified_site_url.has_port() ? ":" + verified_site_url.port() : ""; |
| std::string path_spec = verified_site_url.path() + "*"; |
| std::string verified_site_pattern_spec = |
| base::StringPrintf( |
| "%s://*.%s%s%s", |
| verified_site_url.scheme().c_str(), |
| verified_site_url.host().c_str(), |
| port_spec.c_str(), |
| path_spec.c_str()); |
| |
| URLPattern verified_site_pattern(valid_schemes); |
| URLPattern::ParseResult parse_result = |
| verified_site_pattern.Parse(verified_site_pattern_spec); |
| if (parse_result != URLPattern::PARSE_SUCCESS) { |
| DLOG(WARNING) << "Could not parse " << verified_site_pattern_spec << |
| " as URL pattern " << parse_result; |
| return false; |
| } |
| verified_site_pattern.SetScheme("*"); |
| |
| return verified_site_pattern.MatchesURL(requestor_url); |
| } |
| |
| } // namespace extensions |