[M120 Migration] Set IO|GPU thread type with higher priorites
[platform/framework/web/chromium-efl.git] / url / origin_abstract_tests.h
1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef URL_ORIGIN_ABSTRACT_TESTS_H_
6 #define URL_ORIGIN_ABSTRACT_TESTS_H_
7
8 #include <string>
9 #include <string_view>
10 #include <type_traits>
11
12 #include "base/containers/contains.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 #include "url/gurl.h"
15 #include "url/origin.h"
16 #include "url/scheme_host_port.h"
17 #include "url/url_util.h"
18
19 namespace url {
20
21 void ExpectParsedUrlsEqual(const GURL& a, const GURL& b);
22
23 // AbstractOriginTest below abstracts away differences between url::Origin and
24 // blink::SecurityOrigin by parametrizing the tests with a class that has to
25 // expose the same public members as UrlOriginTestTraits below.
26 class UrlOriginTestTraits {
27  public:
28   using OriginType = Origin;
29
30   // Constructing an origin.
31   static OriginType CreateOriginFromString(std::string_view s);
32   static OriginType CreateUniqueOpaqueOrigin();
33   static OriginType CreateWithReferenceOrigin(
34       std::string_view url,
35       const OriginType& reference_origin);
36   static OriginType DeriveNewOpaqueOrigin(const OriginType& reference_origin);
37
38   // Accessors for origin properties.
39   static bool IsOpaque(const OriginType& origin);
40   static std::string GetScheme(const OriginType& origin);
41   static std::string GetHost(const OriginType& origin);
42   static uint16_t GetPort(const OriginType& origin);
43   static SchemeHostPort GetTupleOrPrecursorTupleIfOpaque(
44       const OriginType& origin);
45
46   // Wrappers for other instance methods of OriginType.
47   static bool IsSameOrigin(const OriginType& a, const OriginType& b);
48   static std::string Serialize(const OriginType& origin);
49
50   // "Accessors" of URL properties.
51   //
52   // TODO(lukasza): Consider merging together OriginTraitsBase here and
53   // UrlTraitsBase in //url/gurl_abstract_tests.h.
54   static bool IsValidUrl(std::string_view str);
55
56   // Only static members = no constructors are needed.
57   UrlOriginTestTraits() = delete;
58 };
59
60 // Test suite for tests that cover both url::Origin and blink::SecurityOrigin.
61 template <typename TOriginTraits>
62 class AbstractOriginTest : public testing::Test {
63  public:
64   void SetUp() override {
65     const char* kSchemesToRegister[] = {
66         "noaccess",
67         "std-with-host",
68         "noaccess-std-with-host",
69         "local",
70         "local-noaccess",
71         "local-std-with-host",
72         "local-noaccess-std-with-host",
73         "also-local",
74         "sec",
75         "sec-std-with-host",
76         "sec-noaccess",
77     };
78     for (const char* kScheme : kSchemesToRegister) {
79       std::string scheme(kScheme);
80       if (base::Contains(scheme, "noaccess"))
81         AddNoAccessScheme(kScheme);
82       if (base::Contains(scheme, "std-with-host"))
83         AddStandardScheme(kScheme, SchemeType::SCHEME_WITH_HOST);
84       if (base::Contains(scheme, "local"))
85         AddLocalScheme(kScheme);
86       if (base::Contains(scheme, "sec"))
87         AddSecureScheme(kScheme);
88     }
89   }
90
91  protected:
92   // Wrappers that help ellide away TOriginTraits.
93   //
94   // Note that calling the wrappers needs to be prefixed with `this->...` to
95   // avoid hitting: explicit qualification required to use member 'IsOpaque'
96   // from dependent base class.
97   using OriginType = typename TOriginTraits::OriginType;
98   OriginType CreateOriginFromString(std::string_view s) {
99     return TOriginTraits::CreateOriginFromString(s);
100   }
101   OriginType CreateUniqueOpaqueOrigin() {
102     return TOriginTraits::CreateUniqueOpaqueOrigin();
103   }
104   OriginType CreateWithReferenceOrigin(std::string_view url,
105                                        const OriginType& reference_origin) {
106     return TOriginTraits::CreateWithReferenceOrigin(url, reference_origin);
107   }
108   OriginType DeriveNewOpaqueOrigin(const OriginType& reference_origin) {
109     return TOriginTraits::DeriveNewOpaqueOrigin(reference_origin);
110   }
111   bool IsOpaque(const OriginType& origin) {
112     return TOriginTraits::IsOpaque(origin);
113   }
114   std::string GetScheme(const OriginType& origin) {
115     return TOriginTraits::GetScheme(origin);
116   }
117   std::string GetHost(const OriginType& origin) {
118     return TOriginTraits::GetHost(origin);
119   }
120   uint16_t GetPort(const OriginType& origin) {
121     return TOriginTraits::GetPort(origin);
122   }
123   SchemeHostPort GetTupleOrPrecursorTupleIfOpaque(const OriginType& origin) {
124     return TOriginTraits::GetTupleOrPrecursorTupleIfOpaque(origin);
125   }
126   bool IsSameOrigin(const OriginType& a, const OriginType& b) {
127     bool is_a_same_with_b = TOriginTraits::IsSameOrigin(a, b);
128     bool is_b_same_with_a = TOriginTraits::IsSameOrigin(b, a);
129     EXPECT_EQ(is_a_same_with_b, is_b_same_with_a);
130     return is_a_same_with_b;
131   }
132   std::string Serialize(const OriginType& origin) {
133     return TOriginTraits::Serialize(origin);
134   }
135   bool IsValidUrl(std::string_view str) {
136     return TOriginTraits::IsValidUrl(str);
137   }
138
139 #define EXPECT_SAME_ORIGIN(a, b)                                 \
140   EXPECT_TRUE(this->IsSameOrigin((a), (b)))                      \
141       << "When checking if \"" << this->Serialize(a) << "\" is " \
142       << "same-origin with \"" << this->Serialize(b) << "\""
143
144 #define EXPECT_CROSS_ORIGIN(a, b)                                \
145   EXPECT_FALSE(this->IsSameOrigin((a), (b)))                     \
146       << "When checking if \"" << this->Serialize(a) << "\" is " \
147       << "cross-origin from \"" << this->Serialize(b) << "\""
148
149   void VerifyOriginInvariants(const OriginType& origin) {
150     // An origin is always same-origin with itself.
151     EXPECT_SAME_ORIGIN(origin, origin);
152
153     // A copy of |origin| should be same-origin as well.
154     auto origin_copy = origin;
155     EXPECT_EQ(this->GetScheme(origin), this->GetScheme(origin_copy));
156     EXPECT_EQ(this->GetHost(origin), this->GetHost(origin_copy));
157     EXPECT_EQ(this->GetPort(origin), this->GetPort(origin_copy));
158     EXPECT_EQ(this->IsOpaque(origin), this->IsOpaque(origin_copy));
159     EXPECT_SAME_ORIGIN(origin, origin_copy);
160
161     // An origin is always cross-origin from another, unique, opaque origin.
162     EXPECT_CROSS_ORIGIN(origin, this->CreateUniqueOpaqueOrigin());
163
164     // An origin is always cross-origin from another tuple origin.
165     auto different_tuple_origin =
166         this->CreateOriginFromString("https://not-in-the-list.test/");
167     EXPECT_CROSS_ORIGIN(origin, different_tuple_origin);
168
169     // Deriving an origin for "about:blank".
170     auto about_blank_origin1 =
171         this->CreateWithReferenceOrigin("about:blank", origin);
172     auto about_blank_origin2 =
173         this->CreateWithReferenceOrigin("about:blank?bar#foo", origin);
174     EXPECT_SAME_ORIGIN(origin, about_blank_origin1);
175     EXPECT_SAME_ORIGIN(origin, about_blank_origin2);
176
177     // Derived opaque origins.
178     std::vector<OriginType> derived_origins = {
179         this->DeriveNewOpaqueOrigin(origin),
180         this->CreateWithReferenceOrigin("data:text/html,baz", origin),
181         this->DeriveNewOpaqueOrigin(about_blank_origin1),
182     };
183     for (size_t i = 0; i < derived_origins.size(); i++) {
184       SCOPED_TRACE(testing::Message() << "Derived origin #" << i);
185       const OriginType& derived_origin = derived_origins[i];
186       EXPECT_TRUE(this->IsOpaque(derived_origin));
187       EXPECT_SAME_ORIGIN(derived_origin, derived_origin);
188       EXPECT_CROSS_ORIGIN(origin, derived_origin);
189       EXPECT_EQ(this->GetTupleOrPrecursorTupleIfOpaque(origin),
190                 this->GetTupleOrPrecursorTupleIfOpaque(derived_origin));
191     }
192   }
193
194   void VerifyUniqueOpaqueOriginInvariants(const OriginType& origin) {
195     if (!this->IsOpaque(origin)) {
196       ADD_FAILURE() << "Got unexpectedly non-opaque origin: "
197                     << this->Serialize(origin);
198       return;  // Skip other test assertions.
199     }
200
201     // Opaque origins should have an "empty" scheme, host and port.
202     EXPECT_EQ("", this->GetScheme(origin));
203     EXPECT_EQ("", this->GetHost(origin));
204     EXPECT_EQ(0, this->GetPort(origin));
205
206     // Unique opaque origins should have an empty precursor tuple.
207     EXPECT_EQ(SchemeHostPort(), this->GetTupleOrPrecursorTupleIfOpaque(origin));
208
209     // Serialization test.
210     EXPECT_EQ("null", this->Serialize(origin));
211
212     // Invariants that should hold for any origin.
213     VerifyOriginInvariants(origin);
214   }
215
216   void TestUniqueOpaqueOrigin(std::string_view test_input) {
217     auto origin = this->CreateOriginFromString(test_input);
218     this->VerifyUniqueOpaqueOriginInvariants(origin);
219
220     // Re-creating from the URL should be cross-origin.
221     auto origin_recreated_from_same_input =
222         this->CreateOriginFromString(test_input);
223     EXPECT_CROSS_ORIGIN(origin, origin_recreated_from_same_input);
224   }
225
226   void VerifyTupleOriginInvariants(const OriginType& origin,
227                                    const SchemeHostPort& expected_tuple) {
228     if (this->IsOpaque(origin)) {
229       ADD_FAILURE() << "Got unexpectedly opaque origin";
230       return;  // Skip other test assertions.
231     }
232     SCOPED_TRACE(testing::Message()
233                  << "Actual origin: " << this->Serialize(origin));
234
235     // Compare `origin` against the `expected_tuple`.
236     EXPECT_EQ(expected_tuple.scheme(), this->GetScheme(origin));
237     EXPECT_EQ(expected_tuple.host(), this->GetHost(origin));
238     EXPECT_EQ(expected_tuple.port(), this->GetPort(origin));
239     EXPECT_EQ(expected_tuple, this->GetTupleOrPrecursorTupleIfOpaque(origin));
240
241     // Serialization test.
242     //
243     // TODO(lukasza): Consider preserving the hostname when serializing file:
244     // URLs.  Dropping the hostname seems incompatible with section 6 of
245     // rfc6454.  Even though section 4 says that "the implementation MAY
246     // return an implementation-defined value", it seems that Chromium
247     // implementation *does* include the hostname in the origin SchemeHostPort
248     // tuple.
249     if (expected_tuple.scheme() != kFileScheme || expected_tuple.host() == "") {
250       EXPECT_SAME_ORIGIN(origin,
251                          this->CreateOriginFromString(this->Serialize(origin)));
252     }
253
254     // Invariants that should hold for any origin.
255     VerifyOriginInvariants(origin);
256   }
257
258  private:
259   ScopedSchemeRegistryForTests scoped_scheme_registry_;
260 };
261
262 TYPED_TEST_SUITE_P(AbstractOriginTest);
263
264 TYPED_TEST_P(AbstractOriginTest, NonStandardSchemeWithAndroidWebViewHack) {
265   EnableNonStandardSchemesForAndroidWebView();
266
267   // Regression test for https://crbug.com/896059.
268   auto origin = this->CreateOriginFromString("unknown-scheme://");
269   EXPECT_FALSE(this->IsOpaque(origin));
270   EXPECT_EQ("unknown-scheme", this->GetScheme(origin));
271   EXPECT_EQ("", this->GetHost(origin));
272   EXPECT_EQ(0, this->GetPort(origin));
273
274   // about:blank translates into an opaque origin, even in presence of
275   // EnableNonStandardSchemesForAndroidWebView.
276   origin = this->CreateOriginFromString("about:blank");
277   EXPECT_TRUE(this->IsOpaque(origin));
278 }
279
280 TYPED_TEST_P(AbstractOriginTest, OpaqueOriginsFromValidUrls) {
281   const char* kTestCases[] = {
282       // Built-in noaccess schemes.
283       "data:text/html,Hello!",
284       "javascript:alert(1)",
285       "about:blank",
286
287       // Opaque blob URLs.
288       "blob:null/foo",        // blob:null (actually a valid URL)
289       "blob:data:foo",        // blob + data (which is nonstandard)
290       "blob:about://blank/",  // blob + about (which is nonstandard)
291       "blob:about:blank/",    // blob + about (which is nonstandard)
292       "blob:blob:http://www.example.com/guid-goes-here",
293       "blob:filesystem:ws:b/.",
294       "blob:filesystem:ftp://a/b",
295       "blob:blob:file://localhost/foo/bar",
296   };
297
298   for (const char* test_input : kTestCases) {
299     SCOPED_TRACE(testing::Message() << "Test input: " << test_input);
300
301     // Verify that `origin` is opaque not just because `test_input` results is
302     // an invalid URL (because of a typo in the scheme name, or because of a
303     // technicality like having no host in a noaccess-std-with-host: scheme).
304     EXPECT_TRUE(this->IsValidUrl(test_input));
305
306     this->TestUniqueOpaqueOrigin(test_input);
307   }
308 }
309
310 TYPED_TEST_P(AbstractOriginTest, OpaqueOriginsFromInvalidUrls) {
311   // TODO(lukasza): Consider moving those to GURL/KURL tests that verify what
312   // inputs are parsed as an invalid URL.
313
314   const char* kTestCases[] = {
315       // Invalid file: URLs.
316       "file://example.com:443/etc/passwd",  // No port expected.
317
318       // Invalid HTTP URLs.
319       "http",
320       "http:",
321       "http:/",
322       "http://",
323       "http://:",
324       "http://:1",
325       "http::///invalid.example.com/",
326       "http://example.com:65536/",                    // Port out of range.
327       "http://example.com:-1/",                       // Port out of range.
328       "http://example.com:18446744073709551616/",     // Port = 2^64.
329       "http://example.com:18446744073709551616999/",  // Lots of port digits.
330
331       // Invalid filesystem URLs.
332       "filesystem:http://example.com/",  // Missing /type/.
333       "filesystem:local:baz./type/",
334       "filesystem:local://hostname/type/",
335       "filesystem:unknown-scheme://hostname/type/",
336       "filesystem:filesystem:http://example.org:88/foo/bar",
337
338       // Invalid IP addresses
339       "http://[]/",
340       "http://[2001:0db8:0000:0000:0000:0000:0000:0000:0001]/",  // 9 groups.
341
342       // Unknown scheme without a colon character (":") gives an invalid URL.
343       "unknown-scheme",
344
345       // Standard schemes require a hostname (and result in an opaque origin if
346       // the hostname is missing).
347       "local-std-with-host:",
348       "noaccess-std-with-host:",
349   };
350
351   for (const char* test_input : kTestCases) {
352     SCOPED_TRACE(testing::Message() << "Test input: " << test_input);
353
354     // All testcases here are expected to represent invalid URLs.
355     // an invalid URL (because of a type in scheme name, or because of a
356     // technicality like having no host in a noaccess-std-with-host: scheme).
357     EXPECT_FALSE(this->IsValidUrl(test_input));
358
359     // Invalid URLs should always result in an opaque origin.
360     this->TestUniqueOpaqueOrigin(test_input);
361   }
362 }
363
364 TYPED_TEST_P(AbstractOriginTest, TupleOrigins) {
365   struct TestCase {
366     const char* input;
367     SchemeHostPort expected_tuple;
368   } kTestCases[] = {
369       // file: URLs
370       {"file:///etc/passwd", {"file", "", 0}},
371       {"file://example.com/etc/passwd", {"file", "example.com", 0}},
372       {"file:///", {"file", "", 0}},
373       {"file://hostname/C:/dir/file.txt", {"file", "hostname", 0}},
374
375       // HTTP URLs
376       {"http://example.com/", {"http", "example.com", 80}},
377       {"http://example.com:80/", {"http", "example.com", 80}},
378       {"http://example.com:123/", {"http", "example.com", 123}},
379       {"http://example.com:0/", {"http", "example.com", 0}},
380       {"http://example.com:65535/", {"http", "example.com", 65535}},
381       {"https://example.com/", {"https", "example.com", 443}},
382       {"https://example.com:443/", {"https", "example.com", 443}},
383       {"https://example.com:123/", {"https", "example.com", 123}},
384       {"https://example.com:0/", {"https", "example.com", 0}},
385       {"https://example.com:65535/", {"https", "example.com", 65535}},
386       {"http://user:pass@example.com/", {"http", "example.com", 80}},
387       {"http://example.com:123/?query", {"http", "example.com", 123}},
388       {"https://example.com/#1234", {"https", "example.com", 443}},
389       {"https://u:p@example.com:123/?query#1234",
390        {"https", "example.com", 123}},
391       {"http://example/", {"http", "example", 80}},
392
393       // Blob URLs.
394       {"blob:http://example.com/guid-goes-here", {"http", "example.com", 80}},
395       {"blob:http://example.com:123/guid-goes-here",
396        {"http", "example.com", 123}},
397       {"blob:https://example.com/guid-goes-here",
398        {"https", "example.com", 443}},
399       {"blob:http://u:p@example.com/guid-goes-here",
400        {"http", "example.com", 80}},
401
402       // Filesystem URLs.
403       {"filesystem:http://example.com/type/", {"http", "example.com", 80}},
404       {"filesystem:http://example.com:123/type/", {"http", "example.com", 123}},
405       {"filesystem:https://example.com/type/", {"https", "example.com", 443}},
406       {"filesystem:https://example.com:123/type/",
407        {"https", "example.com", 123}},
408       {"filesystem:local-std-with-host:baz./type/",
409        {"local-std-with-host", "baz.", 0}},
410
411       // IP Addresses
412       {"http://192.168.9.1/", {"http", "192.168.9.1", 80}},
413       {"http://[2001:db8::1]/", {"http", "[2001:db8::1]", 80}},
414       {"http://[2001:0db8:0000:0000:0000:0000:0000:0001]/",
415        {"http", "[2001:db8::1]", 80}},
416       {"http://1/", {"http", "0.0.0.1", 80}},
417       {"http://1:1/", {"http", "0.0.0.1", 1}},
418       {"http://3232237825/", {"http", "192.168.9.1", 80}},
419
420       // Punycode
421       {"http://☃.net/", {"http", "xn--n3h.net", 80}},
422       {"blob:http://☃.net/", {"http", "xn--n3h.net", 80}},
423       {"local-std-with-host:↑↑↓↓←→←→ba.↑↑↓↓←→←→ba.0.bg",
424        {"local-std-with-host", "xn--ba-rzuadaibfa.xn--ba-rzuadaibfa.0.bg", 0}},
425
426       // Registered URLs
427       {"ftp://example.com/", {"ftp", "example.com", 21}},
428       {"ws://example.com/", {"ws", "example.com", 80}},
429       {"wss://example.com/", {"wss", "example.com", 443}},
430       {"wss://user:pass@example.com/", {"wss", "example.com", 443}},
431   };
432
433   for (const TestCase& test : kTestCases) {
434     SCOPED_TRACE(testing::Message() << "Test input: " << test.input);
435
436     // Only valid URLs should translate into valid, non-opaque origins.
437     EXPECT_TRUE(this->IsValidUrl(test.input));
438
439     auto origin = this->CreateOriginFromString(test.input);
440     this->VerifyTupleOriginInvariants(origin, test.expected_tuple);
441   }
442 }
443
444 TYPED_TEST_P(AbstractOriginTest, CustomSchemes_OpaqueOrigins) {
445   const char* kTestCases[] = {
446       // Unknown scheme
447       "unknown-scheme:foo",
448       "unknown-scheme://bar",
449
450       // Unknown scheme that is a prefix or suffix of a registered scheme.
451       "loca:foo",
452       "ocal:foo",
453       "local-suffix:foo",
454       "prefix-local:foo",
455
456       // Custom no-access schemes translate into an opaque origin (just like the
457       // built-in no-access schemes such as about:blank or data:).
458       "noaccess-std-with-host:foo",
459       "noaccess-std-with-host://bar",
460       "noaccess://host",
461       "local-noaccess://host",
462       "local-noaccess-std-with-host://host",
463   };
464
465   for (const char* test_input : kTestCases) {
466     SCOPED_TRACE(testing::Message() << "Test input: " << test_input);
467
468     // Verify that `origin` is opaque not just because `test_input` results is
469     // an invalid URL (because of a typo in the scheme name, or because of a
470     // technicality like having no host in a noaccess-std-with-host: scheme).
471     EXPECT_TRUE(this->IsValidUrl(test_input));
472
473     this->TestUniqueOpaqueOrigin(test_input);
474   }
475 }
476
477 TYPED_TEST_P(AbstractOriginTest, CustomSchemes_TupleOrigins) {
478   struct TestCase {
479     const char* input;
480     SchemeHostPort expected_tuple;
481   } kTestCases[] = {
482       // Scheme (registered in SetUp()) that's both local and standard.
483       // TODO: Is it really appropriate to do network-host canonicalization of
484       // schemes without ports?
485       {"local-std-with-host:20", {"local-std-with-host", "0.0.0.20", 0}},
486       {"local-std-with-host:20.", {"local-std-with-host", "0.0.0.20", 0}},
487       {"local-std-with-host:foo", {"local-std-with-host", "foo", 0}},
488       {"local-std-with-host://bar:20", {"local-std-with-host", "bar", 0}},
489       {"local-std-with-host:baz.", {"local-std-with-host", "baz.", 0}},
490       {"local-std-with-host:baz..", {"local-std-with-host", "baz..", 0}},
491       {"local-std-with-host:baz..bar", {"local-std-with-host", "baz..bar", 0}},
492       {"local-std-with-host:baz...", {"local-std-with-host", "baz...", 0}},
493
494       // Scheme (registered in SetUp()) that's local but nonstandard. These
495       // always have empty hostnames, but are allowed to be url::Origins.
496       {"local:", {"local", "", 0}},
497       {"local:foo", {"local", "", 0}},
498       {"local://bar", {"local", "", 0}},
499       {"also-local://bar", {"also-local", "", 0}},
500
501       {"std-with-host://host", {"std-with-host", "host", 0}},
502       {"local://host", {"local", "", 0}},
503       {"local-std-with-host://host", {"local-std-with-host", "host", 0}},
504   };
505
506   for (const TestCase& test : kTestCases) {
507     SCOPED_TRACE(testing::Message() << "Test input: " << test.input);
508
509     // Only valid URLs should translate into valid, non-opaque origins.
510     EXPECT_TRUE(this->IsValidUrl(test.input));
511
512     auto origin = this->CreateOriginFromString(test.input);
513     this->VerifyTupleOriginInvariants(origin, test.expected_tuple);
514   }
515 }
516
517 REGISTER_TYPED_TEST_SUITE_P(AbstractOriginTest,
518                             NonStandardSchemeWithAndroidWebViewHack,
519                             OpaqueOriginsFromValidUrls,
520                             OpaqueOriginsFromInvalidUrls,
521                             TupleOrigins,
522                             CustomSchemes_OpaqueOrigins,
523                             CustomSchemes_TupleOrigins);
524
525 }  // namespace url
526
527 #endif  // URL_ORIGIN_ABSTRACT_TESTS_H_