Fix for Geolocation webTCT failures
[platform/framework/web/chromium-efl.git] / url / scheme_host_port.cc
1 // Copyright 2015 The Chromium Authors
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 "url/scheme_host_port.h"
6
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <ostream>
11 #include <string_view>
12 #include <tuple>
13
14 #include "base/check_op.h"
15 #include "base/containers/contains.h"
16 #include "base/notreached.h"
17 #include "base/numerics/safe_conversions.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/trace_event/memory_usage_estimator.h"
20 #include "url/gurl.h"
21 #include "url/third_party/mozilla/url_parse.h"
22 #include "url/url_canon.h"
23 #include "url/url_canon_stdstring.h"
24 #include "url/url_constants.h"
25 #include "url/url_util.h"
26
27 namespace url {
28
29 namespace {
30
31 bool IsCanonicalHost(const std::string_view& host) {
32   std::string canon_host;
33
34   // Try to canonicalize the host (copy/pasted from net/base. :( ).
35   const Component raw_host_component(0,
36                                      base::checked_cast<int>(host.length()));
37   StdStringCanonOutput canon_host_output(&canon_host);
38   CanonHostInfo host_info;
39   CanonicalizeHostVerbose(host.data(), raw_host_component,
40                           &canon_host_output, &host_info);
41
42   if (host_info.out_host.is_nonempty() &&
43       host_info.family != CanonHostInfo::BROKEN) {
44     // Success!  Assert that there's no extra garbage.
45     canon_host_output.Complete();
46     DCHECK_EQ(host_info.out_host.len, static_cast<int>(canon_host.length()));
47   } else {
48     // Empty host, or canonicalization failed.
49     canon_host.clear();
50   }
51
52   return host == canon_host;
53 }
54
55 // Note: When changing IsValidInput, consider also updating
56 // ShouldTreatAsOpaqueOrigin in Blink (there might be existing differences in
57 // behavior between these 2 layers, but we should avoid introducing new
58 // differences).
59 bool IsValidInput(const std::string_view& scheme,
60                   const std::string_view& host,
61                   uint16_t port,
62                   SchemeHostPort::ConstructPolicy policy) {
63   // Empty schemes are never valid.
64   if (scheme.empty())
65     return false;
66
67   // about:blank and other no-access schemes translate into an opaque origin.
68   // This helps consistency with ShouldTreatAsOpaqueOrigin in Blink.
69   if (base::Contains(GetNoAccessSchemes(), scheme))
70     return false;
71
72   SchemeType scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
73   bool is_standard = GetStandardSchemeType(
74       scheme.data(),
75       Component(0, base::checked_cast<int>(scheme.length())),
76       &scheme_type);
77   if (!is_standard) {
78     // To be consistent with ShouldTreatAsOpaqueOrigin in Blink, local
79     // non-standard schemes are currently allowed to be tuple origins.
80     // Nonstandard schemes don't have hostnames, so their tuple is just
81     // ("protocol", "", 0).
82     //
83     // TODO: Migrate "content:" and "externalfile:" to be standard schemes, and
84     // remove this local scheme exception.
85     if (base::Contains(GetLocalSchemes(), scheme) && host.empty() && port == 0)
86       return true;
87
88     // Otherwise, allow non-standard schemes only if the Android WebView
89     // workaround is enabled.
90     return AllowNonStandardSchemesForAndroidWebView();
91   }
92
93 #if BUILDFLAG(IS_TIZEN_TV)
94   if (scheme == kDvbScheme || scheme == kOpAppScheme ||
95       scheme == kTVKeyScheme || scheme == kHbbTVCarouselScheme ||
96       scheme == kCIScheme)
97     return true;
98 #endif
99
100   switch (scheme_type) {
101     case SCHEME_WITH_HOST_AND_PORT:
102     case SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION:
103       // A URL with |scheme| is required to have the host and port, so return an
104       // invalid instance if host is not given.  Note that a valid port is
105       // always provided by SchemeHostPort(const GURL&) constructor (a missing
106       // port is replaced with a default port if needed by
107       // GURL::EffectiveIntPort()).
108       if (host.empty())
109         return false;
110
111       // Don't do an expensive canonicalization if the host is already
112       // canonicalized.
113       DCHECK(policy == SchemeHostPort::CHECK_CANONICALIZATION ||
114              IsCanonicalHost(host));
115       if (policy == SchemeHostPort::CHECK_CANONICALIZATION &&
116           !IsCanonicalHost(host)) {
117         return false;
118       }
119
120       return true;
121
122     case SCHEME_WITH_HOST:
123       if (port != 0) {
124         // Return an invalid object if a URL with the scheme never represents
125         // the port data but the given |port| is non-zero.
126         return false;
127       }
128
129       // Don't do an expensive canonicalization if the host is already
130       // canonicalized.
131       DCHECK(policy == SchemeHostPort::CHECK_CANONICALIZATION ||
132              IsCanonicalHost(host));
133       if (policy == SchemeHostPort::CHECK_CANONICALIZATION &&
134           !IsCanonicalHost(host)) {
135         return false;
136       }
137
138       return true;
139
140     case SCHEME_WITHOUT_AUTHORITY:
141       return false;
142
143     default:
144       NOTREACHED();
145       return false;
146   }
147 }
148
149 }  // namespace
150
151 SchemeHostPort::SchemeHostPort() = default;
152
153 SchemeHostPort::SchemeHostPort(std::string scheme,
154                                std::string host,
155                                uint16_t port,
156                                ConstructPolicy policy) {
157   if (!IsValidInput(scheme, host, port, policy)) {
158     DCHECK(!IsValid());
159     return;
160   }
161
162   scheme_ = std::move(scheme);
163   host_ = std::move(host);
164   port_ = port;
165   DCHECK(IsValid()) << "Scheme: " << scheme_ << " Host: " << host_
166                     << " Port: " << port;
167 }
168
169 SchemeHostPort::SchemeHostPort(std::string_view scheme,
170                                std::string_view host,
171                                uint16_t port)
172     : SchemeHostPort(std::string(scheme),
173                      std::string(host),
174                      port,
175                      ConstructPolicy::CHECK_CANONICALIZATION) {}
176
177 SchemeHostPort::SchemeHostPort(const GURL& url) {
178   if (!url.is_valid())
179     return;
180
181   std::string_view scheme = url.scheme_piece();
182   std::string_view host = url.host_piece();
183
184   // A valid GURL never returns PORT_INVALID.
185   int port = url.EffectiveIntPort();
186   if (port == PORT_UNSPECIFIED) {
187     port = 0;
188   } else {
189     DCHECK_GE(port, 0);
190     DCHECK_LE(port, 65535);
191   }
192
193   if (!IsValidInput(scheme, host, port, ALREADY_CANONICALIZED))
194     return;
195
196   scheme_ = std::string(scheme);
197   host_ = std::string(host);
198   port_ = port;
199 }
200
201 SchemeHostPort::~SchemeHostPort() = default;
202
203 bool SchemeHostPort::IsValid() const {
204   // It suffices to just check |scheme_| for emptiness; the other fields are
205   // never present without it.
206   DCHECK(!scheme_.empty() || host_.empty());
207   DCHECK(!scheme_.empty() || port_ == 0);
208   return !scheme_.empty();
209 }
210
211 std::string SchemeHostPort::Serialize() const {
212   // Null checking for |parsed| in SerializeInternal is probably slower than
213   // just filling it in and discarding it here.
214   url::Parsed parsed;
215   return SerializeInternal(&parsed);
216 }
217
218 GURL SchemeHostPort::GetURL() const {
219   url::Parsed parsed;
220   std::string serialized = SerializeInternal(&parsed);
221
222   if (!IsValid())
223     return GURL(std::move(serialized), parsed, false);
224
225   // SchemeHostPort does not have enough information to determine if an empty
226   // host is valid or not for the given scheme. Force re-parsing.
227   DCHECK(!scheme_.empty());
228   if (host_.empty())
229     return GURL(serialized);
230
231   // If the serialized string is passed to GURL for parsing, it will append an
232   // empty path "/". Add that here. Note: per RFC 6454 we cannot do this for
233   // normal Origin serialization.
234   DCHECK(!parsed.path.is_valid());
235   parsed.path = Component(serialized.length(), 1);
236   serialized.append("/");
237   return GURL(std::move(serialized), parsed, true);
238 }
239
240 size_t SchemeHostPort::EstimateMemoryUsage() const {
241   return base::trace_event::EstimateMemoryUsage(scheme_) +
242          base::trace_event::EstimateMemoryUsage(host_);
243 }
244
245 bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
246   return std::tie(port_, scheme_, host_) <
247          std::tie(other.port_, other.scheme_, other.host_);
248 }
249
250 std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const {
251   std::string result;
252   if (!IsValid())
253     return result;
254
255   // Reserve enough space for the "normal" case of scheme://host/.
256   result.reserve(scheme_.size() + host_.size() + 4);
257
258   if (!scheme_.empty()) {
259     parsed->scheme = Component(0, scheme_.length());
260     result.append(scheme_);
261   }
262
263   result.append(kStandardSchemeSeparator);
264
265   if (!host_.empty()) {
266     parsed->host = Component(result.length(), host_.length());
267     result.append(host_);
268   }
269
270   // Omit the port component if the port matches with the default port
271   // defined for the scheme, if any.
272   int default_port = DefaultPortForScheme(scheme_.data(),
273                                           static_cast<int>(scheme_.length()));
274   if (default_port == PORT_UNSPECIFIED)
275     return result;
276   if (port_ != default_port) {
277     result.push_back(':');
278     std::string port(base::NumberToString(port_));
279     parsed->port = Component(result.length(), port.length());
280     result.append(std::move(port));
281   }
282
283   return result;
284 }
285
286 std::ostream& operator<<(std::ostream& out,
287                          const SchemeHostPort& scheme_host_port) {
288   return out << scheme_host_port.Serialize();
289 }
290
291 }  // namespace url