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 kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
16const char kNoVerifiedSitesError[] =
17    "Inline installs can only be initiated for Chrome Web Store items that "
18    "have one or more verified sites";
19const char kNotFromVerifiedSitesError[] =
20    "Installs can only be initiated by one of the Chrome Web Store item's "
21    "verified sites";
22const char kInlineInstallSupportedError[] =
23    "Inline installation is not supported for this item. The user will be "
24    "redirected to the Chrome Web Store.";
25
26WebstoreInlineInstaller::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
39WebstoreInlineInstaller::~WebstoreInlineInstaller() {}
40
41bool WebstoreInlineInstaller::CheckRequestorAlive() const {
42  // The tab may have gone away - cancel installation in that case.
43  return web_contents() != NULL;
44}
45
46const GURL& WebstoreInlineInstaller::GetRequestorURL() const {
47  return requestor_url_;
48}
49
50scoped_refptr<ExtensionInstallPrompt::Prompt>
51WebstoreInlineInstaller::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
66bool WebstoreInlineInstaller::ShouldShowPostInstallUI() const {
67  return true;
68}
69
70bool WebstoreInlineInstaller::ShouldShowAppInstalledBubble() const {
71  return true;
72}
73
74WebContents* WebstoreInlineInstaller::GetWebContents() const {
75  return web_contents();
76}
77
78bool 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, ui::PAGE_TRANSITION_AUTO_BOOKMARK, false));
103    *error = kInlineInstallSupportedError;
104    return false;
105  }
106
107  *error = "";
108  return true;
109}
110
111bool 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
161void WebstoreInlineInstaller::WebContentsDestroyed() {
162  AbortInstall();
163}
164
165// static
166bool 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