|  | // Copyright 2020 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef URL_ORIGIN_ABSTRACT_TESTS_H_ | 
|  | #define URL_ORIGIN_ABSTRACT_TESTS_H_ | 
|  |  | 
|  | #include <string> | 
|  | #include <type_traits> | 
|  |  | 
|  | #include "base/containers/contains.h" | 
|  | #include "base/strings/string_piece.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "url/gurl.h" | 
|  | #include "url/origin.h" | 
|  | #include "url/scheme_host_port.h" | 
|  | #include "url/url_util.h" | 
|  |  | 
|  | namespace url { | 
|  |  | 
|  | void ExpectParsedUrlsEqual(const GURL& a, const GURL& b); | 
|  |  | 
|  | // AbstractOriginTest below abstracts away differences between url::Origin and | 
|  | // blink::SecurityOrigin by parametrizing the tests with a class that has to | 
|  | // expose the same public members as UrlOriginTestTraits below. | 
|  | class UrlOriginTestTraits { | 
|  | public: | 
|  | using OriginType = Origin; | 
|  |  | 
|  | // Constructing an origin. | 
|  | static OriginType CreateOriginFromString(gurl_base::StringPiece s); | 
|  | static OriginType CreateUniqueOpaqueOrigin(); | 
|  | static OriginType CreateWithReferenceOrigin( | 
|  | gurl_base::StringPiece url, | 
|  | const OriginType& reference_origin); | 
|  | static OriginType DeriveNewOpaqueOrigin(const OriginType& reference_origin); | 
|  |  | 
|  | // Accessors for origin properties. | 
|  | static bool IsOpaque(const OriginType& origin); | 
|  | static std::string GetScheme(const OriginType& origin); | 
|  | static std::string GetHost(const OriginType& origin); | 
|  | static uint16_t GetPort(const OriginType& origin); | 
|  | static SchemeHostPort GetTupleOrPrecursorTupleIfOpaque( | 
|  | const OriginType& origin); | 
|  |  | 
|  | // Wrappers for other instance methods of OriginType. | 
|  | static bool IsSameOrigin(const OriginType& a, const OriginType& b); | 
|  | static std::string Serialize(const OriginType& origin); | 
|  |  | 
|  | // "Accessors" of URL properties. | 
|  | // | 
|  | // TODO(lukasza): Consider merging together OriginTraitsBase here and | 
|  | // UrlTraitsBase in //url/gurl_abstract_tests.h. | 
|  | static bool IsValidUrl(gurl_base::StringPiece str); | 
|  |  | 
|  | // Only static members = no constructors are needed. | 
|  | UrlOriginTestTraits() = delete; | 
|  | }; | 
|  |  | 
|  | // Test suite for tests that cover both url::Origin and blink::SecurityOrigin. | 
|  | template <typename TOriginTraits> | 
|  | class AbstractOriginTest : public testing::Test { | 
|  | public: | 
|  | void SetUp() override { | 
|  | const char* kSchemesToRegister[] = { | 
|  | "noaccess", | 
|  | "std-with-host", | 
|  | "noaccess-std-with-host", | 
|  | "local", | 
|  | "local-noaccess", | 
|  | "local-std-with-host", | 
|  | "local-noaccess-std-with-host", | 
|  | "also-local", | 
|  | "sec", | 
|  | "sec-std-with-host", | 
|  | "sec-noaccess", | 
|  | }; | 
|  | for (const char* kScheme : kSchemesToRegister) { | 
|  | std::string scheme(kScheme); | 
|  | if (gurl_base::Contains(scheme, "noaccess")) | 
|  | AddNoAccessScheme(kScheme); | 
|  | if (gurl_base::Contains(scheme, "std-with-host")) | 
|  | AddStandardScheme(kScheme, SchemeType::SCHEME_WITH_HOST); | 
|  | if (gurl_base::Contains(scheme, "local")) | 
|  | AddLocalScheme(kScheme); | 
|  | if (gurl_base::Contains(scheme, "sec")) | 
|  | AddSecureScheme(kScheme); | 
|  | } | 
|  | } | 
|  |  | 
|  | protected: | 
|  | // Wrappers that help ellide away TOriginTraits. | 
|  | // | 
|  | // Note that calling the wrappers needs to be prefixed with `this->...` to | 
|  | // avoid hitting: explicit qualification required to use member 'IsOpaque' | 
|  | // from dependent base class. | 
|  | using OriginType = typename TOriginTraits::OriginType; | 
|  | OriginType CreateOriginFromString(gurl_base::StringPiece s) { | 
|  | return TOriginTraits::CreateOriginFromString(s); | 
|  | } | 
|  | OriginType CreateUniqueOpaqueOrigin() { | 
|  | return TOriginTraits::CreateUniqueOpaqueOrigin(); | 
|  | } | 
|  | OriginType CreateWithReferenceOrigin(gurl_base::StringPiece url, | 
|  | const OriginType& reference_origin) { | 
|  | return TOriginTraits::CreateWithReferenceOrigin(url, reference_origin); | 
|  | } | 
|  | OriginType DeriveNewOpaqueOrigin(const OriginType& reference_origin) { | 
|  | return TOriginTraits::DeriveNewOpaqueOrigin(reference_origin); | 
|  | } | 
|  | bool IsOpaque(const OriginType& origin) { | 
|  | return TOriginTraits::IsOpaque(origin); | 
|  | } | 
|  | std::string GetScheme(const OriginType& origin) { | 
|  | return TOriginTraits::GetScheme(origin); | 
|  | } | 
|  | std::string GetHost(const OriginType& origin) { | 
|  | return TOriginTraits::GetHost(origin); | 
|  | } | 
|  | uint16_t GetPort(const OriginType& origin) { | 
|  | return TOriginTraits::GetPort(origin); | 
|  | } | 
|  | SchemeHostPort GetTupleOrPrecursorTupleIfOpaque(const OriginType& origin) { | 
|  | return TOriginTraits::GetTupleOrPrecursorTupleIfOpaque(origin); | 
|  | } | 
|  | bool IsSameOrigin(const OriginType& a, const OriginType& b) { | 
|  | bool is_a_same_with_b = TOriginTraits::IsSameOrigin(a, b); | 
|  | bool is_b_same_with_a = TOriginTraits::IsSameOrigin(b, a); | 
|  | EXPECT_EQ(is_a_same_with_b, is_b_same_with_a); | 
|  | return is_a_same_with_b; | 
|  | } | 
|  | std::string Serialize(const OriginType& origin) { | 
|  | return TOriginTraits::Serialize(origin); | 
|  | } | 
|  | bool IsValidUrl(gurl_base::StringPiece str) { | 
|  | return TOriginTraits::IsValidUrl(str); | 
|  | } | 
|  |  | 
|  | #define EXPECT_SAME_ORIGIN(a, b)                                 \ | 
|  | EXPECT_TRUE(this->IsSameOrigin((a), (b)))                      \ | 
|  | << "When checking if \"" << this->Serialize(a) << "\" is " \ | 
|  | << "same-origin with \"" << this->Serialize(b) << "\"" | 
|  |  | 
|  | #define EXPECT_CROSS_ORIGIN(a, b)                                \ | 
|  | EXPECT_FALSE(this->IsSameOrigin((a), (b)))                     \ | 
|  | << "When checking if \"" << this->Serialize(a) << "\" is " \ | 
|  | << "cross-origin from \"" << this->Serialize(b) << "\"" | 
|  |  | 
|  | void VerifyOriginInvariants(const OriginType& origin) { | 
|  | // An origin is always same-origin with itself. | 
|  | EXPECT_SAME_ORIGIN(origin, origin); | 
|  |  | 
|  | // A copy of |origin| should be same-origin as well. | 
|  | auto origin_copy = origin; | 
|  | EXPECT_EQ(this->GetScheme(origin), this->GetScheme(origin_copy)); | 
|  | EXPECT_EQ(this->GetHost(origin), this->GetHost(origin_copy)); | 
|  | EXPECT_EQ(this->GetPort(origin), this->GetPort(origin_copy)); | 
|  | EXPECT_EQ(this->IsOpaque(origin), this->IsOpaque(origin_copy)); | 
|  | EXPECT_SAME_ORIGIN(origin, origin_copy); | 
|  |  | 
|  | // An origin is always cross-origin from another, unique, opaque origin. | 
|  | EXPECT_CROSS_ORIGIN(origin, this->CreateUniqueOpaqueOrigin()); | 
|  |  | 
|  | // An origin is always cross-origin from another tuple origin. | 
|  | auto different_tuple_origin = | 
|  | this->CreateOriginFromString("https://not-in-the-list.test/"); | 
|  | EXPECT_CROSS_ORIGIN(origin, different_tuple_origin); | 
|  |  | 
|  | // Deriving an origin for "about:blank". | 
|  | auto about_blank_origin1 = | 
|  | this->CreateWithReferenceOrigin("about:blank", origin); | 
|  | auto about_blank_origin2 = | 
|  | this->CreateWithReferenceOrigin("about:blank?bar#foo", origin); | 
|  | EXPECT_SAME_ORIGIN(origin, about_blank_origin1); | 
|  | EXPECT_SAME_ORIGIN(origin, about_blank_origin2); | 
|  |  | 
|  | // Derived opaque origins. | 
|  | std::vector<OriginType> derived_origins = { | 
|  | this->DeriveNewOpaqueOrigin(origin), | 
|  | this->CreateWithReferenceOrigin("data:text/html,baz", origin), | 
|  | this->DeriveNewOpaqueOrigin(about_blank_origin1), | 
|  | }; | 
|  | for (size_t i = 0; i < derived_origins.size(); i++) { | 
|  | SCOPED_TRACE(testing::Message() << "Derived origin #" << i); | 
|  | const OriginType& derived_origin = derived_origins[i]; | 
|  | EXPECT_TRUE(this->IsOpaque(derived_origin)); | 
|  | EXPECT_SAME_ORIGIN(derived_origin, derived_origin); | 
|  | EXPECT_CROSS_ORIGIN(origin, derived_origin); | 
|  | EXPECT_EQ(this->GetTupleOrPrecursorTupleIfOpaque(origin), | 
|  | this->GetTupleOrPrecursorTupleIfOpaque(derived_origin)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void VerifyUniqueOpaqueOriginInvariants(const OriginType& origin) { | 
|  | if (!this->IsOpaque(origin)) { | 
|  | ADD_FAILURE() << "Got unexpectedly non-opaque origin: " | 
|  | << this->Serialize(origin); | 
|  | return;  // Skip other test assertions. | 
|  | } | 
|  |  | 
|  | // Opaque origins should have an "empty" scheme, host and port. | 
|  | EXPECT_EQ("", this->GetScheme(origin)); | 
|  | EXPECT_EQ("", this->GetHost(origin)); | 
|  | EXPECT_EQ(0, this->GetPort(origin)); | 
|  |  | 
|  | // Unique opaque origins should have an empty precursor tuple. | 
|  | EXPECT_EQ(SchemeHostPort(), this->GetTupleOrPrecursorTupleIfOpaque(origin)); | 
|  |  | 
|  | // Serialization test. | 
|  | EXPECT_EQ("null", this->Serialize(origin)); | 
|  |  | 
|  | // Invariants that should hold for any origin. | 
|  | VerifyOriginInvariants(origin); | 
|  | } | 
|  |  | 
|  | void TestUniqueOpaqueOrigin(gurl_base::StringPiece test_input) { | 
|  | auto origin = this->CreateOriginFromString(test_input); | 
|  | this->VerifyUniqueOpaqueOriginInvariants(origin); | 
|  |  | 
|  | // Re-creating from the URL should be cross-origin. | 
|  | auto origin_recreated_from_same_input = | 
|  | this->CreateOriginFromString(test_input); | 
|  | EXPECT_CROSS_ORIGIN(origin, origin_recreated_from_same_input); | 
|  | } | 
|  |  | 
|  | void VerifyTupleOriginInvariants(const OriginType& origin, | 
|  | const SchemeHostPort& expected_tuple) { | 
|  | if (this->IsOpaque(origin)) { | 
|  | ADD_FAILURE() << "Got unexpectedly opaque origin"; | 
|  | return;  // Skip other test assertions. | 
|  | } | 
|  | SCOPED_TRACE(testing::Message() | 
|  | << "Actual origin: " << this->Serialize(origin)); | 
|  |  | 
|  | // Compare `origin` against the `expected_tuple`. | 
|  | EXPECT_EQ(expected_tuple.scheme(), this->GetScheme(origin)); | 
|  | EXPECT_EQ(expected_tuple.host(), this->GetHost(origin)); | 
|  | EXPECT_EQ(expected_tuple.port(), this->GetPort(origin)); | 
|  | EXPECT_EQ(expected_tuple, this->GetTupleOrPrecursorTupleIfOpaque(origin)); | 
|  |  | 
|  | // Serialization test. | 
|  | // | 
|  | // TODO(lukasza): Consider preserving the hostname when serializing file: | 
|  | // URLs.  Dropping the hostname seems incompatible with section 6 of | 
|  | // rfc6454.  Even though section 4 says that "the implementation MAY | 
|  | // return an implementation-defined value", it seems that Chromium | 
|  | // implementation *does* include the hostname in the origin SchemeHostPort | 
|  | // tuple. | 
|  | if (expected_tuple.scheme() != kFileScheme || expected_tuple.host() == "") { | 
|  | EXPECT_SAME_ORIGIN(origin, | 
|  | this->CreateOriginFromString(this->Serialize(origin))); | 
|  | } | 
|  |  | 
|  | // Invariants that should hold for any origin. | 
|  | VerifyOriginInvariants(origin); | 
|  | } | 
|  |  | 
|  | private: | 
|  | ScopedSchemeRegistryForTests scoped_scheme_registry_; | 
|  | }; | 
|  |  | 
|  | TYPED_TEST_SUITE_P(AbstractOriginTest); | 
|  |  | 
|  | TYPED_TEST_P(AbstractOriginTest, NonStandardSchemeWithAndroidWebViewHack) { | 
|  | EnableNonStandardSchemesForAndroidWebView(); | 
|  |  | 
|  | // Regression test for https://crbug.com/896059. | 
|  | auto origin = this->CreateOriginFromString("unknown-scheme://"); | 
|  | EXPECT_FALSE(this->IsOpaque(origin)); | 
|  | EXPECT_EQ("unknown-scheme", this->GetScheme(origin)); | 
|  | EXPECT_EQ("", this->GetHost(origin)); | 
|  | EXPECT_EQ(0, this->GetPort(origin)); | 
|  |  | 
|  | // about:blank translates into an opaque origin, even in presence of | 
|  | // EnableNonStandardSchemesForAndroidWebView. | 
|  | origin = this->CreateOriginFromString("about:blank"); | 
|  | EXPECT_TRUE(this->IsOpaque(origin)); | 
|  | } | 
|  |  | 
|  | TYPED_TEST_P(AbstractOriginTest, OpaqueOriginsFromValidUrls) { | 
|  | const char* kTestCases[] = { | 
|  | // Built-in noaccess schemes. | 
|  | "data:text/html,Hello!", | 
|  | "javascript:alert(1)", | 
|  | "about:blank", | 
|  |  | 
|  | // Opaque blob URLs. | 
|  | "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) | 
|  | "blob:blob:http://www.example.com/guid-goes-here", | 
|  | "blob:filesystem:ws:b/.", | 
|  | "blob:filesystem:ftp://a/b", | 
|  | "blob:blob:file://localhost/foo/bar", | 
|  | }; | 
|  |  | 
|  | for (const char* test_input : kTestCases) { | 
|  | SCOPED_TRACE(testing::Message() << "Test input: " << test_input); | 
|  |  | 
|  | // Verify that `origin` is opaque not just because `test_input` results is | 
|  | // an invalid URL (because of a typo in the scheme name, or because of a | 
|  | // technicality like having no host in a noaccess-std-with-host: scheme). | 
|  | EXPECT_TRUE(this->IsValidUrl(test_input)); | 
|  |  | 
|  | this->TestUniqueOpaqueOrigin(test_input); | 
|  | } | 
|  | } | 
|  |  | 
|  | TYPED_TEST_P(AbstractOriginTest, OpaqueOriginsFromInvalidUrls) { | 
|  | // TODO(lukasza): Consider moving those to GURL/KURL tests that verify what | 
|  | // inputs are parsed as an invalid URL. | 
|  |  | 
|  | const char* kTestCases[] = { | 
|  | // Invalid file: URLs. | 
|  | "file://example.com:443/etc/passwd",  // No port expected. | 
|  |  | 
|  | // Invalid HTTP URLs. | 
|  | "http", | 
|  | "http:", | 
|  | "http:/", | 
|  | "http://", | 
|  | "http://:", | 
|  | "http://:1", | 
|  | "http::///invalid.example.com/", | 
|  | "http://example.com:65536/",                    // Port out of range. | 
|  | "http://example.com:-1/",                       // Port out of range. | 
|  | "http://example.com:18446744073709551616/",     // Port = 2^64. | 
|  | "http://example.com:18446744073709551616999/",  // Lots of port digits. | 
|  |  | 
|  | // Invalid filesystem URLs. | 
|  | "filesystem:http://example.com/",  // Missing /type/. | 
|  | "filesystem:local:baz./type/", | 
|  | "filesystem:local://hostname/type/", | 
|  | "filesystem:unknown-scheme://hostname/type/", | 
|  | "filesystem:filesystem:http://example.org:88/foo/bar", | 
|  |  | 
|  | // Invalid IP addresses | 
|  | "http://[]/", | 
|  | "http://[2001:0db8:0000:0000:0000:0000:0000:0000:0001]/",  // 9 groups. | 
|  |  | 
|  | // Unknown scheme without a colon character (":") gives an invalid URL. | 
|  | "unknown-scheme", | 
|  |  | 
|  | // Standard schemes require a hostname (and result in an opaque origin if | 
|  | // the hostname is missing). | 
|  | "local-std-with-host:", | 
|  | "noaccess-std-with-host:", | 
|  | }; | 
|  |  | 
|  | for (const char* test_input : kTestCases) { | 
|  | SCOPED_TRACE(testing::Message() << "Test input: " << test_input); | 
|  |  | 
|  | // All testcases here are expected to represent invalid URLs. | 
|  | // an invalid URL (because of a type in scheme name, or because of a | 
|  | // technicality like having no host in a noaccess-std-with-host: scheme). | 
|  | EXPECT_FALSE(this->IsValidUrl(test_input)); | 
|  |  | 
|  | // Invalid URLs should always result in an opaque origin. | 
|  | this->TestUniqueOpaqueOrigin(test_input); | 
|  | } | 
|  | } | 
|  |  | 
|  | TYPED_TEST_P(AbstractOriginTest, TupleOrigins) { | 
|  | struct TestCase { | 
|  | const char* input; | 
|  | SchemeHostPort expected_tuple; | 
|  | } kTestCases[] = { | 
|  | // file: URLs | 
|  | {"file:///etc/passwd", {"file", "", 0}}, | 
|  | {"file://example.com/etc/passwd", {"file", "example.com", 0}}, | 
|  | {"file:///", {"file", "", 0}}, | 
|  | {"file://hostname/C:/dir/file.txt", {"file", "hostname", 0}}, | 
|  |  | 
|  | // HTTP URLs | 
|  | {"http://example.com/", {"http", "example.com", 80}}, | 
|  | {"http://example.com:80/", {"http", "example.com", 80}}, | 
|  | {"http://example.com:123/", {"http", "example.com", 123}}, | 
|  | {"http://example.com:0/", {"http", "example.com", 0}}, | 
|  | {"http://example.com:65535/", {"http", "example.com", 65535}}, | 
|  | {"https://example.com/", {"https", "example.com", 443}}, | 
|  | {"https://example.com:443/", {"https", "example.com", 443}}, | 
|  | {"https://example.com:123/", {"https", "example.com", 123}}, | 
|  | {"https://example.com:0/", {"https", "example.com", 0}}, | 
|  | {"https://example.com:65535/", {"https", "example.com", 65535}}, | 
|  | {"http://user:pass@example.com/", {"http", "example.com", 80}}, | 
|  | {"http://example.com:123/?query", {"http", "example.com", 123}}, | 
|  | {"https://example.com/#1234", {"https", "example.com", 443}}, | 
|  | {"https://u:p@example.com:123/?query#1234", | 
|  | {"https", "example.com", 123}}, | 
|  | {"http://example/", {"http", "example", 80}}, | 
|  |  | 
|  | // Blob URLs. | 
|  | {"blob:http://example.com/guid-goes-here", {"http", "example.com", 80}}, | 
|  | {"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}}, | 
|  |  | 
|  | // Filesystem URLs. | 
|  | {"filesystem:http://example.com/type/", {"http", "example.com", 80}}, | 
|  | {"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-std-with-host:baz./type/", | 
|  | {"local-std-with-host", "baz.", 0}}, | 
|  |  | 
|  | // IP Addresses | 
|  | {"http://192.168.9.1/", {"http", "192.168.9.1", 80}}, | 
|  | {"http://[2001:db8::1]/", {"http", "[2001:db8::1]", 80}}, | 
|  | {"http://[2001:0db8:0000:0000:0000:0000:0000:0001]/", | 
|  | {"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}}, | 
|  | {"blob:http://☃.net/", {"http", "xn--n3h.net", 80}}, | 
|  | {"local-std-with-host:↑↑↓↓←→←→ba.↑↑↓↓←→←→ba.0.bg", | 
|  | {"local-std-with-host", "xn--ba-rzuadaibfa.xn--ba-rzuadaibfa.0.bg", 0}}, | 
|  |  | 
|  | // Registered URLs | 
|  | {"ftp://example.com/", {"ftp", "example.com", 21}}, | 
|  | {"ws://example.com/", {"ws", "example.com", 80}}, | 
|  | {"wss://example.com/", {"wss", "example.com", 443}}, | 
|  | {"wss://user:pass@example.com/", {"wss", "example.com", 443}}, | 
|  | }; | 
|  |  | 
|  | for (const TestCase& test : kTestCases) { | 
|  | SCOPED_TRACE(testing::Message() << "Test input: " << test.input); | 
|  |  | 
|  | // Only valid URLs should translate into valid, non-opaque origins. | 
|  | EXPECT_TRUE(this->IsValidUrl(test.input)); | 
|  |  | 
|  | auto origin = this->CreateOriginFromString(test.input); | 
|  | this->VerifyTupleOriginInvariants(origin, test.expected_tuple); | 
|  | } | 
|  | } | 
|  |  | 
|  | TYPED_TEST_P(AbstractOriginTest, CustomSchemes_OpaqueOrigins) { | 
|  | const char* kTestCases[] = { | 
|  | // Unknown scheme | 
|  | "unknown-scheme:foo", | 
|  | "unknown-scheme://bar", | 
|  |  | 
|  | // Unknown scheme that is a prefix or suffix of a registered scheme. | 
|  | "loca:foo", | 
|  | "ocal:foo", | 
|  | "local-suffix:foo", | 
|  | "prefix-local:foo", | 
|  |  | 
|  | // Custom no-access schemes translate into an opaque origin (just like the | 
|  | // built-in no-access schemes such as about:blank or data:). | 
|  | "noaccess-std-with-host:foo", | 
|  | "noaccess-std-with-host://bar", | 
|  | "noaccess://host", | 
|  | "local-noaccess://host", | 
|  | "local-noaccess-std-with-host://host", | 
|  | }; | 
|  |  | 
|  | for (const char* test_input : kTestCases) { | 
|  | SCOPED_TRACE(testing::Message() << "Test input: " << test_input); | 
|  |  | 
|  | // Verify that `origin` is opaque not just because `test_input` results is | 
|  | // an invalid URL (because of a typo in the scheme name, or because of a | 
|  | // technicality like having no host in a noaccess-std-with-host: scheme). | 
|  | EXPECT_TRUE(this->IsValidUrl(test_input)); | 
|  |  | 
|  | this->TestUniqueOpaqueOrigin(test_input); | 
|  | } | 
|  | } | 
|  |  | 
|  | TYPED_TEST_P(AbstractOriginTest, CustomSchemes_TupleOrigins) { | 
|  | struct TestCase { | 
|  | const char* input; | 
|  | SchemeHostPort expected_tuple; | 
|  | } kTestCases[] = { | 
|  | // 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-std-with-host:20", {"local-std-with-host", "0.0.0.20", 0}}, | 
|  | {"local-std-with-host:20.", {"local-std-with-host", "0.0.0.20", 0}}, | 
|  | {"local-std-with-host:foo", {"local-std-with-host", "foo", 0}}, | 
|  | {"local-std-with-host://bar:20", {"local-std-with-host", "bar", 0}}, | 
|  | {"local-std-with-host:baz.", {"local-std-with-host", "baz.", 0}}, | 
|  | {"local-std-with-host:baz..", {"local-std-with-host", "baz..", 0}}, | 
|  | {"local-std-with-host:baz..bar", {"local-std-with-host", "baz..bar", 0}}, | 
|  | {"local-std-with-host:baz...", {"local-std-with-host", "baz...", 0}}, | 
|  |  | 
|  | // Scheme (registered in SetUp()) that's local but nonstandard. These | 
|  | // always have empty hostnames, but are allowed to be url::Origins. | 
|  | {"local:", {"local", "", 0}}, | 
|  | {"local:foo", {"local", "", 0}}, | 
|  | {"local://bar", {"local", "", 0}}, | 
|  | {"also-local://bar", {"also-local", "", 0}}, | 
|  |  | 
|  | {"std-with-host://host", {"std-with-host", "host", 0}}, | 
|  | {"local://host", {"local", "", 0}}, | 
|  | {"local-std-with-host://host", {"local-std-with-host", "host", 0}}, | 
|  | }; | 
|  |  | 
|  | for (const TestCase& test : kTestCases) { | 
|  | SCOPED_TRACE(testing::Message() << "Test input: " << test.input); | 
|  |  | 
|  | // Only valid URLs should translate into valid, non-opaque origins. | 
|  | EXPECT_TRUE(this->IsValidUrl(test.input)); | 
|  |  | 
|  | auto origin = this->CreateOriginFromString(test.input); | 
|  | this->VerifyTupleOriginInvariants(origin, test.expected_tuple); | 
|  | } | 
|  | } | 
|  |  | 
|  | REGISTER_TYPED_TEST_SUITE_P(AbstractOriginTest, | 
|  | NonStandardSchemeWithAndroidWebViewHack, | 
|  | OpaqueOriginsFromValidUrls, | 
|  | OpaqueOriginsFromInvalidUrls, | 
|  | TupleOrigins, | 
|  | CustomSchemes_OpaqueOrigins, | 
|  | CustomSchemes_TupleOrigins); | 
|  |  | 
|  | }  // namespace url | 
|  |  | 
|  | #endif  // URL_ORIGIN_ABSTRACT_TESTS_H_ |