summaryrefslogtreecommitdiff
path: root/chromium/url
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-12-10 16:19:40 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-12-10 16:01:50 +0000
commit51f6c2793adab2d864b3d2b360000ef8db1d3e92 (patch)
tree835b3b4446b012c75e80177cef9fbe6972cc7dbe /chromium/url
parent6036726eb981b6c4b42047513b9d3f4ac865daac (diff)
downloadqtwebengine-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.gn1
-rw-r--r--chromium/url/android/java/src/org/chromium/url/IDNStringUtil.java33
-rw-r--r--chromium/url/gurl_unittest.cc21
-rw-r--r--chromium/url/mojom/BUILD.gn1
-rw-r--r--chromium/url/mojom/origin.mojom8
-rw-r--r--chromium/url/mojom/origin_mojom_traits.h50
-rw-r--r--chromium/url/mojom/url_gurl_mojom_traits_unittest.cc29
-rw-r--r--chromium/url/origin.cc201
-rw-r--r--chromium/url/origin.h240
-rw-r--r--chromium/url/origin_unittest.cc503
-rw-r--r--chromium/url/scheme_host_port.cc44
-rw-r--r--chromium/url/scheme_host_port.h24
-rw-r--r--chromium/url/scheme_host_port_unittest.cc14
-rw-r--r--chromium/url/url_util.cc11
-rw-r--r--chromium/url/url_util.h11
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;