blob: 99fa7103cfb3dc78cd7126d5aff7c381c242bedf [file] [log] [blame]
// 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(),
blink::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