From c8fb7667831ffdc0aeb666184ca5aec1da68d462 Mon Sep 17 00:00:00 2001 From: Jeremy Roman Date: Tue, 7 Nov 2023 12:11:51 -0800 Subject: [PATCH] URLPattern: Apply syntax changes from whatwg/urlpattern#179. The following changes apply to patterns which are constructed using a base URL, the string syntax, or both -- but not any pattern which explicitly specifies components separately without a base URL. * Components are not inherited from a base URL if an "earlier" component is explicitly specified. * In the string format, unspecified "later" components are implicitly wildcarded, rather than required to be empty (with the exception of the port, which is always taken to be specified when the hostname is). * Username and password are never implicitly specified or inherited. This means that a pattern like "https://example.com/*" also matches with any username, password, search, and hash. Previously this would be written "https://*:*@example.com/*\\?*#*". Bug: 1468446 Change-Id: Ie0d7a80e36e89e05a0c634f7565c3365909edb2d --- urlpattern/resources/urlpatterntestdata.json | 254 +++++++++---------- urlpattern/resources/urlpatterntests.js | 25 +- 2 files changed, 144 insertions(+), 135 deletions(-) diff --git a/urlpattern/resources/urlpatterntestdata.json b/urlpattern/resources/urlpatterntestdata.json index 56b3a0c0f23dbc7..4a05e4c95cbb67d 100644 --- a/urlpattern/resources/urlpatterntestdata.json +++ b/urlpattern/resources/urlpatterntestdata.json @@ -80,16 +80,19 @@ "baseURL": "https://example.com?query#hash" }], "inputs": [{ "protocol": "https", "hostname": "example.com", "pathname": "/foo/bar" }], - "exactly_empty_components": [ "username", "password", "port" ], - "expected_match": null + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } }, { "pattern": [{ "pathname": "/foo/bar", "baseURL": "https://example.com" }], "inputs": [{ "protocol": "https", "hostname": "example.com", "pathname": "/foo/bar" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_match": { "hostname": { "input": "example.com", "groups": {} }, "pathname": { "input": "/foo/bar", "groups": {} }, @@ -109,8 +112,14 @@ "inputs": [{ "protocol": "https", "hostname": "example.com", "pathname": "/foo/bar", "search": "otherquery", "hash": "otherhash" }], - "exactly_empty_components": [ "username", "password", "port" ], - "expected_match": null + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } }, { "pattern": [{ "pathname": "/foo/bar", @@ -118,9 +127,14 @@ "inputs": [{ "protocol": "https", "hostname": "example.com", "pathname": "/foo/bar", "search": "otherquery", "hash": "otherhash" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], - "expected_match": null + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } }, { "pattern": [{ "pathname": "/foo/bar", @@ -128,40 +142,50 @@ "inputs": [{ "protocol": "https", "hostname": "example.com", "pathname": "/foo/bar", "search": "otherquery", "hash": "otherhash" }], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_match": { - "hash": { "input": "otherhash", "groups": {} }, + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, "hostname": { "input": "example.com", "groups": {} }, "pathname": { "input": "/foo/bar", "groups": {} }, "protocol": { "input": "https", "groups": {} }, - "search": { "input": "otherquery", "groups": {} } + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } } }, { "pattern": [{ "pathname": "/foo/bar", "baseURL": "https://example.com?query#hash" }], "inputs": [ "https://example.com/foo/bar" ], - "exactly_empty_components": [ "username", "password", "port" ], - "expected_match": null + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } }, { "pattern": [{ "pathname": "/foo/bar", "baseURL": "https://example.com?query#hash" }], "inputs": [ "https://example.com/foo/bar?otherquery#otherhash" ], - "exactly_empty_components": [ "username", "password", "port" ], - "expected_match": null + "exactly_empty_components": [ "port" ], + "expected_match": { + "hash": { "input": "otherhash", "groups": { "0": "otherhash" } }, + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} }, + "search": { "input": "otherquery", "groups": { "0": "otherquery" } } + } }, { "pattern": [{ "pathname": "/foo/bar", "baseURL": "https://example.com?query#hash" }], "inputs": [ "https://example.com/foo/bar?query#hash" ], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_match": { - "hash": { "input": "hash", "groups": {} }, + "hash": { "input": "hash", "groups": { "0": "hash" } }, "hostname": { "input": "example.com", "groups": {} }, "pathname": { "input": "/foo/bar", "groups": {} }, "protocol": { "input": "https", "groups": {} }, - "search": { "input": "query", "groups": {} } + "search": { "input": "query", "groups": { "0": "query" } } } }, { @@ -186,21 +210,23 @@ "pattern": [{ "pathname": "/foo/bar", "baseURL": "https://example.com?query#hash" }], "inputs": [{ "pathname": "/foo/bar", "baseURL": "https://example.com" }], - "exactly_empty_components": [ "username", "password", "port" ], - "expected_match": null + "exactly_empty_components": [ "port" ], + "expected_match": { + "hostname": { "input": "example.com", "groups": {} }, + "pathname": { "input": "/foo/bar", "groups": {} }, + "protocol": { "input": "https", "groups": {} } + } }, { "pattern": [{ "pathname": "/foo/bar", "baseURL": "https://example.com?query#hash" }], "inputs": [{ "pathname": "/foo/bar", "baseURL": "https://example.com?query#hash" }], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_match": { - "hash": { "input": "hash", "groups": {} }, "hostname": { "input": "example.com", "groups": {} }, "pathname": { "input": "/foo/bar", "groups": {} }, - "protocol": { "input": "https", "groups": {} }, - "search": { "input": "query", "groups": {} } + "protocol": { "input": "https", "groups": {} } } }, { @@ -1209,8 +1235,7 @@ { "pattern": [{ "pathname": "./foo/bar", "baseURL": "https://example.com" }], "inputs": [{ "pathname": "foo/bar", "baseURL": "https://example.com" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/foo/bar" }, @@ -1223,8 +1248,7 @@ { "pattern": [{ "pathname": "", "baseURL": "https://example.com" }], "inputs": [{ "pathname": "/", "baseURL": "https://example.com" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/" }, @@ -1237,8 +1261,7 @@ { "pattern": [{ "pathname": "{/bar}", "baseURL": "https://example.com/foo/" }], "inputs": [{ "pathname": "./bar", "baseURL": "https://example.com/foo/" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/bar" }, @@ -1247,8 +1270,7 @@ { "pattern": [{ "pathname": "\\/bar", "baseURL": "https://example.com/foo/" }], "inputs": [{ "pathname": "./bar", "baseURL": "https://example.com/foo/" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/bar" }, @@ -1257,8 +1279,7 @@ { "pattern": [{ "pathname": "b", "baseURL": "https://example.com/foo/" }], "inputs": [{ "pathname": "./b", "baseURL": "https://example.com/foo/" }], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/foo/b" }, @@ -1276,8 +1297,7 @@ { "pattern": [{ "pathname": "foo/bar", "baseURL": "https://example.com" }], "inputs": [ "https://example.com/foo/bar" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/foo/bar" }, @@ -1290,8 +1310,7 @@ { "pattern": [{ "pathname": ":name.html", "baseURL": "https://example.com" }], "inputs": [ "https://example.com/foo.html"] , - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/:name.html" }, @@ -1464,9 +1483,10 @@ "pattern": [ "https://example.com:8080/foo?bar#baz" ], "inputs": [{ "pathname": "/foo", "search": "bar", "hash": "baz", "baseURL": "https://example.com:8080" }], - "exactly_empty_components": [ "username", "password" ], "expected_obj": { "protocol": "https", + "username": "*", + "password": "*", "hostname": "example.com", "port": "8080", "pathname": "/foo", @@ -1486,7 +1506,6 @@ "pattern": [ "/foo?bar#baz", "https://example.com:8080" ], "inputs": [{ "pathname": "/foo", "search": "bar", "hash": "baz", "baseURL": "https://example.com:8080" }], - "exactly_empty_components": [ "username", "password" ], "expected_obj": { "pathname": "/foo", "search": "bar", @@ -1512,8 +1531,7 @@ { "pattern": [ "http{s}?://{*.}?example.com/:product/:endpoint" ], "inputs": [ "https://sub.example.com/foo/bar" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "http{s}?", "hostname": "{*.}?example.com", @@ -1529,8 +1547,7 @@ { "pattern": [ "https://example.com?foo" ], "inputs": [ "https://example.com/?foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1547,8 +1564,7 @@ { "pattern": [ "https://example.com#foo" ], "inputs": [ "https://example.com/#foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port", "search" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1565,7 +1581,6 @@ { "pattern": [ "https://example.com:8080?foo" ], "inputs": [ "https://example.com:8080/?foo" ], - "exactly_empty_components": [ "username", "password", "hash" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1584,7 +1599,7 @@ { "pattern": [ "https://example.com:8080#foo" ], "inputs": [ "https://example.com:8080/#foo" ], - "exactly_empty_components": [ "username", "password", "search" ], + "exactly_empty_components": [ "search" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1603,7 +1618,7 @@ { "pattern": [ "https://example.com/?foo" ], "inputs": [ "https://example.com/?foo" ], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1620,7 +1635,7 @@ { "pattern": [ "https://example.com/#foo" ], "inputs": [ "https://example.com/#foo" ], - "exactly_empty_components": [ "username", "password", "port", "search" ], + "exactly_empty_components": [ "port", "search" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1637,8 +1652,7 @@ { "pattern": [ "https://example.com/*?foo" ], "inputs": [ "https://example.com/?foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1649,7 +1663,7 @@ { "pattern": [ "https://example.com/*\\?foo" ], "inputs": [ "https://example.com/?foo" ], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1666,8 +1680,7 @@ { "pattern": [ "https://example.com/:name?foo" ], "inputs": [ "https://example.com/bar?foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1678,7 +1691,7 @@ { "pattern": [ "https://example.com/:name\\?foo" ], "inputs": [ "https://example.com/bar?foo" ], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1695,7 +1708,7 @@ { "pattern": [ "https://example.com/(bar)?foo" ], "inputs": [ "https://example.com/bar?foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1706,7 +1719,7 @@ { "pattern": [ "https://example.com/(bar)\\?foo" ], "inputs": [ "https://example.com/bar?foo" ], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1723,7 +1736,7 @@ { "pattern": [ "https://example.com/{bar}?foo" ], "inputs": [ "https://example.com/bar?foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1734,7 +1747,7 @@ { "pattern": [ "https://example.com/{bar}\\?foo" ], "inputs": [ "https://example.com/bar?foo" ], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1751,8 +1764,7 @@ { "pattern": [ "https://example.com/" ], "inputs": [ "https://example.com:8080/" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1769,8 +1781,7 @@ { "pattern": [ "data\\:foobar" ], "inputs": [ "data:foobar" ], - "exactly_empty_components": [ "username", "password", "hostname", "port", - "search", "hash" ], + "exactly_empty_components": [ "hostname", "port" ], "expected_obj": { "protocol": "data", "pathname": "foobar" @@ -1783,8 +1794,7 @@ { "pattern": [ "https://{sub.}?example.com/foo" ], "inputs": [ "https://example.com/foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "{sub.}?example.com", @@ -1809,8 +1819,7 @@ { "pattern": [ "https://(sub.)?example.com/foo" ], "inputs": [ "https://example.com/foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "(sub.)?example.com", @@ -1826,12 +1835,11 @@ { "pattern": [ "https://(sub.)?example(.com/)foo" ], "inputs": [ "https://example.com/foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "(sub.)?example(.com/)foo", - "pathname": "/" + "pathname": "*" }, "expected_match": null }, @@ -1848,8 +1856,7 @@ { "pattern": [ "https://(sub(?:.))?example.com/foo" ], "inputs": [ "https://example.com/foo" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "(sub(?:.))?example.com", @@ -1865,8 +1872,7 @@ { "pattern": [ "file:///foo/bar" ], "inputs": [ "file:///foo/bar" ], - "exactly_empty_components": [ "username", "password", "hostname", "port", - "search", "hash" ], + "exactly_empty_components": [ "hostname", "port" ], "expected_obj": { "protocol": "file", "pathname": "/foo/bar" @@ -1879,8 +1885,7 @@ { "pattern": [ "data:" ], "inputs": [ "data:" ], - "exactly_empty_components": [ "username", "password", "hostname", "port", - "pathname", "search", "hash" ], + "exactly_empty_components": [ "hostname", "port", "pathname" ], "expected_obj": { "protocol": "data" }, @@ -1891,8 +1896,7 @@ { "pattern": [ "foo://bar" ], "inputs": [ "foo://bad_url_browser_interop" ], - "exactly_empty_components": [ "username", "password", "port", "pathname", - "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "foo", "hostname": "bar" @@ -1909,7 +1913,7 @@ "search": "?bar", "hash": "#baz", "baseURL": "http://example.com/foo" }], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1917,13 +1921,7 @@ "search": "bar", "hash": "baz" }, - "expected_match": { - "protocol": { "input": "https", "groups": {} }, - "hostname": { "input": "example.com", "groups": {} }, - "pathname": { "input": "/foo", "groups": {} }, - "search": { "input": "bar", "groups": {} }, - "hash": { "input": "baz", "groups": {} } - } + "expected_match": null }, { "pattern": [{ "protocol": "http{s}?:", @@ -1946,7 +1944,7 @@ { "pattern": [ "?bar#baz", "https://example.com/foo" ], "inputs": [ "?bar#baz", "https://example.com/foo" ], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -1965,12 +1963,13 @@ { "pattern": [ "?bar", "https://example.com/foo#baz" ], "inputs": [ "?bar", "https://example.com/foo#snafu" ], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", "pathname": "/foo", - "search": "bar" + "search": "bar", + "hash": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, @@ -1982,7 +1981,7 @@ { "pattern": [ "#baz", "https://example.com/foo?bar" ], "inputs": [ "#baz", "https://example.com/foo?bar" ], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -2001,7 +2000,7 @@ { "pattern": [ "#baz", "https://example.com/foo" ], "inputs": [ "#baz", "https://example.com/foo" ], - "exactly_empty_components": [ "username", "password", "port", "search" ], + "exactly_empty_components": [ "port", "search" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -2028,99 +2027,98 @@ { "pattern": [ "https://foo\\:bar@example.com" ], "inputs": [ "https://foo:bar@example.com" ], - "exactly_empty_components": [ "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "username": "foo", "password": "bar", "hostname": "example.com", - "pathname": "/" + "pathname": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, "username": { "input": "foo", "groups": {} }, "password": { "input": "bar", "groups": {} }, "hostname": { "input": "example.com", "groups": {} }, - "pathname": { "input": "/", "groups": {} } + "pathname": { "input": "/", "groups": { "0": "/" } } } }, { "pattern": [ "https://foo@example.com" ], "inputs": [ "https://foo@example.com" ], - "exactly_empty_components": [ "password", "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "username": "foo", "hostname": "example.com", - "pathname": "/" + "pathname": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, "username": { "input": "foo", "groups": {} }, "hostname": { "input": "example.com", "groups": {} }, - "pathname": { "input": "/", "groups": {} } + "pathname": { "input": "/", "groups": { "0": "/" } } } }, { "pattern": [ "https://\\:bar@example.com" ], "inputs": [ "https://:bar@example.com" ], - "exactly_empty_components": [ "username", "port", "search", "hash" ], + "exactly_empty_components": [ "username", "port" ], "expected_obj": { "protocol": "https", "password": "bar", "hostname": "example.com", - "pathname": "/" + "pathname": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, "password": { "input": "bar", "groups": {} }, "hostname": { "input": "example.com", "groups": {} }, - "pathname": { "input": "/", "groups": {} } + "pathname": { "input": "/", "groups": { "0": "/" } } } }, { "pattern": [ "https://:user::pass@example.com" ], "inputs": [ "https://foo:bar@example.com" ], - "exactly_empty_components": [ "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "username": ":user", "password": ":pass", "hostname": "example.com", - "pathname": "/" + "pathname": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, "username": { "input": "foo", "groups": { "user": "foo" } }, "password": { "input": "bar", "groups": { "pass": "bar" } }, "hostname": { "input": "example.com", "groups": {} }, - "pathname": { "input": "/", "groups": {} } + "pathname": { "input": "/", "groups": { "0": "/" } } } }, { "pattern": [ "https\\:foo\\:bar@example.com" ], "inputs": [ "https:foo:bar@example.com" ], - "exactly_empty_components": [ "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "username": "foo", "password": "bar", "hostname": "example.com", - "pathname": "/" + "pathname": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, "username": { "input": "foo", "groups": {} }, "password": { "input": "bar", "groups": {} }, "hostname": { "input": "example.com", "groups": {} }, - "pathname": { "input": "/", "groups": {} } + "pathname": { "input": "/", "groups": { "0": "/" } } } }, { "pattern": [ "data\\:foo\\:bar@example.com" ], "inputs": [ "data:foo:bar@example.com" ], - "exactly_empty_components": [ "username", "password", "hostname", "port", - "search", "hash" ], + "exactly_empty_components": [ "hostname", "port" ], "expected_obj": { "protocol": "data", "pathname": "foo\\:bar@example.com" @@ -2133,24 +2131,24 @@ { "pattern": [ "https://foo{\\:}bar@example.com" ], "inputs": [ "https://foo:bar@example.com" ], - "exactly_empty_components": [ "password", "port", "search", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "username": "foo%3Abar", - "hostname": "example.com", - "pathname": "/" + "hostname": "example.com" }, "expected_match": null }, { "pattern": [ "data{\\:}channel.html", "https://example.com" ], "inputs": [ "https://example.com/data:channel.html" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "https", "hostname": "example.com", - "pathname": "/data\\:channel.html" + "pathname": "/data\\:channel.html", + "search": "*", + "hash": "*" }, "expected_match": { "protocol": { "input": "https", "groups": {} }, @@ -2161,8 +2159,7 @@ { "pattern": [ "http://[\\:\\:1]/" ], "inputs": [ "http://[::1]/" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "http", "hostname": "[\\:\\:1]", @@ -2177,7 +2174,6 @@ { "pattern": [ "http://[\\:\\:1]:8080/" ], "inputs": [ "http://[::1]:8080/" ], - "exactly_empty_components": [ "username", "password", "search", "hash" ], "expected_obj": { "protocol": "http", "hostname": "[\\:\\:1]", @@ -2194,8 +2190,7 @@ { "pattern": [ "http://[\\:\\:a]/" ], "inputs": [ "http://[::a]/" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "http", "hostname": "[\\:\\:a]", @@ -2210,8 +2205,7 @@ { "pattern": [ "http://[:address]/" ], "inputs": [ "http://[::1]/" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "http", "hostname": "[:address]", @@ -2226,8 +2220,7 @@ { "pattern": [ "http://[\\:\\:AB\\::num]/" ], "inputs": [ "http://[::ab:1]/" ], - "exactly_empty_components": [ "username", "password", "port", "search", - "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "protocol": "http", "hostname": "[\\:\\:ab\\::num]", @@ -2299,8 +2292,7 @@ { "pattern": [ "data\\:text/javascript,let x = 100/:tens?5;" ], "inputs": [ "data:text/javascript,let x = 100/5;" ], - "exactly_empty_components": [ "username", "password", "hostname", "port", - "search", "hash" ], + "exactly_empty_components": [ "hostname", "port" ], "expected_obj": { "protocol": "data", "pathname": "text/javascript,let x = 100/:tens?5;" @@ -2747,7 +2739,6 @@ { "ignoreCase": true }], "inputs": [{ "pathname": "/FOO", "search": "BAR", "hash": "BAZ", "baseURL": "https://example.com:8080" }], - "exactly_empty_components": [ "username", "password" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -2770,7 +2761,6 @@ { "ignoreCase": true }], "inputs": [{ "pathname": "/FOO", "search": "BAR", "hash": "BAZ", "baseURL": "https://example.com:8080" }], - "exactly_empty_components": [ "username", "password" ], "expected_obj": { "protocol": "https", "hostname": "example.com", @@ -2798,7 +2788,7 @@ { "pattern": [{ "search": "foo", "baseURL": "https://example.com/a/+/b" }], "inputs": [{ "search": "foo", "baseURL": "https://example.com/a/+/b" }], - "exactly_empty_components": [ "username", "password", "port", "hash" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "pathname": "/a/\\+/b" }, @@ -2812,7 +2802,7 @@ { "pattern": [{ "hash": "foo", "baseURL": "https://example.com/?q=*&v=?&hmm={}&umm=()" }], "inputs": [{ "hash": "foo", "baseURL": "https://example.com/?q=*&v=?&hmm={}&umm=()" }], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "search": "q=\\*&v=\\?&hmm=\\{\\}&umm=\\(\\)" }, @@ -2827,7 +2817,7 @@ { "pattern": [ "#foo", "https://example.com/?q=*&v=?&hmm={}&umm=()" ], "inputs": [ "https://example.com/?q=*&v=?&hmm={}&umm=()#foo" ], - "exactly_empty_components": [ "username", "password", "port" ], + "exactly_empty_components": [ "port" ], "expected_obj": { "search": "q=\\*&v=\\?&hmm=\\{\\}&umm=\\(\\)", "hash": "foo" diff --git a/urlpattern/resources/urlpatterntests.js b/urlpattern/resources/urlpatterntests.js index f774699bff99c3b..761f96b6fd33920 100644 --- a/urlpattern/resources/urlpatterntests.js +++ b/urlpattern/resources/urlpatterntests.js @@ -46,22 +46,41 @@ function runTests(data) { baseURL = new URL(entry.pattern[1]); } + const EARLIER_COMPONENTS = { + protocol: [], + hostname: ["protocol"], + port: ["protocol", "hostname"], + username: [], + password: [], + pathname: ["protocol", "hostname", "port"], + search: ["protocol", "hostname", "port", "pathname"], + hash: ["protocol", "hostname", "port", "pathname", "search"], + }; + // We automatically populate the expected pattern string using // the following options in priority order: // // 1. If the original input explicitly provided a pattern, then // echo that back as the expected value. - // 2. If the baseURL exists and provides a component value then + // 2. If an "earlier" component is specified, then a wildcard + // will be used rather than inheriting from the base URL. + // 3. If the baseURL exists and provides a component value then // use that for the expected pattern. - // 3. Otherwise fall back on the default pattern of `*` for an + // 4. Otherwise fall back on the default pattern of `*` for an // empty component pattern. + // + // Note that username and password are never inherited, and will only + // need to match if explicitly specified. if (entry.exactly_empty_components && entry.exactly_empty_components.includes(component)) { expected = ''; } else if (typeof entry.pattern[0] === 'object' && entry.pattern[0][component]) { expected = entry.pattern[0][component]; - } else if (baseURL) { + } else if (typeof entry.pattern[0] === 'object' && + EARLIER_COMPONENTS[component].some(c => c in entry.pattern[0])) { + expected = '*'; + } else if (baseURL && component !== 'username' && component !== 'password') { let base_value = baseURL[component]; // Unfortunately some URL() getters include separator chars; e.g. // the trailing `:` for the protocol. Strip those off if necessary.