From 7d99ffc3f6f893bcee0deae22f804578fa85cb27 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 00:28:04 +0530 Subject: [PATCH 01/13] url: add value argument to has and delete methods The change aims to add value argument to two methods of URLSearchParams class i.e the has method and the delete method. For has method, if value argument is provided, then use it to check for presence. For delete method, if value argument provided, use it to delete. Fixes: https://github.com/nodejs/node/issues/47883 --- lib/internal/url.js | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 441a02f0455df4..924168f1e86f34 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -435,7 +435,7 @@ class URLSearchParams { } } - delete(name) { + delete(name, value) { if (typeof this !== 'object' || this === null || !(#searchParams in this)) throw new ERR_INVALID_THIS('URLSearchParams'); @@ -445,12 +445,28 @@ class URLSearchParams { const list = this.#searchParams; name = toUSVString(name); + + if (value !== undefined) { + value = toUSVString(value); + } + for (let i = 0; i < list.length;) { - const cur = list[i]; - if (cur === name) { - list.splice(i, 2); + if (value !== undefined) { + const key = list[i] + const val = list[i + 1] + if (key === name && val === value) { + list.splice(i, 2); + } + else { + i += 2; + } } else { - i += 2; + const cur = list[i]; + if (cur === name) { + list.splice(i, 2); + } else { + i += 2; + } } } if (this.#context) { @@ -495,7 +511,7 @@ class URLSearchParams { return values; } - has(name) { + has(name, value) { if (typeof this !== 'object' || this === null || !(#searchParams in this)) throw new ERR_INVALID_THIS('URLSearchParams'); @@ -505,8 +521,16 @@ class URLSearchParams { const list = this.#searchParams; name = toUSVString(name); + + if (value !== undefined) { + value = toUSVString(value); + } + for (let i = 0; i < list.length; i += 2) { - if (list[i] === name) { + if(value !== undefined && list[i] === name && list[i + 1] === value) { + return true; + } + if (value === undefined && list[i] === name) { return true; } } From ecf95b5b73db4dcd37be7a78a2e1ba48cf0eaa40 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 02:32:25 +0530 Subject: [PATCH 02/13] url: add wpt tests --- test/fixtures/wpt/README.md | 2 +- test/fixtures/wpt/url/README.md | 50 +- test/fixtures/wpt/url/failure.html | 11 +- test/fixtures/wpt/url/historical.any.js | 7 + .../wpt/url/resources/a-element-origin.js | 31 +- test/fixtures/wpt/url/resources/a-element.js | 33 +- .../wpt/url/resources/urltestdata.json | 1036 +++++++++-------- test/fixtures/wpt/url/url-constructor.any.js | 23 +- test/fixtures/wpt/url/url-origin.any.js | 21 +- .../wpt/url/urlsearchparams-delete.any.js | 9 + .../wpt/url/urlsearchparams-has.any.js | 13 + test/fixtures/wpt/versions.json | 2 +- 12 files changed, 646 insertions(+), 592 deletions(-) diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 3f4c7dc79e134d..02c7fd712d9f93 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -27,7 +27,7 @@ Last update: - resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing - resources: https://github.com/web-platform-tests/wpt/tree/919874f84f/resources - streams: https://github.com/web-platform-tests/wpt/tree/51750bc8d7/streams -- url: https://github.com/web-platform-tests/wpt/tree/7c5c3cc125/url +- url: https://github.com/web-platform-tests/wpt/tree/c4726447f3/url - user-timing: https://github.com/web-platform-tests/wpt/tree/df24fb604e/user-timing - wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi - wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi diff --git a/test/fixtures/wpt/url/README.md b/test/fixtures/wpt/url/README.md index 50a71bb482df9e..fa5e3b0dc72385 100644 --- a/test/fixtures/wpt/url/README.md +++ b/test/fixtures/wpt/url/README.md @@ -1,31 +1,29 @@ ## urltestdata.json -These tests are for browsers, but the data for -`a-element.html`, `url-constructor.html`, `a-element-xhtml.xhtml`, and `failure.html` -is in `resources/urltestdata.json` and can be re-used by non-browser implementations. -This file contains a JSON array of comments as strings and test cases as objects. -The keys for each test case are: - -* `base`: an absolute URL as a string whose [parsing] without a base of its own must succeed. - This key is always present, - and may have a value like `"about:blank"` when `input` is an absolute URL. -* `input`: an URL as a string to be [parsed][parsing] with `base` as its base URL. -* Either: - * `failure` with the value `true`, indicating that parsing `input` should return failure, - * or `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, - `pathname`, `search`, and `hash` with string values; - indicating that parsing `input` should return an URL record - and that the getters of each corresponding attribute in that URL’s [API] - should return the corresponding value. - - The `origin` key may be missing. - In that case, the API’s `origin` attribute is not tested. - -In addition to testing that parsing `input` against `base` gives the result, a test harness for the -`URL` constructor (or similar APIs) should additionally test the following pattern: if `failure` is -true, parsing `about:blank` against `input` must give failure. This tests that the logic for -converting base URLs into strings properly fails the whole parsing algorithm if the base URL cannot -be parsed. +`resources/urltestdata.json` contains URL parsing tests suitable for any URL parser implementation. + +It's used as a source of tests by `a-element.html`, `failure.html`, `url-constructor.any.js`, and +other test files in this directory. + +The format of `resources/urltestdata.json` is a JSON array of comments as strings and test cases as +objects. The keys for each test case are: + +* `input`: a string to be parsed as URL. +* `base`: null or a serialized URL (i.e., does not fail parsing). +* Then either + + * `failure` whose value is `true`, indicating that parsing `input` relative to `base` returns + failure + * `relativeTo` whose value is "`non-opaque-path-base`" (input does parse against a non-null base + URL without an opaque path) or "`any-base`" (input parses against any non-null base URL), or is + omitted in its entirety (input never parses successfully) + + or `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, + `pathname`, `search`, and `hash` with string values; indicating that parsing `input` should return + an URL record and that the getters of each corresponding attribute in that URL’s [API] should + return the corresponding value. + + The `origin` key may be missing. In that case, the API’s `origin` attribute is not tested. ## setters_tests.json diff --git a/test/fixtures/wpt/url/failure.html b/test/fixtures/wpt/url/failure.html index 67873ea2115738..8a408412668fd3 100644 --- a/test/fixtures/wpt/url/failure.html +++ b/test/fixtures/wpt/url/failure.html @@ -9,14 +9,15 @@ promise_test(() => fetch("resources/urltestdata.json").then(res => res.json()).then(runTests), "Loading data…") function runTests(testData) { - for(const test of testData) { - if (typeof test === "string" || !test.failure || test.base !== "about:blank") { - continue + for (const test of testData) { + if (typeof test === "string" || !test.failure || test.base !== null) { + continue; } const name = test.input + " should throw" - self.test(() => { // URL's constructor's first argument is tested by url-constructor.html + self.test(() => { + // URL's constructor's first argument is tested by url-constructor.html // If a URL fails to parse with any valid base, it must also fail to parse with no base, i.e. // when used as a base URL itself. assert_throws_js(TypeError, () => new URL("about:blank", test.input)); @@ -30,7 +31,7 @@ // The following use cases resolve the URL input relative to the current // document's URL. If this test input could be construed as a valid URL // when resolved against a base URL, skip these cases. - if (!test.inputCanBeRelative) { + if (test.relativeTo === undefined) { self.test(() => { const client = new XMLHttpRequest() assert_throws_dom("SyntaxError", () => client.open("GET", test.input)) diff --git a/test/fixtures/wpt/url/historical.any.js b/test/fixtures/wpt/url/historical.any.js index cbeb36a63f25c2..9c4b5f0ae9bfad 100644 --- a/test/fixtures/wpt/url/historical.any.js +++ b/test/fixtures/wpt/url/historical.any.js @@ -36,4 +36,11 @@ test(() => { assert_throws_dom("DataCloneError", () => self.structuredClone(new URLSearchParams())); }, "URLSearchParams: no structured serialize/deserialize support"); +test(() => { + const url = new URL("about:blank"); + url.toString = () => { throw 1 }; + assert_throws_exactly(1, () => new URL(url), "url argument"); + assert_throws_exactly(1, () => new URL("about:blank", url), "base argument"); +}, "Constructor only takes strings"); + done(); diff --git a/test/fixtures/wpt/url/resources/a-element-origin.js b/test/fixtures/wpt/url/resources/a-element-origin.js index cb7d4a895c40c4..de72988ea93c12 100644 --- a/test/fixtures/wpt/url/resources/a-element-origin.js +++ b/test/fixtures/wpt/url/resources/a-element-origin.js @@ -5,23 +5,28 @@ function setBase(base) { } function bURL(url, base) { - base = base || "about:blank" - setBase(base) - var a = document.createElement("a") - a.setAttribute("href", url) - return a + setBase(base); + const a = document.createElement("a"); + a.setAttribute("href", url); + return a; } -function runURLTests(urltests) { - for(var i = 0, l = urltests.length; i < l; i++) { - var expected = urltests[i] - if (typeof expected === "string" || !("origin" in expected)) continue - // skip without base because you cannot unset the baseURL of a document - if (expected.base === null) continue; +function runURLTests(urlTests) { + for (const expected of urlTests) { + // Skip comments and tests without "origin" expectation + if (typeof expected === "string" || !("origin" in expected)) + continue; + + // Fragments are relative against "about:blank" (this might always be redundant due to requiring "origin" in expected) + if (expected.base === null && expected.input.startsWith("#")) + continue; + + // We cannot use a null base for HTML tests + const base = expected.base === null ? "about:blank" : expected.base; test(function() { - var url = bURL(expected.input, expected.base) + var url = bURL(expected.input, base) assert_equals(url.origin, expected.origin, "origin") - }, "Parsing origin: <" + expected.input + "> against <" + expected.base + ">") + }, "Parsing origin: <" + expected.input + "> against <" + base + ">") } } diff --git a/test/fixtures/wpt/url/resources/a-element.js b/test/fixtures/wpt/url/resources/a-element.js index 65c7e85281360f..d87937d002b24a 100644 --- a/test/fixtures/wpt/url/resources/a-element.js +++ b/test/fixtures/wpt/url/resources/a-element.js @@ -1,23 +1,28 @@ promise_test(() => fetch("resources/urltestdata.json").then(res => res.json()).then(runURLTests), "Loading data…"); function setBase(base) { - document.getElementById("base").href = base + document.getElementById("base").href = base; } function bURL(url, base) { - base = base || "about:blank" - setBase(base) - var a = document.createElement("a") - a.setAttribute("href", url) - return a + setBase(base); + const a = document.createElement("a"); + a.setAttribute("href", url); + return a; } -function runURLTests(urltests) { - for(var i = 0, l = urltests.length; i < l; i++) { - var expected = urltests[i] - if (typeof expected === "string") continue // skip comments - // skip without base because you cannot unset the baseURL of a document - if (expected.base === null) continue; +function runURLTests(urlTests) { + for (const expected of urlTests) { + // Skip comments + if (typeof expected === "string") + continue; + + // Fragments are relative against "about:blank" + if (expected.relativeTo === "any-base") + continue; + + // We cannot use a null base for HTML tests + const base = expected.base === null ? "about:blank" : expected.base; function getKey(expected) { if (expected.protocol) { @@ -30,7 +35,7 @@ function runURLTests(urltests) { } subsetTestByKey(getKey(expected), test, function() { - var url = bURL(expected.input, expected.base) + var url = bURL(expected.input, base) if(expected.failure) { if(url.protocol !== ':') { assert_unreached("Expected URL to fail parsing") @@ -49,6 +54,6 @@ function runURLTests(urltests) { assert_equals(url.pathname, expected.pathname, "pathname") assert_equals(url.search, expected.search, "search") assert_equals(url.hash, expected.hash, "hash") - }, "Parsing: <" + expected.input + "> against <" + expected.base + ">") + }, "Parsing: <" + expected.input + "> against <" + base + ">") } } diff --git a/test/fixtures/wpt/url/resources/urltestdata.json b/test/fixtures/wpt/url/resources/urltestdata.json index a3cf976534cce6..58f6b87a8624bf 100644 --- a/test/fixtures/wpt/url/resources/urltestdata.json +++ b/test/fixtures/wpt/url/resources/urltestdata.json @@ -1,5 +1,5 @@ [ - "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/segments.js", + "See ../README.md for a description of the format.", { "input": "http://example\t.\norg", "base": "http://example.org/foo/bar", @@ -32,7 +32,7 @@ }, { "input": "https://test:@test", - "base": "about:blank", + "base": null, "href": "https://test@test/", "origin": "https://test", "protocol": "https:", @@ -47,7 +47,7 @@ }, { "input": "https://:@test", - "base": "about:blank", + "base": null, "href": "https://test/", "origin": "https://test", "protocol": "https:", @@ -62,7 +62,7 @@ }, { "input": "non-special://test:@test/x", - "base": "about:blank", + "base": null, "href": "non-special://test@test/x", "origin": "null", "protocol": "non-special:", @@ -77,7 +77,7 @@ }, { "input": "non-special://:@test/x", - "base": "about:blank", + "base": null, "href": "non-special://test/x", "origin": "null", "protocol": "non-special:", @@ -167,7 +167,7 @@ }, { "input": "lolscheme:x x#x x", - "base": "about:blank", + "base": null, "href": "lolscheme:x x#x%20x", "protocol": "lolscheme:", "username": "", @@ -1075,22 +1075,22 @@ }, { "input": "file://example:1/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://example:test/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://example%/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://[example]/", - "base": "about:blank", + "base": null, "failure": true }, { @@ -1754,7 +1754,7 @@ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/path.js", { "input": "http://example.com/././foo", - "base": "about:blank", + "base": null, "href": "http://example.com/foo", "origin": "http://example.com", "protocol": "http:", @@ -1769,7 +1769,7 @@ }, { "input": "http://example.com/./.foo", - "base": "about:blank", + "base": null, "href": "http://example.com/.foo", "origin": "http://example.com", "protocol": "http:", @@ -1784,7 +1784,7 @@ }, { "input": "http://example.com/foo/.", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/", "origin": "http://example.com", "protocol": "http:", @@ -1799,7 +1799,7 @@ }, { "input": "http://example.com/foo/./", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/", "origin": "http://example.com", "protocol": "http:", @@ -1814,7 +1814,7 @@ }, { "input": "http://example.com/foo/bar/..", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/", "origin": "http://example.com", "protocol": "http:", @@ -1829,7 +1829,7 @@ }, { "input": "http://example.com/foo/bar/../", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/", "origin": "http://example.com", "protocol": "http:", @@ -1844,7 +1844,7 @@ }, { "input": "http://example.com/foo/..bar", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/..bar", "origin": "http://example.com", "protocol": "http:", @@ -1859,7 +1859,7 @@ }, { "input": "http://example.com/foo/bar/../ton", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/ton", "origin": "http://example.com", "protocol": "http:", @@ -1874,7 +1874,7 @@ }, { "input": "http://example.com/foo/bar/../ton/../../a", - "base": "about:blank", + "base": null, "href": "http://example.com/a", "origin": "http://example.com", "protocol": "http:", @@ -1889,7 +1889,7 @@ }, { "input": "http://example.com/foo/../../..", - "base": "about:blank", + "base": null, "href": "http://example.com/", "origin": "http://example.com", "protocol": "http:", @@ -1904,7 +1904,7 @@ }, { "input": "http://example.com/foo/../../../ton", - "base": "about:blank", + "base": null, "href": "http://example.com/ton", "origin": "http://example.com", "protocol": "http:", @@ -1919,7 +1919,7 @@ }, { "input": "http://example.com/foo/%2e", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/", "origin": "http://example.com", "protocol": "http:", @@ -1934,7 +1934,7 @@ }, { "input": "http://example.com/foo/%2e%2", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/%2e%2", "origin": "http://example.com", "protocol": "http:", @@ -1949,7 +1949,7 @@ }, { "input": "http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar", - "base": "about:blank", + "base": null, "href": "http://example.com/%2e.bar", "origin": "http://example.com", "protocol": "http:", @@ -1964,7 +1964,7 @@ }, { "input": "http://example.com////../..", - "base": "about:blank", + "base": null, "href": "http://example.com//", "origin": "http://example.com", "protocol": "http:", @@ -1979,7 +1979,7 @@ }, { "input": "http://example.com/foo/bar//../..", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/", "origin": "http://example.com", "protocol": "http:", @@ -1994,7 +1994,7 @@ }, { "input": "http://example.com/foo/bar//..", - "base": "about:blank", + "base": null, "href": "http://example.com/foo/bar/", "origin": "http://example.com", "protocol": "http:", @@ -2009,7 +2009,7 @@ }, { "input": "http://example.com/foo", - "base": "about:blank", + "base": null, "href": "http://example.com/foo", "origin": "http://example.com", "protocol": "http:", @@ -2024,7 +2024,7 @@ }, { "input": "http://example.com/%20foo", - "base": "about:blank", + "base": null, "href": "http://example.com/%20foo", "origin": "http://example.com", "protocol": "http:", @@ -2039,7 +2039,7 @@ }, { "input": "http://example.com/foo%", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%", "origin": "http://example.com", "protocol": "http:", @@ -2054,7 +2054,7 @@ }, { "input": "http://example.com/foo%2", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%2", "origin": "http://example.com", "protocol": "http:", @@ -2069,7 +2069,7 @@ }, { "input": "http://example.com/foo%2zbar", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%2zbar", "origin": "http://example.com", "protocol": "http:", @@ -2084,7 +2084,7 @@ }, { "input": "http://example.com/foo%2©zbar", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%2%C3%82%C2%A9zbar", "origin": "http://example.com", "protocol": "http:", @@ -2099,7 +2099,7 @@ }, { "input": "http://example.com/foo%41%7a", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%41%7a", "origin": "http://example.com", "protocol": "http:", @@ -2114,7 +2114,7 @@ }, { "input": "http://example.com/foo\t\u0091%91", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%C2%91%91", "origin": "http://example.com", "protocol": "http:", @@ -2129,7 +2129,7 @@ }, { "input": "http://example.com/foo%00%51", - "base": "about:blank", + "base": null, "href": "http://example.com/foo%00%51", "origin": "http://example.com", "protocol": "http:", @@ -2144,7 +2144,7 @@ }, { "input": "http://example.com/(%28:%3A%29)", - "base": "about:blank", + "base": null, "href": "http://example.com/(%28:%3A%29)", "origin": "http://example.com", "protocol": "http:", @@ -2159,7 +2159,7 @@ }, { "input": "http://example.com/%3A%3a%3C%3c", - "base": "about:blank", + "base": null, "href": "http://example.com/%3A%3a%3C%3c", "origin": "http://example.com", "protocol": "http:", @@ -2174,7 +2174,7 @@ }, { "input": "http://example.com/foo\tbar", - "base": "about:blank", + "base": null, "href": "http://example.com/foobar", "origin": "http://example.com", "protocol": "http:", @@ -2189,7 +2189,7 @@ }, { "input": "http://example.com\\\\foo\\\\bar", - "base": "about:blank", + "base": null, "href": "http://example.com//foo//bar", "origin": "http://example.com", "protocol": "http:", @@ -2204,7 +2204,7 @@ }, { "input": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd", - "base": "about:blank", + "base": null, "href": "http://example.com/%7Ffp3%3Eju%3Dduvgw%3Dd", "origin": "http://example.com", "protocol": "http:", @@ -2219,7 +2219,7 @@ }, { "input": "http://example.com/@asdf%40", - "base": "about:blank", + "base": null, "href": "http://example.com/@asdf%40", "origin": "http://example.com", "protocol": "http:", @@ -2234,7 +2234,7 @@ }, { "input": "http://example.com/你好你好", - "base": "about:blank", + "base": null, "href": "http://example.com/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD", "origin": "http://example.com", "protocol": "http:", @@ -2249,7 +2249,7 @@ }, { "input": "http://example.com/‥/foo", - "base": "about:blank", + "base": null, "href": "http://example.com/%E2%80%A5/foo", "origin": "http://example.com", "protocol": "http:", @@ -2264,7 +2264,7 @@ }, { "input": "http://example.com//foo", - "base": "about:blank", + "base": null, "href": "http://example.com/%EF%BB%BF/foo", "origin": "http://example.com", "protocol": "http:", @@ -2279,7 +2279,7 @@ }, { "input": "http://example.com/‮/foo/‭/bar", - "base": "about:blank", + "base": null, "href": "http://example.com/%E2%80%AE/foo/%E2%80%AD/bar", "origin": "http://example.com", "protocol": "http:", @@ -2295,7 +2295,7 @@ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/relative.js", { "input": "http://www.google.com/foo?bar=baz#", - "base": "about:blank", + "base": null, "href": "http://www.google.com/foo?bar=baz#", "origin": "http://www.google.com", "protocol": "http:", @@ -2310,7 +2310,7 @@ }, { "input": "http://www.google.com/foo?bar=baz# »", - "base": "about:blank", + "base": null, "href": "http://www.google.com/foo?bar=baz#%20%C2%BB", "origin": "http://www.google.com", "protocol": "http:", @@ -2325,7 +2325,7 @@ }, { "input": "data:test# »", - "base": "about:blank", + "base": null, "href": "data:test#%20%C2%BB", "origin": "null", "protocol": "data:", @@ -2340,7 +2340,7 @@ }, { "input": "http://www.google.com", - "base": "about:blank", + "base": null, "href": "http://www.google.com/", "origin": "http://www.google.com", "protocol": "http:", @@ -2355,7 +2355,7 @@ }, { "input": "http://192.0x00A80001", - "base": "about:blank", + "base": null, "href": "http://192.168.0.1/", "origin": "http://192.168.0.1", "protocol": "http:", @@ -2370,7 +2370,7 @@ }, { "input": "http://www/foo%2Ehtml", - "base": "about:blank", + "base": null, "href": "http://www/foo%2Ehtml", "origin": "http://www", "protocol": "http:", @@ -2385,7 +2385,7 @@ }, { "input": "http://www/foo/%2E/html", - "base": "about:blank", + "base": null, "href": "http://www/foo/html", "origin": "http://www", "protocol": "http:", @@ -2400,12 +2400,12 @@ }, { "input": "http://user:pass@/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://%25DOMAIN:foobar@foodomain.com/", - "base": "about:blank", + "base": null, "href": "http://%25DOMAIN:foobar@foodomain.com/", "origin": "http://foodomain.com", "protocol": "http:", @@ -2420,7 +2420,7 @@ }, { "input": "http:\\\\www.google.com\\foo", - "base": "about:blank", + "base": null, "href": "http://www.google.com/foo", "origin": "http://www.google.com", "protocol": "http:", @@ -2435,7 +2435,7 @@ }, { "input": "http://foo:80/", - "base": "about:blank", + "base": null, "href": "http://foo/", "origin": "http://foo", "protocol": "http:", @@ -2450,7 +2450,7 @@ }, { "input": "http://foo:81/", - "base": "about:blank", + "base": null, "href": "http://foo:81/", "origin": "http://foo:81", "protocol": "http:", @@ -2465,7 +2465,7 @@ }, { "input": "httpa://foo:80/", - "base": "about:blank", + "base": null, "href": "httpa://foo:80/", "origin": "null", "protocol": "httpa:", @@ -2480,12 +2480,12 @@ }, { "input": "http://foo:-80/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://foo:443/", - "base": "about:blank", + "base": null, "href": "https://foo/", "origin": "https://foo", "protocol": "https:", @@ -2500,7 +2500,7 @@ }, { "input": "https://foo:80/", - "base": "about:blank", + "base": null, "href": "https://foo:80/", "origin": "https://foo:80", "protocol": "https:", @@ -2515,7 +2515,7 @@ }, { "input": "ftp://foo:21/", - "base": "about:blank", + "base": null, "href": "ftp://foo/", "origin": "ftp://foo", "protocol": "ftp:", @@ -2530,7 +2530,7 @@ }, { "input": "ftp://foo:80/", - "base": "about:blank", + "base": null, "href": "ftp://foo:80/", "origin": "ftp://foo:80", "protocol": "ftp:", @@ -2545,7 +2545,7 @@ }, { "input": "gopher://foo:70/", - "base": "about:blank", + "base": null, "href": "gopher://foo:70/", "origin": "null", "protocol": "gopher:", @@ -2560,7 +2560,7 @@ }, { "input": "gopher://foo:443/", - "base": "about:blank", + "base": null, "href": "gopher://foo:443/", "origin": "null", "protocol": "gopher:", @@ -2575,7 +2575,7 @@ }, { "input": "ws://foo:80/", - "base": "about:blank", + "base": null, "href": "ws://foo/", "origin": "ws://foo", "protocol": "ws:", @@ -2590,7 +2590,7 @@ }, { "input": "ws://foo:81/", - "base": "about:blank", + "base": null, "href": "ws://foo:81/", "origin": "ws://foo:81", "protocol": "ws:", @@ -2605,7 +2605,7 @@ }, { "input": "ws://foo:443/", - "base": "about:blank", + "base": null, "href": "ws://foo:443/", "origin": "ws://foo:443", "protocol": "ws:", @@ -2620,7 +2620,7 @@ }, { "input": "ws://foo:815/", - "base": "about:blank", + "base": null, "href": "ws://foo:815/", "origin": "ws://foo:815", "protocol": "ws:", @@ -2635,7 +2635,7 @@ }, { "input": "wss://foo:80/", - "base": "about:blank", + "base": null, "href": "wss://foo:80/", "origin": "wss://foo:80", "protocol": "wss:", @@ -2650,7 +2650,7 @@ }, { "input": "wss://foo:81/", - "base": "about:blank", + "base": null, "href": "wss://foo:81/", "origin": "wss://foo:81", "protocol": "wss:", @@ -2665,7 +2665,7 @@ }, { "input": "wss://foo:443/", - "base": "about:blank", + "base": null, "href": "wss://foo/", "origin": "wss://foo", "protocol": "wss:", @@ -2680,7 +2680,7 @@ }, { "input": "wss://foo:815/", - "base": "about:blank", + "base": null, "href": "wss://foo:815/", "origin": "wss://foo:815", "protocol": "wss:", @@ -2695,7 +2695,7 @@ }, { "input": "http:/example.com/", - "base": "about:blank", + "base": null, "href": "http://example.com/", "origin": "http://example.com", "protocol": "http:", @@ -2710,7 +2710,7 @@ }, { "input": "ftp:/example.com/", - "base": "about:blank", + "base": null, "href": "ftp://example.com/", "origin": "ftp://example.com", "protocol": "ftp:", @@ -2725,7 +2725,7 @@ }, { "input": "https:/example.com/", - "base": "about:blank", + "base": null, "href": "https://example.com/", "origin": "https://example.com", "protocol": "https:", @@ -2740,7 +2740,7 @@ }, { "input": "madeupscheme:/example.com/", - "base": "about:blank", + "base": null, "href": "madeupscheme:/example.com/", "origin": "null", "protocol": "madeupscheme:", @@ -2755,7 +2755,7 @@ }, { "input": "file:/example.com/", - "base": "about:blank", + "base": null, "href": "file:///example.com/", "protocol": "file:", "username": "", @@ -2769,7 +2769,7 @@ }, { "input": "ftps:/example.com/", - "base": "about:blank", + "base": null, "href": "ftps:/example.com/", "origin": "null", "protocol": "ftps:", @@ -2784,7 +2784,7 @@ }, { "input": "gopher:/example.com/", - "base": "about:blank", + "base": null, "href": "gopher:/example.com/", "origin": "null", "protocol": "gopher:", @@ -2799,7 +2799,7 @@ }, { "input": "ws:/example.com/", - "base": "about:blank", + "base": null, "href": "ws://example.com/", "origin": "ws://example.com", "protocol": "ws:", @@ -2814,7 +2814,7 @@ }, { "input": "wss:/example.com/", - "base": "about:blank", + "base": null, "href": "wss://example.com/", "origin": "wss://example.com", "protocol": "wss:", @@ -2829,7 +2829,7 @@ }, { "input": "data:/example.com/", - "base": "about:blank", + "base": null, "href": "data:/example.com/", "origin": "null", "protocol": "data:", @@ -2844,7 +2844,7 @@ }, { "input": "javascript:/example.com/", - "base": "about:blank", + "base": null, "href": "javascript:/example.com/", "origin": "null", "protocol": "javascript:", @@ -2859,7 +2859,7 @@ }, { "input": "mailto:/example.com/", - "base": "about:blank", + "base": null, "href": "mailto:/example.com/", "origin": "null", "protocol": "mailto:", @@ -2874,7 +2874,7 @@ }, { "input": "http:example.com/", - "base": "about:blank", + "base": null, "href": "http://example.com/", "origin": "http://example.com", "protocol": "http:", @@ -2889,7 +2889,7 @@ }, { "input": "ftp:example.com/", - "base": "about:blank", + "base": null, "href": "ftp://example.com/", "origin": "ftp://example.com", "protocol": "ftp:", @@ -2904,7 +2904,7 @@ }, { "input": "https:example.com/", - "base": "about:blank", + "base": null, "href": "https://example.com/", "origin": "https://example.com", "protocol": "https:", @@ -2919,7 +2919,7 @@ }, { "input": "madeupscheme:example.com/", - "base": "about:blank", + "base": null, "href": "madeupscheme:example.com/", "origin": "null", "protocol": "madeupscheme:", @@ -2934,7 +2934,7 @@ }, { "input": "ftps:example.com/", - "base": "about:blank", + "base": null, "href": "ftps:example.com/", "origin": "null", "protocol": "ftps:", @@ -2949,7 +2949,7 @@ }, { "input": "gopher:example.com/", - "base": "about:blank", + "base": null, "href": "gopher:example.com/", "origin": "null", "protocol": "gopher:", @@ -2964,7 +2964,7 @@ }, { "input": "ws:example.com/", - "base": "about:blank", + "base": null, "href": "ws://example.com/", "origin": "ws://example.com", "protocol": "ws:", @@ -2979,7 +2979,7 @@ }, { "input": "wss:example.com/", - "base": "about:blank", + "base": null, "href": "wss://example.com/", "origin": "wss://example.com", "protocol": "wss:", @@ -2994,7 +2994,7 @@ }, { "input": "data:example.com/", - "base": "about:blank", + "base": null, "href": "data:example.com/", "origin": "null", "protocol": "data:", @@ -3009,7 +3009,7 @@ }, { "input": "javascript:example.com/", - "base": "about:blank", + "base": null, "href": "javascript:example.com/", "origin": "null", "protocol": "javascript:", @@ -3024,7 +3024,7 @@ }, { "input": "mailto:example.com/", - "base": "about:blank", + "base": null, "href": "mailto:example.com/", "origin": "null", "protocol": "mailto:", @@ -3040,7 +3040,7 @@ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/segments-userinfo-vs-host.html", { "input": "http:@www.example.com", - "base": "about:blank", + "base": null, "href": "http://www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3055,7 +3055,7 @@ }, { "input": "http:/@www.example.com", - "base": "about:blank", + "base": null, "href": "http://www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3070,7 +3070,7 @@ }, { "input": "http://@www.example.com", - "base": "about:blank", + "base": null, "href": "http://www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3085,7 +3085,7 @@ }, { "input": "http:a:b@www.example.com", - "base": "about:blank", + "base": null, "href": "http://a:b@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3100,7 +3100,7 @@ }, { "input": "http:/a:b@www.example.com", - "base": "about:blank", + "base": null, "href": "http://a:b@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3115,7 +3115,7 @@ }, { "input": "http://a:b@www.example.com", - "base": "about:blank", + "base": null, "href": "http://a:b@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3130,7 +3130,7 @@ }, { "input": "http://@pple.com", - "base": "about:blank", + "base": null, "href": "http://pple.com/", "origin": "http://pple.com", "protocol": "http:", @@ -3145,7 +3145,7 @@ }, { "input": "http::b@www.example.com", - "base": "about:blank", + "base": null, "href": "http://:b@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3160,7 +3160,7 @@ }, { "input": "http:/:b@www.example.com", - "base": "about:blank", + "base": null, "href": "http://:b@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3175,7 +3175,7 @@ }, { "input": "http://:b@www.example.com", - "base": "about:blank", + "base": null, "href": "http://:b@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3190,64 +3190,64 @@ }, { "input": "http:/:@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http://user@/www.example.com", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http:@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http:/@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http://@/www.example.com", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https:@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http:a:b@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http:/a:b@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http://a:b@/www.example.com", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http::@/www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http:a:@www.example.com", - "base": "about:blank", + "base": null, "href": "http://a@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3262,7 +3262,7 @@ }, { "input": "http:/a:@www.example.com", - "base": "about:blank", + "base": null, "href": "http://a@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3277,7 +3277,7 @@ }, { "input": "http://a:@www.example.com", - "base": "about:blank", + "base": null, "href": "http://a@www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3292,7 +3292,7 @@ }, { "input": "http://www.@pple.com", - "base": "about:blank", + "base": null, "href": "http://www.@pple.com/", "origin": "http://pple.com", "protocol": "http:", @@ -3307,24 +3307,24 @@ }, { "input": "http:@:www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http:/@:www.example.com", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "http://@:www.example.com", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://:@www.example.com", - "base": "about:blank", + "base": null, "href": "http://www.example.com/", "origin": "http://www.example.com", "protocol": "http:", @@ -3622,7 +3622,7 @@ "Leading and trailing C0 control or space", { "input": "\u0000\u001b\u0004\u0012 http://example.com/\u001f \u000d ", - "base": "about:blank", + "base": null, "href": "http://example.com/", "origin": "http://example.com", "protocol": "http:", @@ -3666,17 +3666,17 @@ "U+FFFD", { "input": "https://\ufffd", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://%EF%BF%BD", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://x/\ufffd?\ufffd#\ufffd", - "base": "about:blank", + "base": null, "href": "https://x/%EF%BF%BD?%EF%BF%BD#%EF%BF%BD", "origin": "https://x", "protocol": "https:", @@ -3692,33 +3692,33 @@ "Domain is ASCII, but a label is invalid IDNA", { "input": "http://a.b.c.xn--pokxncvks", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://10.0.0.xn--pokxncvks", - "base": "about:blank", + "base": null, "failure": true }, "IDNA labels should be matched case-insensitively", { "input": "http://a.b.c.XN--pokxncvks", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a.b.c.Xn--pokxncvks", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://10.0.0.XN--pokxncvks", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://10.0.0.xN--pokxncvks", - "base": "about:blank", + "base": null, "failure": true }, "Test name prepping, fullwidth input should be converted to ASCII and NOT IDN-ized. This is 'Go' in fullwidth UTF-8/UTF-16.", @@ -3777,7 +3777,7 @@ }, { "input": "https://faß.ExAmPlE/", - "base": "about:blank", + "base": null, "href": "https://xn--fa-hia.example/", "origin": "https://xn--fa-hia.example", "protocol": "https:", @@ -3792,7 +3792,7 @@ }, { "input": "sc://faß.ExAmPlE/", - "base": "about:blank", + "base": null, "href": "sc://fa%C3%9F.ExAmPlE/", "origin": "null", "protocol": "sc:", @@ -3872,7 +3872,7 @@ }, { "input": "https://x x:12", - "base": "about:blank", + "base": null, "failure": true }, "Fullwidth and escaped UTF-8 fullwidth should still be treated as IP", @@ -3894,7 +3894,7 @@ "Domains with empty labels", { "input": "http://./", - "base": "about:blank", + "base": null, "href": "http://./", "origin": "http://.", "protocol": "http:", @@ -3909,7 +3909,7 @@ }, { "input": "http://../", - "base": "about:blank", + "base": null, "href": "http://../", "origin": "http://..", "protocol": "http:", @@ -3925,7 +3925,7 @@ "Non-special domains with empty labels", { "input": "h://.", - "base": "about:blank", + "base": null, "href": "h://.", "origin": "null", "protocol": "h:", @@ -3941,7 +3941,7 @@ "Broken IPv6", { "input": "http://[www.google.com]/", - "base": "about:blank", + "base": null, "failure": true }, { @@ -4066,6 +4066,21 @@ "search": "", "hash": "#x" }, + { + "input": "#x:y", + "base": "about:blank", + "href": "about:blank#x:y", + "origin": "null", + "protocol": "about:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "blank", + "search": "", + "hash": "#x:y" + }, { "input": "#", "base": "test:test?test", @@ -4131,7 +4146,7 @@ "byte is ' and url is special", { "input": "http://host/?'", - "base": "about:blank", + "base": null, "href": "http://host/?%27", "origin": "http://host", "protocol": "http:", @@ -4146,7 +4161,7 @@ }, { "input": "notspecial://host/?'", - "base": "about:blank", + "base": null, "href": "notspecial://host/?'", "origin": "null", "protocol": "notspecial:", @@ -4504,7 +4519,7 @@ "# make sure that relative URL logic works on known typically non-relative schemes too", { "input": "about:/../", - "base": "about:blank", + "base": null, "href": "about:/", "origin": "null", "protocol": "about:", @@ -4519,7 +4534,7 @@ }, { "input": "data:/../", - "base": "about:blank", + "base": null, "href": "data:/", "origin": "null", "protocol": "data:", @@ -4534,7 +4549,7 @@ }, { "input": "javascript:/../", - "base": "about:blank", + "base": null, "href": "javascript:/", "origin": "null", "protocol": "javascript:", @@ -4549,7 +4564,7 @@ }, { "input": "mailto:/../", - "base": "about:blank", + "base": null, "href": "mailto:/", "origin": "null", "protocol": "mailto:", @@ -4565,7 +4580,7 @@ "# unknown schemes and their hosts", { "input": "sc://ñ.test/", - "base": "about:blank", + "base": null, "href": "sc://%C3%B1.test/", "origin": "null", "protocol": "sc:", @@ -4580,7 +4595,7 @@ }, { "input": "sc://%/", - "base": "about:blank", + "base": null, "href": "sc://%/", "protocol": "sc:", "username": "", @@ -4594,22 +4609,22 @@ }, { "input": "sc://@/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://te@s:t@/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://:/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://:12/", - "base": "about:blank", + "base": null, "failure": true }, { @@ -4630,7 +4645,7 @@ "# unknown schemes and backslashes", { "input": "sc:\\../", - "base": "about:blank", + "base": null, "href": "sc:\\../", "origin": "null", "protocol": "sc:", @@ -4646,7 +4661,7 @@ "# unknown scheme with path looking like a password", { "input": "sc::a@example.net", - "base": "about:blank", + "base": null, "href": "sc::a@example.net", "origin": "null", "protocol": "sc:", @@ -4662,7 +4677,7 @@ "# unknown scheme with bogus percent-encoding", { "input": "wow:%NBD", - "base": "about:blank", + "base": null, "href": "wow:%NBD", "origin": "null", "protocol": "wow:", @@ -4677,7 +4692,7 @@ }, { "input": "wow:%1G", - "base": "about:blank", + "base": null, "href": "wow:%1G", "origin": "null", "protocol": "wow:", @@ -4693,7 +4708,7 @@ "# unknown scheme with non-URL characters", { "input": "wow:\uFFFF", - "base": "about:blank", + "base": null, "href": "wow:%EF%BF%BF", "origin": "null", "protocol": "wow:", @@ -4708,7 +4723,7 @@ }, { "input": "http://example.com/\uD800\uD801\uDFFE\uDFFF\uFDD0\uFDCF\uFDEF\uFDF0\uFFFE\uFFFF?\uD800\uD801\uDFFE\uDFFF\uFDD0\uFDCF\uFDEF\uFDF0\uFFFE\uFFFF", - "base": "about:blank", + "base": null, "href": "http://example.com/%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF?%EF%BF%BD%F0%90%9F%BE%EF%BF%BD%EF%B7%90%EF%B7%8F%EF%B7%AF%EF%B7%B0%EF%BF%BE%EF%BF%BF", "origin": "http://example.com", "protocol": "http:", @@ -4724,53 +4739,53 @@ "Forbidden host code points", { "input": "sc://a\u0000b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://a b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://ab", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://a[b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://a\\b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://a]b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://a^b", - "base": "about:blank", + "base": null, "failure": true }, { "input": "sc://a|b/", - "base": "about:blank", + "base": null, "failure": true }, "Forbidden host codepoints: tabs and newlines are removed during preprocessing", { "input": "foo://ho\u0009st/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -4784,7 +4799,7 @@ }, { "input": "foo://ho\u000Ast/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -4798,7 +4813,7 @@ }, { "input": "foo://ho\u000Dst/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -4813,198 +4828,198 @@ "Forbidden domain code-points", { "input": "http://a\u0000b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0001b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0002b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0003b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0004b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0005b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0006b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0007b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0008b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u000Bb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u000Cb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u000Eb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u000Fb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0010b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0011b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0012b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0013b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0014b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0015b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0016b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0017b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0018b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u0019b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u001Ab/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u001Bb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u001Cb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u001Db/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u001Eb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u001Fb/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a%b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ab", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a[b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a]b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a^b", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a|b/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://a\u007Fb/", - "base": "about:blank", + "base": null, "failure": true }, "Forbidden domain codepoints: tabs and newlines are removed during preprocessing", { "input": "http://ho\u0009st/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -5018,7 +5033,7 @@ }, { "input": "http://ho\u000Ast/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -5032,7 +5047,7 @@ }, { "input": "http://ho\u000Dst/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -5047,238 +5062,238 @@ "Encoded forbidden domain codepoints in special URLs", { "input": "http://ho%00st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%01st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%02st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%03st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%04st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%05st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%06st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%07st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%08st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%09st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%0Ast/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%0Bst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%0Cst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%0Dst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%0Est/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%0Fst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%10st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%11st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%12st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%13st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%14st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%15st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%16st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%17st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%18st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%19st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%1Ast/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%1Bst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%1Cst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%1Dst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%1Est/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%1Fst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%20st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%23st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%25st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%2Fst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%3Ast/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%3Cst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%3Est/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%3Fst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%40st/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%5Bst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%5Cst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%5Dst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%7Cst/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://ho%7Fst/", - "base": "about:blank", + "base": null, "failure": true }, "Allowed host/domain code points", { "input": "http://!\"$&'()*+,-.;=_`{}~/", - "base": "about:blank", + "base": null, "href": "http://!\"$&'()*+,-.;=_`{}~/", "origin": "http://!\"$&'()*+,-.;=_`{}~", "protocol": "http:", @@ -5293,7 +5308,7 @@ }, { "input": "sc://\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u007F!\"$%&'()*+,-.;=_`{}~/", - "base": "about:blank", + "base": null, "href": "sc://%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~/", "origin": "null", "protocol": "sc:", @@ -5309,27 +5324,27 @@ "# Hosts and percent-encoding", { "input": "ftp://example.com%80/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "ftp://example.com%A0/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://example.com%80/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://example.com%A0/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "ftp://%e2%98%83", - "base": "about:blank", + "base": null, "href": "ftp://xn--n3h/", "origin": "ftp://xn--n3h", "protocol": "ftp:", @@ -5344,7 +5359,7 @@ }, { "input": "https://%e2%98%83", - "base": "about:blank", + "base": null, "href": "https://xn--n3h/", "origin": "https://xn--n3h", "protocol": "https:", @@ -5360,7 +5375,7 @@ "# tests from jsdom/whatwg-url designed for code coverage", { "input": "http://127.0.0.1:10100/relative_import.html", - "base": "about:blank", + "base": null, "href": "http://127.0.0.1:10100/relative_import.html", "origin": "http://127.0.0.1:10100", "protocol": "http:", @@ -5375,7 +5390,7 @@ }, { "input": "http://facebook.com/?foo=%7B%22abc%22", - "base": "about:blank", + "base": null, "href": "http://facebook.com/?foo=%7B%22abc%22", "origin": "http://facebook.com", "protocol": "http:", @@ -5390,7 +5405,7 @@ }, { "input": "https://localhost:3000/jqueryui@1.2.3", - "base": "about:blank", + "base": null, "href": "https://localhost:3000/jqueryui@1.2.3", "origin": "https://localhost:3000", "protocol": "https:", @@ -5406,7 +5421,7 @@ "# tab/LF/CR", { "input": "h\tt\nt\rp://h\to\ns\rt:9\t0\n0\r0/p\ta\nt\rh?q\tu\ne\rry#f\tr\na\rg", - "base": "about:blank", + "base": null, "href": "http://host:9000/path?query#frag", "origin": "http://host:9000", "protocol": "http:", @@ -5493,7 +5508,7 @@ "# Percent encoding of fragments", { "input": "http://foo.bar/baz?qux#foo\bbar", - "base": "about:blank", + "base": null, "href": "http://foo.bar/baz?qux#foo%08bar", "origin": "http://foo.bar", "protocol": "http:", @@ -5509,7 +5524,7 @@ }, { "input": "http://foo.bar/baz?qux#foo\"bar", - "base": "about:blank", + "base": null, "href": "http://foo.bar/baz?qux#foo%22bar", "origin": "http://foo.bar", "protocol": "http:", @@ -5525,7 +5540,7 @@ }, { "input": "http://foo.bar/baz?qux#foobar", - "base": "about:blank", + "base": null, "href": "http://foo.bar/baz?qux#foo%3Ebar", "origin": "http://foo.bar", "protocol": "http:", @@ -5557,7 +5572,7 @@ }, { "input": "http://foo.bar/baz?qux#foo`bar", - "base": "about:blank", + "base": null, "href": "http://foo.bar/baz?qux#foo%60bar", "origin": "http://foo.bar", "protocol": "http:", @@ -5789,7 +5804,7 @@ }, { "input": "https://0x.0x.0", - "base": "about:blank", + "base": null, "href": "https://0.0.0.0/", "origin": "https://0.0.0.0", "protocol": "https:", @@ -5805,18 +5820,18 @@ "More IPv4 parsing (via https://github.com/jsdom/whatwg-url/issues/92)", { "input": "https://0x100000000/test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://256.0.0.1/test", - "base": "about:blank", + "base": null, "failure": true }, "# file URLs containing percent-encoded Windows drive letters (shouldn't work)", { "input": "file:///C%3A/", - "base": "about:blank", + "base": null, "href": "file:///C%3A/", "protocol": "file:", "username": "", @@ -5830,7 +5845,7 @@ }, { "input": "file:///C%7C/", - "base": "about:blank", + "base": null, "href": "file:///C%7C/", "protocol": "file:", "username": "", @@ -5844,42 +5859,42 @@ }, { "input": "file://%43%3A", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://%43%7C", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://%43|", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://C%7C", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://%43%7C/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://%43%7C/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "asdf://%43|/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "asdf://%43%7C/", - "base": "about:blank", + "base": null, "href": "asdf://%43%7C/", "origin": "null", "protocol": "asdf:", @@ -6121,7 +6136,7 @@ "# File URLs and many (back)slashes", { "input": "file:\\\\//", - "base": "about:blank", + "base": null, "href": "file:////", "protocol": "file:", "username": "", @@ -6135,7 +6150,7 @@ }, { "input": "file:\\\\\\\\", - "base": "about:blank", + "base": null, "href": "file:////", "protocol": "file:", "username": "", @@ -6149,7 +6164,7 @@ }, { "input": "file:\\\\\\\\?fox", - "base": "about:blank", + "base": null, "href": "file:////?fox", "protocol": "file:", "username": "", @@ -6163,7 +6178,7 @@ }, { "input": "file:\\\\\\\\#guppy", - "base": "about:blank", + "base": null, "href": "file:////#guppy", "protocol": "file:", "username": "", @@ -6177,7 +6192,7 @@ }, { "input": "file://spider///", - "base": "about:blank", + "base": null, "href": "file://spider///", "protocol": "file:", "username": "", @@ -6191,7 +6206,7 @@ }, { "input": "file:\\\\localhost//", - "base": "about:blank", + "base": null, "href": "file:////", "protocol": "file:", "username": "", @@ -6205,7 +6220,7 @@ }, { "input": "file:///localhost//cat", - "base": "about:blank", + "base": null, "href": "file:///localhost//cat", "protocol": "file:", "username": "", @@ -6219,7 +6234,7 @@ }, { "input": "file://\\/localhost//cat", - "base": "about:blank", + "base": null, "href": "file:////localhost//cat", "protocol": "file:", "username": "", @@ -6233,7 +6248,7 @@ }, { "input": "file://localhost//a//../..//", - "base": "about:blank", + "base": null, "href": "file://///", "protocol": "file:", "username": "", @@ -6545,7 +6560,7 @@ "# Do not drop the host in the presence of a drive letter", { "input": "file://example.net/C:/", - "base": "about:blank", + "base": null, "href": "file://example.net/C:/", "protocol": "file:", "username": "", @@ -6559,7 +6574,7 @@ }, { "input": "file://1.2.3.4/C:/", - "base": "about:blank", + "base": null, "href": "file://1.2.3.4/C:/", "protocol": "file:", "username": "", @@ -6573,7 +6588,7 @@ }, { "input": "file://[1::8]/C:/", - "base": "about:blank", + "base": null, "href": "file://[1::8]/C:/", "protocol": "file:", "username": "", @@ -6702,7 +6717,7 @@ "# Windows drive letter quirk (no host)", { "input": "file:/C|/", - "base": "about:blank", + "base": null, "href": "file:///C:/", "protocol": "file:", "username": "", @@ -6716,7 +6731,7 @@ }, { "input": "file://C|/", - "base": "about:blank", + "base": null, "href": "file:///C:/", "protocol": "file:", "username": "", @@ -6731,7 +6746,7 @@ "# file URLs without base URL by Rimas Misevičius", { "input": "file:", - "base": "about:blank", + "base": null, "href": "file:///", "protocol": "file:", "username": "", @@ -6745,7 +6760,7 @@ }, { "input": "file:?q=v", - "base": "about:blank", + "base": null, "href": "file:///?q=v", "protocol": "file:", "username": "", @@ -6759,7 +6774,7 @@ }, { "input": "file:#frag", - "base": "about:blank", + "base": null, "href": "file:///#frag", "protocol": "file:", "username": "", @@ -6774,7 +6789,7 @@ "# file: drive letter cases from https://crbug.com/1078698", { "input": "file:///Y:", - "base": "about:blank", + "base": null, "href": "file:///Y:", "protocol": "file:", "username": "", @@ -6788,7 +6803,7 @@ }, { "input": "file:///Y:/", - "base": "about:blank", + "base": null, "href": "file:///Y:/", "protocol": "file:", "username": "", @@ -6802,7 +6817,7 @@ }, { "input": "file:///./Y", - "base": "about:blank", + "base": null, "href": "file:///Y", "protocol": "file:", "username": "", @@ -6816,7 +6831,7 @@ }, { "input": "file:///./Y:", - "base": "about:blank", + "base": null, "href": "file:///Y:", "protocol": "file:", "username": "", @@ -6830,14 +6845,14 @@ }, { "input": "\\\\\\.\\Y:", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, "# file: drive letter cases from https://crbug.com/1078698 but lowercased", { "input": "file:///y:", - "base": "about:blank", + "base": null, "href": "file:///y:", "protocol": "file:", "username": "", @@ -6851,7 +6866,7 @@ }, { "input": "file:///y:/", - "base": "about:blank", + "base": null, "href": "file:///y:/", "protocol": "file:", "username": "", @@ -6865,7 +6880,7 @@ }, { "input": "file:///./y", - "base": "about:blank", + "base": null, "href": "file:///y", "protocol": "file:", "username": "", @@ -6879,7 +6894,7 @@ }, { "input": "file:///./y:", - "base": "about:blank", + "base": null, "href": "file:///y:", "protocol": "file:", "username": "", @@ -6893,14 +6908,14 @@ }, { "input": "\\\\\\.\\y:", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, "# Additional file URL tests for (https://github.com/whatwg/url/issues/405)", { "input": "file://localhost//a//../..//foo", - "base": "about:blank", + "base": null, "href": "file://///foo", "protocol": "file:", "username": "", @@ -6914,7 +6929,7 @@ }, { "input": "file://localhost////foo", - "base": "about:blank", + "base": null, "href": "file://////foo", "protocol": "file:", "username": "", @@ -6928,7 +6943,7 @@ }, { "input": "file:////foo", - "base": "about:blank", + "base": null, "href": "file:////foo", "protocol": "file:", "username": "", @@ -7027,7 +7042,7 @@ "File URL tests for https://github.com/whatwg/url/issues/549", { "input": "file:.//p", - "base": "about:blank", + "base": null, "href": "file:////p", "protocol": "file:", "username": "", @@ -7041,7 +7056,7 @@ }, { "input": "file:/.//p", - "base": "about:blank", + "base": null, "href": "file:////p", "protocol": "file:", "username": "", @@ -7076,48 +7091,48 @@ }, { "input": "https://[0::0::0]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://[0:.0]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://[0:0:]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://[0:1:2:3:4:5:6:7.0.0.0.1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://[0:1.00.0.0.0]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://[0:1.290.0.0.0]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://[0:1.23.23]", - "base": "about:blank", + "base": null, "failure": true }, "# Empty host", { "input": "http://?", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://#", - "base": "about:blank", + "base": null, "failure": true }, "Port overflow (2^32 + 81)", @@ -7141,7 +7156,7 @@ "# Non-special-URL path tests", { "input": "sc://ñ", - "base": "about:blank", + "base": null, "href": "sc://%C3%B1", "origin": "null", "protocol": "sc:", @@ -7156,7 +7171,7 @@ }, { "input": "sc://ñ?x", - "base": "about:blank", + "base": null, "href": "sc://%C3%B1?x", "origin": "null", "protocol": "sc:", @@ -7171,7 +7186,7 @@ }, { "input": "sc://ñ#x", - "base": "about:blank", + "base": null, "href": "sc://%C3%B1#x", "origin": "null", "protocol": "sc:", @@ -7216,7 +7231,7 @@ }, { "input": "sc://?", - "base": "about:blank", + "base": null, "href": "sc://?", "protocol": "sc:", "username": "", @@ -7230,7 +7245,7 @@ }, { "input": "sc://#", - "base": "about:blank", + "base": null, "href": "sc://#", "protocol": "sc:", "username": "", @@ -7286,7 +7301,7 @@ }, { "input": "tftp://foobar.com/someconfig;mode=netascii", - "base": "about:blank", + "base": null, "href": "tftp://foobar.com/someconfig;mode=netascii", "origin": "null", "protocol": "tftp:", @@ -7301,7 +7316,7 @@ }, { "input": "telnet://user:pass@foobar.com:23/", - "base": "about:blank", + "base": null, "href": "telnet://user:pass@foobar.com:23/", "origin": "null", "protocol": "telnet:", @@ -7316,7 +7331,7 @@ }, { "input": "ut2004://10.10.10.10:7777/Index.ut2", - "base": "about:blank", + "base": null, "href": "ut2004://10.10.10.10:7777/Index.ut2", "origin": "null", "protocol": "ut2004:", @@ -7331,7 +7346,7 @@ }, { "input": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz", - "base": "about:blank", + "base": null, "href": "redis://foo:bar@somehost:6379/0?baz=bam&qux=baz", "origin": "null", "protocol": "redis:", @@ -7346,7 +7361,7 @@ }, { "input": "rsync://foo@host:911/sup", - "base": "about:blank", + "base": null, "href": "rsync://foo@host:911/sup", "origin": "null", "protocol": "rsync:", @@ -7361,7 +7376,7 @@ }, { "input": "git://github.com/foo/bar.git", - "base": "about:blank", + "base": null, "href": "git://github.com/foo/bar.git", "origin": "null", "protocol": "git:", @@ -7376,7 +7391,7 @@ }, { "input": "irc://myserver.com:6999/channel?passwd", - "base": "about:blank", + "base": null, "href": "irc://myserver.com:6999/channel?passwd", "origin": "null", "protocol": "irc:", @@ -7391,7 +7406,7 @@ }, { "input": "dns://fw.example.org:9999/foo.bar.org?type=TXT", - "base": "about:blank", + "base": null, "href": "dns://fw.example.org:9999/foo.bar.org?type=TXT", "origin": "null", "protocol": "dns:", @@ -7406,7 +7421,7 @@ }, { "input": "ldap://localhost:389/ou=People,o=JNDITutorial", - "base": "about:blank", + "base": null, "href": "ldap://localhost:389/ou=People,o=JNDITutorial", "origin": "null", "protocol": "ldap:", @@ -7421,7 +7436,7 @@ }, { "input": "git+https://github.com/foo/bar", - "base": "about:blank", + "base": null, "href": "git+https://github.com/foo/bar", "origin": "null", "protocol": "git+https:", @@ -7436,7 +7451,7 @@ }, { "input": "urn:ietf:rfc:2648", - "base": "about:blank", + "base": null, "href": "urn:ietf:rfc:2648", "origin": "null", "protocol": "urn:", @@ -7451,7 +7466,7 @@ }, { "input": "tag:joe@example.org,2001:foo/bar", - "base": "about:blank", + "base": null, "href": "tag:joe@example.org,2001:foo/bar", "origin": "null", "protocol": "tag:", @@ -7467,7 +7482,7 @@ "Serialize /. in path", { "input": "non-spec:/.//", - "base": "about:blank", + "base": null, "href": "non-spec:/.//", "protocol": "non-spec:", "username": "", @@ -7481,7 +7496,7 @@ }, { "input": "non-spec:/..//", - "base": "about:blank", + "base": null, "href": "non-spec:/.//", "protocol": "non-spec:", "username": "", @@ -7495,7 +7510,7 @@ }, { "input": "non-spec:/a/..//", - "base": "about:blank", + "base": null, "href": "non-spec:/.//", "protocol": "non-spec:", "username": "", @@ -7509,7 +7524,7 @@ }, { "input": "non-spec:/.//path", - "base": "about:blank", + "base": null, "href": "non-spec:/.//path", "protocol": "non-spec:", "username": "", @@ -7523,7 +7538,7 @@ }, { "input": "non-spec:/..//path", - "base": "about:blank", + "base": null, "href": "non-spec:/.//path", "protocol": "non-spec:", "username": "", @@ -7537,7 +7552,7 @@ }, { "input": "non-spec:/a/..//path", - "base": "about:blank", + "base": null, "href": "non-spec:/.//path", "protocol": "non-spec:", "username": "", @@ -7651,7 +7666,7 @@ "# percent encoded hosts in non-special-URLs", { "input": "non-special://%E2%80%A0/", - "base": "about:blank", + "base": null, "href": "non-special://%E2%80%A0/", "protocol": "non-special:", "username": "", @@ -7665,7 +7680,7 @@ }, { "input": "non-special://H%4fSt/path", - "base": "about:blank", + "base": null, "href": "non-special://H%4fSt/path", "protocol": "non-special:", "username": "", @@ -7680,7 +7695,7 @@ "# IPv6 in non-special-URLs", { "input": "non-special://[1:2:0:0:5:0:0:0]/", - "base": "about:blank", + "base": null, "href": "non-special://[1:2:0:0:5::]/", "protocol": "non-special:", "username": "", @@ -7694,7 +7709,7 @@ }, { "input": "non-special://[1:2:0:0:0:0:0:3]/", - "base": "about:blank", + "base": null, "href": "non-special://[1:2::3]/", "protocol": "non-special:", "username": "", @@ -7708,7 +7723,7 @@ }, { "input": "non-special://[1:2::3]:80/", - "base": "about:blank", + "base": null, "href": "non-special://[1:2::3]:80/", "protocol": "non-special:", "username": "", @@ -7722,12 +7737,12 @@ }, { "input": "non-special://[:80/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "blob:https://example.com:443/", - "base": "about:blank", + "base": null, "href": "blob:https://example.com:443/", "origin": "https://example.com", "protocol": "blob:", @@ -7742,7 +7757,7 @@ }, { "input": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", - "base": "about:blank", + "base": null, "href": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", "origin": "null", "protocol": "blob:", @@ -7757,7 +7772,7 @@ }, { "input": "blob:", - "base": "about:blank", + "base": null, "href": "blob:", "origin": "null", "protocol": "blob:", @@ -7773,7 +7788,7 @@ "Invalid IPv4 radix digits", { "input": "http://0x7f.0.0.0x7g", - "base": "about:blank", + "base": null, "href": "http://0x7f.0.0.0x7g/", "protocol": "http:", "username": "", @@ -7787,7 +7802,7 @@ }, { "input": "http://0X7F.0.0.0X7G", - "base": "about:blank", + "base": null, "href": "http://0x7f.0.0.0x7g/", "protocol": "http:", "username": "", @@ -7802,13 +7817,13 @@ "Invalid IPv4 portion of IPv6 address", { "input": "http://[::127.0.0.0.1]", - "base": "about:blank", + "base": null, "failure": true }, "Uncompressed IPv6 addresses with 0", { "input": "http://[0:1:0:1:0:1:0:1]", - "base": "about:blank", + "base": null, "href": "http://[0:1:0:1:0:1:0:1]/", "protocol": "http:", "username": "", @@ -7822,7 +7837,7 @@ }, { "input": "http://[1:0:1:0:1:0:1:0]", - "base": "about:blank", + "base": null, "href": "http://[1:0:1:0:1:0:1:0]/", "protocol": "http:", "username": "", @@ -7837,7 +7852,7 @@ "Percent-encoded query and fragment", { "input": "http://example.org/test?\u0022", - "base": "about:blank", + "base": null, "href": "http://example.org/test?%22", "protocol": "http:", "username": "", @@ -7851,7 +7866,7 @@ }, { "input": "http://example.org/test?\u0023", - "base": "about:blank", + "base": null, "href": "http://example.org/test?#", "protocol": "http:", "username": "", @@ -7865,7 +7880,7 @@ }, { "input": "http://example.org/test?\u003C", - "base": "about:blank", + "base": null, "href": "http://example.org/test?%3C", "protocol": "http:", "username": "", @@ -7879,7 +7894,7 @@ }, { "input": "http://example.org/test?\u003E", - "base": "about:blank", + "base": null, "href": "http://example.org/test?%3E", "protocol": "http:", "username": "", @@ -7893,7 +7908,7 @@ }, { "input": "http://example.org/test?\u2323", - "base": "about:blank", + "base": null, "href": "http://example.org/test?%E2%8C%A3", "protocol": "http:", "username": "", @@ -7907,7 +7922,7 @@ }, { "input": "http://example.org/test?%23%23", - "base": "about:blank", + "base": null, "href": "http://example.org/test?%23%23", "protocol": "http:", "username": "", @@ -7921,7 +7936,7 @@ }, { "input": "http://example.org/test?%GH", - "base": "about:blank", + "base": null, "href": "http://example.org/test?%GH", "protocol": "http:", "username": "", @@ -7935,7 +7950,7 @@ }, { "input": "http://example.org/test?a#%EF", - "base": "about:blank", + "base": null, "href": "http://example.org/test?a#%EF", "protocol": "http:", "username": "", @@ -7949,7 +7964,7 @@ }, { "input": "http://example.org/test?a#%GH", - "base": "about:blank", + "base": null, "href": "http://example.org/test?a#%GH", "protocol": "http:", "username": "", @@ -7964,21 +7979,21 @@ "URLs that require a non-about:blank base. (Also serve as invalid base tests.)", { "input": "a", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "a/", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, { "input": "a//", - "base": "about:blank", + "base": null, "failure": true, - "inputCanBeRelative": true + "relativeTo": "non-opaque-path-base" }, "Bases that don't fail to parse but fail to be bases", { @@ -8051,7 +8066,7 @@ "Null code point in fragment", { "input": "http://example.org/test?a#b\u0000c", - "base": "about:blank", + "base": null, "href": "http://example.org/test?a#b%00c", "protocol": "http:", "username": "", @@ -8065,7 +8080,7 @@ }, { "input": "non-spec://example.org/test?a#b\u0000c", - "base": "about:blank", + "base": null, "href": "non-spec://example.org/test?a#b%00c", "protocol": "non-spec:", "username": "", @@ -8079,7 +8094,7 @@ }, { "input": "non-spec:/test?a#b\u0000c", - "base": "about:blank", + "base": null, "href": "non-spec:/test?a#b%00c", "protocol": "non-spec:", "username": "", @@ -8139,7 +8154,7 @@ "IDNA ignored code points in file URLs hosts", { "input": "file://a\u00ADb/p", - "base": "about:blank", + "base": null, "href": "file://ab/p", "protocol": "file:", "username": "", @@ -8153,7 +8168,7 @@ }, { "input": "file://a%C2%ADb/p", - "base": "about:blank", + "base": null, "href": "file://ab/p", "protocol": "file:", "username": "", @@ -8168,7 +8183,7 @@ "IDNA hostnames which get mapped to 'localhost'", { "input": "file://loC𝐀𝐋𝐇𝐨𝐬𝐭/usr/bin", - "base": "about:blank", + "base": null, "href": "file:///usr/bin", "protocol": "file:", "username": "", @@ -8183,17 +8198,17 @@ "Empty host after the domain to ASCII", { "input": "file://\u00ad/p", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://%C2%AD/p", - "base": "about:blank", + "base": null, "failure": true }, { "input": "file://xn--/p", - "base": "about:blank", + "base": null, "failure": true }, "https://bugzilla.mozilla.org/show_bug.cgi?id=1647058", @@ -8214,7 +8229,7 @@ "UTF-8 percent-encode of C0 control percent-encode set and supersets", { "input": "non-special:cannot-be-a-base-url-\u0000\u0001\u001F\u001E\u007E\u007F\u0080", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8229,7 +8244,7 @@ }, { "input": "https://www.example.com/path{\u007Fpath.html?query'\u007F=query#fragment<\u007Ffragment", - "base": "about:blank", + "base": null, "hash": "#fragment%3C%7Ffragment", "host": "www.example.com", "hostname": "www.example.com", @@ -8260,7 +8275,7 @@ "Tests for the distinct percent-encode sets", { "input": "foo:// !\"$%&'()*+,-.;<=>@[\\]^_`{|}~@host/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8275,7 +8290,7 @@ }, { "input": "wss:// !\"$%&'()*+,-.;<=>@[]^_`{|}~@host/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8290,7 +8305,7 @@ }, { "input": "foo://joe: !\"$%&'()*+,-.:;<=>@[\\]^_`{|}~@host/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8305,7 +8320,7 @@ }, { "input": "wss://joe: !\"$%&'()*+,-.:;<=>@[]^_`{|}~@host/", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8320,7 +8335,7 @@ }, { "input": "foo://!\"$%&'()*+,-.;=_`{}~/", - "base": "about:blank", + "base": null, "hash": "", "host": "!\"$%&'()*+,-.;=_`{}~", "hostname": "!\"$%&'()*+,-.;=_`{}~", @@ -8335,7 +8350,7 @@ }, { "input": "wss://!\"$&'()*+,-.;=_`{}~/", - "base": "about:blank", + "base": null, "hash": "", "host": "!\"$&'()*+,-.;=_`{}~", "hostname": "!\"$&'()*+,-.;=_`{}~", @@ -8350,7 +8365,7 @@ }, { "input": "foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8365,7 +8380,7 @@ }, { "input": "wss://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8380,7 +8395,7 @@ }, { "input": "foo://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8395,7 +8410,7 @@ }, { "input": "wss://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", + "base": null, "hash": "", "host": "host", "hostname": "host", @@ -8410,7 +8425,7 @@ }, { "input": "foo://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", + "base": null, "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", "host": "host", "hostname": "host", @@ -8425,7 +8440,7 @@ }, { "input": "wss://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", - "base": "about:blank", + "base": null, "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", "host": "host", "hostname": "host", @@ -8499,12 +8514,14 @@ { "input": "#", "base": null, - "failure": true + "failure": true, + "relativeTo": "any-base" }, { "input": "?", "base": null, - "failure": true + "failure": true, + "relativeTo": "non-opaque-path-base" }, "Last component looks like a number, but not valid IPv4", { @@ -8519,12 +8536,12 @@ }, { "input": "http://0..0x300/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://0..0x300./", - "base": "about:blank", + "base": null, "failure": true }, { @@ -8539,102 +8556,102 @@ }, { "input": "http://1.2.3.08", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://1.2.3.08.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://1.2.3.09", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://09.2.3.4", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://09.2.3.4.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://01.2.3.4.5", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://01.2.3.4.5.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://0x100.2.3.4", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://0x100.2.3.4.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://0x1.2.3.4.5", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://0x1.2.3.4.5.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.1.2.3.4", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.1.2.3.4.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.2.3.4", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.2.3.4.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.09", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.09.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.0x4", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.0x4.", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.09..", - "base": "about:blank", + "base": null, "hash": "", "host": "foo.09..", "hostname": "foo.09..", @@ -8648,33 +8665,33 @@ }, { "input": "http://0999999999999999999/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.0x", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://foo.0XFfFfFfFfFfFfFfFfFfAcE123", - "base": "about:blank", + "base": null, "failure": true }, { "input": "http://💩.123/", - "base": "about:blank", + "base": null, "failure": true }, "U+0000 and U+FFFF in various places", { "input": "https://\u0000y", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://x/\u0000y", - "base": "about:blank", + "base": null, "hash": "", "host": "x", "hostname": "x", @@ -8688,7 +8705,7 @@ }, { "input": "https://x/?\u0000y", - "base": "about:blank", + "base": null, "hash": "", "host": "x", "hostname": "x", @@ -8702,7 +8719,7 @@ }, { "input": "https://x/?#\u0000y", - "base": "about:blank", + "base": null, "hash": "#%00y", "host": "x", "hostname": "x", @@ -8716,12 +8733,12 @@ }, { "input": "https://\uFFFFy", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://x/\uFFFFy", - "base": "about:blank", + "base": null, "hash": "", "host": "x", "hostname": "x", @@ -8735,7 +8752,7 @@ }, { "input": "https://x/?\uFFFFy", - "base": "about:blank", + "base": null, "hash": "", "host": "x", "hostname": "x", @@ -8749,7 +8766,7 @@ }, { "input": "https://x/?#\uFFFFy", - "base": "about:blank", + "base": null, "hash": "#%EF%BF%BFy", "host": "x", "hostname": "x", @@ -8763,7 +8780,7 @@ }, { "input": "non-special:\u0000y", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8777,7 +8794,7 @@ }, { "input": "non-special:x/\u0000y", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8791,7 +8808,7 @@ }, { "input": "non-special:x/?\u0000y", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8805,7 +8822,7 @@ }, { "input": "non-special:x/?#\u0000y", - "base": "about:blank", + "base": null, "hash": "#%00y", "host": "", "hostname": "", @@ -8819,7 +8836,7 @@ }, { "input": "non-special:\uFFFFy", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8833,7 +8850,7 @@ }, { "input": "non-special:x/\uFFFFy", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8847,7 +8864,7 @@ }, { "input": "non-special:x/?\uFFFFy", - "base": "about:blank", + "base": null, "hash": "", "host": "", "hostname": "", @@ -8861,7 +8878,7 @@ }, { "input": "non-special:x/?#\uFFFFy", - "base": "about:blank", + "base": null, "hash": "#%EF%BF%BFy", "host": "", "hostname": "", @@ -8875,12 +8892,13 @@ }, { "input": "", - "base": "about:blank", - "failure": true + "base": null, + "failure": true, + "relativeTo": "non-opaque-path-base" }, { "input": "https://example.com/\"quoted\"", - "base": "about:blank", + "base": null, "hash": "", "host": "example.com", "hostname": "example.com", @@ -8895,7 +8913,7 @@ }, { "input": "https://a%C2%ADb/", - "base": "about:blank", + "base": null, "hash": "", "host": "ab", "hostname": "ab", @@ -8911,23 +8929,23 @@ { "comment": "Empty host after domain to ASCII", "input": "https://\u00AD/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://%C2%AD/", - "base": "about:blank", + "base": null, "failure": true }, { "input": "https://xn--/", - "base": "about:blank", + "base": null, "failure": true }, "Non-special schemes that some implementations might incorrectly treat as special", { "input": "data://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "data://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "data:", @@ -8942,7 +8960,7 @@ }, { "input": "data:///test", - "base": "about:blank", + "base": null, "href": "data:///test", "origin": "null", "protocol": "data:", @@ -8957,7 +8975,7 @@ }, { "input": "data://test/a/../b", - "base": "about:blank", + "base": null, "href": "data://test/b", "origin": "null", "protocol": "data:", @@ -8972,22 +8990,22 @@ }, { "input": "data://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "data://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "data://[:1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "javascript://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "javascript://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "javascript:", @@ -9002,7 +9020,7 @@ }, { "input": "javascript:///test", - "base": "about:blank", + "base": null, "href": "javascript:///test", "origin": "null", "protocol": "javascript:", @@ -9017,7 +9035,7 @@ }, { "input": "javascript://test/a/../b", - "base": "about:blank", + "base": null, "href": "javascript://test/b", "origin": "null", "protocol": "javascript:", @@ -9032,22 +9050,22 @@ }, { "input": "javascript://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "javascript://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "javascript://[:1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "mailto://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "mailto://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "mailto:", @@ -9062,7 +9080,7 @@ }, { "input": "mailto:///test", - "base": "about:blank", + "base": null, "href": "mailto:///test", "origin": "null", "protocol": "mailto:", @@ -9077,7 +9095,7 @@ }, { "input": "mailto://test/a/../b", - "base": "about:blank", + "base": null, "href": "mailto://test/b", "origin": "null", "protocol": "mailto:", @@ -9092,22 +9110,22 @@ }, { "input": "mailto://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "mailto://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "mailto://[:1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "intent://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "intent://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "intent:", @@ -9122,7 +9140,7 @@ }, { "input": "intent:///test", - "base": "about:blank", + "base": null, "href": "intent:///test", "origin": "null", "protocol": "intent:", @@ -9137,7 +9155,7 @@ }, { "input": "intent://test/a/../b", - "base": "about:blank", + "base": null, "href": "intent://test/b", "origin": "null", "protocol": "intent:", @@ -9152,22 +9170,22 @@ }, { "input": "intent://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "intent://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "intent://[:1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "urn://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "urn://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "urn:", @@ -9182,7 +9200,7 @@ }, { "input": "urn:///test", - "base": "about:blank", + "base": null, "href": "urn:///test", "origin": "null", "protocol": "urn:", @@ -9197,7 +9215,7 @@ }, { "input": "urn://test/a/../b", - "base": "about:blank", + "base": null, "href": "urn://test/b", "origin": "null", "protocol": "urn:", @@ -9212,22 +9230,22 @@ }, { "input": "urn://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "urn://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "urn://[:1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "turn://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "turn://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "turn:", @@ -9242,7 +9260,7 @@ }, { "input": "turn:///test", - "base": "about:blank", + "base": null, "href": "turn:///test", "origin": "null", "protocol": "turn:", @@ -9257,7 +9275,7 @@ }, { "input": "turn://test/a/../b", - "base": "about:blank", + "base": null, "href": "turn://test/b", "origin": "null", "protocol": "turn:", @@ -9272,22 +9290,22 @@ }, { "input": "turn://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "turn://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "turn://[:1]", - "base": "about:blank", + "base": null, "failure": true }, { "input": "stun://example.com:8080/pathname?search#hash", - "base": "about:blank", + "base": null, "href": "stun://example.com:8080/pathname?search#hash", "origin": "null", "protocol": "stun:", @@ -9302,7 +9320,7 @@ }, { "input": "stun:///test", - "base": "about:blank", + "base": null, "href": "stun:///test", "origin": "null", "protocol": "stun:", @@ -9317,7 +9335,7 @@ }, { "input": "stun://test/a/../b", - "base": "about:blank", + "base": null, "href": "stun://test/b", "origin": "null", "protocol": "stun:", @@ -9332,17 +9350,17 @@ }, { "input": "stun://:443", - "base": "about:blank", + "base": null, "failure": true }, { "input": "stun://test:test", - "base": "about:blank", + "base": null, "failure": true }, { "input": "stun://[:1]", - "base": "about:blank", + "base": null, "failure": true } ] diff --git a/test/fixtures/wpt/url/url-constructor.any.js b/test/fixtures/wpt/url/url-constructor.any.js index 297b8ffd9eaf37..bea06d025b7188 100644 --- a/test/fixtures/wpt/url/url-constructor.any.js +++ b/test/fixtures/wpt/url/url-constructor.any.js @@ -5,14 +5,13 @@ // META: variant=?include=mailto // META: variant=?exclude=(file|javascript|mailto) -function bURL(url, base) { - return base ? new URL(url, base) : new URL(url) -} +function runURLTests(urlTests) { + for (const expected of urlTests) { + // Skip comments + if (typeof expected === "string") + continue; -function runURLTests(urltests) { - for(var i = 0, l = urltests.length; i < l; i++) { - var expected = urltests[i] - if (typeof expected === "string") continue // skip comments + const base = expected.base !== null ? expected.base : undefined; function getKey(expected) { if (expected.protocol) { @@ -27,12 +26,12 @@ function runURLTests(urltests) { subsetTestByKey(getKey(expected), test, function() { if (expected.failure) { assert_throws_js(TypeError, function() { - bURL(expected.input, expected.base) - }) - return + new URL(expected.input, base); + }); + return; } - var url = bURL(expected.input, expected.base) + const url = new URL(expected.input, base); assert_equals(url.href, expected.href, "href") assert_equals(url.protocol, expected.protocol, "protocol") assert_equals(url.username, expected.username, "username") @@ -47,7 +46,7 @@ function runURLTests(urltests) { assert_equals(url.searchParams.toString(), expected.searchParams, "searchParams") } assert_equals(url.hash, expected.hash, "hash") - }, "Parsing: <" + expected.input + "> against <" + expected.base + ">") + }, `Parsing: <${expected.input}> ${base ? "against <" + base + ">" : "without base"}`) } } diff --git a/test/fixtures/wpt/url/url-origin.any.js b/test/fixtures/wpt/url/url-origin.any.js index 9c1f97ed2e5949..599984c6c17dfe 100644 --- a/test/fixtures/wpt/url/url-origin.any.js +++ b/test/fixtures/wpt/url/url-origin.any.js @@ -1,17 +1,16 @@ promise_test(() => fetch("resources/urltestdata.json").then(res => res.json()).then(runURLTests), "Loading data…"); -function bURL(url, base) { - return base ? new URL(url, base) : new URL(url) -} +function runURLTests(urlTests) { + for (const expected of urlTests) { + // Skip comments and tests without "origin" expectation + if (typeof expected === "string" || !("origin" in expected)) + continue; -function runURLTests(urltests) { - for(var i = 0, l = urltests.length; i < l; i++) { - var expected = urltests[i] - if (typeof expected === "string" || !("origin" in expected)) continue + const base = expected.base !== null ? expected.base : undefined; - test(function() { - var url = bURL(expected.input, expected.base) - assert_equals(url.origin, expected.origin, "origin") - }, "Origin parsing: <" + expected.input + "> against <" + expected.base + ">") + test(() => { + const url = new URL(expected.input, base); + assert_equals(url.origin, expected.origin, "origin"); + }, `Origin parsing: <${expected.input}> ${base ? "against <" + base + ">" : "without base"}`); } } diff --git a/test/fixtures/wpt/url/urlsearchparams-delete.any.js b/test/fixtures/wpt/url/urlsearchparams-delete.any.js index 28ebbce5f13bd8..f9c623b90b175d 100644 --- a/test/fixtures/wpt/url/urlsearchparams-delete.any.js +++ b/test/fixtures/wpt/url/urlsearchparams-delete.any.js @@ -61,3 +61,12 @@ test(() => { assert_equals(url.pathname, 'space '); assert_equals(url.href, 'data:space #test'); }, 'Changing the query of a URL with an opaque path can impact the path if the URL has no fragment'); + +test(() => { + const params = new URLSearchParams(); + params.append('a', 'b'); + params.append('a', 'c'); + params.append('a', 'd'); + params.delete('a', 'c'); + assert_equals(params.toString(), 'a=b&a=d'); +}, "Two-argument delete()"); diff --git a/test/fixtures/wpt/url/urlsearchparams-has.any.js b/test/fixtures/wpt/url/urlsearchparams-has.any.js index 673dce77dc44ab..54cbf286db4a3d 100644 --- a/test/fixtures/wpt/url/urlsearchparams-has.any.js +++ b/test/fixtures/wpt/url/urlsearchparams-has.any.js @@ -22,3 +22,16 @@ test(function() { params.delete('first'); assert_false(params.has('first'), 'Search params object has no name "first"'); }, 'has() following delete()'); + +test(() => { + const params = new URLSearchParams("a=b&a=d&c&e&"); + assert_true(params.has('a', 'b')); + assert_false(params.has('a', 'c')); + assert_true(params.has('a', 'd')); + assert_true(params.has('e', '')); + params.append('first', null); + assert_false(params.has('first', '')); + assert_true(params.has('first', 'null')); + params.delete('a', 'b'); + assert_true(params.has('a', 'd')); +}, "Two-argument has()"); diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 6b9c6cb80a4659..3af47ed54b8c94 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -68,7 +68,7 @@ "path": "streams" }, "url": { - "commit": "7c5c3cc125979b4768d414471e6ab655b473aae8", + "commit": "c4726447f3bad1c71244fb99c98738ff0354a034", "path": "url" }, "user-timing": { From 5031f589ad1a85b59f2196815ae55caa3f9d29c8 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 02:35:40 +0530 Subject: [PATCH 03/13] url: ran linter --- lib/internal/url.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index 924168f1e86f34..cd3c451252e20e 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -445,21 +445,20 @@ class URLSearchParams { const list = this.#searchParams; name = toUSVString(name); - + if (value !== undefined) { value = toUSVString(value); } for (let i = 0; i < list.length;) { if (value !== undefined) { - const key = list[i] - const val = list[i + 1] + const key = list[i]; + const val = list[i + 1]; if (key === name && val === value) { list.splice(i, 2); - } - else { + } else { i += 2; - } + } } else { const cur = list[i]; if (cur === name) { @@ -527,7 +526,7 @@ class URLSearchParams { } for (let i = 0; i < list.length; i += 2) { - if(value !== undefined && list[i] === name && list[i + 1] === value) { + if (value !== undefined && list[i] === name && list[i + 1] === value) { return true; } if (value === undefined && list[i] === name) { From f1845e3167245f690a640db8d22c7f8f4f9e8772 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 09:24:26 +0530 Subject: [PATCH 04/13] fixup! refactor delete method one loop into two for better readability --- lib/internal/url.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index cd3c451252e20e..e6829de0693989 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -450,18 +450,17 @@ class URLSearchParams { value = toUSVString(value); } - for (let i = 0; i < list.length;) { - if (value !== undefined) { - const key = list[i]; - const val = list[i + 1]; - if (key === name && val === value) { + if (value !== undefined) { + for (let i = 0; i < list.length;) { + if (list[i] === name && list[i + 1] === value) { list.splice(i, 2); } else { i += 2; } - } else { - const cur = list[i]; - if (cur === name) { + } + } else { + for (let i = 0; i < list.length;) { + if (list[i] === name) { list.splice(i, 2); } else { i += 2; From 284670452618e2b5c3d78ccbfc8e8533329cca95 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 11:49:22 +0530 Subject: [PATCH 05/13] url: refactor has method --- lib/internal/url.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index e6829de0693989..a62b833f6c4f9a 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -525,13 +525,14 @@ class URLSearchParams { } for (let i = 0; i < list.length; i += 2) { - if (value !== undefined && list[i] === name && list[i + 1] === value) { - return true; - } - if (value === undefined && list[i] === name) { + if (list[i] === name) { + if (value !== undefined) { + return list[i + 1] === value; + } return true; } } + return false; } From a0522c84097201f56586e4da4c08632c012aaf88 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 12:27:33 +0530 Subject: [PATCH 06/13] url: correction in hsa method, exact key-value tuple search --- lib/internal/url.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index a62b833f6c4f9a..ced4423e517a4a 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -525,10 +525,7 @@ class URLSearchParams { } for (let i = 0; i < list.length; i += 2) { - if (list[i] === name) { - if (value !== undefined) { - return list[i + 1] === value; - } + if (list[i] === name && (value === undefined ? true : list[i + 1] === value)) { return true; } } From 3c1521fef26b003c5fe507ad2690e07878e79ca5 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 13:44:11 +0530 Subject: [PATCH 07/13] doc: update has method and delete method --- doc/api/url.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/api/url.md b/doc/api/url.md index 06abd2910b938a..8be9ff5a1881c7 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -859,11 +859,15 @@ new URLSearchParams([ Append a new name-value pair to the query string. -#### `urlSearchParams.delete(name)` +#### `urlSearchParams.delete(name, value)` * `name` {string} +* `value` {string} + +If `value` is provided, then delete all name-value pairs with +whose `name` is name and `value` is value. -Remove all name-value pairs whose name is `name`. +If `value` not provided, remove all name-value pairs whose name is `name`. #### `urlSearchParams.entries()` @@ -918,12 +922,20 @@ are no such pairs, `null` is returned. Returns the values of all name-value pairs whose name is `name`. If there are no such pairs, an empty array is returned. -#### `urlSearchParams.has(name)` +#### `urlSearchParams.has(name, value)` * `name` {string} +* `value` {string} * Returns: {boolean} -Returns `true` if there is at least one name-value pair whose name is `name`. +Checks if the `URLSearchParams` object contains key-value pair(s) based on +`name` and an optional `value` argument. + +If `value` is provided, returns true when name-value pair with +same `name` and `value` exists. + +If `value` is not provided, returns `true` if there is at least one name-value +pair whose name is `name`. #### `urlSearchParams.keys()` From e66fe1e07328308bf535ffa510915952a1427a1e Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sat, 6 May 2023 16:41:34 +0530 Subject: [PATCH 08/13] fixup! add changes in doc and refactor urlsearchparam-has --- doc/api/url.md | 18 ++++++++++++++++-- lib/internal/url.js | 8 ++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/doc/api/url.md b/doc/api/url.md index 8be9ff5a1881c7..1e9820fa1f2836 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -859,7 +859,14 @@ new URLSearchParams([ Append a new name-value pair to the query string. -#### `urlSearchParams.delete(name, value)` +#### `urlSearchParams.delete(name[, value])` + + * `name` {string} * `value` {string} @@ -922,7 +929,14 @@ are no such pairs, `null` is returned. Returns the values of all name-value pairs whose name is `name`. If there are no such pairs, an empty array is returned. -#### `urlSearchParams.has(name, value)` +#### `urlSearchParams.has(name[, value])` + + * `name` {string} * `value` {string} diff --git a/lib/internal/url.js b/lib/internal/url.js index ced4423e517a4a..369d0d8851621f 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -525,8 +525,12 @@ class URLSearchParams { } for (let i = 0; i < list.length; i += 2) { - if (list[i] === name && (value === undefined ? true : list[i + 1] === value)) { - return true; + if (list[i] === name) { + if (value === undefined) { + return true; + } else if (list[i + 1] === value) { + return true; + } } } From 68624b36380ee40239bcf2889799d42257d62058 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Mon, 8 May 2023 15:03:18 +0530 Subject: [PATCH 09/13] fixup! add default arg as undefined to value --- doc/api/url.md | 2 +- lib/internal/url.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/url.md b/doc/api/url.md index 1e9820fa1f2836..097b83736aeef2 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -874,7 +874,7 @@ changes: If `value` is provided, then delete all name-value pairs with whose `name` is name and `value` is value. -If `value` not provided, remove all name-value pairs whose name is `name`. +If `value` is not provided, remove all name-value pairs whose name is `name`. #### `urlSearchParams.entries()` diff --git a/lib/internal/url.js b/lib/internal/url.js index 369d0d8851621f..8499bf21aa99d5 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -435,7 +435,7 @@ class URLSearchParams { } } - delete(name, value) { + delete(name, value = undefined) { if (typeof this !== 'object' || this === null || !(#searchParams in this)) throw new ERR_INVALID_THIS('URLSearchParams'); @@ -509,7 +509,7 @@ class URLSearchParams { return values; } - has(name, value) { + has(name, value = undefined) { if (typeof this !== 'object' || this === null || !(#searchParams in this)) throw new ERR_INVALID_THIS('URLSearchParams'); From 29c316ca78843228bd31d62091c658fc2fa7d6d8 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Mon, 8 May 2023 21:11:28 +0530 Subject: [PATCH 10/13] tools: fix to accommodate base as null, benchmark test fixed --- benchmark/common.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/benchmark/common.js b/benchmark/common.js index ac8e88917bc8ad..efe21e871571fc 100644 --- a/benchmark/common.js +++ b/benchmark/common.js @@ -360,8 +360,9 @@ function getUrlData(withBase) { for (const item of data) { if (item.failure || !item.input) continue; if (withBase) { - result.push([item.input, item.base]); - } else if (item.base !== 'about:blank') { + // item.base might be null. It should be converted into `undefined`. + result.push([item.input, item.base ?? undefined]); + } else if (item.base !== null) { result.push(item.base); } } From 9126a64e9eef0d3384dcc412b4b39e33dc585279 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Mon, 8 May 2023 21:24:18 +0530 Subject: [PATCH 11/13] fixup! refactor delete method and minor correction in docs --- doc/api/url.md | 2 +- lib/internal/url.js | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/api/url.md b/doc/api/url.md index 097b83736aeef2..6d867276311d9e 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -945,7 +945,7 @@ changes: Checks if the `URLSearchParams` object contains key-value pair(s) based on `name` and an optional `value` argument. -If `value` is provided, returns true when name-value pair with +If `value` is provided, returns `true` when name-value pair with same `name` and `value` exists. If `value` is not provided, returns `true` if there is at least one name-value diff --git a/lib/internal/url.js b/lib/internal/url.js index 8499bf21aa99d5..cb5da08a8be90b 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -448,9 +448,6 @@ class URLSearchParams { if (value !== undefined) { value = toUSVString(value); - } - - if (value !== undefined) { for (let i = 0; i < list.length;) { if (list[i] === name && list[i + 1] === value) { list.splice(i, 2); From 3b194234281cd1755c2b361afa85d890cf998212 Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Mon, 8 May 2023 22:14:59 +0530 Subject: [PATCH 12/13] url: refactor conditional statements in the loop --- lib/internal/url.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/url.js b/lib/internal/url.js index cb5da08a8be90b..ccd89830f91ded 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -523,9 +523,7 @@ class URLSearchParams { for (let i = 0; i < list.length; i += 2) { if (list[i] === name) { - if (value === undefined) { - return true; - } else if (list[i + 1] === value) { + if (value === undefined || list[i + 1] === value) { return true; } } From fb0e57d68b17e15d6a9bcfb94651f5649ec49d2a Mon Sep 17 00:00:00 2001 From: sankalp1999 Date: Sun, 14 May 2023 11:26:36 +0530 Subject: [PATCH 13/13] fixup! minor changes in docs --- doc/api/url.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/api/url.md b/doc/api/url.md index 6d867276311d9e..a07cda465ca46e 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -865,16 +865,16 @@ Append a new name-value pair to the query string. changes: - version: REPLACEME pr-url: https://github.com/nodejs/node/pull/47885 - description: added optional value argument. + description: Add support for optional `value` argument. --> * `name` {string} * `value` {string} -If `value` is provided, then delete all name-value pairs with -whose `name` is name and `value` is value. +If `value` is provided, removes all name-value pairs +where name is `name` and value is `value`.. -If `value` is not provided, remove all name-value pairs whose name is `name`. +If `value` is not provided, removes all name-value pairs whose name is `name`. #### `urlSearchParams.entries()` @@ -935,7 +935,7 @@ no such pairs, an empty array is returned. changes: - version: REPLACEME pr-url: https://github.com/nodejs/node/pull/47885 - description: added optional value argument. + description: Add support for optional `value` argument. --> * `name` {string}