From 8038297a1591ed59a87b337d8a701aff828e0aeb Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Tue, 18 Mar 2025 20:05:14 +1300 Subject: [PATCH] LibURL/Pattern: Implement matching a URLPattern --- Libraries/LibURL/Pattern/Pattern.cpp | 229 +++++++++++++++++- Libraries/LibURL/Pattern/Pattern.h | 3 +- .../wpt-import/urlpattern/urlpattern.any.txt | 84 +++---- 3 files changed, 270 insertions(+), 46 deletions(-) diff --git a/Libraries/LibURL/Pattern/Pattern.cpp b/Libraries/LibURL/Pattern/Pattern.cpp index 7b05e9f327c..7a4da8edeb1 100644 --- a/Libraries/LibURL/Pattern/Pattern.cpp +++ b/Libraries/LibURL/Pattern/Pattern.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -163,10 +164,232 @@ PatternErrorOr Pattern::create(Input const& input, Optional con } // https://urlpattern.spec.whatwg.org/#url-pattern-match -PatternErrorOr> Pattern::match(Input const&, Optional const&) const +PatternErrorOr> Pattern::match(Variant const& input, Optional const& base_url_string) const { - dbgln("FIXME: Implement URL::Pattern::match"); - return OptionalNone {}; + // 1. Let protocol be the empty string. + String protocol; + + // 2. Let username be the empty string. + String username; + + // 3. Let password be the empty string. + String password; + + // 4. Let hostname be the empty string. + String hostname; + + // 5. Let port be the empty string. + String port; + + // 6. Let pathname be the empty string. + String pathname; + + // 7. Let search be the empty string. + String search; + + // 8. Let hash be the empty string. + String hash; + + // 9. Let inputs be an empty list. + Vector inputs; + + // 10. If input is a URL, then append the serialization of input to inputs. + if (auto const* input_url = input.get_pointer()) { + inputs.append(input_url->serialize()); + } + // 11. Otherwise, append input to inputs. + else { + inputs.append(input.downcast()); + } + + // 12. If input is a URLPatternInit then: + if (auto const* input_init = input.get_pointer()) { + // 1. If baseURLString was given, throw a TypeError. + if (base_url_string.has_value()) + return ErrorInfo { "Base URL cannot be provided when URLPatternInput is provided"_string }; + + // 2. Let applyResult be the result of process a URLPatternInit given input, "url", protocol, username, password, + // hostname, port, pathname, search, and hash. If this throws an exception, catch it, and return null. + auto apply_result_or_error = process_a_url_pattern_init(*input_init, PatternProcessType::URL, + protocol, username, password, hostname, port, pathname, search, hash); + if (apply_result_or_error.is_error()) + return OptionalNone {}; + auto apply_result = apply_result_or_error.release_value(); + + // 3. Set protocol to applyResult["protocol"]. + protocol = apply_result.protocol.value(); + + // 4. Set username to applyResult["username"]. + username = apply_result.username.value(); + + // 5. Set password to applyResult["password"]. + password = apply_result.password.value(); + + // 6. Set hostname to applyResult["hostname"]. + hostname = apply_result.hostname.value(); + + // 7. Set port to applyResult["port"]. + port = apply_result.port.value(); + + // 8. Set pathname to applyResult["pathname"]. + pathname = apply_result.pathname.value(); + + // 9. Set search to applyResult["search"]. + search = apply_result.search.value(); + + // 10. Set hash to applyResult["hash"]. + hash = apply_result.hash.value(); + } + // 13. Otherwise: + else { + // 1. Let url be input. + auto url_or_string = input.downcast(); + + // 2. If input is a USVString: + if (auto const* input_string = input.get_pointer()) { + // 1. Let baseURL be null. + Optional base_url; + + // 2. If baseURLString was given, then: + if (base_url_string.has_value()) { + // 1. Set baseURL to the result of running the basic URL parser on baseURLString. + base_url = Parser::basic_parse(base_url_string.value()); + + // 2. If baseURL is failure, return null. + if (!base_url.has_value()) + return OptionalNone {}; + + // 3. Append baseURLString to inputs. + inputs.append(base_url_string.value()); + } + + // 3. Set url to the result of running the basic URL parser on input with baseURL. + // 4. If url is failure, return null. + auto maybe_url = Parser::basic_parse(*input_string, base_url); + if (!maybe_url.has_value()) + return OptionalNone {}; + url_or_string = maybe_url.release_value(); + } + + // 3. Assert: url is a URL. + VERIFY(url_or_string.has()); + auto& url = url_or_string.get(); + + // 4. Set protocol to url’s scheme. + protocol = url.scheme(); + + // 5. Set username to url’s username. + username = url.username(); + + // 6. Set password to url’s password. + password = url.password(); + + // 7. Set hostname to url’s host, serialized, or the empty string if the value is null. + if (url.host().has_value()) + hostname = url.host()->serialize(); + else + hostname = String {}; + + // 8. Set port to url’s port, serialized, or the empty string if the value is null. + if (url.port().has_value()) + port = String::number(url.port().value()); + else + port = String {}; + + // 9. Set pathname to the result of URL path serializing url. + pathname = url.serialize_path(); + + // 10. Set search to url’s query or the empty string if the value is null. + search = url.query().value_or(String {}); + + // 11. Set hash to url’s fragment or the empty string if the value is null. + hash = url.fragment().value_or(String {}); + } + + // 14. Let protocolExecResult be RegExpBuiltinExec(urlPattern’s protocol component's regular expression, protocol). + auto protocol_exec_result = m_protocol_component.regular_expression->match(protocol); + if (!protocol_exec_result.success) + return OptionalNone {}; + + // 15. Let usernameExecResult be RegExpBuiltinExec(urlPattern’s username component's regular expression, username). + auto username_exec_result = m_username_component.regular_expression->match(username); + if (!username_exec_result.success) + return OptionalNone {}; + + // 16. Let passwordExecResult be RegExpBuiltinExec(urlPattern’s password component's regular expression, password). + auto password_exec_result = m_password_component.regular_expression->match(password); + if (!password_exec_result.success) + return OptionalNone {}; + + // 17. Let hostnameExecResult be RegExpBuiltinExec(urlPattern’s hostname component's regular expression, hostname). + auto hostname_exec_result = m_hostname_component.regular_expression->match(hostname); + if (!hostname_exec_result.success) + return OptionalNone {}; + + // 18. Let portExecResult be RegExpBuiltinExec(urlPattern’s port component's regular expression, port). + auto port_exec_result = m_port_component.regular_expression->match(port); + if (!port_exec_result.success) + return OptionalNone {}; + + // 19. Let pathnameExecResult be RegExpBuiltinExec(urlPattern’s pathname component's regular expression, pathname). + auto pathname_exec_result = m_pathname_component.regular_expression->match(pathname); + if (!pathname_exec_result.success) + return OptionalNone {}; + + // 20. Let searchExecResult be RegExpBuiltinExec(urlPattern’s search component's regular expression, search). + auto search_exec_result = m_search_component.regular_expression->match(search); + if (!search_exec_result.success) + return OptionalNone {}; + + // 21. Let hashExecResult be RegExpBuiltinExec(urlPattern’s hash component's regular expression, hash). + auto hash_exec_result = m_hash_component.regular_expression->match(hash); + if (!hash_exec_result.success) + return OptionalNone {}; + + // 22. If protocolExecResult, usernameExecResult, passwordExecResult, hostnameExecResult, portExecResult, + // pathnameExecResult, searchExecResult, or hashExecResult are null then return null. + // NOTE: Done in steps above at point of exec. + + // 23. Let result be a new URLPatternResult. + Result result; + + // 24. Set result["inputs"] to inputs. + result.inputs = move(inputs); + + // 25. Set result["protocol"] to the result of creating a component match result given urlPattern’s protocol + // component, protocol, and protocolExecResult. + result.protocol = m_protocol_component.create_match_result(protocol, protocol_exec_result); + + // 26. Set result["username"] to the result of creating a component match result given urlPattern’s username + // component, username, and usernameExecResult. + result.username = m_username_component.create_match_result(username, username_exec_result); + + // 27. Set result["password"] to the result of creating a component match result given urlPattern’s password + // component, password, and passwordExecResult. + result.password = m_password_component.create_match_result(password, password_exec_result); + + // 28. Set result["hostname"] to the result of creating a component match result given urlPattern’s hostname + // component, hostname, and hostnameExecResult. + result.hostname = m_hostname_component.create_match_result(hostname, hostname_exec_result); + + // 29. Set result["port"] to the result of creating a component match result given urlPattern’s port component, + // port, and portExecResult. + result.port = m_port_component.create_match_result(port, port_exec_result); + + // 30. Set result["pathname"] to the result of creating a component match result given urlPattern’s pathname + // component, pathname, and pathnameExecResult. + result.pathname = m_pathname_component.create_match_result(pathname, pathname_exec_result); + + // 31. Set result["search"] to the result of creating a component match result given urlPattern’s search component, + // search, and searchExecResult. + result.search = m_search_component.create_match_result(search, search_exec_result); + + // 32. Set result["hash"] to the result of creating a component match result given urlPattern’s hash component, + // hash, and hashExecResult. + result.hash = m_hash_component.create_match_result(hash, hash_exec_result); + + // 33. Return result. + return result; } // https://urlpattern.spec.whatwg.org/#url-pattern-has-regexp-groups diff --git a/Libraries/LibURL/Pattern/Pattern.h b/Libraries/LibURL/Pattern/Pattern.h index 40d7f6db0cc..8a76716d89c 100644 --- a/Libraries/LibURL/Pattern/Pattern.h +++ b/Libraries/LibURL/Pattern/Pattern.h @@ -13,6 +13,7 @@ #include #include #include +#include namespace URL::Pattern { @@ -44,7 +45,7 @@ class Pattern { public: static PatternErrorOr create(Input const&, Optional const& base_url = {}, IgnoreCase = IgnoreCase::No); - PatternErrorOr> match(Input const&, Optional const& base_url_string) const; + PatternErrorOr> match(Variant const&, Optional const& base_url_string) const; bool has_regexp_groups() const; diff --git a/Tests/LibWeb/Text/expected/wpt-import/urlpattern/urlpattern.any.txt b/Tests/LibWeb/Text/expected/wpt-import/urlpattern/urlpattern.any.txt index 61119413549..47225472e17 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/urlpattern/urlpattern.any.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/urlpattern/urlpattern.any.txt @@ -2,14 +2,14 @@ Harness status: OK Found 350 tests -117 Pass -233 Fail +155 Pass +195 Fail Pass Loading data... Fail Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"}] Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/ba"}] Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/"}] Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar/baz"}] -Fail Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar"] +Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar"] Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: ["https://example.com/foo/bar/baz"] Fail Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"hostname":"example.com","pathname":"/foo/bar"}] Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"hostname":"example.com","pathname":"/foo/bar/baz"}] @@ -23,9 +23,9 @@ Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com"}] Inputs: Fail Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: [{"protocol":"https","hostname":"example.com","pathname":"/foo/bar","search":"otherquery","hash":"otherhash"}] Fail Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com"}] Inputs: [{"protocol":"https","hostname":"example.com","pathname":"/foo/bar","search":"otherquery","hash":"otherhash"}] Fail Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?otherquery#otherhash"}] Inputs: [{"protocol":"https","hostname":"example.com","pathname":"/foo/bar","search":"otherquery","hash":"otherhash"}] -Fail Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar"] -Fail Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar?otherquery#otherhash"] -Fail Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar?query#hash"] +Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar"] +Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar?otherquery#otherhash"] +Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar?query#hash"] Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://example.com/foo/bar/baz"] Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["https://other.com/foo/bar"] Pass Pattern: [{"pathname":"/foo/bar","baseURL":"https://example.com?query#hash"}] Inputs: ["http://other.com/foo/bar"] @@ -175,7 +175,7 @@ Pass Pattern: [{"protocol":"http","port":"100000"}] Inputs: [{"protocol":"http", Pass Pattern: [{"port":"80"}] Inputs: [{"protocol":"http","port":"80"}] Pass Pattern: [{"protocol":"http{s}?","port":"80"}] Inputs: [{"protocol":"http","port":"80"}] Fail Pattern: [{"port":"80"}] Inputs: [{"port":"80"}] -Pass Pattern: [{"port":"(.*)"}] Inputs: [{"port":"invalid80"}] +Fail Pattern: [{"port":"(.*)"}] Inputs: [{"port":"invalid80"}] Fail Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/./bar"}] Fail Pattern: [{"pathname":"/foo/baz"}] Inputs: [{"pathname":"/foo/bar/../baz"}] Fail Pattern: [{"pathname":"/caf%C3%A9"}] Inputs: [{"pathname":"/café"}] @@ -190,7 +190,7 @@ Pass Pattern: [{"pathname":"{/bar}","baseURL":"https://example.com/foo/"}] Input Pass Pattern: [{"pathname":"\\/bar","baseURL":"https://example.com/foo/"}] Inputs: [{"pathname":"./bar","baseURL":"https://example.com/foo/"}] Fail Pattern: [{"pathname":"b","baseURL":"https://example.com/foo/"}] Inputs: [{"pathname":"./b","baseURL":"https://example.com/foo/"}] Fail Pattern: [{"pathname":"foo/bar"}] Inputs: ["https://example.com/foo/bar"] -Fail Pattern: [{"pathname":"foo/bar","baseURL":"https://example.com"}] Inputs: ["https://example.com/foo/bar"] +Pass Pattern: [{"pathname":"foo/bar","baseURL":"https://example.com"}] Inputs: ["https://example.com/foo/bar"] Fail Pattern: [{"pathname":":name.html","baseURL":"https://example.com"}] Inputs: ["https://example.com/foo.html"] Fail Pattern: [{"search":"q=caf%C3%A9"}] Inputs: [{"search":"q=café"}] Fail Pattern: [{"search":"q=café"}] Inputs: [{"search":"q=café"}] @@ -198,8 +198,8 @@ Pass Pattern: [{"search":"q=caf%c3%a9"}] Inputs: [{"search":"q=café"}] Fail Pattern: [{"hash":"caf%C3%A9"}] Inputs: [{"hash":"café"}] Fail Pattern: [{"hash":"café"}] Inputs: [{"hash":"café"}] Pass Pattern: [{"hash":"caf%c3%a9"}] Inputs: [{"hash":"café"}] -Fail Pattern: [{"protocol":"about","pathname":"(blank|sourcedoc)"}] Inputs: ["about:blank"] -Fail Pattern: [{"protocol":"data","pathname":":number([0-9]+)"}] Inputs: ["data:8675309"] +Pass Pattern: [{"protocol":"about","pathname":"(blank|sourcedoc)"}] Inputs: ["about:blank"] +Pass Pattern: [{"protocol":"data","pathname":":number([0-9]+)"}] Inputs: ["data:8675309"] Pass Pattern: [{"pathname":"/(\\m)"}] Inputs: undefined Fail Pattern: [{"pathname":"/foo!"}] Inputs: [{"pathname":"/foo!"}] Fail Pattern: [{"pathname":"/foo\\:"}] Inputs: [{"pathname":"/foo:"}] @@ -211,63 +211,63 @@ Fail Pattern: [{"protocol":"javascript","pathname":"var x = 1;"}] Inputs: [{"bas Fail Pattern: [{"protocol":"(data|javascript)","pathname":"var x = 1;"}] Inputs: [{"protocol":"javascript","pathname":"var x = 1;"}] Fail Pattern: [{"protocol":"(https|javascript)","pathname":"var x = 1;"}] Inputs: [{"protocol":"javascript","pathname":"var x = 1;"}] Fail Pattern: [{"pathname":"var x = 1;"}] Inputs: [{"pathname":"var x = 1;"}] -Fail Pattern: [{"pathname":"/foo/bar"}] Inputs: ["./foo/bar","https://example.com"] -Fail Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"},"https://example.com"] +Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: ["./foo/bar","https://example.com"] +Pass Pattern: [{"pathname":"/foo/bar"}] Inputs: [{"pathname":"/foo/bar"},"https://example.com"] Fail Pattern: ["https://example.com:8080/foo?bar#baz"] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}] Fail Pattern: ["/foo?bar#baz","https://example.com:8080"] Inputs: [{"pathname":"/foo","search":"bar","hash":"baz","baseURL":"https://example.com:8080"}] Pass Pattern: ["/foo"] Inputs: undefined Pass Pattern: ["example.com/foo"] Inputs: undefined -Fail Pattern: ["http{s}?://{*.}?example.com/:product/:endpoint"] Inputs: ["https://sub.example.com/foo/bar"] +Pass Pattern: ["http{s}?://{*.}?example.com/:product/:endpoint"] Inputs: ["https://sub.example.com/foo/bar"] Fail Pattern: ["https://example.com?foo"] Inputs: ["https://example.com/?foo"] Fail Pattern: ["https://example.com#foo"] Inputs: ["https://example.com/#foo"] Fail Pattern: ["https://example.com:8080?foo"] Inputs: ["https://example.com:8080/?foo"] Fail Pattern: ["https://example.com:8080#foo"] Inputs: ["https://example.com:8080/#foo"] -Fail Pattern: ["https://example.com/?foo"] Inputs: ["https://example.com/?foo"] -Fail Pattern: ["https://example.com/#foo"] Inputs: ["https://example.com/#foo"] +Pass Pattern: ["https://example.com/?foo"] Inputs: ["https://example.com/?foo"] +Pass Pattern: ["https://example.com/#foo"] Inputs: ["https://example.com/#foo"] Fail Pattern: ["https://example.com/*?foo"] Inputs: ["https://example.com/?foo"] -Fail Pattern: ["https://example.com/*\\?foo"] Inputs: ["https://example.com/?foo"] +Pass Pattern: ["https://example.com/*\\?foo"] Inputs: ["https://example.com/?foo"] Fail Pattern: ["https://example.com/:name?foo"] Inputs: ["https://example.com/bar?foo"] -Fail Pattern: ["https://example.com/:name\\?foo"] Inputs: ["https://example.com/bar?foo"] +Pass Pattern: ["https://example.com/:name\\?foo"] Inputs: ["https://example.com/bar?foo"] Fail Pattern: ["https://example.com/(bar)?foo"] Inputs: ["https://example.com/bar?foo"] -Fail Pattern: ["https://example.com/(bar)\\?foo"] Inputs: ["https://example.com/bar?foo"] +Pass Pattern: ["https://example.com/(bar)\\?foo"] Inputs: ["https://example.com/bar?foo"] Fail Pattern: ["https://example.com/{bar}?foo"] Inputs: ["https://example.com/bar?foo"] -Fail Pattern: ["https://example.com/{bar}\\?foo"] Inputs: ["https://example.com/bar?foo"] +Pass Pattern: ["https://example.com/{bar}\\?foo"] Inputs: ["https://example.com/bar?foo"] Pass Pattern: ["https://example.com/"] Inputs: ["https://example.com:8080/"] Pass Pattern: ["data:foobar"] Inputs: ["data:foobar"] Fail Pattern: ["data\\:foobar"] Inputs: ["data:foobar"] -Fail Pattern: ["https://{sub.}?example.com/foo"] Inputs: ["https://example.com/foo"] +Pass Pattern: ["https://{sub.}?example.com/foo"] Inputs: ["https://example.com/foo"] Fail Pattern: ["https://{sub.}?example{.com/}foo"] Inputs: ["https://example.com/foo"] Pass Pattern: ["{https://}example.com/foo"] Inputs: ["https://example.com/foo"] -Fail Pattern: ["https://(sub.)?example.com/foo"] Inputs: ["https://example.com/foo"] +Pass Pattern: ["https://(sub.)?example.com/foo"] Inputs: ["https://example.com/foo"] Pass Pattern: ["https://(sub.)?example(.com/)foo"] Inputs: ["https://example.com/foo"] Pass Pattern: ["(https://)example.com/foo"] Inputs: ["https://example.com/foo"] Pass Pattern: ["https://{sub{.}}example.com/foo"] Inputs: ["https://example.com/foo"] -Fail Pattern: ["https://(sub(?:.))?example.com/foo"] Inputs: ["https://example.com/foo"] -Fail Pattern: ["file:///foo/bar"] Inputs: ["file:///foo/bar"] -Fail Pattern: ["data:"] Inputs: ["data:"] +Pass Pattern: ["https://(sub(?:.))?example.com/foo"] Inputs: ["https://example.com/foo"] +Pass Pattern: ["file:///foo/bar"] Inputs: ["file:///foo/bar"] +Pass Pattern: ["data:"] Inputs: ["data:"] Pass Pattern: ["foo://bar"] Inputs: ["foo://bad_url_browser_interop"] Pass Pattern: ["(café)://foo"] Inputs: undefined Pass Pattern: ["https://example.com/foo?bar#baz"] Inputs: [{"protocol":"https:","search":"?bar","hash":"#baz","baseURL":"http://example.com/foo"}] -Fail Pattern: [{"protocol":"http{s}?:","search":"?bar","hash":"#baz"}] Inputs: ["http://example.com/foo?bar#baz"] -Fail Pattern: ["?bar#baz","https://example.com/foo"] Inputs: ["?bar#baz","https://example.com/foo"] -Fail Pattern: ["?bar","https://example.com/foo#baz"] Inputs: ["?bar","https://example.com/foo#snafu"] -Fail Pattern: ["#baz","https://example.com/foo?bar"] Inputs: ["#baz","https://example.com/foo?bar"] -Fail Pattern: ["#baz","https://example.com/foo"] Inputs: ["#baz","https://example.com/foo"] +Pass Pattern: [{"protocol":"http{s}?:","search":"?bar","hash":"#baz"}] Inputs: ["http://example.com/foo?bar#baz"] +Pass Pattern: ["?bar#baz","https://example.com/foo"] Inputs: ["?bar#baz","https://example.com/foo"] +Pass Pattern: ["?bar","https://example.com/foo#baz"] Inputs: ["?bar","https://example.com/foo#snafu"] +Pass Pattern: ["#baz","https://example.com/foo?bar"] Inputs: ["#baz","https://example.com/foo?bar"] +Pass Pattern: ["#baz","https://example.com/foo"] Inputs: ["#baz","https://example.com/foo"] Pass Pattern: [{"pathname":"*"}] Inputs: ["foo","data:data-urls-cannot-be-base-urls"] Pass Pattern: [{"pathname":"*"}] Inputs: ["foo","not|a|valid|url"] -Fail Pattern: ["https://foo\\:bar@example.com"] Inputs: ["https://foo:bar@example.com"] -Fail Pattern: ["https://foo@example.com"] Inputs: ["https://foo@example.com"] -Fail Pattern: ["https://\\:bar@example.com"] Inputs: ["https://:bar@example.com"] -Fail Pattern: ["https://:user::pass@example.com"] Inputs: ["https://foo:bar@example.com"] +Pass Pattern: ["https://foo\\:bar@example.com"] Inputs: ["https://foo:bar@example.com"] +Pass Pattern: ["https://foo@example.com"] Inputs: ["https://foo@example.com"] +Pass Pattern: ["https://\\:bar@example.com"] Inputs: ["https://:bar@example.com"] +Pass Pattern: ["https://:user::pass@example.com"] Inputs: ["https://foo:bar@example.com"] Fail Pattern: ["https\\:foo\\:bar@example.com"] Inputs: ["https:foo:bar@example.com"] Fail Pattern: ["data\\:foo\\:bar@example.com"] Inputs: ["data:foo:bar@example.com"] Pass Pattern: ["https://foo{\\:}bar@example.com"] Inputs: ["https://foo:bar@example.com"] -Fail Pattern: ["data{\\:}channel.html","https://example.com"] Inputs: ["https://example.com/data:channel.html"] -Fail Pattern: ["http://[\\:\\:1]/"] Inputs: ["http://[::1]/"] -Fail Pattern: ["http://[\\:\\:1]:8080/"] Inputs: ["http://[::1]:8080/"] -Fail Pattern: ["http://[\\:\\:a]/"] Inputs: ["http://[::a]/"] -Fail Pattern: ["http://[:address]/"] Inputs: ["http://[::1]/"] -Fail Pattern: ["http://[\\:\\:AB\\::num]/"] Inputs: ["http://[::ab:1]/"] +Pass Pattern: ["data{\\:}channel.html","https://example.com"] Inputs: ["https://example.com/data:channel.html"] +Pass Pattern: ["http://[\\:\\:1]/"] Inputs: ["http://[::1]/"] +Pass Pattern: ["http://[\\:\\:1]:8080/"] Inputs: ["http://[::1]:8080/"] +Pass Pattern: ["http://[\\:\\:a]/"] Inputs: ["http://[::a]/"] +Pass Pattern: ["http://[:address]/"] Inputs: ["http://[::1]/"] +Pass Pattern: ["http://[\\:\\:AB\\::num]/"] Inputs: ["http://[::ab:1]/"] Fail Pattern: [{"hostname":"[\\:\\:AB\\::num]"}] Inputs: [{"hostname":"[::ab:1]"}] Pass Pattern: [{"hostname":"[\\:\\:xY\\::num]"}] Inputs: undefined Fail Pattern: [{"hostname":"{[\\:\\:ab\\::num]}"}] Inputs: [{"hostname":"[::ab:1]"}] @@ -306,8 +306,8 @@ Pass Pattern: [{"hostname":"bad|hostname"}] Inputs: undefined Fail Pattern: [{"hostname":"bad\nhostname"}] Inputs: [{"hostname":"badhostname"}] Fail Pattern: [{"hostname":"bad\rhostname"}] Inputs: [{"hostname":"badhostname"}] Fail Pattern: [{"hostname":"bad\thostname"}] Inputs: [{"hostname":"badhostname"}] -Fail Pattern: [{}] Inputs: ["https://example.com/"] -Fail Pattern: [] Inputs: ["https://example.com/"] +Pass Pattern: [{}] Inputs: ["https://example.com/"] +Pass Pattern: [] Inputs: ["https://example.com/"] Fail Pattern: [] Inputs: [{}] Fail Pattern: [] Inputs: [] Fail Pattern: [{"pathname":"(foo)(.*)"}] Inputs: [{"pathname":"foobarbaz"}] @@ -349,7 +349,7 @@ Fail Pattern: ["/foo?bar#baz","https://example.com:8080",{"ignoreCase":true}] In Pass Pattern: ["/foo?bar#baz",{"ignoreCase":true},"https://example.com:8080"] Inputs: [{"pathname":"/FOO","search":"BAR","hash":"BAZ","baseURL":"https://example.com:8080"}] Fail Pattern: [{"search":"foo","baseURL":"https://example.com/a/+/b"}] Inputs: [{"search":"foo","baseURL":"https://example.com/a/+/b"}] Fail Pattern: [{"hash":"foo","baseURL":"https://example.com/?q=*&v=?&hmm={}&umm=()"}] Inputs: [{"hash":"foo","baseURL":"https://example.com/?q=*&v=?&hmm={}&umm=()"}] -Fail Pattern: ["#foo","https://example.com/?q=*&v=?&hmm={}&umm=()"] Inputs: ["https://example.com/?q=*&v=?&hmm={}&umm=()#foo"] +Pass Pattern: ["#foo","https://example.com/?q=*&v=?&hmm={}&umm=()"] Inputs: ["https://example.com/?q=*&v=?&hmm={}&umm=()#foo"] Pass Pattern: [{"pathname":"/([[a-z]--a])"}] Inputs: [{"pathname":"/a"}] Fail Pattern: [{"pathname":"/([[a-z]--a])"}] Inputs: [{"pathname":"/z"}] Fail Pattern: [{"pathname":"/([\\d&&[0-1]])"}] Inputs: [{"pathname":"/0"}]