Upload upstream chromium 85.0.4183.84
[platform/framework/web/chromium-efl.git] / url / origin_unittest.cc
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6 #include <stdint.h>
7
8 #include "base/macros.h"
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "url/gurl.h"
12 #include "url/origin.h"
13 #include "url/url_util.h"
14
15 namespace url {
16
17 void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) {
18   EXPECT_EQ(a, b);
19   const Parsed& a_parsed = a.parsed_for_possibly_invalid_spec();
20   const Parsed& b_parsed = b.parsed_for_possibly_invalid_spec();
21   EXPECT_EQ(a_parsed.scheme.begin, b_parsed.scheme.begin);
22   EXPECT_EQ(a_parsed.scheme.len, b_parsed.scheme.len);
23   EXPECT_EQ(a_parsed.username.begin, b_parsed.username.begin);
24   EXPECT_EQ(a_parsed.username.len, b_parsed.username.len);
25   EXPECT_EQ(a_parsed.password.begin, b_parsed.password.begin);
26   EXPECT_EQ(a_parsed.password.len, b_parsed.password.len);
27   EXPECT_EQ(a_parsed.host.begin, b_parsed.host.begin);
28   EXPECT_EQ(a_parsed.host.len, b_parsed.host.len);
29   EXPECT_EQ(a_parsed.port.begin, b_parsed.port.begin);
30   EXPECT_EQ(a_parsed.port.len, b_parsed.port.len);
31   EXPECT_EQ(a_parsed.path.begin, b_parsed.path.begin);
32   EXPECT_EQ(a_parsed.path.len, b_parsed.path.len);
33   EXPECT_EQ(a_parsed.query.begin, b_parsed.query.begin);
34   EXPECT_EQ(a_parsed.query.len, b_parsed.query.len);
35   EXPECT_EQ(a_parsed.ref.begin, b_parsed.ref.begin);
36   EXPECT_EQ(a_parsed.ref.len, b_parsed.ref.len);
37 }
38
39 class OriginTest : public ::testing::Test {
40  public:
41   void SetUp() override {
42     // Add two schemes which are local but nonstandard.
43     AddLocalScheme("local-but-nonstandard");
44     AddLocalScheme("also-local-but-nonstandard");
45
46     // Add a scheme that's both local and standard.
47     AddStandardScheme("local-and-standard", SchemeType::SCHEME_WITH_HOST);
48     AddLocalScheme("local-and-standard");
49
50     // Add a scheme that's standard but no-access. We still want these to
51     // form valid SchemeHostPorts, even though they always commit as opaque
52     // origins, so that they can represent the source of the resource even if
53     // it's not committable as a non-opaque origin.
54     AddStandardScheme("standard-but-noaccess", SchemeType::SCHEME_WITH_HOST);
55     AddNoAccessScheme("standard-but-noaccess");
56   }
57
58   ::testing::AssertionResult DoEqualityComparisons(const url::Origin& a,
59                                                    const url::Origin& b,
60                                                    bool should_compare_equal) {
61     ::testing::AssertionResult failure = ::testing::AssertionFailure();
62     failure << "DoEqualityComparisons failure. Expecting "
63             << (should_compare_equal ? "equality" : "inequality")
64             << " between:\n  a\n    Which is: " << a
65             << "\n  b\n    Which is: " << b << "\nThe following check failed: ";
66     if (a.IsSameOriginWith(b) != should_compare_equal)
67       return failure << "a.IsSameOriginWith(b)";
68     if (b.IsSameOriginWith(a) != should_compare_equal)
69       return failure << "b.IsSameOriginWith(a)";
70     if ((a == b) != should_compare_equal)
71       return failure << "(a == b)";
72     if ((b == a) != should_compare_equal)
73       return failure << "(b == a)";
74     if ((b != a) != !should_compare_equal)
75       return failure << "(b != a)";
76     if ((a != b) != !should_compare_equal)
77       return failure << "(a != b)";
78     return ::testing::AssertionSuccess();
79   }
80
81   bool HasNonceTokenBeenInitialized(const url::Origin& origin) {
82     EXPECT_TRUE(origin.opaque());
83     // Avoid calling nonce_.token() here, to not trigger lazy initialization.
84     return !origin.nonce_->token_.is_empty();
85   }
86
87   Origin::Nonce CreateNonce() { return Origin::Nonce(); }
88
89   Origin::Nonce CreateNonce(base::UnguessableToken nonce) {
90     return Origin::Nonce(nonce);
91   }
92
93   base::Optional<base::UnguessableToken> GetNonce(const Origin& origin) {
94     return origin.GetNonceForSerialization();
95   }
96
97   // Wrappers around url::Origin methods to expose it to tests.
98
99   base::Optional<Origin> UnsafelyCreateOpaqueOriginWithoutNormalization(
100       base::StringPiece precursor_scheme,
101       base::StringPiece precursor_host,
102       uint16_t precursor_port,
103       const Origin::Nonce& nonce) {
104     return Origin::UnsafelyCreateOpaqueOriginWithoutNormalization(
105         precursor_scheme, precursor_host, precursor_port, nonce);
106   }
107
108   base::Optional<std::string> SerializeWithNonce(const Origin& origin) {
109     return origin.SerializeWithNonce();
110   }
111
112   base::Optional<Origin> Deserialize(const std::string& value) {
113     return Origin::Deserialize(value);
114   }
115
116  private:
117   ScopedSchemeRegistryForTests scoped_registry_;
118 };
119
120 TEST_F(OriginTest, OpaqueOriginComparison) {
121   // A default-constructed Origin should should be cross origin to everything
122   // but itself.
123   url::Origin opaque_a, opaque_b;
124   EXPECT_TRUE(opaque_a.opaque());
125   EXPECT_EQ("", opaque_a.scheme());
126   EXPECT_EQ("", opaque_a.host());
127   EXPECT_EQ(0, opaque_a.port());
128   EXPECT_EQ(SchemeHostPort(), opaque_a.GetTupleOrPrecursorTupleIfOpaque());
129   EXPECT_FALSE(opaque_a.GetTupleOrPrecursorTupleIfOpaque().IsValid());
130
131   EXPECT_TRUE(opaque_b.opaque());
132   EXPECT_EQ("", opaque_b.scheme());
133   EXPECT_EQ("", opaque_b.host());
134   EXPECT_EQ(0, opaque_b.port());
135   EXPECT_EQ(SchemeHostPort(), opaque_b.GetTupleOrPrecursorTupleIfOpaque());
136   EXPECT_FALSE(opaque_b.GetTupleOrPrecursorTupleIfOpaque().IsValid());
137
138   // Two default-constructed Origins should always be cross origin to each
139   // other.
140   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
141   EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
142   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
143
144   // The streaming operator should not trigger lazy initialization to the token.
145   std::ostringstream stream;
146   stream << opaque_a;
147   EXPECT_STREQ("null [internally: (nonce TBD) anonymous]",
148                stream.str().c_str());
149   EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
150
151   // None of the operations thus far should have triggered lazy-generation of
152   // the UnguessableToken. Copying an origin, however, should trigger this.
153   EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
154   EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_b));
155   opaque_b = opaque_a;
156
157   EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a));
158   EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
159   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, true));
160   EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
161   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
162
163   // Move-initializing to a fresh Origin should restore the lazy initialization.
164   opaque_a = url::Origin();
165   EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
166   EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
167   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
168   EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
169   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
170
171   // Comparing two opaque Origins with matching SchemeHostPorts should trigger
172   // lazy initialization.
173   EXPECT_FALSE(HasNonceTokenBeenInitialized(opaque_a));
174   EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
175   bool should_swap = opaque_b < opaque_a;
176   EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_a));
177   EXPECT_TRUE(HasNonceTokenBeenInitialized(opaque_b));
178
179   if (should_swap)
180     std::swap(opaque_a, opaque_b);
181   EXPECT_LT(opaque_a, opaque_b);
182   EXPECT_FALSE(opaque_b < opaque_a);
183
184   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_b, false));
185   EXPECT_TRUE(DoEqualityComparisons(opaque_b, opaque_b, true));
186   EXPECT_TRUE(DoEqualityComparisons(opaque_a, opaque_a, true));
187
188   EXPECT_LT(opaque_a, url::Origin::Create(GURL("http://www.google.com")));
189   EXPECT_LT(opaque_b, url::Origin::Create(GURL("http://www.google.com")));
190
191   EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL("about:blank"), opaque_b));
192   EXPECT_EQ(opaque_b, url::Origin::Resolve(GURL("about:srcdoc"), opaque_b));
193   EXPECT_EQ(opaque_b,
194             url::Origin::Resolve(GURL("about:blank?hello#whee"), opaque_b));
195
196   const char* const urls[] = {
197       "data:text/html,Hello!",
198       "javascript:alert(1)",
199       "about:blank",
200       "file://example.com:443/etc/passwd",
201       "unknown-scheme:foo",
202       "unknown-scheme://bar",
203       "http",
204       "http:",
205       "http:/",
206       "http://",
207       "http://:",
208       "http://:1",
209       "yay",
210       "http::///invalid.example.com/",
211       "blob:null/foo",                   // blob:null (actually a valid URL)
212       "blob:data:foo",                   // blob + data (which is nonstandard)
213       "blob:about://blank/",             // blob + about (which is nonstandard)
214       "blob:about:blank/",               // blob + about (which is nonstandard)
215       "filesystem:http://example.com/",  // Invalid (missing /type/)
216       "filesystem:local-but-nonstandard:baz./type/",  // fs requires standard
217       "filesystem:local-but-nonstandard://hostname/type/",
218       "filesystem:unknown-scheme://hostname/type/",
219       "local-but-nonstandar:foo",  // Prefix of registered scheme.
220       "but-nonstandard:foo",       // Suffix of registered scheme.
221       "local-and-standard:",       // Standard scheme needs a hostname.
222       "standard-but-noaccess:",    // Standard scheme needs a hostname.
223       "blob:blob:http://www.example.com/guid-goes-here",  // Double blob.
224   };
225
226   for (auto* test_url : urls) {
227     SCOPED_TRACE(test_url);
228     GURL url(test_url);
229     const url::Origin opaque_origin;
230
231     // Opaque origins returned by Origin::Create().
232     {
233       Origin origin = Origin::Create(url);
234       EXPECT_EQ("", origin.scheme());
235       EXPECT_EQ("", origin.host());
236       EXPECT_EQ(0, origin.port());
237       EXPECT_TRUE(origin.opaque());
238       // An origin is always same-origin with itself.
239       EXPECT_EQ(origin, origin);
240       EXPECT_NE(origin, url::Origin());
241       EXPECT_EQ(SchemeHostPort(), origin.GetTupleOrPrecursorTupleIfOpaque());
242       // A copy of |origin| should be same-origin as well.
243       Origin origin_copy = origin;
244       EXPECT_EQ("", origin_copy.scheme());
245       EXPECT_EQ("", origin_copy.host());
246       EXPECT_EQ(0, origin_copy.port());
247       EXPECT_TRUE(origin_copy.opaque());
248       EXPECT_EQ(origin, origin_copy);
249       // And it should always be cross-origin to another opaque Origin.
250       EXPECT_NE(origin, opaque_origin);
251       // Re-creating from the URL should also be cross-origin.
252       EXPECT_NE(origin, Origin::Create(url));
253
254       ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
255     }
256   }
257 }
258
259 TEST_F(OriginTest, ConstructFromTuple) {
260   struct TestCases {
261     const char* const scheme;
262     const char* const host;
263     const uint16_t port;
264   } cases[] = {
265       {"http", "example.com", 80},
266       {"http", "example.com", 123},
267       {"https", "example.com", 443},
268   };
269
270   for (const auto& test_case : cases) {
271     testing::Message scope_message;
272     scope_message << test_case.scheme << "://" << test_case.host << ":"
273                   << test_case.port;
274     SCOPED_TRACE(scope_message);
275     Origin origin = Origin::CreateFromNormalizedTuple(
276         test_case.scheme, test_case.host, test_case.port);
277
278     EXPECT_EQ(test_case.scheme, origin.scheme());
279     EXPECT_EQ(test_case.host, origin.host());
280     EXPECT_EQ(test_case.port, origin.port());
281   }
282 }
283
284 TEST_F(OriginTest, ConstructFromGURL) {
285   Origin different_origin =
286       Origin::Create(GURL("https://not-in-the-list.test/"));
287
288   struct TestCases {
289     const char* const url;
290     const char* const expected_scheme;
291     const char* const expected_host;
292     const uint16_t expected_port;
293   } cases[] = {
294       // IP Addresses
295       {"http://192.168.9.1/", "http", "192.168.9.1", 80},
296       {"http://[2001:db8::1]/", "http", "[2001:db8::1]", 80},
297       {"http://1/", "http", "0.0.0.1", 80},
298       {"http://1:1/", "http", "0.0.0.1", 1},
299       {"http://3232237825/", "http", "192.168.9.1", 80},
300
301       // Punycode
302       {"http://☃.net/", "http", "xn--n3h.net", 80},
303       {"blob:http://☃.net/", "http", "xn--n3h.net", 80},
304
305       // Generic URLs
306       {"http://example.com/", "http", "example.com", 80},
307       {"http://example.com:123/", "http", "example.com", 123},
308       {"https://example.com/", "https", "example.com", 443},
309       {"https://example.com:123/", "https", "example.com", 123},
310       {"http://user:pass@example.com/", "http", "example.com", 80},
311       {"http://example.com:123/?query", "http", "example.com", 123},
312       {"https://example.com/#1234", "https", "example.com", 443},
313       {"https://u:p@example.com:123/?query#1234", "https", "example.com", 123},
314
315       // Registered URLs
316       {"ftp://example.com/", "ftp", "example.com", 21},
317       {"ws://example.com/", "ws", "example.com", 80},
318       {"wss://example.com/", "wss", "example.com", 443},
319       {"wss://user:pass@example.com/", "wss", "example.com", 443},
320
321       // Scheme (registered in SetUp()) that's both local and standard.
322       // TODO: Is it really appropriate to do network-host canonicalization of
323       // schemes without ports?
324       {"local-and-standard:20", "local-and-standard", "0.0.0.20", 0},
325       {"local-and-standard:20.", "local-and-standard", "0.0.0.20", 0},
326       {"local-and-standard:↑↑↓↓←→←→ba.↑↑↓↓←→←→ba.0.bg", "local-and-standard",
327        "xn--ba-rzuadaibfa.xn--ba-rzuadaibfa.0.bg", 0},
328       {"local-and-standard:foo", "local-and-standard", "foo", 0},
329       {"local-and-standard://bar:20", "local-and-standard", "bar", 0},
330       {"local-and-standard:baz.", "local-and-standard", "baz.", 0},
331       {"local-and-standard:baz..", "local-and-standard", "baz..", 0},
332       {"local-and-standard:baz..bar", "local-and-standard", "baz..bar", 0},
333       {"local-and-standard:baz...", "local-and-standard", "baz...", 0},
334
335       // Scheme (registered in SetUp()) that's local but nonstandard. These
336       // always have empty hostnames, but are allowed to be url::Origins.
337       {"local-but-nonstandard:", "local-but-nonstandard", "", 0},
338       {"local-but-nonstandard:foo", "local-but-nonstandard", "", 0},
339       {"local-but-nonstandard://bar", "local-but-nonstandard", "", 0},
340       {"also-local-but-nonstandard://bar", "also-local-but-nonstandard", "", 0},
341
342       // Scheme (registered in SetUp()) that's standard but marked as noaccess.
343       // url::Origin doesn't currently take the noaccess property into account,
344       // so these aren't expected to result in opaque origins.
345       {"standard-but-noaccess:foo", "standard-but-noaccess", "foo", 0},
346       {"standard-but-noaccess://bar", "standard-but-noaccess", "bar", 0},
347
348       // file: URLs
349       {"file:///etc/passwd", "file", "", 0},
350       {"file://example.com/etc/passwd", "file", "example.com", 0},
351
352       // Filesystem:
353       {"filesystem:http://example.com/type/", "http", "example.com", 80},
354       {"filesystem:http://example.com:123/type/", "http", "example.com", 123},
355       {"filesystem:https://example.com/type/", "https", "example.com", 443},
356       {"filesystem:https://example.com:123/type/", "https", "example.com", 123},
357       {"filesystem:local-and-standard:baz./type/", "local-and-standard", "baz.",
358        0},
359
360       // Blob:
361       {"blob:http://example.com/guid-goes-here", "http", "example.com", 80},
362       {"blob:http://example.com:123/guid-goes-here", "http", "example.com",
363        123},
364       {"blob:https://example.com/guid-goes-here", "https", "example.com", 443},
365       {"blob:http://u:p@example.com/guid-goes-here", "http", "example.com", 80},
366   };
367
368   for (const auto& test_case : cases) {
369     SCOPED_TRACE(test_case.url);
370     GURL url(test_case.url);
371     EXPECT_TRUE(url.is_valid());
372     Origin origin = Origin::Create(url);
373     EXPECT_EQ(test_case.expected_scheme, origin.scheme());
374     EXPECT_EQ(test_case.expected_host, origin.host());
375     EXPECT_EQ(test_case.expected_port, origin.port());
376     EXPECT_FALSE(origin.opaque());
377     EXPECT_EQ(origin, origin);
378     EXPECT_NE(different_origin, origin);
379     EXPECT_NE(origin, different_origin);
380     EXPECT_EQ(origin, Origin::Resolve(GURL("about:blank"), origin));
381     EXPECT_EQ(origin, Origin::Resolve(GURL("about:blank?bar#foo"), origin));
382
383     ExpectParsedUrlsEqual(GURL(origin.Serialize()), origin.GetURL());
384
385     url::Origin derived_opaque =
386         Origin::Resolve(GURL("about:blank?bar#foo"), origin)
387             .DeriveNewOpaqueOrigin();
388     EXPECT_TRUE(derived_opaque.opaque());
389     EXPECT_NE(origin, derived_opaque);
390     EXPECT_TRUE(derived_opaque.GetTupleOrPrecursorTupleIfOpaque().IsValid());
391     EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(),
392               derived_opaque.GetTupleOrPrecursorTupleIfOpaque());
393     EXPECT_EQ(derived_opaque, derived_opaque);
394
395     url::Origin derived_opaque_via_data_url =
396         Origin::Resolve(GURL("data:text/html,baz"), origin);
397     EXPECT_TRUE(derived_opaque_via_data_url.opaque());
398     EXPECT_NE(origin, derived_opaque_via_data_url);
399     EXPECT_TRUE(derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque()
400                     .IsValid());
401     EXPECT_EQ(origin.GetTupleOrPrecursorTupleIfOpaque(),
402               derived_opaque_via_data_url.GetTupleOrPrecursorTupleIfOpaque());
403     EXPECT_NE(derived_opaque, derived_opaque_via_data_url);
404     EXPECT_NE(derived_opaque_via_data_url, derived_opaque);
405     EXPECT_NE(derived_opaque.DeriveNewOpaqueOrigin(), derived_opaque);
406     EXPECT_EQ(derived_opaque_via_data_url, derived_opaque_via_data_url);
407   }
408 }
409
410 TEST_F(OriginTest, Serialization) {
411   struct TestCases {
412     const char* const url;
413     const char* const expected;
414     const char* const expected_log;
415   } cases[] = {
416       {"http://192.168.9.1/", "http://192.168.9.1"},
417       {"http://[2001:db8::1]/", "http://[2001:db8::1]"},
418       {"http://☃.net/", "http://xn--n3h.net"},
419       {"http://example.com/", "http://example.com"},
420       {"http://example.com:123/", "http://example.com:123"},
421       {"https://example.com/", "https://example.com"},
422       {"https://example.com:123/", "https://example.com:123"},
423       {"file:///etc/passwd", "file://", "file:// [internally: file://]"},
424       {"file://example.com/etc/passwd", "file://",
425        "file:// [internally: file://example.com]"},
426       {"data:,", "null", "null [internally: (nonce TBD) anonymous]"},
427   };
428
429   for (const auto& test_case : cases) {
430     SCOPED_TRACE(test_case.url);
431     GURL url(test_case.url);
432     EXPECT_TRUE(url.is_valid());
433     Origin origin = Origin::Create(url);
434     std::string serialized = origin.Serialize();
435     ExpectParsedUrlsEqual(GURL(serialized), origin.GetURL());
436
437     EXPECT_EQ(test_case.expected, serialized);
438
439     // The '<<' operator sometimes produces additional information.
440     std::stringstream out;
441     out << origin;
442     if (test_case.expected_log)
443       EXPECT_EQ(test_case.expected_log, out.str());
444     else
445       EXPECT_EQ(test_case.expected, out.str());
446   }
447 }
448
449 TEST_F(OriginTest, Comparison) {
450   // These URLs are arranged in increasing order:
451   const char* const urls[] = {
452       "data:uniqueness", "http://a:80",  "http://b:80",
453       "https://a:80",    "https://b:80", "http://a:81",
454       "http://b:81",     "https://a:81", "https://b:81",
455   };
456   // Validate the comparison logic still works when creating a canonical origin,
457   // when any created opaque origins contain a nonce.
458   {
459     // Pre-create the origins, as the internal nonce for unique origins changes
460     // with each freshly-constructed Origin (that's not copied).
461     std::vector<Origin> origins;
462     for (const auto* test_url : urls)
463       origins.push_back(Origin::Create(GURL(test_url)));
464     for (size_t i = 0; i < origins.size(); i++) {
465       const Origin& current = origins[i];
466       for (size_t j = i; j < origins.size(); j++) {
467         const Origin& to_compare = origins[j];
468         EXPECT_EQ(i < j, current < to_compare) << i << " < " << j;
469         EXPECT_EQ(j < i, to_compare < current) << j << " < " << i;
470       }
471     }
472   }
473 }
474
475 TEST_F(OriginTest, UnsafelyCreate) {
476   struct TestCase {
477     const char* scheme;
478     const char* host;
479     uint16_t port;
480   } cases[] = {
481       {"http", "example.com", 80},
482       {"http", "example.com", 123},
483       {"https", "example.com", 443},
484       {"https", "example.com", 123},
485       {"http", "example.com", 0},  // 0 is a valid port for http.
486       {"file", "", 0},             // 0 indicates "no port" for file: scheme.
487       {"file", "example.com", 0},
488   };
489
490   for (const auto& test : cases) {
491     SCOPED_TRACE(testing::Message()
492                  << test.scheme << "://" << test.host << ":" << test.port);
493     base::Optional<url::Origin> origin =
494         url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
495             test.scheme, test.host, test.port);
496     ASSERT_TRUE(origin);
497     EXPECT_EQ(test.scheme, origin->scheme());
498     EXPECT_EQ(test.host, origin->host());
499     EXPECT_EQ(test.port, origin->port());
500     EXPECT_FALSE(origin->opaque());
501     EXPECT_TRUE(origin->IsSameOriginWith(*origin));
502
503     ExpectParsedUrlsEqual(GURL(origin->Serialize()), origin->GetURL());
504
505     base::UnguessableToken nonce = base::UnguessableToken::Create();
506     base::Optional<url::Origin> opaque_origin =
507         UnsafelyCreateOpaqueOriginWithoutNormalization(
508             test.scheme, test.host, test.port, CreateNonce(nonce));
509     ASSERT_TRUE(opaque_origin);
510     EXPECT_TRUE(opaque_origin->opaque());
511     EXPECT_FALSE(*opaque_origin == origin);
512     EXPECT_EQ(opaque_origin->GetTupleOrPrecursorTupleIfOpaque(),
513               origin->GetTupleOrPrecursorTupleIfOpaque());
514     EXPECT_EQ(opaque_origin,
515               UnsafelyCreateOpaqueOriginWithoutNormalization(
516                   test.scheme, test.host, test.port, CreateNonce(nonce)));
517     EXPECT_FALSE(*opaque_origin == origin->DeriveNewOpaqueOrigin());
518   }
519 }
520
521 TEST_F(OriginTest, UnsafelyCreateUniqueOnInvalidInput) {
522   url::AddStandardScheme("host-only", url::SCHEME_WITH_HOST);
523   url::AddStandardScheme("host-port-only", url::SCHEME_WITH_HOST_AND_PORT);
524   struct TestCases {
525     const char* scheme;
526     const char* host;
527     uint16_t port = 80;
528   } cases[] = {{"", "", 33},
529                {"data", "", 0},
530                {"blob", "", 0},
531                {"filesystem", "", 0},
532                {"data", "example.com"},
533                {"http", "☃.net"},
534                {"http\nmore", "example.com"},
535                {"http\rmore", "example.com"},
536                {"http\n", "example.com"},
537                {"http\r", "example.com"},
538                {"http", "example.com\nnot-example.com"},
539                {"http", "example.com\rnot-example.com"},
540                {"http", "example.com\n"},
541                {"http", "example.com\r"},
542                {"unknown-scheme", "example.com"},
543                {"host-only", "\r", 0},
544                {"host-only", "example.com", 22},
545                {"file", "", 123}};  // file: shouldn't have a port.
546
547   for (const auto& test : cases) {
548     SCOPED_TRACE(testing::Message()
549                  << test.scheme << "://" << test.host << ":" << test.port);
550     EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization(
551         test.scheme, test.host, test.port, CreateNonce()));
552     EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
553         test.scheme, test.host, test.port));
554   }
555
556   // An empty scheme/host/port tuple is not a valid tuple origin.
557   EXPECT_FALSE(
558       url::Origin::UnsafelyCreateTupleOriginWithoutNormalization("", "", 0));
559
560   // Opaque origins with unknown precursors are allowed.
561   base::UnguessableToken token = base::UnguessableToken::Create();
562   base::Optional<url::Origin> anonymous_opaque =
563       UnsafelyCreateOpaqueOriginWithoutNormalization("", "", 0,
564                                                      CreateNonce(token));
565   ASSERT_TRUE(anonymous_opaque)
566       << "An invalid tuple is a valid input to "
567       << "UnsafelyCreateOpaqueOriginWithoutNormalization, so long as it is "
568       << "the canonical form of the invalid tuple.";
569   EXPECT_TRUE(anonymous_opaque->opaque());
570   EXPECT_EQ(GetNonce(anonymous_opaque.value()), token);
571   EXPECT_EQ(anonymous_opaque->GetTupleOrPrecursorTupleIfOpaque(),
572             url::SchemeHostPort());
573 }
574
575 TEST_F(OriginTest, UnsafelyCreateUniqueViaEmbeddedNulls) {
576   struct TestCases {
577     base::StringPiece scheme;
578     base::StringPiece host;
579     uint16_t port = 80;
580   } cases[] = {{{"http\0more", 9}, {"example.com", 11}},
581                {{"http\0", 5}, {"example.com", 11}},
582                {{"\0http", 5}, {"example.com", 11}},
583                {{"http"}, {"example.com\0not-example.com", 27}},
584                {{"http"}, {"example.com\0", 12}},
585                {{"http"}, {"\0example.com", 12}},
586                {{""}, {"\0", 1}, 0},
587                {{"\0", 1}, {""}, 0}};
588
589   for (const auto& test : cases) {
590     SCOPED_TRACE(testing::Message()
591                  << test.scheme << "://" << test.host << ":" << test.port);
592     EXPECT_FALSE(url::Origin::UnsafelyCreateTupleOriginWithoutNormalization(
593         test.scheme, test.host, test.port));
594     EXPECT_FALSE(UnsafelyCreateOpaqueOriginWithoutNormalization(
595         test.scheme, test.host, test.port, CreateNonce()));
596   }
597 }
598
599 TEST_F(OriginTest, DomainIs) {
600   const struct {
601     const char* url;
602     const char* lower_ascii_domain;
603     bool expected_domain_is;
604   } kTestCases[] = {
605       {"http://google.com/foo", "google.com", true},
606       {"http://www.google.com:99/foo", "google.com", true},
607       {"http://www.google.com.cn/foo", "google.com", false},
608       {"http://www.google.comm", "google.com", false},
609       {"http://www.iamnotgoogle.com/foo", "google.com", false},
610       {"http://www.google.com/foo", "Google.com", false},
611
612       // If the host ends with a dot, it matches domains with or without a dot.
613       {"http://www.google.com./foo", "google.com", true},
614       {"http://www.google.com./foo", "google.com.", true},
615       {"http://www.google.com./foo", ".com", true},
616       {"http://www.google.com./foo", ".com.", true},
617
618       // But, if the host doesn't end with a dot and the input domain does, then
619       // it's considered to not match.
620       {"http://google.com/foo", "google.com.", false},
621
622       // If the host ends with two dots, it doesn't match.
623       {"http://www.google.com../foo", "google.com", false},
624
625       // Filesystem scheme.
626       {"filesystem:http://www.google.com:99/foo/", "google.com", true},
627       {"filesystem:http://www.iamnotgoogle.com/foo/", "google.com", false},
628
629       // File scheme.
630       {"file:///home/user/text.txt", "", false},
631       {"file:///home/user/text.txt", "txt", false},
632   };
633
634   for (const auto& test_case : kTestCases) {
635     SCOPED_TRACE(testing::Message()
636                  << "(url, domain): (" << test_case.url << ", "
637                  << test_case.lower_ascii_domain << ")");
638     GURL url(test_case.url);
639     ASSERT_TRUE(url.is_valid());
640     Origin origin = Origin::Create(url);
641
642     EXPECT_EQ(test_case.expected_domain_is,
643               origin.DomainIs(test_case.lower_ascii_domain));
644     EXPECT_FALSE(
645         origin.DeriveNewOpaqueOrigin().DomainIs(test_case.lower_ascii_domain));
646   }
647
648   // If the URL is invalid, DomainIs returns false.
649   GURL invalid_url("google.com");
650   ASSERT_FALSE(invalid_url.is_valid());
651   EXPECT_FALSE(Origin::Create(invalid_url).DomainIs("google.com"));
652
653   // Unique origins.
654   EXPECT_FALSE(Origin().DomainIs(""));
655   EXPECT_FALSE(Origin().DomainIs("com"));
656 }
657
658 TEST_F(OriginTest, DebugAlias) {
659   Origin origin1 = Origin::Create(GURL("https://foo.com/bar"));
660   DEBUG_ALIAS_FOR_ORIGIN(origin1_debug_alias, origin1);
661   EXPECT_STREQ("https://foo.com", origin1_debug_alias);
662 }
663
664 TEST_F(OriginTest, NonStandardScheme) {
665   Origin origin = Origin::Create(GURL("cow://"));
666   EXPECT_TRUE(origin.opaque());
667 }
668
669 TEST_F(OriginTest, NonStandardSchemeWithAndroidWebViewHack) {
670   EnableNonStandardSchemesForAndroidWebView();
671   Origin origin = Origin::Create(GURL("cow://"));
672   EXPECT_FALSE(origin.opaque());
673   EXPECT_EQ("cow", origin.scheme());
674   EXPECT_EQ("", origin.host());
675   EXPECT_EQ(0, origin.port());
676 }
677
678 TEST_F(OriginTest, CanBeDerivedFrom) {
679   AddStandardScheme("new-standard", SchemeType::SCHEME_WITH_HOST);
680   Origin opaque_unique_origin = Origin();
681
682   Origin regular_origin = Origin::Create(GURL("https://a.com/"));
683   Origin opaque_precursor_origin = regular_origin.DeriveNewOpaqueOrigin();
684
685   Origin file_origin = Origin::Create(GURL("file:///foo/bar"));
686   Origin file_opaque_precursor_origin = file_origin.DeriveNewOpaqueOrigin();
687   Origin file_host_origin = Origin::Create(GURL("file://a.com/foo/bar"));
688   Origin file_host_opaque_precursor_origin =
689       file_host_origin.DeriveNewOpaqueOrigin();
690
691   Origin non_standard_scheme_origin =
692       Origin::Create(GURL("non-standard-scheme:foo"));
693   Origin non_standard_opaque_precursor_origin =
694       non_standard_scheme_origin.DeriveNewOpaqueOrigin();
695
696   // Also, add new standard scheme that is local to the test.
697   Origin new_standard_origin = Origin::Create(GURL("new-standard://host/"));
698   Origin new_standard_opaque_precursor_origin =
699       new_standard_origin.DeriveNewOpaqueOrigin();
700
701   // No access schemes always get unique opaque origins.
702   Origin no_access_origin =
703       Origin::Create(GURL("standard-but-noaccess://b.com"));
704   Origin no_access_opaque_precursor_origin =
705       no_access_origin.DeriveNewOpaqueOrigin();
706
707   Origin local_non_standard_origin =
708       Origin::Create(GURL("local-but-nonstandard://a.com"));
709   Origin local_non_standard_opaque_precursor_origin =
710       local_non_standard_origin.DeriveNewOpaqueOrigin();
711
712   // Call origin.CanBeDerivedFrom(url) for each of the following test cases
713   // and ensure that it returns |expected_value|
714   const struct {
715     const char* url;
716     Origin* origin;
717     bool expected_value;
718   } kTestCases[] = {
719       {"https://a.com", &regular_origin, true},
720       // Web URL can commit in an opaque origin with precursor information.
721       // Example: iframe sandbox navigated to a.com.
722       {"https://a.com", &opaque_precursor_origin, true},
723       // URL that comes from the web can never commit in an opaque unique
724       // origin. It must have precursor information.
725       {"https://a.com", &opaque_unique_origin, false},
726
727       // Cross-origin URLs should never work.
728       {"https://b.com", &regular_origin, false},
729       {"https://b.com", &opaque_precursor_origin, false},
730
731       // data: URL can never commit in a regular, non-opaque origin.
732       {"data:text/html,foo", &regular_origin, false},
733       // This is the default case: data: URLs commit in opaque origin carrying
734       // precursor information for the origin that created them.
735       {"data:text/html,foo", &opaque_precursor_origin, true},
736       // Browser-initiated navigations can result in data: URL committing in
737       // opaque unique origin.
738       {"data:text/html,foo", &opaque_unique_origin, true},
739
740       // about:blank can commit in regular origin (default case for iframes).
741       {"about:blank", &regular_origin, true},
742       // This can happen if data: URL that originated at a.com creates an
743       // about:blank iframe.
744       {"about:blank", &opaque_precursor_origin, true},
745       // Browser-initiated navigations can result in about:blank URL committing
746       // in opaque unique origin.
747       {"about:blank", &opaque_unique_origin, true},
748
749       // Default behavior of srcdoc is to inherit the origin of the parent
750       // document.
751       {"about:srcdoc", &regular_origin, true},
752       // This happens for sandboxed srcdoc iframe.
753       {"about:srcdoc", &opaque_precursor_origin, true},
754       // This can happen with browser-initiated navigation to about:blank or
755       // data: URL, which in turn add srcdoc iframe.
756       {"about:srcdoc", &opaque_unique_origin, true},
757
758       // Just like srcdoc, blob: URLs can be created in all the cases.
759       {"blob:https://a.com/foo", &regular_origin, true},
760       {"blob:https://a.com/foo", &opaque_precursor_origin, true},
761       {"blob:https://a.com/foo", &opaque_unique_origin, true},
762
763       {"filesystem:https://a.com/foo", &regular_origin, true},
764       {"filesystem:https://a.com/foo", &opaque_precursor_origin, true},
765       // Unlike blob: URLs, filesystem: ones cannot be created in an unique
766       // opaque origin.
767       {"filesystem:https://a.com/foo", &opaque_unique_origin, false},
768
769       // file: URLs cannot result in regular web origins, regardless of
770       // opaqueness.
771       {"file:///etc/passwd", &regular_origin, false},
772       {"file:///etc/passwd", &opaque_precursor_origin, false},
773       // However, they can result in regular file: origin and an opaque one
774       // containing another file: origin as precursor.
775       {"file:///etc/passwd", &file_origin, true},
776       {"file:///etc/passwd", &file_opaque_precursor_origin, true},
777       // It should not be possible to get an opaque unique origin for file:
778       // as it is a standard scheme and will always result in a tuple origin
779       // or will always be derived by other origin.
780       // Note: file:// URLs should become unique opaque origins at some point.
781       {"file:///etc/passwd", &opaque_unique_origin, false},
782
783       // The same set as above, but including a host.
784       {"file://a.com/etc/passwd", &regular_origin, false},
785       {"file://a.com/etc/passwd", &opaque_precursor_origin, false},
786       {"file://a.com/etc/passwd", &file_host_origin, true},
787       {"file://a.com/etc/passwd", &file_host_opaque_precursor_origin, true},
788       {"file://a.com/etc/passwd", &opaque_unique_origin, false},
789
790       // Locally registered standard scheme should behave the same way
791       // as built-in standard schemes.
792       {"new-standard://host/foo", &new_standard_origin, true},
793       {"new-standard://host/foo", &new_standard_opaque_precursor_origin, true},
794       {"new-standard://host/foo", &opaque_unique_origin, false},
795       {"new-standard://host2/foo", &new_standard_origin, false},
796       {"new-standard://host2/foo", &new_standard_opaque_precursor_origin,
797        false},
798
799       // A non-standard scheme should never commit in an standard origin or
800       // opaque origin with standard precursor information.
801       {"non-standard-scheme://a.com/foo", &regular_origin, false},
802       {"non-standard-scheme://a.com/foo", &opaque_precursor_origin, false},
803       // However, it should be fine to commit in unique opaque origins or in its
804       // own origin.
805       // Note: since non-standard scheme URLs don't parse out anything
806       // but the scheme, using a random different hostname here would work.
807       {"non-standard-scheme://b.com/foo2", &opaque_unique_origin, true},
808       {"non-standard-scheme://b.com/foo3", &non_standard_scheme_origin, true},
809       {"non-standard-scheme://b.com/foo4",
810        &non_standard_opaque_precursor_origin, true},
811
812       // No access scheme can only commit in opaque origin.
813       {"standard-but-noaccess://a.com/foo", &regular_origin, false},
814       {"standard-but-noaccess://a.com/foo", &opaque_precursor_origin, false},
815       {"standard-but-noaccess://a.com/foo", &opaque_unique_origin, true},
816       {"standard-but-noaccess://a.com/foo", &no_access_origin, false},
817       {"standard-but-noaccess://a.com/foo", &no_access_opaque_precursor_origin,
818        false},
819       {"standard-but-noaccess://b.com/foo", &no_access_origin, false},
820       {"standard-but-noaccess://b.com/foo", &no_access_opaque_precursor_origin,
821        true},
822
823       // Local schemes can be non-standard, verify they also work as expected.
824       {"local-but-nonstandard://a.com", &regular_origin, false},
825       {"local-but-nonstandard://a.com", &opaque_precursor_origin, false},
826       {"local-but-nonstandard://a.com", &opaque_unique_origin, true},
827       {"local-but-nonstandard://a.com", &local_non_standard_origin, true},
828       {"local-but-nonstandard://a.com",
829        &local_non_standard_opaque_precursor_origin, true},
830   };
831
832   for (const auto& test_case : kTestCases) {
833     SCOPED_TRACE(testing::Message() << "(origin, url): (" << *test_case.origin
834                                     << ", " << test_case.url << ")");
835     EXPECT_EQ(test_case.expected_value,
836               test_case.origin->CanBeDerivedFrom(GURL(test_case.url)));
837   }
838 }
839
840 TEST_F(OriginTest, GetDebugString) {
841   Origin http_origin = Origin::Create(GURL("http://192.168.9.1"));
842   EXPECT_STREQ(http_origin.GetDebugString().c_str(), "http://192.168.9.1");
843
844   Origin http_opaque_origin = http_origin.DeriveNewOpaqueOrigin();
845   EXPECT_THAT(
846       http_opaque_origin.GetDebugString().c_str(),
847       ::testing::MatchesRegex(
848           "null \\[internally: \\(\\w*\\) derived from http://192.168.9.1\\]"));
849
850   Origin data_origin = Origin::Create(GURL("data:"));
851   EXPECT_STREQ(data_origin.GetDebugString().c_str(),
852                "null [internally: (nonce TBD) anonymous]");
853
854   // The nonce of the origin will be initialized if a new opaque origin is
855   // derived.
856   Origin data_derived_origin = data_origin.DeriveNewOpaqueOrigin();
857   EXPECT_THAT(
858       data_derived_origin.GetDebugString().c_str(),
859       ::testing::MatchesRegex("null \\[internally: \\(\\w*\\) anonymous\\]"));
860
861   Origin file_origin = Origin::Create(GURL("file:///etc/passwd"));
862   EXPECT_STREQ(file_origin.GetDebugString().c_str(),
863                "file:// [internally: file://]");
864
865   Origin file_server_origin =
866       Origin::Create(GURL("file://example.com/etc/passwd"));
867   EXPECT_STREQ(file_server_origin.GetDebugString().c_str(),
868                "file:// [internally: file://example.com]");
869 }
870
871 TEST_F(OriginTest, Deserialize) {
872   std::vector<GURL> valid_urls = {
873       GURL("https://a.com"),         GURL("http://a"),
874       GURL("http://a:80"),           GURL("file://a.com/etc/passwd"),
875       GURL("file:///etc/passwd"),    GURL("http://192.168.1.1"),
876       GURL("http://[2001:db8::1]/"),
877   };
878   for (const GURL& url : valid_urls) {
879     SCOPED_TRACE(url.spec());
880     Origin origin = Origin::Create(url);
881     base::Optional<std::string> serialized = SerializeWithNonce(origin);
882     ASSERT_TRUE(serialized);
883
884     base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
885     ASSERT_TRUE(deserialized.has_value());
886
887     EXPECT_TRUE(DoEqualityComparisons(origin, deserialized.value(), true));
888     EXPECT_EQ(origin.GetDebugString(), deserialized.value().GetDebugString());
889   }
890 }
891
892 TEST_F(OriginTest, DeserializeInvalid) {
893   EXPECT_EQ(base::nullopt, Deserialize(std::string()));
894   EXPECT_EQ(base::nullopt, Deserialize("deadbeef"));
895   EXPECT_EQ(base::nullopt, Deserialize("0123456789"));
896   EXPECT_EQ(base::nullopt, Deserialize("https://a.com"));
897   EXPECT_EQ(base::nullopt, Deserialize("https://192.168.1.1"));
898 }
899
900 TEST_F(OriginTest, SerializeTBDNonce) {
901   std::vector<GURL> invalid_urls = {
902       GURL("data:uniqueness"),       GURL("data:,"),
903       GURL("data:text/html,Hello!"), GURL("javascript:alert(1)"),
904       GURL("about:blank"),           GURL("google.com"),
905   };
906   for (const GURL& url : invalid_urls) {
907     SCOPED_TRACE(url.spec());
908     Origin origin = Origin::Create(url);
909     base::Optional<std::string> serialized = SerializeWithNonce(origin);
910     base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
911     ASSERT_TRUE(deserialized.has_value());
912
913     // Can't use DoEqualityComparisons here since empty nonces are never ==
914     // unless they are the same object.
915     EXPECT_EQ(origin.GetDebugString(), deserialized.value().GetDebugString());
916   }
917
918   // Same basic test as above, but without a GURL to create tuple_.
919   Origin opaque;
920   base::Optional<std::string> serialized = SerializeWithNonce(opaque);
921   ASSERT_TRUE(serialized);
922
923   base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
924   ASSERT_TRUE(deserialized.has_value());
925
926   // Can't use DoEqualityComparisons here since empty nonces are never == unless
927   // they are the same object.
928   EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
929 }
930
931 TEST_F(OriginTest, DeserializeValidNonce) {
932   Origin opaque;
933   GetNonce(opaque);
934
935   base::Optional<std::string> serialized = SerializeWithNonce(opaque);
936   ASSERT_TRUE(serialized);
937
938   base::Optional<Origin> deserialized = Deserialize(std::move(*serialized));
939   ASSERT_TRUE(deserialized.has_value());
940
941   EXPECT_TRUE(DoEqualityComparisons(opaque, deserialized.value(), true));
942   EXPECT_EQ(opaque.GetDebugString(), deserialized.value().GetDebugString());
943 }
944
945 }  // namespace url