diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-12-10 16:19:40 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-12-10 16:01:50 +0000 |
commit | 51f6c2793adab2d864b3d2b360000ef8db1d3e92 (patch) | |
tree | 835b3b4446b012c75e80177cef9fbe6972cc7dbe /chromium/url | |
parent | 6036726eb981b6c4b42047513b9d3f4ac865daac (diff) | |
download | qtwebengine-chromium-51f6c2793adab2d864b3d2b360000ef8db1d3e92.tar.gz |
BASELINE: Update Chromium to 71.0.3578.93
Change-Id: I6a32086c33670e1b033f8b10e6bf1fd4da1d105d
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/url')
-rw-r--r-- | chromium/url/BUILD.gn | 1 | ||||
-rw-r--r-- | chromium/url/android/java/src/org/chromium/url/IDNStringUtil.java | 33 | ||||
-rw-r--r-- | chromium/url/gurl_unittest.cc | 21 | ||||
-rw-r--r-- | chromium/url/mojom/BUILD.gn | 1 | ||||
-rw-r--r-- | chromium/url/mojom/origin.mojom | 8 | ||||
-rw-r--r-- | chromium/url/mojom/origin_mojom_traits.h | 50 | ||||
-rw-r--r-- | chromium/url/mojom/url_gurl_mojom_traits_unittest.cc | 29 | ||||
-rw-r--r-- | chromium/url/origin.cc | 201 | ||||
-rw-r--r-- | chromium/url/origin.h | 240 | ||||
-rw-r--r-- | chromium/url/origin_unittest.cc | 503 | ||||
-rw-r--r-- | chromium/url/scheme_host_port.cc | 44 | ||||
-rw-r--r-- | chromium/url/scheme_host_port.h | 24 | ||||
-rw-r--r-- | chromium/url/scheme_host_port_unittest.cc | 14 | ||||
-rw-r--r-- | chromium/url/url_util.cc | 11 | ||||
-rw-r--r-- | chromium/url/url_util.h | 11 |
15 files changed, 886 insertions, 305 deletions
diff --git a/chromium/url/BUILD.gn b/chromium/url/BUILD.gn index 1365fcea0c4..605ee82cad3 100644 --- a/chromium/url/BUILD.gn +++ b/chromium/url/BUILD.gn @@ -66,6 +66,7 @@ component("url") { deps = [ "//base", "//base/third_party/dynamic_annotations", + "//ipc:param_traits", ] if (is_win) { diff --git a/chromium/url/android/java/src/org/chromium/url/IDNStringUtil.java b/chromium/url/android/java/src/org/chromium/url/IDNStringUtil.java new file mode 100644 index 00000000000..37d77dc05bd --- /dev/null +++ b/chromium/url/android/java/src/org/chromium/url/IDNStringUtil.java @@ -0,0 +1,33 @@ +// Copyright 2014 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. + +package org.chromium.url; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +import java.net.IDN; + +/** + * This class is used to convert unicode IDN domain names to ASCII, when not + * building with ICU. + */ +@JNINamespace("url::android") +public class IDNStringUtil { + /** + * Attempts to convert a Unicode string to an ASCII string using IDN rules. + * As of May 2014, the underlying Java function IDNA2003. + * @param src String to convert. + * @return: String containing only ASCII characters on success, null on + * failure. + */ + @CalledByNative + private static String idnToASCII(String src) { + try { + return IDN.toASCII(src, IDN.USE_STD3_ASCII_RULES); + } catch (Exception e) { + return null; + } + } +}
\ No newline at end of file diff --git a/chromium/url/gurl_unittest.cc b/chromium/url/gurl_unittest.cc index eefa73650a7..d8c87c4fb22 100644 --- a/chromium/url/gurl_unittest.cc +++ b/chromium/url/gurl_unittest.cc @@ -329,14 +329,19 @@ TEST(GURLTest, GetOrigin) { const char* input; const char* expected; } cases[] = { - {"http://www.google.com", "http://www.google.com/"}, - {"javascript:window.alert(\"hello,world\");", ""}, - {"http://user:pass@www.google.com:21/blah#baz", "http://www.google.com:21/"}, - {"http://user@www.google.com", "http://www.google.com/"}, - {"http://:pass@www.google.com", "http://www.google.com/"}, - {"http://:@www.google.com", "http://www.google.com/"}, - {"filesystem:http://www.google.com/temp/foo?q#b", "http://www.google.com/"}, - {"filesystem:http://user:pass@google.com:21/blah#baz", "http://google.com:21/"}, + {"http://www.google.com", "http://www.google.com/"}, + {"javascript:window.alert(\"hello,world\");", ""}, + {"http://user:pass@www.google.com:21/blah#baz", + "http://www.google.com:21/"}, + {"http://user@www.google.com", "http://www.google.com/"}, + {"http://:pass@www.google.com", "http://www.google.com/"}, + {"http://:@www.google.com", "http://www.google.com/"}, + {"filesystem:http://www.google.com/temp/foo?q#b", + "http://www.google.com/"}, + {"filesystem:http://user:pass@google.com:21/blah#baz", + "http://google.com:21/"}, + {"blob:null/guid-goes-here", ""}, + {"blob:http://origin/guid-goes-here", "" /* should be http://origin/ */}, }; for (size_t i = 0; i < arraysize(cases); i++) { GURL url(cases[i].input); diff --git a/chromium/url/mojom/BUILD.gn b/chromium/url/mojom/BUILD.gn index 1f77a2ff202..55796a98742 100644 --- a/chromium/url/mojom/BUILD.gn +++ b/chromium/url/mojom/BUILD.gn @@ -17,6 +17,7 @@ mojom("url_mojom_origin") { public_deps = [ ":url_mojom_gurl", + "//mojo/public/mojom/base", ] check_includes_blink = false diff --git a/chromium/url/mojom/origin.mojom b/chromium/url/mojom/origin.mojom index 884357ba37a..5a9e319e8c8 100644 --- a/chromium/url/mojom/origin.mojom +++ b/chromium/url/mojom/origin.mojom @@ -4,9 +4,15 @@ module url.mojom; +import "mojo/public/mojom/base/unguessable_token.mojom"; + struct Origin { string scheme; string host; uint16 port; - bool unique; + + // When a nonce is provided, this origin is opaque. The scheme/host/port do + // not need to be valid, but if they are, they identify the tuple origin + // from which this opaque origin is derived. + mojo_base.mojom.UnguessableToken? nonce_if_opaque; }; diff --git a/chromium/url/mojom/origin_mojom_traits.h b/chromium/url/mojom/origin_mojom_traits.h index 8c78093f6e3..ac34fe91e96 100644 --- a/chromium/url/mojom/origin_mojom_traits.h +++ b/chromium/url/mojom/origin_mojom_traits.h @@ -6,6 +6,8 @@ #define URL_MOJO_ORIGIN_MOJOM_TRAITS_H_ #include "base/strings/string_piece.h" +#include "base/unguessable_token.h" +#include "mojo/public/cpp/base/unguessable_token_mojom_traits.h" #include "url/mojom/origin.mojom.h" #include "url/origin.h" @@ -13,28 +15,38 @@ namespace mojo { template <> struct StructTraits<url::mojom::OriginDataView, url::Origin> { - static const std::string& scheme(const url::Origin& r) { return r.scheme(); } - static const std::string& host(const url::Origin& r) { return r.host(); } - static uint16_t port(const url::Origin& r) { return r.port(); } - static bool unique(const url::Origin& r) { return r.unique(); } + static const std::string& scheme(const url::Origin& r) { + return r.GetTupleOrPrecursorTupleIfOpaque().scheme(); + } + static const std::string& host(const url::Origin& r) { + return r.GetTupleOrPrecursorTupleIfOpaque().host(); + } + static uint16_t port(const url::Origin& r) { + return r.GetTupleOrPrecursorTupleIfOpaque().port(); + } + static const base::Optional<base::UnguessableToken> nonce_if_opaque( + const url::Origin& r) { + // TODO(nasko): Consider returning a const reference here. + return r.GetNonceForSerialization(); + } static bool Read(url::mojom::OriginDataView data, url::Origin* out) { - if (data.unique()) { - *out = url::Origin(); - } else { - base::StringPiece scheme, host; - if (!data.ReadScheme(&scheme) || !data.ReadHost(&host)) - return false; - - *out = url::Origin::UnsafelyCreateOriginWithoutNormalization(scheme, host, - data.port()); - } - - // If a unique origin was created, but the unique flag wasn't set, then - // the values provided to 'UnsafelyCreateOriginWithoutNormalization' were - // invalid. - if (!data.unique() && out->unique()) + base::StringPiece scheme, host; + base::Optional<base::UnguessableToken> nonce_if_opaque; + if (!data.ReadScheme(&scheme) || !data.ReadHost(&host) || + !data.ReadNonceIfOpaque(&nonce_if_opaque)) + return false; + + base::Optional<url::Origin> creation_result = + nonce_if_opaque + ? url::Origin::UnsafelyCreateOpaqueOriginWithoutNormalization( + scheme, host, data.port(), + url::Origin::Nonce(*nonce_if_opaque)) + : url::Origin::UnsafelyCreateTupleOriginWithoutNormalization( + scheme, host, data.port()); + if (!creation_result) return false; + *out = std::move(creation_result.value()); return true; } }; diff --git a/chromium/url/mojom/url_gurl_mojom_traits_unittest.cc b/chromium/url/mojom/url_gurl_mojom_traits_unittest.cc index e11d063ca1c..a331ba2622b 100644 --- a/chromium/url/mojom/url_gurl_mojom_traits_unittest.cc +++ b/chromium/url/mojom/url_gurl_mojom_traits_unittest.cc @@ -72,22 +72,35 @@ TEST(MojoGURLStructTraitsTest, Basic) { } // Test basic Origin serialization. - Origin non_unique = Origin::UnsafelyCreateOriginWithoutNormalization( - "http", "www.google.com", 80); + Origin non_unique = Origin::UnsafelyCreateTupleOriginWithoutNormalization( + "http", "www.google.com", 80) + .value(); Origin output; EXPECT_TRUE(proxy->BounceOrigin(non_unique, &output)); EXPECT_EQ(non_unique, output); - EXPECT_FALSE(output.unique()); - - Origin unique; - EXPECT_TRUE(proxy->BounceOrigin(unique, &output)); - EXPECT_TRUE(output.unique()); + EXPECT_FALSE(output.opaque()); + + Origin unique1; + Origin unique2 = non_unique.DeriveNewOpaqueOrigin(); + EXPECT_NE(unique1, unique2); + EXPECT_NE(unique2, unique1); + EXPECT_NE(unique2, non_unique); + EXPECT_TRUE(proxy->BounceOrigin(unique1, &output)); + EXPECT_TRUE(output.opaque()); + EXPECT_EQ(unique1, output); + Origin output2; + EXPECT_TRUE(proxy->BounceOrigin(unique2, &output2)); + EXPECT_EQ(unique2, output2); + EXPECT_NE(unique2, output); + EXPECT_NE(unique1, output2); Origin normalized = Origin::CreateFromNormalizedTuple("http", "www.google.com", 80); + EXPECT_EQ(normalized, non_unique); EXPECT_TRUE(proxy->BounceOrigin(normalized, &output)); EXPECT_EQ(normalized, output); - EXPECT_FALSE(output.unique()); + EXPECT_EQ(non_unique, output); + EXPECT_FALSE(output.opaque()); } } // namespace url diff --git a/chromium/url/origin.cc b/chromium/url/origin.cc index f8c664c932f..f6403f58b6d 100644 --- a/chromium/url/origin.cc +++ b/chromium/url/origin.cc @@ -5,10 +5,13 @@ #include "url/origin.h" #include <stdint.h> -#include <string.h> + +#include <algorithm> #include "base/logging.h" +#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "url/gurl.h" #include "url/url_canon.h" #include "url/url_canon_stdstring.h" @@ -17,10 +20,10 @@ namespace url { -Origin::Origin() {} +Origin::Origin() : nonce_(Nonce()) {} Origin Origin::Create(const GURL& url) { - if (!url.is_valid() || (!url.IsStandard() && !url.SchemeIsBlob())) + if (!url.is_valid()) return Origin(); SchemeHostPort tuple; @@ -35,59 +38,67 @@ Origin Origin::Create(const GURL& url) { tuple = SchemeHostPort(GURL(url.GetContent())); } else { tuple = SchemeHostPort(url); + + // It's SchemeHostPort's responsibility to filter out unrecognized schemes; + // sanity check that this is happening. + DCHECK(tuple.IsInvalid() || url.IsStandard() || + base::ContainsValue(GetLocalSchemes(), url.scheme_piece()) || + AllowNonStandardSchemesForAndroidWebView()); } if (tuple.IsInvalid()) return Origin(); - return Origin(std::move(tuple)); } -// Note: this is very similar to Create(const GURL&), but opaque origins are -// created with CreateUniqueOpaque() rather than the default constructor. -Origin Origin::CreateCanonical(const GURL& url) { - if (!url.is_valid() || (!url.IsStandard() && !url.SchemeIsBlob())) - return CreateUniqueOpaque(); - - SchemeHostPort tuple; - - if (url.SchemeIsFileSystem()) { - tuple = SchemeHostPort(*url.inner_url()); - } else if (url.SchemeIsBlob()) { - // If we're dealing with a 'blob:' URL, https://url.spec.whatwg.org/#origin - // defines the origin as the origin of the URL which results from parsing - // the "path", which boils down to everything after the scheme. GURL's - // 'GetContent()' gives us exactly that. - tuple = SchemeHostPort(GURL(url.GetContent())); - } else { - tuple = SchemeHostPort(url); - } - - if (tuple.IsInvalid()) - return CreateUniqueOpaque(); - - return Origin(std::move(tuple)); +Origin Origin::Resolve(const GURL& url, const Origin& base_origin) { + if (url.IsAboutBlank()) + return base_origin; + Origin result = Origin::Create(url); + if (!result.opaque()) + return result; + return base_origin.DeriveNewOpaqueOrigin(); } Origin::Origin(const Origin& other) = default; Origin& Origin::operator=(const Origin& other) = default; Origin::Origin(Origin&& other) = default; Origin& Origin::operator=(Origin&& other) = default; - Origin::~Origin() = default; // static -Origin Origin::UnsafelyCreateOriginWithoutNormalization( +base::Optional<Origin> Origin::UnsafelyCreateTupleOriginWithoutNormalization( base::StringPiece scheme, base::StringPiece host, uint16_t port) { SchemeHostPort tuple(scheme.as_string(), host.as_string(), port, SchemeHostPort::CHECK_CANONICALIZATION); if (tuple.IsInvalid()) - return Origin(); + return base::nullopt; return Origin(std::move(tuple)); } +// static +base::Optional<Origin> Origin::UnsafelyCreateOpaqueOriginWithoutNormalization( + base::StringPiece precursor_scheme, + base::StringPiece precursor_host, + uint16_t precursor_port, + const Origin::Nonce& nonce) { + SchemeHostPort precursor(precursor_scheme.as_string(), + precursor_host.as_string(), precursor_port, + SchemeHostPort::CHECK_CANONICALIZATION); + // For opaque origins, it is okay for the SchemeHostPort to be invalid; + // however, this should only arise when the arguments indicate the + // canonical representation of the invalid SchemeHostPort. + if (precursor.IsInvalid() && + !(precursor_scheme.empty() && precursor_host.empty() && + precursor_port == 0)) { + return base::nullopt; + } + return Origin(std::move(nonce), std::move(precursor)); +} + +// static Origin Origin::CreateFromNormalizedTuple(std::string scheme, std::string host, uint16_t port) { @@ -98,8 +109,21 @@ Origin Origin::CreateFromNormalizedTuple(std::string scheme, return Origin(std::move(tuple)); } +// static +Origin Origin::CreateOpaqueFromNormalizedPrecursorTuple( + std::string precursor_scheme, + std::string precursor_host, + uint16_t precursor_port, + const Origin::Nonce& nonce) { + SchemeHostPort precursor(std::move(precursor_scheme), + std::move(precursor_host), precursor_port, + SchemeHostPort::ALREADY_CANONICALIZED); + // For opaque origins, it is okay for the SchemeHostPort to be invalid. + return Origin(std::move(nonce), std::move(precursor)); +} + std::string Origin::Serialize() const { - if (unique()) + if (opaque()) return "null"; if (scheme() == kFileScheme) @@ -109,7 +133,7 @@ std::string Origin::Serialize() const { } GURL Origin::GetURL() const { - if (unique()) + if (opaque()) return GURL(); if (scheme() == kFileScheme) @@ -118,35 +142,130 @@ GURL Origin::GetURL() const { return tuple_.GetURL(); } +base::Optional<base::UnguessableToken> Origin::GetNonceForSerialization() + const { + // TODO(nasko): Consider not making a copy here, but return a reference to + // the nonce. + return nonce_ ? base::make_optional(nonce_->token()) : base::nullopt; +} + bool Origin::IsSameOriginWith(const Origin& other) const { - return tuple_.Equals(other.tuple_) && - (!unique() || (nonce_ && nonce_ == other.nonce_)); + // scheme/host/port must match, even for opaque origins where |tuple_| holds + // the precursor origin. + return std::tie(tuple_, nonce_) == std::tie(other.tuple_, other.nonce_); } bool Origin::DomainIs(base::StringPiece canonical_domain) const { - return !unique() && url::DomainIs(tuple_.host(), canonical_domain); + return !opaque() && url::DomainIs(tuple_.host(), canonical_domain); } bool Origin::operator<(const Origin& other) const { return std::tie(tuple_, nonce_) < std::tie(other.tuple_, other.nonce_); } -Origin Origin::CreateUniqueOpaque() { - return Origin(ConstructAsOpaque::kTag); +Origin Origin::DeriveNewOpaqueOrigin() const { + return Origin(Nonce(), tuple_); } -Origin::Origin(ConstructAsOpaque) : nonce_(base::UnguessableToken::Create()) {} - Origin::Origin(SchemeHostPort tuple) : tuple_(std::move(tuple)) { + DCHECK(!opaque()); DCHECK(!tuple_.IsInvalid()); } +// Constructs an opaque origin derived from |precursor|. +Origin::Origin(const Nonce& nonce, SchemeHostPort precursor) + : tuple_(std::move(precursor)), nonce_(std::move(nonce)) { + DCHECK(opaque()); + // |precursor| is retained, but not accessible via scheme()/host()/port(). + DCHECK_EQ("", scheme()); + DCHECK_EQ("", host()); + DCHECK_EQ(0U, port()); +} + std::ostream& operator<<(std::ostream& out, const url::Origin& origin) { - return out << origin.Serialize(); + out << origin.Serialize(); + + if (origin.opaque()) { + // For opaque origins, log the nonce and precursor as well. Without this, + // EXPECT_EQ failures between opaque origins are nearly impossible to + // understand. + out << " [internally: " << *origin.nonce_; + if (origin.tuple_.IsInvalid()) + out << " anonymous"; + else + out << " derived from " << origin.tuple_; + out << "]"; + } else if (origin.scheme() == kFileScheme) { + out << " [internally: " << origin.tuple_ << "]"; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, const url::Origin::Nonce& nonce) { + // Subtle: don't let logging trigger lazy-generation of the token value. + if (nonce.raw_token().is_empty()) + return (out << "(nonce TBD)"); + else + return (out << nonce.raw_token()); } bool IsSameOriginWith(const GURL& a, const GURL& b) { return Origin::Create(a).IsSameOriginWith(Origin::Create(b)); } +Origin::Nonce::Nonce() {} +Origin::Nonce::Nonce(const base::UnguessableToken& token) : token_(token) { + CHECK(!token_.is_empty()); +} + +const base::UnguessableToken& Origin::Nonce::token() const { + // Inspecting the value of a nonce triggers lazy-generation. + // TODO(dcheng): UnguessableToken::is_empty should go away -- what sentinel + // value to use instead? + if (token_.is_empty()) + token_ = base::UnguessableToken::Create(); + return token_; +} + +const base::UnguessableToken& Origin::Nonce::raw_token() const { + return token_; +} + +// Copying a Nonce triggers lazy-generation of the token. +Origin::Nonce::Nonce(const Origin::Nonce& other) : token_(other.token()) {} + +Origin::Nonce& Origin::Nonce::operator=(const Origin::Nonce& other) { + // Copying a Nonce triggers lazy-generation of the token. + token_ = other.token(); + return *this; +} + +// Moving a nonce does NOT trigger lazy-generation of the token. +Origin::Nonce::Nonce(Origin::Nonce&& other) : token_(other.token_) { + other.token_ = base::UnguessableToken(); // Reset |other|. +} + +Origin::Nonce& Origin::Nonce::operator=(Origin::Nonce&& other) { + token_ = other.token_; + other.token_ = base::UnguessableToken(); // Reset |other|. + return *this; +} + +bool Origin::Nonce::operator<(const Origin::Nonce& other) const { + // When comparing, lazy-generation is required of both tokens, so that an + // ordering is established. + return token() < other.token(); +} + +bool Origin::Nonce::operator==(const Origin::Nonce& other) const { + // Equality testing doesn't actually require that the tokens be generated. + // If the tokens are both zero, equality only holds if they're the same + // object. + return (other.token_ == token_) && !(token_.is_empty() && (&other != this)); +} + +bool Origin::Nonce::operator!=(const Origin::Nonce& other) const { + return !(*this == other); +} + } // namespace url diff --git a/chromium/url/origin.h b/chromium/url/origin.h index 75807359d77..894cb0bf4dd 100644 --- a/chromium/url/origin.h +++ b/chromium/url/origin.h @@ -15,6 +15,7 @@ #include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/unguessable_token.h" +#include "ipc/ipc_param_traits.h" #include "url/scheme_host_port.h" #include "url/third_party/mozilla/url_parse.h" #include "url/url_canon.h" @@ -23,12 +24,32 @@ class GURL; +namespace blink { +class SecurityOrigin; +} // namespace blink + +namespace ipc_fuzzer { +template <class T> +struct FuzzTraits; +} // namespace ipc_fuzzer + +namespace mojo { +template <typename DataViewType, typename T> +struct StructTraits; +struct UrlOriginAdapter; +} // namespace mojo + namespace url { +namespace mojom { +class OriginDataView; +} // namespace mojom + // Per https://html.spec.whatwg.org/multipage/origin.html#origin, an origin is // either: // - a tuple origin of (scheme, host, port) as described in RFC 6454. -// - an opaque origin with an internal value +// - an opaque origin with an internal value, and a memory of the tuple origin +// from which it was derived. // // TL;DR: If you need to make a security-relevant decision, use 'url::Origin'. // If you only need to extract the bits of a URL which are relevant for a @@ -47,14 +68,13 @@ namespace url { // between contexts. Two tuple origins are same-origin if the tuples are equal. // A tuple origin may also be re-created from its serialization. // -// An opaque origin is cross-origin to any origin, including itself and copies -// of itself. Unlike tuple origins, an opaque origin cannot be re-created from -// its serialization, which is always the string "null". -// -// TODO(https://crbug.com/768460): work is in progress to associate an internal -// globally unique identifier with an opaque origin: completing this work will -// allow a copy of an opaque origin to be same-origin to the original instance -// of that opaque origin. +// An opaque origin has an internal globally unique identifier. When creating a +// new opaque origin from a URL, a fresh globally unique identifier is +// generated. However, if an opaque origin is copied or moved, the internal +// globally unique identifier is preserved. Two opaque origins are same-origin +// iff the globally unique identifiers match. Unlike tuple origins, an opaque +// origin cannot be re-created from its serialization, which is always the +// string "null". // // IMPORTANT: Since opaque origins always serialize as the string "null", it is // *never* safe to use the serialization for security checks! @@ -63,9 +83,7 @@ namespace url { // // There are a few subtleties to note: // -// * A default constructed Origin is opaque, but unlike the spec definition, has -// no associated identifier. A default constructed Origin is cross-origin to -// every other Origin object. +// * A default constructed Origin is opaque, with no precursor origin. // // * Invalid and non-standard GURLs are parsed as opaque origins. This includes // non-hierarchical URLs like 'data:text/html,...' and 'javascript:alert(1)'. @@ -91,7 +109,7 @@ namespace url { // origin.scheme(); // "https" // origin.host(); // "example.com" // origin.port(); // 443 -// origin.unique(); // false +// origin.opaque(); // false // // * To answer the question "Are |this| and |that| "same-origin" with each // other?", use |Origin::IsSameOriginWith|: @@ -101,8 +119,8 @@ namespace url { // } class URL_EXPORT Origin { public: - // Creates an opaque and always unique Origin. The returned Origin is - // always cross-origin to any Origin, including itself. + // Creates an opaque Origin with a nonce that is different from all previously + // existing origins. Origin(); // Creates an Origin from |url|, as described at @@ -112,11 +130,21 @@ class URL_EXPORT Origin { // 2. 'filesystem' URLs behave as 'blob' URLs (that is, the origin is parsed // out of everything in the URL which follows the scheme). // 3. 'file' URLs all parse as ("file", "", 0). - // - // If this method returns an opaque Origin, the returned Origin will be - // cross-origin to any Origin, including itself. static Origin Create(const GURL& url); + // Creates an Origin for the resource |url| as if it were requested + // from the context of |base_origin|. If |url| is standard + // (in the sense that it embeds a complete origin, like http/https), + // this returns the same value as would Create(). + // + // If |url| is "about:blank", this returns a copy of |base_origin|. + // + // Otherwise, returns a new opaque origin derived from |base_origin|. + // In this case, the resulting opaque origin will inherit the tuple + // (or precursor tuple) of |base_origin|, but will not be same origin + // with |base_origin|, even if |base_origin| is already opaque. + static Origin Resolve(const GURL& url, const Origin& base_origin); + // Copyable and movable. Origin(const Origin&); Origin& operator=(const Origin&); @@ -124,14 +152,14 @@ class URL_EXPORT Origin { Origin& operator=(Origin&&); // Creates an Origin from a |scheme|, |host|, and |port|. All the parameters - // must be valid and canonicalized. Do not use this method to create opaque - // origins. Use Origin() or Origin::CreateOpaque() for that. + // must be valid and canonicalized. Returns nullopt if any parameter is not + // canonical, or if all the parameters are empty. // // This constructor should be used in order to pass 'Origin' objects back and // forth over IPC (as transitioning through GURL would risk potentially // dangerous recanonicalization); other potential callers should prefer the // 'GURL'-based constructor. - static Origin UnsafelyCreateOriginWithoutNormalization( + static base::Optional<Origin> UnsafelyCreateTupleOriginWithoutNormalization( base::StringPiece scheme, base::StringPiece host, uint16_t port); @@ -148,25 +176,48 @@ class URL_EXPORT Origin { // For opaque origins, these return ("", "", 0). const std::string& scheme() const { - return !unique() ? tuple_.scheme() : base::EmptyString(); + return !opaque() ? tuple_.scheme() : base::EmptyString(); } const std::string& host() const { - return !unique() ? tuple_.host() : base::EmptyString(); + return !opaque() ? tuple_.host() : base::EmptyString(); } - uint16_t port() const { return !unique() ? tuple_.port() : 0; } + uint16_t port() const { return !opaque() ? tuple_.port() : 0; } - // TODO(dcheng): Rename this to opaque(). - bool unique() const { return tuple_.IsInvalid(); } + bool opaque() const { return nonce_.has_value(); } // An ASCII serialization of the Origin as per Section 6.2 of RFC 6454, with // the addition that all Origins with a 'file' scheme serialize to "file://". std::string Serialize() const; - // Two Origins are "same-origin" if their schemes, hosts, and ports are exact - // matches; and neither is unique. + // Two non-opaque Origins are "same-origin" if their schemes, hosts, and ports + // are exact matches. Two opaque origins are same-origin only if their + // internal nonce values match. A non-opaque origin is never same-origin with + // an opaque origin. bool IsSameOriginWith(const Origin& other) const; - bool operator==(const Origin& other) const { - return IsSameOriginWith(other); + bool operator==(const Origin& other) const { return IsSameOriginWith(other); } + bool operator!=(const Origin& other) const { + return !IsSameOriginWith(other); + } + + // Get the scheme, host, and port from which this origin derives. For + // a tuple Origin, this gives the same values as calling scheme(), host() + // and port(). For an opaque Origin that was created by calling + // Origin::DeriveNewOpaqueOrigin() on a precursor or Origin::Resolve(), + // this returns the tuple inherited from the precursor. + // + // If this Origin is opaque and was created via the default constructor or + // Origin::Create(), the precursor origin is unknown. + // + // Use with great caution: opaque origins should generally not inherit + // privileges from the origins they derive from. However, in some cases + // (such as restrictions on process placement, or determining the http lock + // icon) this information may be relevant to ensure that entering an + // opaque origin does not grant privileges initially denied to the original + // non-opaque origin. + // + // This method has a deliberately obnoxious name to prompt caution in its use. + const SchemeHostPort& GetTupleOrPrecursorTupleIfOpaque() const { + return tuple_; } // Efficiently returns what GURL(Serialize()) would without re-parsing the @@ -181,23 +232,21 @@ class URL_EXPORT Origin { // URL (e.g. with a path component). GURL GetURL() const; - // Same as GURL::DomainIs. If |this| origin is unique, then returns false. + // Same as GURL::DomainIs. If |this| origin is opaque, then returns false. bool DomainIs(base::StringPiece canonical_domain) const; // Allows Origin to be used as a key in STL (for example, a std::set or // std::map). bool operator<(const Origin& other) const; - private: - friend class OriginTest; - // Creates a new opaque origin that is guaranteed to be cross-origin to all // currently existing origins. An origin created by this method retains its // identity across copies. Copies are guaranteed to be same-origin to each // other, e.g. // - // url::Origin a = Origin::CreateUniqueOpaque(); - // url::Origin b = Origin::CreateUniqueOpaque(); + // url::Origin page = Origin::Create(GURL("http://example.com")) + // url::Origin a = page.DeriveNewOpaqueOrigin(); + // url::Origin b = page.DeriveNewOpaqueOrigin(); // url::Origin c = a; // url::Origin d = b; // @@ -206,35 +255,120 @@ class URL_EXPORT Origin { // of origins are considered cross-origin, e.g. |a| is cross-origin to |b| and // |d|, |b| is cross-origin to |a| and |c|, |c| is cross-origin to |b| and // |d|, and |d| is cross-origin to |a| and |c|. + Origin DeriveNewOpaqueOrigin() const; + + private: + friend class blink::SecurityOrigin; + friend class OriginTest; + friend struct mojo::UrlOriginAdapter; + friend struct ipc_fuzzer::FuzzTraits<Origin>; + friend struct mojo::StructTraits<url::mojom::OriginDataView, url::Origin>; + friend IPC::ParamTraits<url::Origin>; + friend URL_EXPORT std::ostream& operator<<(std::ostream& out, + const Origin& origin); + + // Origin::Nonce is a wrapper around base::UnguessableToken that generates + // the random value only when the value is first accessed. The lazy generation + // allows Origin to be default-constructed quickly, without spending time + // in random number generation. // - // Note that this is private internal helper, since relatively few locations - // should be responsible for deriving a canonical origin from a GURL. - static Origin CreateUniqueOpaque(); + // TODO(nick): Should this optimization move into UnguessableToken, once it no + // longer treats the Null case specially? + class URL_EXPORT Nonce { + public: + // Creates a nonce to hold a newly-generated UnguessableToken. The actual + // token value will be generated lazily. + Nonce(); + + // Creates a nonce to hold an already-generated UnguessableToken value. This + // constructor should only be used for IPC serialization and testing -- + // regular code should never need to touch the UnguessableTokens directly, + // and the default constructor is faster. + explicit Nonce(const base::UnguessableToken& token); + + // Accessor, which lazily initializes the underlying |token_| member. + const base::UnguessableToken& token() const; + + // Do not use in cases where lazy initialization is expected! This + // accessor does not initialize the |token_| member. + const base::UnguessableToken& raw_token() const; + + // Copyable and movable. Copying a Nonce triggers lazy-initialization, + // moving it does not. + Nonce(const Nonce&); + Nonce& operator=(const Nonce&); + Nonce(Nonce&&); + Nonce& operator=(Nonce&&); + + // Note that operator<, used by maps type containers, will trigger |token_| + // lazy-initialization. Equality comparisons do not. + bool operator<(const Nonce& other) const; + bool operator==(const Nonce& other) const; + bool operator!=(const Nonce& other) const; + + private: + friend class OriginTest; - // Similar to Create(const GURL&). However, if the returned Origin is an - // opaque origin, it will be created with CreateUniqueOpaque(), have an - // associated identity, and be considered same-origin to copies of itself. - static Origin CreateCanonical(const GURL&); + // mutable to support lazy generation. + mutable base::UnguessableToken token_; + }; - enum class ConstructAsOpaque { kTag }; - explicit Origin(ConstructAsOpaque); + // This needs to be friended within Origin as well, since Nonce is a private + // nested class of Origin. + friend URL_EXPORT std::ostream& operator<<(std::ostream& out, + const Nonce& nonce); - // |tuple| must be valid, implying that the created Origin is never an opaque - // origin. + // Creates an origin without sanity checking that the host is canonicalized. + // This should only be used when converting between already normalized types, + // and should NOT be used for IPC. Method takes std::strings for use with move + // operators to avoid copies. + static Origin CreateOpaqueFromNormalizedPrecursorTuple( + std::string precursor_scheme, + std::string precursor_host, + uint16_t precursor_port, + const Nonce& nonce); + + // Creates an opaque Origin with the identity given by |nonce|, and an + // optional precursor origin given by |precursor_scheme|, |precursor_host| and + // |precursor_port|. Returns nullopt if any parameter is not canonical. When + // the precursor is unknown, the precursor parameters should be ("", "", 0). + // + // This factory method should be used in order to pass opaque Origin objects + // back and forth over IPC (as transitioning through GURL would risk + // potentially dangerous recanonicalization). + static base::Optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization( + base::StringPiece precursor_scheme, + base::StringPiece precursor_host, + uint16_t precursor_port, + const Nonce& nonce); + + // Constructs a non-opaque tuple origin. |tuple| must be valid. explicit Origin(SchemeHostPort tuple); - // Helpers for managing union for destroy, copy, and move. - // The tuple is used for tuple origins (e.g. https://example.com:80). This - // is expected to be the common case. |IsInvalid()| will be true for opaque - // origins. + // Constructs an opaque origin derived from the |precursor| tuple, with the + // given |nonce|. + Origin(const Nonce& nonce, SchemeHostPort precursor); + + // Get the nonce associated with this origin, if it is opaque. This should be + // used only when trying to send an Origin across an IPC pipe. + base::Optional<base::UnguessableToken> GetNonceForSerialization() const; + + // The tuple is used for both tuple origins (e.g. https://example.com:80), as + // well as for opaque origins, where it tracks the tuple origin from which + // the opaque origin was initially derived (we call this the "precursor" + // origin). SchemeHostPort tuple_; // The nonce is used for maintaining identity of an opaque origin. This - // nonce is preserved when an opaque origin is copied or moved. - base::Optional<base::UnguessableToken> nonce_; + // nonce is preserved when an opaque origin is copied or moved. An Origin + // is considered opaque if and only if |nonce_| holds a value. + base::Optional<Nonce> nonce_; }; +// Pretty-printers for logging. These expose the internal state of the nonce. URL_EXPORT std::ostream& operator<<(std::ostream& out, const Origin& origin); +URL_EXPORT std::ostream& operator<<(std::ostream& out, + const Origin::Nonce& origin); URL_EXPORT bool IsSameOriginWith(const GURL& a, const GURL& b); diff --git a/chromium/url/origin_unittest.cc b/chromium/url/origin_unittest.cc index bfa095ac27a..5f889c09a14 100644 --- a/chromium/url/origin_unittest.cc +++ b/chromium/url/origin_unittest.cc @@ -10,6 +10,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/origin.h" +#include "url/url_util.h" namespace url { @@ -36,95 +37,207 @@ void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) { } class OriginTest : public ::testing::Test { - protected: - Origin CreateUniqueOpaque() { return Origin::CreateUniqueOpaque(); } + public: + void SetUp() override { + // Add two schemes which are local but nonstandard. + AddLocalScheme("local-but-nonstandard"); + AddLocalScheme("also-local-but-nonstandard"); + + // Add a scheme that's both local and standard. + AddStandardScheme("local-and-standard", SchemeType::SCHEME_WITH_HOST); + AddLocalScheme("local-and-standard"); + + // Add a scheme that's standard but no-access. We still want these to + // form valid SchemeHostPorts, even though they always commit as opaque + // origins, so that they can represent the source of the resource even if + // it's not committable as a non-opaque origin. + AddStandardScheme("standard-but-noaccess", SchemeType::SCHEME_WITH_HOST); + AddNoAccessScheme("standard-but-noaccess"); + } + void TearDown() override { url::Shutdown(); } + + ::testing::AssertionResult DoEqualityComparisons(const url::Origin& a, + const url::Origin& b, + bool should_compare_equal) { + ::testing::AssertionResult failure = ::testing::AssertionFailure(); + failure << "DoEqualityComparisons failure. Expecting " + << (should_compare_equal ? "equality" : "inequality") + << " between:\n a\n Which is: " << a + << "\n b\n Which is: " << b << "\nThe following check failed: "; + if (a.IsSameOriginWith(b) != should_compare_equal) + return failure << "a.IsSameOriginWith(b)"; + if (b.IsSameOriginWith(a) != should_compare_equal) + return failure << "b.IsSameOriginWith(a)"; + if ((a == b) != should_compare_equal) + return failure << "(a == b)"; + if ((b == a) != should_compare_equal) + return failure << "(b == a)"; + if ((b != a) != !should_compare_equal) + return failure << "(b != a)"; + if ((a != b) != !should_compare_equal) + return failure << "(a != b)"; + return ::testing::AssertionSuccess(); + } + + bool HasNonceTokenBeenInitialized(const url::Origin& origin) { + EXPECT_TRUE(origin.opaque()); + // Avoid calling nonce_.token() here, to not trigger lazy initialization. + return !origin.nonce_->token_.is_empty(); + } + + Origin::Nonce CreateNonce() { return Origin::Nonce(); } + + Origin::Nonce CreateNonce(base::UnguessableToken nonce) { + return Origin::Nonce(nonce); + } - Origin CreateCanonical(const GURL& url) { - return Origin::CreateCanonical(url); + base::Optional<base::UnguessableToken> GetNonce(const Origin& origin) { + return origin.GetNonceForSerialization(); + } + + // Wrapper around url::Origin method to expose it to tests. + base::Optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization( + base::StringPiece precursor_scheme, + base::StringPiece precursor_host, + uint16_t precursor_port, + const Origin::Nonce& nonce) { + return Origin::UnsafelyCreateOpaqueOriginWithoutNormalization( + precursor_scheme, precursor_host, precursor_port, nonce); } }; TEST_F(OriginTest, OpaqueOriginComparison) { - // A default constructed Origin should be cross origin to everything, - // including itself. - Origin unique_origin; - EXPECT_EQ("", unique_origin.scheme()); - EXPECT_EQ("", unique_origin.host()); - EXPECT_EQ(0, unique_origin.port()); - EXPECT_TRUE(unique_origin.unique()); - EXPECT_FALSE(unique_origin.IsSameOriginWith(unique_origin)); - - // An opaque Origin with a nonce should be same origin to itself though. - Origin opaque_origin = CreateUniqueOpaque(); - EXPECT_EQ("", opaque_origin.scheme()); - EXPECT_EQ("", opaque_origin.host()); - EXPECT_EQ(0, opaque_origin.port()); - EXPECT_TRUE(opaque_origin.unique()); - EXPECT_TRUE(opaque_origin.IsSameOriginWith(opaque_origin)); - - // The default constructed Origin and the opaque Origin should always be - // cross origin to each other. - EXPECT_FALSE(opaque_origin.IsSameOriginWith(unique_origin)); - - const char* const urls[] = {"data:text/html,Hello!", - "javascript:alert(1)", - "about:blank", - "file://example.com:443/etc/passwd", - "yay", - "http::///invalid.example.com/"}; + // A default-constructed Origin should should be cross origin to everything + // but itself. + url::Origin opaque_a, opaque_b; + EXPECT_TRUE(opaque_a.opaque()); + EXPECT_EQ("", opaque_a.scheme()); + EXPECT_EQ("", opaque_a.host()); + EXPECT_EQ(0, opaque_a.port()); + EXPECT_EQ(SchemeHostPort(), opaque_a.GetTupleOrPrecursorTupleIfOpaque()); + EXPECT_TRUE(opaque_a.GetTupleOrPrecursorTupleIfOpaque().IsInvalid()); + + EXPECT_TRUE(opaque_b.opaque()); + EXPECT_EQ("", opaque_b.scheme()); + EXPECT_EQ("", opaque_b.host()); + EXPECT_EQ(0, opaque_b.port()); + EXPECT_EQ(SchemeHostPort(), opaque_b.GetTupleOrPrecursorTupleIfOpaque()); + EXPECT_TRUE(opaque_b.GetTupleOrPrecursorTupleIfOpaque().IsInvalid()); + + // Two default-constructed Origins should always be cross origin to each + // other. + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false)); + EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true)); + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true)); + + // The streaming operator should not trigger lazy initialization to the token. + std::ostringstream stream; + stream << opaque_a; + EXPECT_STREQ("null [internally: (nonce TBD) anonymous]", + stream.str().c_str()); + EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a)); + + // None of the operations thus far should have triggered lazy-generation of + // the UnguessableToken. Copying an origin, however, should trigger this. + EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a)); + EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_b)); + opaque_b = opaque_a; + + EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a)); + EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b)); + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, true)); + EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true)); + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true)); + + // Move-initializing to a fresh Origin should restore the lazy initialization. + opaque_a = url::Origin(); + EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a)); + EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b)); + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false)); + EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true)); + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true)); + + // Comparing two opaque Origins with matching SchemeHostPorts should trigger + // lazy initialization. + EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a)); + EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b)); + bool should_swap = opaque_b < opaque_a; + EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a)); + EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b)); + + if (should_swap) + std::swap(opaque_a, opaque_b); + EXPECT_LT(opaque_a, opaque_b); + EXPECT_FALSE(opaque_b < opaque_a); + + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false)); + EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true)); + EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true)); + + EXPECT_LT(opaque_a, url::Origin::Create(GURL("http://www.google.com"))); + EXPECT_LT(opaque_b, url::Origin::Create(GURL("http://www.google.com"))); + + EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL("about:blank"), opaque_b)); + EXPECT_EQ(opaque_b, + url::Origin::Resolve(GURL("about:blank?hello#whee"), opaque_b)); + + const char* const urls[] = { + "data:text/html,Hello!", + "javascript:alert(1)", + "about:blank", + "file://example.com:443/etc/passwd", + "unknown-scheme:foo", + "unknown-scheme://bar", + "http", + "http:", + "http:/", + "http://", + "http://:", + "http://:1", + "yay", + "http::///invalid.example.com/", + "blob:null/foo", // blob:null (actually a valid URL) + "blob:data:foo", // blob + data (which is nonstandard) + "blob:about://blank/", // blob + about (which is nonstandard) + "blob:about:blank/", // blob + about (which is nonstandard) + "filesystem:http://example.com/", // Invalid (missing /type/) + "filesystem:local-but-nonstandard:baz./type/", // fs requires standard + "filesystem:local-but-nonstandard://hostname/type/", + "filesystem:unknown-scheme://hostname/type/", + "local-but-nonstandar:foo", // Prefix of registered scheme. + "but-nonstandard:foo", // Suffix of registered scheme. + "local-and-standard:", // Standard scheme needs a hostname. + "standard-but-noaccess:", // Standard scheme needs a hostname. + "blob:blob:http://www.example.com/guid-goes-here", // Double blob. + }; for (auto* test_url : urls) { SCOPED_TRACE(test_url); GURL url(test_url); + const url::Origin opaque_origin; - // no nonce mode of opaque origins + // Opaque origins returned by Origin::Create(). { Origin origin = Origin::Create(url); EXPECT_EQ("", origin.scheme()); EXPECT_EQ("", origin.host()); EXPECT_EQ(0, origin.port()); - EXPECT_TRUE(origin.unique()); - // An opaque Origin with no nonce is always cross-origin to itself. - EXPECT_FALSE(origin.IsSameOriginWith(origin)); - // A copy of |origin| should be cross-origin as well. + EXPECT_TRUE(origin.opaque()); + // An origin is always same-origin with itself. + EXPECT_EQ(origin, origin); + EXPECT_NE(origin, url::Origin()); + EXPECT_EQ(SchemeHostPort(), origin.GetTupleOrPrecursorTupleIfOpaque()); + // A copy of |origin| should be same-origin as well. Origin origin_copy = origin; EXPECT_EQ("", origin_copy.scheme()); EXPECT_EQ("", origin_copy.host()); EXPECT_EQ(0, origin_copy.port()); - EXPECT_TRUE(origin_copy.unique()); - EXPECT_FALSE(origin.IsSameOriginWith(origin_copy)); + EXPECT_TRUE(origin_copy.opaque()); + EXPECT_EQ(origin, origin_copy); // And it should always be cross-origin to another opaque Origin. - EXPECT_FALSE(origin.IsSameOriginWith(opaque_origin)); - // As well as the default constructed Origin. - EXPECT_FALSE(origin.IsSameOriginWith(unique_origin)); + EXPECT_NE(origin, opaque_origin); // Re-creating from the URL should also be cross-origin. - EXPECT_FALSE(origin.IsSameOriginWith(Origin::Create(url))); - - ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); - } - - // opaque origins with a nonce - { - Origin origin = CreateCanonical(url); - EXPECT_EQ("", origin.scheme()); - EXPECT_EQ("", origin.host()); - EXPECT_EQ(0, origin.port()); - EXPECT_TRUE(origin.unique()); - // An opaque Origin with a nonce is always same-origin to itself. - EXPECT_TRUE(origin.IsSameOriginWith(origin)); - // A copy of |origin| should be same-origin as well. - Origin origin_copy = origin; - EXPECT_EQ("", origin_copy.scheme()); - EXPECT_EQ("", origin_copy.host()); - EXPECT_EQ(0, origin_copy.port()); - EXPECT_TRUE(origin_copy.unique()); - EXPECT_TRUE(origin.IsSameOriginWith(origin_copy)); - // But it should always be cross origin to another opaque Origin. - EXPECT_FALSE(origin.IsSameOriginWith(opaque_origin)); - // As well as the default constructed Origin. - EXPECT_FALSE(origin.IsSameOriginWith(unique_origin)); - // Re-creating from the URL should also be cross origin. - EXPECT_FALSE(origin.IsSameOriginWith(CreateCanonical(url))); + EXPECT_NE(origin, Origin::Create(url)); ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); } @@ -169,6 +282,9 @@ TEST_F(OriginTest, ConstructFromGURL) { // IP Addresses {"http://192.168.9.1/", "http", "192.168.9.1", 80}, {"http://[2001:db8::1]/", "http", "[2001:db8::1]", 80}, + {"http://1/", "http", "0.0.0.1", 80}, + {"http://1:1/", "http", "0.0.0.1", 1}, + {"http://3232237825/", "http", "192.168.9.1", 80}, // Punycode {"http://☃.net/", "http", "xn--n3h.net", 80}, @@ -189,6 +305,34 @@ TEST_F(OriginTest, ConstructFromGURL) { {"gopher://example.com/", "gopher", "example.com", 70}, {"ws://example.com/", "ws", "example.com", 80}, {"wss://example.com/", "wss", "example.com", 443}, + {"wss://user:pass@example.com/", "wss", "example.com", 443}, + + // Scheme (registered in SetUp()) that's both local and standard. + // TODO: Is it really appropriate to do network-host canonicalization of + // schemes without ports? + {"local-and-standard:20", "local-and-standard", "0.0.0.20", 0}, + {"local-and-standard:20.", "local-and-standard", "0.0.0.20", 0}, + {"local-and-standard:↑↑↓↓←→←→ba.↑↑↓↓←→←→ba.0.bg", "local-and-standard", + "xn--ba-rzuadaibfa.xn--ba-rzuadaibfa.0.bg", 0}, + {"local-and-standard:foo", "local-and-standard", "foo", 0}, + {"local-and-standard://bar:20", "local-and-standard", "bar", 0}, + {"local-and-standard:baz.", "local-and-standard", "baz.", 0}, + {"local-and-standard:baz..", "local-and-standard", "baz..", 0}, + {"local-and-standard:baz..bar", "local-and-standard", "baz..bar", 0}, + {"local-and-standard:baz...", "local-and-standard", "baz...", 0}, + + // Scheme (registered in SetUp()) that's local but nonstandard. These + // always have empty hostnames, but are allowed to be url::Origins. + {"local-but-nonstandard:", "local-but-nonstandard", "", 0}, + {"local-but-nonstandard:foo", "local-but-nonstandard", "", 0}, + {"local-but-nonstandard://bar", "local-but-nonstandard", "", 0}, + {"also-local-but-nonstandard://bar", "also-local-but-nonstandard", "", 0}, + + // Scheme (registered in SetUp()) that's standard but marked as noaccess. + // url::Origin doesn't currently take the noaccess property into account, + // so these aren't expected to result in opaque origins. + {"standard-but-noaccess:foo", "standard-but-noaccess", "foo", 0}, + {"standard-but-noaccess://bar", "standard-but-noaccess", "bar", 0}, // file: URLs {"file:///etc/passwd", "file", "", 0}, @@ -199,12 +343,18 @@ TEST_F(OriginTest, ConstructFromGURL) { {"filesystem:http://example.com:123/type/", "http", "example.com", 123}, {"filesystem:https://example.com/type/", "https", "example.com", 443}, {"filesystem:https://example.com:123/type/", "https", "example.com", 123}, + {"filesystem:local-and-standard:baz./type/", "local-and-standard", "baz.", + 0}, // Blob: {"blob:http://example.com/guid-goes-here", "http", "example.com", 80}, - {"blob:http://example.com:123/guid-goes-here", "http", "example.com", 123}, + {"blob:http://example.com:123/guid-goes-here", "http", "example.com", + 123}, {"blob:https://example.com/guid-goes-here", "https", "example.com", 443}, {"blob:http://u:p@example.com/guid-goes-here", "http", "example.com", 80}, + + // Gopher: + {"gopher://8u.9.Vx6", "gopher", "8u.9.vx6", 70}, }; for (const auto& test_case : cases) { @@ -215,12 +365,37 @@ TEST_F(OriginTest, ConstructFromGURL) { EXPECT_EQ(test_case.expected_scheme, origin.scheme()); EXPECT_EQ(test_case.expected_host, origin.host()); EXPECT_EQ(test_case.expected_port, origin.port()); - EXPECT_FALSE(origin.unique()); - EXPECT_TRUE(origin.IsSameOriginWith(origin)); - EXPECT_FALSE(different_origin.IsSameOriginWith(origin)); - EXPECT_FALSE(origin.IsSameOriginWith(different_origin)); + EXPECT_FALSE(origin.opaque()); + EXPECT_EQ(origin, origin); + EXPECT_NE(different_origin, origin); + EXPECT_NE(origin, different_origin); + EXPECT_EQ(origin, Origin::Resolve(GURL("about:blank"), origin)); + EXPECT_EQ(origin, Origin::Resolve(GURL("about:blank?bar#foo"), origin)); ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); + + url::Origin derived_opaque = + Origin::Resolve(GURL("about:blank?bar#foo"), origin) + .DeriveNewOpaqueOrigin(); + EXPECT_TRUE(derived_opaque.opaque()); + EXPECT_NE(origin, derived_opaque); + EXPECT_FALSE(derived_opaque.GetTupleOrPrecursorTupleIfOpaque().IsInvalid()); + EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(), + derived_opaque.GetTupleOrPrecursorTupleIfOpaque()); + EXPECT_EQ(derived_opaque, derived_opaque); + + url::Origin derived_opaque_via_data_url = + Origin::Resolve(GURL("data:text/html,baz"), origin); + EXPECT_TRUE(derived_opaque_via_data_url.opaque()); + EXPECT_NE(origin, derived_opaque_via_data_url); + EXPECT_FALSE(derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque() + .IsInvalid()); + EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(), + derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque()); + EXPECT_NE(derived_opaque, derived_opaque_via_data_url); + EXPECT_NE(derived_opaque_via_data_url, derived_opaque); + EXPECT_NE(derived_opaque.DeriveNewOpaqueOrigin(), derived_opaque); + EXPECT_EQ(derived_opaque_via_data_url, derived_opaque_via_data_url); } } @@ -228,6 +403,7 @@ TEST_F(OriginTest, Serialization) { struct TestCases { const char* const url; const char* const expected; + const char* const expected_log; } cases[] = { {"http://192.168.9.1/", "http://192.168.9.1"}, {"http://[2001:db8::1]/", "http://[2001:db8::1]"}, @@ -236,8 +412,10 @@ TEST_F(OriginTest, Serialization) { {"http://example.com:123/", "http://example.com:123"}, {"https://example.com/", "https://example.com"}, {"https://example.com:123/", "https://example.com:123"}, - {"file:///etc/passwd", "file://"}, - {"file://example.com/etc/passwd", "file://"}, + {"file:///etc/passwd", "file://", "file:// [internally: file://]"}, + {"file://example.com/etc/passwd", "file://", + "file:// [internally: file://example.com]"}, + {"data:,", "null", "null [internally: (nonce TBD) anonymous]"}, }; for (const auto& test_case : cases) { @@ -250,44 +428,23 @@ TEST_F(OriginTest, Serialization) { EXPECT_EQ(test_case.expected, serialized); - // The '<<' operator should produce the same serialization as Serialize(). + // The '<<' operator sometimes produces additional information. std::stringstream out; out << origin; - EXPECT_EQ(test_case.expected, out.str()); + if (test_case.expected_log) + EXPECT_EQ(test_case.expected_log, out.str()); + else + EXPECT_EQ(test_case.expected, out.str()); } } TEST_F(OriginTest, Comparison) { // These URLs are arranged in increasing order: const char* const urls[] = { - "data:uniqueness", - "http://a:80", - "http://b:80", - "https://a:80", - "https://b:80", - "http://a:81", - "http://b:81", - "https://a:81", - "https://b:81", + "data:uniqueness", "http://a:80", "http://b:80", + "https://a:80", "https://b:80", "http://a:81", + "http://b:81", "https://a:81", "https://b:81", }; - - { - // Unlike below, pre-creation here isn't necessary, since the old creation - // path doesn't populate a nonce. It makes for easier copy and paste though. - std::vector<Origin> origins; - for (const auto* test_url : urls) - origins.push_back(CreateCanonical(GURL(test_url))); - - for (size_t i = 0; i < origins.size(); i++) { - const Origin& current = origins[i]; - for (size_t j = i; j < origins.size(); j++) { - const Origin& to_compare = origins[j]; - EXPECT_EQ(i < j, current < to_compare) << i << " < " << j; - EXPECT_EQ(j < i, to_compare < current) << j << " < " << i; - } - } - } - // Validate the comparison logic still works when creating a canonical origin, // when any created opaque origins contain a nonce. { @@ -295,8 +452,7 @@ TEST_F(OriginTest, Comparison) { // with each freshly-constructed Origin (that's not copied). std::vector<Origin> origins; for (const auto* test_url : urls) - origins.push_back(CreateCanonical(GURL(test_url))); - + origins.push_back(Origin::Create(GURL(test_url))); for (size_t i = 0; i < origins.size(); i++) { const Origin& current = origins[i]; for (size_t j = i; j < origins.size(); j++) { @@ -323,26 +479,44 @@ TEST_F(OriginTest, UnsafelyCreate) { }; for (const auto& test : cases) { - SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":" - << test.port); - Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization( - test.scheme, test.host, test.port); - EXPECT_EQ(test.scheme, origin.scheme()); - EXPECT_EQ(test.host, origin.host()); - EXPECT_EQ(test.port, origin.port()); - EXPECT_FALSE(origin.unique()); - EXPECT_TRUE(origin.IsSameOriginWith(origin)); - - ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); + SCOPED_TRACE(testing::Message() + << test.scheme << "://" << test.host << ":" << test.port); + base::Optional<url::Origin> origin = + url::Origin::UnsafelyCreateTupleOriginWithoutNormalization( + test.scheme, test.host, test.port); + ASSERT_TRUE(origin); + EXPECT_EQ(test.scheme, origin->scheme()); + EXPECT_EQ(test.host, origin->host()); + EXPECT_EQ(test.port, origin->port()); + EXPECT_FALSE(origin->opaque()); + EXPECT_TRUE(origin->IsSameOriginWith(*origin)); + + ExpectParsedUrlsEqual(GURL(origin->Serialize()), origin->GetURL()); + + base::UnguessableToken nonce = base::UnguessableToken::Create(); + base::Optional<url::Origin> opaque_origin = + UnsafelyCreateOpaqueOriginWithoutNormalization( + test.scheme, test.host, test.port, CreateNonce(nonce)); + ASSERT_TRUE(opaque_origin); + EXPECT_TRUE(opaque_origin->opaque()); + EXPECT_FALSE(*opaque_origin == origin); + EXPECT_EQ(opaque_origin->GetTupleOrPrecursorTupleIfOpaque(), + origin->GetTupleOrPrecursorTupleIfOpaque()); + EXPECT_EQ(opaque_origin, + UnsafelyCreateOpaqueOriginWithoutNormalization( + test.scheme, test.host, test.port, CreateNonce(nonce))); + EXPECT_FALSE(*opaque_origin == origin->DeriveNewOpaqueOrigin()); } } TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) { + url::AddStandardScheme("host-only", url::SCHEME_WITH_HOST); + url::AddStandardScheme("host-port-only", url::SCHEME_WITH_HOST_AND_PORT); struct TestCases { const char* scheme; const char* host; uint16_t port = 80; - } cases[] = {{"", "", 0}, + } cases[] = {{"", "", 33}, {"data", "", 0}, {"blob", "", 0}, {"filesystem", "", 0}, @@ -357,50 +531,61 @@ TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) { {"http", "example.com\n"}, {"http", "example.com\r"}, {"http", "example.com", 0}, + {"unknown-scheme", "example.com"}, + {"host-only", "\r", 0}, + {"host-only", "example.com", 22}, + {"host-port-only", "example.com", 0}, {"file", ""}}; for (const auto& test : cases) { - SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":" - << test.port); - Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization( - test.scheme, test.host, test.port); - EXPECT_EQ("", origin.scheme()); - EXPECT_EQ("", origin.host()); - EXPECT_EQ(0, origin.port()); - EXPECT_TRUE(origin.unique()); - EXPECT_FALSE(origin.IsSameOriginWith(origin)); - - ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); + SCOPED_TRACE(testing::Message() + << test.scheme << "://" << test.host << ":" << test.port); + EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization( + test.scheme, test.host, test.port, CreateNonce())); + EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization( + test.scheme, test.host, test.port)); } + + // An empty scheme/host/port tuple is not a valid tuple origin. + EXPECT_FALSE( + url::Origin::UnsafelyCreateTupleOriginWithoutNormalization("", "", 0)); + + // Opaque origins with unknown precursors are allowed. + base::UnguessableToken token = base::UnguessableToken::Create(); + base::Optional<url::Origin> anonymous_opaque = + UnsafelyCreateOpaqueOriginWithoutNormalization("", "", 0, + CreateNonce(token)); + ASSERT_TRUE(anonymous_opaque) + << "An invalid tuple is a valid input to " + << "UnsafelyCreateOpaqueOriginWithoutNormalization, so long as it is " + << "the canonical form of the invalid tuple."; + EXPECT_TRUE(anonymous_opaque->opaque()); + EXPECT_EQ(GetNonce(anonymous_opaque.value()), token); + EXPECT_EQ(anonymous_opaque->GetTupleOrPrecursorTupleIfOpaque(), + url::SchemeHostPort()); } TEST_F(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) { struct TestCases { - const char* scheme; - size_t scheme_length; - const char* host; - size_t host_length; + base::StringPiece scheme; + base::StringPiece host; uint16_t port = 80; - } cases[] = {{"http\0more", 9, "example.com", 11}, - {"http\0", 5, "example.com", 11}, - {"\0http", 5, "example.com", 11}, - {"http", 4, "example.com\0not-example.com", 27}, - {"http", 4, "example.com\0", 12}, - {"http", 4, "\0example.com", 12}}; + } cases[] = {{{"http\0more", 9}, {"example.com", 11}}, + {{"http\0", 5}, {"example.com", 11}}, + {{"\0http", 5}, {"example.com", 11}}, + {{"http"}, {"example.com\0not-example.com", 27}}, + {{"http"}, {"example.com\0", 12}}, + {{"http"}, {"\0example.com", 12}}, + {{""}, {"\0", 1}, 0}, + {{"\0", 1}, {""}, 0}}; for (const auto& test : cases) { - SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":" - << test.port); - Origin origin = Origin::UnsafelyCreateOriginWithoutNormalization( - std::string(test.scheme, test.scheme_length), - std::string(test.host, test.host_length), test.port); - EXPECT_EQ("", origin.scheme()); - EXPECT_EQ("", origin.host()); - EXPECT_EQ(0, origin.port()); - EXPECT_TRUE(origin.unique()); - EXPECT_FALSE(origin.IsSameOriginWith(origin)); - - ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL()); + SCOPED_TRACE(testing::Message() + << test.scheme << "://" << test.host << ":" << test.port); + EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization( + test.scheme, test.host, test.port)); + EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization( + test.scheme, test.host, test.port, CreateNonce())); } } @@ -449,6 +634,8 @@ TEST_F(OriginTest, DomainIs) { EXPECT_EQ(test_case.expected_domain_is, origin.DomainIs(test_case.lower_ascii_domain)); + EXPECT_FALSE( + origin.DeriveNewOpaqueOrigin().DomainIs(test_case.lower_ascii_domain)); } // If the URL is invalid, DomainIs returns false. @@ -467,4 +654,18 @@ TEST_F(OriginTest, DebugAlias) { EXPECT_STREQ("https://foo.com", origin1_debug_alias); } +TEST_F(OriginTest, NonStandardScheme) { + Origin origin = Origin::Create(GURL("cow://")); + EXPECT_TRUE(origin.opaque()); +} +TEST_F(OriginTest, NonStandardSchemeWithAndroidWebViewHack) { + EnableNonStandardSchemesForAndroidWebView(); + Origin origin = Origin::Create(GURL("cow://")); + EXPECT_FALSE(origin.opaque()); + EXPECT_EQ("cow", origin.scheme()); + EXPECT_EQ("", origin.host()); + EXPECT_EQ(0, origin.port()); + Shutdown(); +} + } // namespace url diff --git a/chromium/url/scheme_host_port.cc b/chromium/url/scheme_host_port.cc index c4f0ba5d5cc..e588aece42f 100644 --- a/chromium/url/scheme_host_port.cc +++ b/chromium/url/scheme_host_port.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/numerics/safe_conversions.h" +#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "url/gurl.h" #include "url/third_party/mozilla/url_parse.h" @@ -51,13 +52,30 @@ bool IsValidInput(const base::StringPiece& scheme, const base::StringPiece& host, uint16_t port, SchemeHostPort::ConstructPolicy policy) { + // Empty schemes are never valid. + if (scheme.empty()) + return false; + SchemeType scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION; bool is_standard = GetStandardSchemeType( scheme.data(), Component(0, base::checked_cast<int>(scheme.length())), &scheme_type); - if (!is_standard) - return false; + if (!is_standard) { + // To be consistent with blink, local non-standard schemes are currently + // allowed to be tuple origins. Nonstandard schemes don't have hostnames, + // so their tuple is just ("protocol", "", 0). + // + // TODO: Migrate "content:" and "externalfile:" to be standard schemes, and + // remove this local scheme exception. + if (base::ContainsValue(GetLocalSchemes(), scheme) && host.empty() && + port == 0) + return true; + + // Otherwise, allow non-standard schemes only if the Android WebView + // workaround is enabled. + return AllowNonStandardSchemesForAndroidWebView(); + } switch (scheme_type) { case SCHEME_WITH_HOST_AND_PORT: @@ -116,12 +134,16 @@ SchemeHostPort::SchemeHostPort(std::string scheme, uint16_t port, ConstructPolicy policy) : port_(0) { - if (!IsValidInput(scheme, host, port, policy)) + if (!IsValidInput(scheme, host, port, policy)) { + DCHECK(IsInvalid()); return; + } scheme_ = std::move(scheme); host_ = std::move(host); port_ = port; + DCHECK(!IsInvalid()) << "Scheme: " << scheme_ << " Host: " << host_ + << " Port: " << port; } SchemeHostPort::SchemeHostPort(base::StringPiece scheme, @@ -159,7 +181,11 @@ SchemeHostPort::SchemeHostPort(const GURL& url) : port_(0) { SchemeHostPort::~SchemeHostPort() = default; bool SchemeHostPort::IsInvalid() const { - return scheme_.empty() && host_.empty() && !port_; + // It suffices to just check |scheme_| for emptiness; the other fields are + // never present without it. + DCHECK(!scheme_.empty() || host_.empty()); + DCHECK(!scheme_.empty() || port_ == 0); + return scheme_.empty(); } std::string SchemeHostPort::Serialize() const { @@ -191,11 +217,6 @@ GURL SchemeHostPort::GetURL() const { return GURL(std::move(serialized), parsed, true); } -bool SchemeHostPort::Equals(const SchemeHostPort& other) const { - return port_ == other.port() && scheme_ == other.scheme() && - host_ == other.host(); -} - bool SchemeHostPort::operator<(const SchemeHostPort& other) const { return std::tie(port_, scheme_, host_) < std::tie(other.port_, other.scheme_, other.host_); @@ -240,4 +261,9 @@ std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const { return result; } +std::ostream& operator<<(std::ostream& out, + const SchemeHostPort& scheme_host_port) { + return out << scheme_host_port.Serialize(); +} + } // namespace url diff --git a/chromium/url/scheme_host_port.h b/chromium/url/scheme_host_port.h index 18562acf6f5..c36df4b07b8 100644 --- a/chromium/url/scheme_host_port.h +++ b/chromium/url/scheme_host_port.h @@ -49,9 +49,9 @@ struct Parsed; // these constructs. // // * SchemeHostPort has no notion of the Origin concept (RFC 6454), and in -// particular, it has no notion of a "unique" Origin. If you need to take -// uniqueness into account (and, if you're making security-relevant decisions -// then you absolutely do), please use 'url::Origin' instead. +// particular, it has no notion of an opaque Origin. If you need to take +// opaque origins into account (and, if you're making security-relevant +// decisions then you absolutely do), please use 'url::Origin' instead. // // Usage: // @@ -71,7 +71,7 @@ struct Parsed; // tuple.port(); // 443 // // GURL url("https://example.com/"); -// tuple.Equals(url::SchemeHostPort(url)); // true +// tuple == url::SchemeHostPort(url); // true class URL_EXPORT SchemeHostPort { public: // Creates an invalid (scheme, host, port) tuple, which represents an invalid @@ -129,7 +129,7 @@ class URL_EXPORT SchemeHostPort { // While this string form resembles the Origin serialization specified in // Section 6.2 of RFC 6454, it is important to note that invalid // SchemeHostPort tuples serialize to the empty string, rather than being - // serialized as a unique Origin. + // serialized as would an opaque Origin. std::string Serialize() const; // Efficiently returns what GURL(Serialize()) would return, without needing to @@ -141,9 +141,14 @@ class URL_EXPORT SchemeHostPort { // // Note that this comparison is _not_ the same as an origin-based comparison. // In particular, invalid SchemeHostPort objects match each other (and - // themselves). Unique origins, on the other hand, would not. - bool Equals(const SchemeHostPort& other) const; - + // themselves). Opaque origins, on the other hand, would not. + bool operator==(const SchemeHostPort& other) const { + return port_ == other.port() && scheme_ == other.scheme() && + host_ == other.host(); + } + bool operator!=(const SchemeHostPort& other) const { + return !(*this == other); + } // Allows SchemeHostPort to be used as a key in STL (for example, a std::set // or std::map). bool operator<(const SchemeHostPort& other) const; @@ -156,6 +161,9 @@ class URL_EXPORT SchemeHostPort { uint16_t port_; }; +URL_EXPORT std::ostream& operator<<(std::ostream& out, + const SchemeHostPort& scheme_host_port); + } // namespace url #endif // URL_SCHEME_HOST_PORT_H_ diff --git a/chromium/url/scheme_host_port_unittest.cc b/chromium/url/scheme_host_port_unittest.cc index 9a18d2f6761..7b00f4fc76d 100644 --- a/chromium/url/scheme_host_port_unittest.cc +++ b/chromium/url/scheme_host_port_unittest.cc @@ -53,7 +53,7 @@ TEST_F(SchemeHostPortTest, Invalid) { EXPECT_EQ("", invalid.host()); EXPECT_EQ(0, invalid.port()); EXPECT_TRUE(invalid.IsInvalid()); - EXPECT_TRUE(invalid.Equals(invalid)); + EXPECT_EQ(invalid, invalid); const char* urls[] = { "data:text/html,Hello!", "javascript:alert(1)", @@ -77,9 +77,9 @@ TEST_F(SchemeHostPortTest, Invalid) { EXPECT_EQ("", tuple.host()); EXPECT_EQ(0, tuple.port()); EXPECT_TRUE(tuple.IsInvalid()); - EXPECT_TRUE(tuple.Equals(tuple)); - EXPECT_TRUE(tuple.Equals(invalid)); - EXPECT_TRUE(invalid.Equals(tuple)); + EXPECT_EQ(tuple, tuple); + EXPECT_EQ(tuple, invalid); + EXPECT_EQ(invalid, tuple); ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -106,7 +106,7 @@ TEST_F(SchemeHostPortTest, ExplicitConstruction) { EXPECT_EQ(test.host, tuple.host()); EXPECT_EQ(test.port, tuple.port()); EXPECT_FALSE(tuple.IsInvalid()); - EXPECT_TRUE(tuple.Equals(tuple)); + EXPECT_EQ(tuple, tuple); ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -142,7 +142,7 @@ TEST_F(SchemeHostPortTest, InvalidConstruction) { EXPECT_EQ("", tuple.host()); EXPECT_EQ(0, tuple.port()); EXPECT_TRUE(tuple.IsInvalid()); - EXPECT_TRUE(tuple.Equals(tuple)); + EXPECT_EQ(tuple, tuple); ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } @@ -206,7 +206,7 @@ TEST_F(SchemeHostPortTest, GURLConstruction) { EXPECT_EQ(test.host, tuple.host()); EXPECT_EQ(test.port, tuple.port()); EXPECT_FALSE(tuple.IsInvalid()); - EXPECT_TRUE(tuple.Equals(tuple)); + EXPECT_EQ(tuple, tuple); ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL()); } } diff --git a/chromium/url/url_util.cc b/chromium/url/url_util.cc index 4bede9dd29c..0db56b000dc 100644 --- a/chromium/url/url_util.cc +++ b/chromium/url/url_util.cc @@ -19,6 +19,8 @@ namespace url { namespace { +bool g_allow_non_standard_schemes = false; + // Pass this enum through for methods which would like to know if whitespace // removal is necessary. enum WhitespaceRemovalPolicy { @@ -531,6 +533,7 @@ void Initialize() { void Shutdown() { initialized = false; + g_allow_non_standard_schemes = false; delete standard_schemes; standard_schemes = nullptr; delete referrer_schemes; @@ -551,6 +554,14 @@ void Shutdown() { empty_document_schemes = nullptr; } +void EnableNonStandardSchemesForAndroidWebView() { + g_allow_non_standard_schemes = true; +} + +bool AllowNonStandardSchemesForAndroidWebView() { + return g_allow_non_standard_schemes; +} + void AddStandardScheme(const char* new_scheme, SchemeType type) { Initialize(); DoAddSchemeWithType(new_scheme, type, standard_schemes); diff --git a/chromium/url/url_util.h b/chromium/url/url_util.h index 32e7f0d9eac..3d783292349 100644 --- a/chromium/url/url_util.h +++ b/chromium/url/url_util.h @@ -39,6 +39,17 @@ URL_EXPORT void Shutdown(); // Schemes --------------------------------------------------------------------- +// Changes the behavior of SchemeHostPort / Origin to allow non-standard schemes +// to be specified, instead of canonicalizing them to an invalid SchemeHostPort +// or opaque Origin, respectively. This is used for Android WebView backwards +// compatibility, which allows the use of custom schemes: content hosted in +// Android WebView assumes that one URL with a non-standard scheme will be +// same-origin to another URL with the same non-standard scheme. +URL_EXPORT void EnableNonStandardSchemesForAndroidWebView(); + +// Whether or not SchemeHostPort and Origin allow non-standard schemes. +URL_EXPORT bool AllowNonStandardSchemesForAndroidWebView(); + // A pair for representing a standard scheme name and the SchemeType for it. struct URL_EXPORT SchemeWithType { const char* scheme; |