1 // Copyright 2014 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.
5 #include "components/google/core/browser/google_util.h"
10 #include "base/command_line.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/google/core/browser/google_switches.h"
17 #include "components/google/core/browser/google_url_tracker.h"
18 #include "components/url_formatter/url_fixer.h"
19 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
20 #include "net/base/url_util.h"
23 // Only use Link Doctor on official builds. It uses an API key, too, but
24 // seems best to just disable it, for more responsive error pages and to reduce
26 #if defined(GOOGLE_CHROME_BUILD)
27 #define LINKDOCTOR_SERVER_REQUEST_URL "https://www.googleapis.com/rpc"
29 #define LINKDOCTOR_SERVER_REQUEST_URL ""
33 // Helpers --------------------------------------------------------------------
37 bool gUseMockLinkDoctorBaseURLForTesting
= false;
39 bool IsPathHomePageBase(const std::string
& path
) {
40 return (path
== "/") || (path
== "/webhp");
43 // True if |host| is "[www.]<domain_in_lower_case>.<TLD>" with a valid TLD. If
44 // |subdomain_permission| is ALLOW_SUBDOMAIN, we check against host
45 // "*.<domain_in_lower_case>.<TLD>" instead.
46 bool IsValidHostName(const std::string
& host
,
47 const std::string
& domain_in_lower_case
,
48 google_util::SubdomainPermission subdomain_permission
) {
49 size_t tld_length
= net::registry_controlled_domains::GetRegistryLength(
51 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES
,
52 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES
);
53 if ((tld_length
== 0) || (tld_length
== std::string::npos
))
55 // Removes the tld and the preceding dot.
56 std::string
host_minus_tld(host
, 0, host
.length() - tld_length
- 1);
57 if (base::LowerCaseEqualsASCII(host_minus_tld
, domain_in_lower_case
.c_str()))
59 if (subdomain_permission
== google_util::ALLOW_SUBDOMAIN
)
60 return base::EndsWith(host_minus_tld
, "." + domain_in_lower_case
,
61 base::CompareCase::INSENSITIVE_ASCII
);
62 return base::LowerCaseEqualsASCII(host_minus_tld
,
63 ("www." + domain_in_lower_case
).c_str());
66 // True if |url| is a valid URL with HTTP or HTTPS scheme. If |port_permission|
67 // is DISALLOW_NON_STANDARD_PORTS, this also requires |url| to use the standard
68 // port for its scheme (80 for HTTP, 443 for HTTPS).
69 bool IsValidURL(const GURL
& url
, google_util::PortPermission port_permission
) {
70 return url
.is_valid() && url
.SchemeIsHTTPOrHTTPS() &&
71 (url
.port().empty() ||
72 (port_permission
== google_util::ALLOW_NON_STANDARD_PORTS
));
78 namespace google_util
{
80 // Global functions -----------------------------------------------------------
82 bool HasGoogleSearchQueryParam(const std::string
& str
) {
83 url::Component
query(0, static_cast<int>(str
.length())), key
, value
;
84 while (url::ExtractQueryKeyValue(str
.c_str(), &query
, &key
, &value
)) {
85 if (value
.is_nonempty()) {
86 base::StringPiece
key_str(&str
[key
.begin
], key
.len
);
87 if (key_str
== "q" || key_str
== "as_q")
94 GURL
LinkDoctorBaseURL() {
95 if (gUseMockLinkDoctorBaseURLForTesting
)
96 return GURL("http://mock.linkdoctor.url/for?testing");
97 return GURL(LINKDOCTOR_SERVER_REQUEST_URL
);
100 void SetMockLinkDoctorBaseURLForTesting() {
101 gUseMockLinkDoctorBaseURLForTesting
= true;
104 std::string
GetGoogleLocale(const std::string
& application_locale
) {
105 // Google does not recognize "nb" for Norwegian Bokmal; it uses "no".
106 return (application_locale
== "nb") ? "no" : application_locale
;
109 GURL
AppendGoogleLocaleParam(const GURL
& url
,
110 const std::string
& application_locale
) {
111 return net::AppendQueryParameter(
112 url
, "hl", GetGoogleLocale(application_locale
));
115 std::string
GetGoogleCountryCode(GURL google_homepage_url
) {
116 const std::string google_hostname
= google_homepage_url
.host();
117 const size_t last_dot
= google_hostname
.find_last_of('.');
118 if (last_dot
== std::string::npos
) {
121 std::string country_code
= google_hostname
.substr(last_dot
+ 1);
122 // Assume the com TLD implies the US.
123 if (country_code
== "com")
125 // Google uses the Unicode Common Locale Data Repository (CLDR), and the CLDR
126 // code for the UK is "gb".
127 if (country_code
== "uk")
129 // Catalonia does not have a CLDR country code, since it's a region in Spain,
130 // so use Spain instead.
131 if (country_code
== "cat")
136 GURL
GetGoogleSearchURL(GURL google_homepage_url
) {
137 // To transform the homepage URL into the corresponding search URL, add the
138 // "search" and the "q=" query string.
139 GURL::Replacements replacements
;
140 replacements
.SetPathStr("search");
141 replacements
.SetQueryStr("q=");
142 return google_homepage_url
.ReplaceComponents(replacements
);
145 GURL
CommandLineGoogleBaseURL() {
146 // Unit tests may add command-line flags after the first call to this
147 // function, so we don't simply initialize a static |base_url| directly and
148 // then unconditionally return it.
149 CR_DEFINE_STATIC_LOCAL(std::string
, switch_value
, ());
150 CR_DEFINE_STATIC_LOCAL(GURL
, base_url
, ());
151 std::string
current_switch_value(
152 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
153 switches::kGoogleBaseURL
));
154 if (current_switch_value
!= switch_value
) {
155 switch_value
= current_switch_value
;
156 base_url
= url_formatter::FixupURL(switch_value
, std::string());
157 if (!base_url
.is_valid() || base_url
.has_query() || base_url
.has_ref())
163 bool StartsWithCommandLineGoogleBaseURL(const GURL
& url
) {
164 GURL
base_url(CommandLineGoogleBaseURL());
165 return base_url
.is_valid() &&
166 base::StartsWith(url
.possibly_invalid_spec(), base_url
.spec(),
167 base::CompareCase::SENSITIVE
);
170 bool IsGoogleHostname(const std::string
& host
,
171 SubdomainPermission subdomain_permission
) {
172 GURL
base_url(CommandLineGoogleBaseURL());
173 if (base_url
.is_valid() && (host
== base_url
.host()))
176 return IsValidHostName(host
, "google", subdomain_permission
);
179 bool IsGoogleDomainUrl(const GURL
& url
,
180 SubdomainPermission subdomain_permission
,
181 PortPermission port_permission
) {
182 return IsValidURL(url
, port_permission
) &&
183 IsGoogleHostname(url
.host(), subdomain_permission
);
186 bool IsGoogleHomePageUrl(const GURL
& url
) {
187 // First check to see if this has a Google domain.
188 if (!IsGoogleDomainUrl(url
, DISALLOW_SUBDOMAIN
, DISALLOW_NON_STANDARD_PORTS
))
191 // Make sure the path is a known home page path.
192 std::string
path(url
.path());
193 return IsPathHomePageBase(path
) ||
194 base::StartsWith(path
, "/ig", base::CompareCase::INSENSITIVE_ASCII
);
197 bool IsGoogleSearchUrl(const GURL
& url
) {
198 // First check to see if this has a Google domain.
199 if (!IsGoogleDomainUrl(url
, DISALLOW_SUBDOMAIN
, DISALLOW_NON_STANDARD_PORTS
))
202 // Make sure the path is a known search path.
203 std::string
path(url
.path());
204 bool is_home_page_base
= IsPathHomePageBase(path
);
205 if (!is_home_page_base
&& (path
!= "/search"))
208 // Check for query parameter in URL parameter and hash fragment, depending on
210 return HasGoogleSearchQueryParam(url
.ref()) ||
211 (!is_home_page_base
&& HasGoogleSearchQueryParam(url
.query()));
214 bool IsYoutubeDomainUrl(const GURL
& url
,
215 SubdomainPermission subdomain_permission
,
216 PortPermission port_permission
) {
217 return IsValidURL(url
, port_permission
) &&
218 IsValidHostName(url
.host(), "youtube", subdomain_permission
);
221 } // namespace google_util