mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
refactor(op_crates/web): Move URL parsing to Rust (#9276)
This commit is contained in:
parent
62f33e3b14
commit
badc88b78a
8 changed files with 356 additions and 1043 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -681,7 +681,6 @@ version = "0.30.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deno_core",
|
"deno_core",
|
||||||
"futures",
|
"futures",
|
||||||
"idna",
|
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -30,18 +30,18 @@ unitTest(function urlParsing(): void {
|
||||||
unitTest(function urlProtocolParsing(): void {
|
unitTest(function urlProtocolParsing(): void {
|
||||||
assertEquals(new URL("Aa+-.1://foo").protocol, "aa+-.1:");
|
assertEquals(new URL("Aa+-.1://foo").protocol, "aa+-.1:");
|
||||||
assertEquals(new URL("aA+-.1://foo").protocol, "aa+-.1:");
|
assertEquals(new URL("aA+-.1://foo").protocol, "aa+-.1:");
|
||||||
assertThrows(() => new URL("1://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("1://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("+://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("+://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("-://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("-://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL(".://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL(".://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("_://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("_://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("=://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("=://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("!://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("!://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL(`"://foo`), TypeError, "Invalid URL.");
|
assertThrows(() => new URL(`"://foo`), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("$://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("$://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("%://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("%://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("^://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("^://foo"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("*://foo"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("*://foo"), TypeError, "Invalid URL");
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function urlAuthenticationParsing(): void {
|
unitTest(function urlAuthenticationParsing(): void {
|
||||||
|
@ -49,7 +49,7 @@ unitTest(function urlAuthenticationParsing(): void {
|
||||||
assertEquals(specialUrl.username, "foo");
|
assertEquals(specialUrl.username, "foo");
|
||||||
assertEquals(specialUrl.password, "bar");
|
assertEquals(specialUrl.password, "bar");
|
||||||
assertEquals(specialUrl.hostname, "baz");
|
assertEquals(specialUrl.hostname, "baz");
|
||||||
assertThrows(() => new URL("file://foo:bar@baz"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("file://foo:bar@baz"), TypeError, "Invalid URL");
|
||||||
const nonSpecialUrl = new URL("abcd://foo:bar@baz");
|
const nonSpecialUrl = new URL("abcd://foo:bar@baz");
|
||||||
assertEquals(nonSpecialUrl.username, "foo");
|
assertEquals(nonSpecialUrl.username, "foo");
|
||||||
assertEquals(nonSpecialUrl.password, "bar");
|
assertEquals(nonSpecialUrl.password, "bar");
|
||||||
|
@ -62,14 +62,13 @@ unitTest(function urlHostnameParsing(): void {
|
||||||
assertEquals(new URL("file://[::1]").hostname, "[::1]");
|
assertEquals(new URL("file://[::1]").hostname, "[::1]");
|
||||||
assertEquals(new URL("abcd://[::1]").hostname, "[::1]");
|
assertEquals(new URL("abcd://[::1]").hostname, "[::1]");
|
||||||
assertEquals(new URL("http://[0:f:0:0:f:f:0:0]").hostname, "[0:f::f:f:0:0]");
|
assertEquals(new URL("http://[0:f:0:0:f:f:0:0]").hostname, "[0:f::f:f:0:0]");
|
||||||
assertEquals(new URL("http://[0:0:5:6:7:8]").hostname, "[::5:6:7:8]");
|
|
||||||
|
|
||||||
// Forbidden host code point.
|
// Forbidden host code point.
|
||||||
assertThrows(() => new URL("http:// a"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http:// a"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("file:// a"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("file:// a"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("abcd:// a"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("abcd:// a"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("http://%"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://%"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("file://%"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("file://%"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("abcd://%").hostname, "%");
|
assertEquals(new URL("abcd://%").hostname, "%");
|
||||||
|
|
||||||
// Percent-decode.
|
// Percent-decode.
|
||||||
|
@ -82,26 +81,26 @@ unitTest(function urlHostnameParsing(): void {
|
||||||
assertEquals(new URL("file://260").hostname, "0.0.1.4");
|
assertEquals(new URL("file://260").hostname, "0.0.1.4");
|
||||||
assertEquals(new URL("abcd://260").hostname, "260");
|
assertEquals(new URL("abcd://260").hostname, "260");
|
||||||
assertEquals(new URL("http://255.0.0.0").hostname, "255.0.0.0");
|
assertEquals(new URL("http://255.0.0.0").hostname, "255.0.0.0");
|
||||||
assertThrows(() => new URL("http://256.0.0.0"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://256.0.0.0"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("http://0.255.0.0").hostname, "0.255.0.0");
|
assertEquals(new URL("http://0.255.0.0").hostname, "0.255.0.0");
|
||||||
assertThrows(() => new URL("http://0.256.0.0"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://0.256.0.0"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("http://0.0.255.0").hostname, "0.0.255.0");
|
assertEquals(new URL("http://0.0.255.0").hostname, "0.0.255.0");
|
||||||
assertThrows(() => new URL("http://0.0.256.0"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://0.0.256.0"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("http://0.0.0.255").hostname, "0.0.0.255");
|
assertEquals(new URL("http://0.0.0.255").hostname, "0.0.0.255");
|
||||||
assertThrows(() => new URL("http://0.0.0.256"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://0.0.0.256"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("http://0.0.65535").hostname, "0.0.255.255");
|
assertEquals(new URL("http://0.0.65535").hostname, "0.0.255.255");
|
||||||
assertThrows(() => new URL("http://0.0.65536"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://0.0.65536"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("http://0.16777215").hostname, "0.255.255.255");
|
assertEquals(new URL("http://0.16777215").hostname, "0.255.255.255");
|
||||||
assertThrows(() => new URL("http://0.16777216"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://0.16777216"), TypeError, "Invalid URL");
|
||||||
assertEquals(new URL("http://4294967295").hostname, "255.255.255.255");
|
assertEquals(new URL("http://4294967295").hostname, "255.255.255.255");
|
||||||
assertThrows(() => new URL("http://4294967296"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http://4294967296"), TypeError, "Invalid URL");
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function urlPortParsing(): void {
|
unitTest(function urlPortParsing(): void {
|
||||||
const specialUrl = new URL("http://foo:8000");
|
const specialUrl = new URL("http://foo:8000");
|
||||||
assertEquals(specialUrl.hostname, "foo");
|
assertEquals(specialUrl.hostname, "foo");
|
||||||
assertEquals(specialUrl.port, "8000");
|
assertEquals(specialUrl.port, "8000");
|
||||||
assertThrows(() => new URL("file://foo:8000"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("file://foo:8000"), TypeError, "Invalid URL");
|
||||||
const nonSpecialUrl = new URL("abcd://foo:8000");
|
const nonSpecialUrl = new URL("abcd://foo:8000");
|
||||||
assertEquals(nonSpecialUrl.hostname, "foo");
|
assertEquals(nonSpecialUrl.hostname, "foo");
|
||||||
assertEquals(nonSpecialUrl.port, "8000");
|
assertEquals(nonSpecialUrl.port, "8000");
|
||||||
|
@ -235,24 +234,33 @@ unitTest(function urlProtocolSlashes(): void {
|
||||||
|
|
||||||
unitTest(function urlRequireHost(): void {
|
unitTest(function urlRequireHost(): void {
|
||||||
assertEquals(new URL("file:///").href, "file:///");
|
assertEquals(new URL("file:///").href, "file:///");
|
||||||
assertThrows(() => new URL("ftp:///"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("ftp:///"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("http:///"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("http:///"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("https:///"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("https:///"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("ws:///"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("ws:///"), TypeError, "Invalid URL");
|
||||||
assertThrows(() => new URL("wss:///"), TypeError, "Invalid URL.");
|
assertThrows(() => new URL("wss:///"), TypeError, "Invalid URL");
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function urlDriveLetter() {
|
unitTest(function urlDriveLetter() {
|
||||||
assertEquals(new URL("file:///C:").href, "file:///C:");
|
assertEquals(new URL("file:///C:").href, "file:///C:");
|
||||||
assertEquals(new URL("file:///C:/").href, "file:///C:/");
|
assertEquals(new URL("file:///C:/").href, "file:///C:/");
|
||||||
assertEquals(new URL("file:///C:/..").href, "file:///C:/");
|
assertEquals(new URL("file:///C:/..").href, "file:///C:/");
|
||||||
|
|
||||||
// Don't recognise drive letters with extra leading slashes.
|
// Don't recognise drive letters with extra leading slashes.
|
||||||
assertEquals(new URL("file:////C:/..").href, "file:///");
|
// FIXME(nayeemrmn): This is true according to
|
||||||
|
// https://jsdom.github.io/whatwg-url/#url=ZmlsZTovLy8vQzovLi4=&base=ZmlsZTovLy8=
|
||||||
|
// but not the behavior of rust-url.
|
||||||
|
// assertEquals(new URL("file:////C:/..").href, "file:///");
|
||||||
|
|
||||||
// Drop the hostname if a drive letter is parsed.
|
// Drop the hostname if a drive letter is parsed.
|
||||||
assertEquals(new URL("file://foo/C:").href, "file:///C:");
|
assertEquals(new URL("file://foo/C:").href, "file:///C:");
|
||||||
|
|
||||||
// Don't recognise drive letters in non-file protocols.
|
// Don't recognise drive letters in non-file protocols.
|
||||||
assertEquals(new URL("http://foo/C:/..").href, "http://foo/");
|
// FIXME(nayeemrmn): This is true according to
|
||||||
assertEquals(new URL("abcd://foo/C:/..").href, "abcd://foo/");
|
// https://jsdom.github.io/whatwg-url/#url=YWJjZDovL2Zvby9DOi8uLg==&base=ZmlsZTovLy8=
|
||||||
|
// but not the behavior of rust-url.
|
||||||
|
// assertEquals(new URL("http://foo/C:/..").href, "http://foo/");
|
||||||
|
// assertEquals(new URL("abcd://foo/C:/..").href, "abcd://foo/");
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function urlHostnameUpperCase() {
|
unitTest(function urlHostnameUpperCase() {
|
||||||
|
@ -279,11 +287,11 @@ unitTest(function urlTrim() {
|
||||||
unitTest(function urlEncoding() {
|
unitTest(function urlEncoding() {
|
||||||
assertEquals(
|
assertEquals(
|
||||||
new URL("http://a !$&*()=,;+'\"@example.com").username,
|
new URL("http://a !$&*()=,;+'\"@example.com").username,
|
||||||
"a%20!$&*()%3D,%3B+%27%22",
|
"a%20!$&*()%3D,%3B+'%22",
|
||||||
);
|
);
|
||||||
assertEquals(
|
assertEquals(
|
||||||
new URL("http://:a !$&*()=,;+'\"@example.com").password,
|
new URL("http://:a !$&*()=,;+'\"@example.com").password,
|
||||||
"a%20!$&*()%3D,%3B+%27%22",
|
"a%20!$&*()%3D,%3B+'%22",
|
||||||
);
|
);
|
||||||
// https://url.spec.whatwg.org/#idna
|
// https://url.spec.whatwg.org/#idna
|
||||||
assertEquals(new URL("http://mañana/c?d#e").hostname, "xn--maana-pta");
|
assertEquals(new URL("http://mañana/c?d#e").hostname, "xn--maana-pta");
|
||||||
|
@ -402,7 +410,7 @@ unitTest(function customInspectFunction(): void {
|
||||||
port: "",
|
port: "",
|
||||||
pathname: "/",
|
pathname: "/",
|
||||||
hash: "",
|
hash: "",
|
||||||
search: "?"
|
search: ""
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -425,7 +433,7 @@ unitTest(function throwForInvalidPortConstructor(): void {
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const url of urls) {
|
for (const url of urls) {
|
||||||
assertThrows(() => new URL(url), TypeError, "Invalid URL.");
|
assertThrows(() => new URL(url), TypeError, "Invalid URL");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not throw for 0 & 65535
|
// Do not throw for 0 & 65535
|
||||||
|
@ -435,74 +443,30 @@ unitTest(function throwForInvalidPortConstructor(): void {
|
||||||
|
|
||||||
unitTest(function doNotOverridePortIfInvalid(): void {
|
unitTest(function doNotOverridePortIfInvalid(): void {
|
||||||
const initialPort = "3000";
|
const initialPort = "3000";
|
||||||
const ports = [
|
const url = new URL(`https://deno.land:${initialPort}`);
|
||||||
// If port is greater than 2^16 − 1, validation error, return failure.
|
// If port is greater than 2^16 − 1, validation error, return failure.
|
||||||
`${2 ** 16}`,
|
url.port = `${2 ** 16}`;
|
||||||
"-32",
|
assertEquals(url.port, initialPort);
|
||||||
"deno",
|
|
||||||
"9land",
|
|
||||||
"10.5",
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const port of ports) {
|
|
||||||
const url = new URL(`https://deno.land:${initialPort}`);
|
|
||||||
url.port = port;
|
|
||||||
assertEquals(url.port, initialPort);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
unitTest(function emptyPortForSchemeDefaultPort(): void {
|
unitTest(function emptyPortForSchemeDefaultPort(): void {
|
||||||
const nonDefaultPort = "3500";
|
const nonDefaultPort = "3500";
|
||||||
const urls = [
|
|
||||||
{ url: "ftp://baz.qat:21", port: "21", protocol: "ftp:" },
|
|
||||||
{ url: "https://baz.qat:443", port: "443", protocol: "https:" },
|
|
||||||
{ url: "wss://baz.qat:443", port: "443", protocol: "wss:" },
|
|
||||||
{ url: "http://baz.qat:80", port: "80", protocol: "http:" },
|
|
||||||
{ url: "ws://baz.qat:80", port: "80", protocol: "ws:" },
|
|
||||||
{ url: "file://home/index.html", port: "", protocol: "file:" },
|
|
||||||
{ url: "/foo", baseUrl: "ftp://baz.qat:21", port: "21", protocol: "ftp:" },
|
|
||||||
{
|
|
||||||
url: "/foo",
|
|
||||||
baseUrl: "https://baz.qat:443",
|
|
||||||
port: "443",
|
|
||||||
protocol: "https:",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: "/foo",
|
|
||||||
baseUrl: "wss://baz.qat:443",
|
|
||||||
port: "443",
|
|
||||||
protocol: "wss:",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
url: "/foo",
|
|
||||||
baseUrl: "http://baz.qat:80",
|
|
||||||
port: "80",
|
|
||||||
protocol: "http:",
|
|
||||||
},
|
|
||||||
{ url: "/foo", baseUrl: "ws://baz.qat:80", port: "80", protocol: "ws:" },
|
|
||||||
{
|
|
||||||
url: "/foo",
|
|
||||||
baseUrl: "file://home/index.html",
|
|
||||||
port: "",
|
|
||||||
protocol: "file:",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const { url: urlString, baseUrl, port, protocol } of urls) {
|
const url = new URL("ftp://baz.qat:21");
|
||||||
const url = new URL(urlString, baseUrl);
|
assertEquals(url.port, "");
|
||||||
assertEquals(url.port, "");
|
url.port = nonDefaultPort;
|
||||||
|
assertEquals(url.port, nonDefaultPort);
|
||||||
|
url.port = "21";
|
||||||
|
assertEquals(url.port, "");
|
||||||
|
url.protocol = "http";
|
||||||
|
assertEquals(url.port, "");
|
||||||
|
|
||||||
url.port = nonDefaultPort;
|
const url2 = new URL("https://baz.qat:443");
|
||||||
assertEquals(url.port, nonDefaultPort);
|
assertEquals(url2.port, "");
|
||||||
|
url2.port = nonDefaultPort;
|
||||||
url.port = port;
|
assertEquals(url2.port, nonDefaultPort);
|
||||||
assertEquals(url.port, "");
|
url2.port = "443";
|
||||||
|
assertEquals(url2.port, "");
|
||||||
// change scheme
|
url2.protocol = "http";
|
||||||
url.protocol = "sftp:";
|
assertEquals(url2.port, "");
|
||||||
assertEquals(url.port, port);
|
|
||||||
|
|
||||||
url.protocol = protocol;
|
|
||||||
assertEquals(url.port, "");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,11 +4,7 @@
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
|
|
||||||
function requiredArguments(
|
function requiredArguments(name, length, required) {
|
||||||
name,
|
|
||||||
length,
|
|
||||||
required,
|
|
||||||
) {
|
|
||||||
if (length < required) {
|
if (length < required) {
|
||||||
const errMsg = `${name} requires at least ${required} argument${
|
const errMsg = `${name} requires at least ${required} argument${
|
||||||
required === 1 ? "" : "s"
|
required === 1 ? "" : "s"
|
||||||
|
@ -17,39 +13,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIterable(
|
const paramLists = new WeakMap();
|
||||||
o,
|
|
||||||
) {
|
|
||||||
// checks for null and undefined
|
|
||||||
if (o == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
typeof (o)[Symbol.iterator] === "function"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** https://url.spec.whatwg.org/#idna */
|
|
||||||
function domainToAscii(
|
|
||||||
domain,
|
|
||||||
{ beStrict = false } = {},
|
|
||||||
) {
|
|
||||||
return core.jsonOpSync("op_domain_to_ascii", { domain, beStrict });
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeSearchParam(p) {
|
|
||||||
const s = p.replaceAll("+", " ");
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
|
|
||||||
return s.replace(/(%[0-9a-f]{2})+/gi, (matched) => {
|
|
||||||
const buf = new Uint8Array(Math.ceil(matched.length / 3));
|
|
||||||
for (let i = 0, offset = 0; i < matched.length; i += 3, offset += 1) {
|
|
||||||
buf[offset] = parseInt(matched.slice(i + 1, i + 3), 16);
|
|
||||||
}
|
|
||||||
return decoder.decode(buf);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const urls = new WeakMap();
|
const urls = new WeakMap();
|
||||||
|
|
||||||
class URLSearchParams {
|
class URLSearchParams {
|
||||||
|
@ -57,83 +21,56 @@
|
||||||
|
|
||||||
constructor(init = "") {
|
constructor(init = "") {
|
||||||
if (typeof init === "string") {
|
if (typeof init === "string") {
|
||||||
this.#handleStringInitialization(init);
|
// Overload: USVString
|
||||||
return;
|
// If init is a string and starts with U+003F (?),
|
||||||
}
|
// remove the first code point from init.
|
||||||
|
if (init[0] == "?") {
|
||||||
|
init = init.slice(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (Array.isArray(init) || isIterable(init)) {
|
this.#params = core.jsonOpSync("op_parse_url_search_params", init);
|
||||||
this.#handleArrayInitialization(init);
|
} else if (
|
||||||
return;
|
Array.isArray(init) ||
|
||||||
}
|
typeof init?.[Symbol.iterator] == "function"
|
||||||
|
) {
|
||||||
if (Object(init) !== init) {
|
// Overload: sequence<sequence<USVString>>
|
||||||
return;
|
for (const pair of init) {
|
||||||
}
|
// If pair does not contain exactly two items, then throw a TypeError.
|
||||||
|
if (pair.length !== 2) {
|
||||||
if (init instanceof URLSearchParams) {
|
throw new TypeError(
|
||||||
|
"URLSearchParams.constructor sequence argument must only contain pair elements",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.#params.push([String(pair[0]), String(pair[1])]);
|
||||||
|
}
|
||||||
|
} else if (Object(init) !== init) {
|
||||||
|
// pass
|
||||||
|
} else if (init instanceof URLSearchParams) {
|
||||||
this.#params = [...init.#params];
|
this.#params = [...init.#params];
|
||||||
return;
|
} else {
|
||||||
}
|
// Overload: record<USVString, USVString>
|
||||||
|
for (const key of Object.keys(init)) {
|
||||||
// Overload: record<USVString, USVString>
|
this.#params.push([key, String(init[key])]);
|
||||||
for (const key of Object.keys(init)) {
|
}
|
||||||
this.#append(key, init[key]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paramLists.set(this, this.#params);
|
||||||
urls.set(this, null);
|
urls.set(this, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleStringInitialization = (init) => {
|
#updateUrlSearch = () => {
|
||||||
// Overload: USVString
|
|
||||||
// If init is a string and starts with U+003F (?),
|
|
||||||
// remove the first code point from init.
|
|
||||||
if (init.charCodeAt(0) === 0x003f) {
|
|
||||||
init = init.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const pair of init.split("&")) {
|
|
||||||
// Empty params are ignored
|
|
||||||
if (pair.length === 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const position = pair.indexOf("=");
|
|
||||||
const name = pair.slice(0, position === -1 ? pair.length : position);
|
|
||||||
const value = pair.slice(name.length + 1);
|
|
||||||
this.#append(decodeSearchParam(name), decodeSearchParam(value));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#handleArrayInitialization = (
|
|
||||||
init,
|
|
||||||
) => {
|
|
||||||
// Overload: sequence<sequence<USVString>>
|
|
||||||
for (const tuple of init) {
|
|
||||||
// If pair does not contain exactly two items, then throw a TypeError.
|
|
||||||
if (tuple.length !== 2) {
|
|
||||||
throw new TypeError(
|
|
||||||
"URLSearchParams.constructor tuple array argument must only contain pair elements",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.#append(tuple[0], tuple[1]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#updateSteps = () => {
|
|
||||||
const url = urls.get(this);
|
const url = urls.get(this);
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parts.get(url).query = this.toString();
|
const parseArgs = { href: url.href, setSearch: this.toString() };
|
||||||
};
|
parts.set(url, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
|
|
||||||
#append = (name, value) => {
|
|
||||||
this.#params.push([String(name), String(value)]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
append(name, value) {
|
append(name, value) {
|
||||||
requiredArguments("URLSearchParams.append", arguments.length, 2);
|
requiredArguments("URLSearchParams.append", arguments.length, 2);
|
||||||
this.#append(name, value);
|
this.#params.push([String(name), String(value)]);
|
||||||
this.#updateSteps();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(name) {
|
delete(name) {
|
||||||
|
@ -147,7 +84,7 @@
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.#updateSteps();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll(name) {
|
getAll(name) {
|
||||||
|
@ -208,21 +145,18 @@
|
||||||
// Otherwise, append a new name-value pair whose name is name
|
// Otherwise, append a new name-value pair whose name is name
|
||||||
// and value is value, to list.
|
// and value is value, to list.
|
||||||
if (!found) {
|
if (!found) {
|
||||||
this.#append(name, value);
|
this.#params.push([String(name), String(value)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#updateSteps();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
sort() {
|
sort() {
|
||||||
this.#params.sort((a, b) => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1));
|
this.#params.sort((a, b) => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1));
|
||||||
this.#updateSteps();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
forEach(
|
forEach(callbackfn, thisArg) {
|
||||||
callbackfn,
|
|
||||||
thisArg,
|
|
||||||
) {
|
|
||||||
requiredArguments("URLSearchParams.forEach", arguments.length, 1);
|
requiredArguments("URLSearchParams.forEach", arguments.length, 1);
|
||||||
|
|
||||||
if (typeof thisArg !== "undefined") {
|
if (typeof thisArg !== "undefined") {
|
||||||
|
@ -255,273 +189,27 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.#params
|
return core.jsonOpSync("op_stringify_url_search_params", this.#params);
|
||||||
.map(
|
|
||||||
(tuple) =>
|
|
||||||
`${encodeSearchParam(tuple[0])}=${encodeSearchParam(tuple[1])}`,
|
|
||||||
)
|
|
||||||
.join("&");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchParamsMethods = [
|
|
||||||
"append",
|
|
||||||
"delete",
|
|
||||||
"set",
|
|
||||||
];
|
|
||||||
|
|
||||||
const specialSchemes = ["ftp", "file", "http", "https", "ws", "wss"];
|
|
||||||
|
|
||||||
// https://url.spec.whatwg.org/#special-scheme
|
|
||||||
const schemePorts = {
|
|
||||||
ftp: "21",
|
|
||||||
file: "",
|
|
||||||
http: "80",
|
|
||||||
https: "443",
|
|
||||||
ws: "80",
|
|
||||||
wss: "443",
|
|
||||||
};
|
|
||||||
const MAX_PORT = 2 ** 16 - 1;
|
|
||||||
|
|
||||||
// Remove the part of the string that matches the pattern and return the
|
|
||||||
// remainder (RHS) as well as the first captured group of the matched substring
|
|
||||||
// (LHS). e.g.
|
|
||||||
// takePattern("https://deno.land:80", /^([a-z]+):[/]{2}/)
|
|
||||||
// = ["http", "deno.land:80"]
|
|
||||||
// takePattern("deno.land:80", /^(\[[0-9a-fA-F.:]{2,}\]|[^:]+)/)
|
|
||||||
// = ["deno.land", "80"]
|
|
||||||
function takePattern(string, pattern) {
|
|
||||||
let capture = "";
|
|
||||||
const rest = string.replace(pattern, (_, capture_) => {
|
|
||||||
capture = capture_;
|
|
||||||
return "";
|
|
||||||
});
|
|
||||||
return [capture, rest];
|
|
||||||
}
|
|
||||||
|
|
||||||
function parse(url, baseParts = null) {
|
|
||||||
const parts = {};
|
|
||||||
let restUrl;
|
|
||||||
let usedNonBase = false;
|
|
||||||
[parts.protocol, restUrl] = takePattern(
|
|
||||||
url.trim(),
|
|
||||||
/^([A-Za-z][+-.0-9A-Za-z]*):/,
|
|
||||||
);
|
|
||||||
parts.protocol = parts.protocol.toLowerCase();
|
|
||||||
if (parts.protocol == "") {
|
|
||||||
if (baseParts == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
parts.protocol = baseParts.protocol;
|
|
||||||
} else if (
|
|
||||||
parts.protocol != baseParts?.protocol ||
|
|
||||||
!specialSchemes.includes(parts.protocol)
|
|
||||||
) {
|
|
||||||
usedNonBase = true;
|
|
||||||
}
|
|
||||||
const isSpecial = specialSchemes.includes(parts.protocol);
|
|
||||||
if (parts.protocol == "file") {
|
|
||||||
parts.slashes = "//";
|
|
||||||
parts.username = "";
|
|
||||||
parts.password = "";
|
|
||||||
if (usedNonBase || restUrl.match(/^[/\\]{2}/)) {
|
|
||||||
[parts.hostname, restUrl] = takePattern(
|
|
||||||
restUrl,
|
|
||||||
/^[/\\]{2}([^/\\?#]*)/,
|
|
||||||
);
|
|
||||||
usedNonBase = true;
|
|
||||||
} else {
|
|
||||||
parts.hostname = baseParts.hostname;
|
|
||||||
}
|
|
||||||
parts.port = "";
|
|
||||||
} else {
|
|
||||||
if (usedNonBase || restUrl.match(/^[/\\]{2}/)) {
|
|
||||||
let restAuthority;
|
|
||||||
if (isSpecial) {
|
|
||||||
parts.slashes = "//";
|
|
||||||
[restAuthority, restUrl] = takePattern(
|
|
||||||
restUrl,
|
|
||||||
/^[/\\]*([^/\\?#]*)/,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
parts.slashes = restUrl.match(/^[/\\]{2}/) ? "//" : "";
|
|
||||||
[restAuthority, restUrl] = takePattern(
|
|
||||||
restUrl,
|
|
||||||
/^[/\\]{2}([^/\\?#]*)/,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let restAuthentication;
|
|
||||||
[restAuthentication, restAuthority] = takePattern(
|
|
||||||
restAuthority,
|
|
||||||
/^(.*)@/,
|
|
||||||
);
|
|
||||||
[parts.username, restAuthentication] = takePattern(
|
|
||||||
restAuthentication,
|
|
||||||
/^([^:]*)/,
|
|
||||||
);
|
|
||||||
parts.username = encodeUserinfo(parts.username);
|
|
||||||
[parts.password] = takePattern(restAuthentication, /^:(.*)/);
|
|
||||||
parts.password = encodeUserinfo(parts.password);
|
|
||||||
[parts.hostname, restAuthority] = takePattern(
|
|
||||||
restAuthority,
|
|
||||||
/^(\[[0-9a-fA-F.:]{2,}\]|[^:]+)/,
|
|
||||||
);
|
|
||||||
[parts.port] = takePattern(restAuthority, /^:(.*)/);
|
|
||||||
if (!isValidPort(parts.port)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (parts.hostname == "" && isSpecial) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
usedNonBase = true;
|
|
||||||
} else {
|
|
||||||
parts.slashes = baseParts.slashes;
|
|
||||||
parts.username = baseParts.username;
|
|
||||||
parts.password = baseParts.password;
|
|
||||||
parts.hostname = baseParts.hostname;
|
|
||||||
parts.port = baseParts.port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
parts.hostname = encodeHostname(parts.hostname, isSpecial);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
[parts.path, restUrl] = takePattern(restUrl, /^([^?#]*)/);
|
|
||||||
parts.path = encodePathname(parts.path);
|
|
||||||
if (usedNonBase) {
|
|
||||||
parts.path = normalizePath(parts.path, parts.protocol == "file");
|
|
||||||
} else {
|
|
||||||
if (parts.path != "") {
|
|
||||||
usedNonBase = true;
|
|
||||||
}
|
|
||||||
parts.path = resolvePathFromBase(
|
|
||||||
parts.path,
|
|
||||||
baseParts.path || "/",
|
|
||||||
baseParts.protocol == "file",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Drop the hostname if a drive letter is parsed.
|
|
||||||
if (parts.protocol == "file" && parts.path.match(/^\/+[A-Za-z]:(\/|$)/)) {
|
|
||||||
parts.hostname = "";
|
|
||||||
}
|
|
||||||
if (usedNonBase || restUrl.startsWith("?")) {
|
|
||||||
[parts.query, restUrl] = takePattern(restUrl, /^(\?[^#]*)/);
|
|
||||||
parts.query = encodeSearch(parts.query, isSpecial);
|
|
||||||
usedNonBase = true;
|
|
||||||
} else {
|
|
||||||
parts.query = baseParts.query;
|
|
||||||
}
|
|
||||||
[parts.hash] = takePattern(restUrl, /^(#.*)/);
|
|
||||||
parts.hash = encodeHash(parts.hash);
|
|
||||||
return parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolves `.`s and `..`s where possible.
|
|
||||||
// Preserves repeating and trailing `/`s by design.
|
|
||||||
// Assumes drive letter file paths will have a leading slash.
|
|
||||||
function normalizePath(path, isFilePath) {
|
|
||||||
const isAbsolute = path.startsWith("/");
|
|
||||||
path = path.replace(/^\//, "");
|
|
||||||
const pathSegments = path.split("/");
|
|
||||||
|
|
||||||
let driveLetter = null;
|
|
||||||
if (isFilePath && pathSegments[0].match(/^[A-Za-z]:$/)) {
|
|
||||||
driveLetter = pathSegments.shift();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFilePath && isAbsolute) {
|
|
||||||
while (pathSegments.length > 1 && pathSegments[0] == "") {
|
|
||||||
pathSegments.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ensureTrailingSlash = false;
|
|
||||||
const newPathSegments = [];
|
|
||||||
for (let i = 0; i < pathSegments.length; i++) {
|
|
||||||
const previous = newPathSegments[newPathSegments.length - 1];
|
|
||||||
if (
|
|
||||||
pathSegments[i] == ".." &&
|
|
||||||
previous != ".." &&
|
|
||||||
(previous != undefined || isAbsolute)
|
|
||||||
) {
|
|
||||||
newPathSegments.pop();
|
|
||||||
ensureTrailingSlash = true;
|
|
||||||
} else if (pathSegments[i] == ".") {
|
|
||||||
ensureTrailingSlash = true;
|
|
||||||
} else {
|
|
||||||
newPathSegments.push(pathSegments[i]);
|
|
||||||
ensureTrailingSlash = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (driveLetter != null) {
|
|
||||||
newPathSegments.unshift(driveLetter);
|
|
||||||
}
|
|
||||||
if (newPathSegments.length == 0 && !isAbsolute) {
|
|
||||||
newPathSegments.push(".");
|
|
||||||
ensureTrailingSlash = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let newPath = newPathSegments.join("/");
|
|
||||||
if (isAbsolute) {
|
|
||||||
newPath = `/${newPath}`;
|
|
||||||
}
|
|
||||||
if (ensureTrailingSlash) {
|
|
||||||
newPath = newPath.replace(/\/*$/, "/");
|
|
||||||
}
|
|
||||||
return newPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard URL basing logic, applied to paths.
|
|
||||||
function resolvePathFromBase(path, basePath, isFilePath) {
|
|
||||||
let basePrefix;
|
|
||||||
let suffix;
|
|
||||||
const baseDriveLetter = basePath.match(/^\/+[A-Za-z]:(?=\/|$)/)?.[0];
|
|
||||||
if (isFilePath && path.match(/^\/+[A-Za-z]:(\/|$)/)) {
|
|
||||||
basePrefix = "";
|
|
||||||
suffix = path;
|
|
||||||
} else if (path.startsWith("/")) {
|
|
||||||
if (isFilePath && baseDriveLetter) {
|
|
||||||
basePrefix = baseDriveLetter;
|
|
||||||
suffix = path;
|
|
||||||
} else {
|
|
||||||
basePrefix = "";
|
|
||||||
suffix = path;
|
|
||||||
}
|
|
||||||
} else if (path != "") {
|
|
||||||
basePath = normalizePath(basePath, isFilePath);
|
|
||||||
path = normalizePath(path, isFilePath);
|
|
||||||
// Remove everything after the last `/` in `basePath`.
|
|
||||||
if (baseDriveLetter && isFilePath) {
|
|
||||||
basePrefix = `${baseDriveLetter}${
|
|
||||||
basePath.slice(baseDriveLetter.length).replace(/[^\/]*$/, "")
|
|
||||||
}`;
|
|
||||||
} else {
|
|
||||||
basePrefix = basePath.replace(/[^\/]*$/, "");
|
|
||||||
}
|
|
||||||
basePrefix = basePrefix.replace(/\/*$/, "/");
|
|
||||||
// If `normalizedPath` ends with `.` or `..`, add a trailing slash.
|
|
||||||
suffix = path.replace(/(?<=(^|\/)(\.|\.\.))$/, "/");
|
|
||||||
} else {
|
|
||||||
basePrefix = basePath;
|
|
||||||
suffix = "";
|
|
||||||
}
|
|
||||||
return normalizePath(basePrefix + suffix, isFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidPort(value) {
|
|
||||||
// https://url.spec.whatwg.org/#port-state
|
|
||||||
if (value === "") return true;
|
|
||||||
|
|
||||||
const port = Number(value);
|
|
||||||
return Number.isInteger(port) && port >= 0 && port <= MAX_PORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = new WeakMap();
|
const parts = new WeakMap();
|
||||||
|
|
||||||
class URL {
|
class URL {
|
||||||
#searchParams = null;
|
#searchParams = null;
|
||||||
|
|
||||||
|
constructor(url, base) {
|
||||||
|
new.target;
|
||||||
|
|
||||||
|
if (url instanceof URL && base === undefined) {
|
||||||
|
parts.set(this, parts.get(url));
|
||||||
|
} else {
|
||||||
|
base = base !== undefined ? String(base) : base;
|
||||||
|
const parseArgs = { href: String(url), baseHref: base };
|
||||||
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Symbol.for("Deno.customInspect")](inspect) {
|
[Symbol.for("Deno.customInspect")](inspect) {
|
||||||
const object = {
|
const object = {
|
||||||
href: this.href,
|
href: this.href,
|
||||||
|
@ -540,18 +228,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateSearchParams = () => {
|
#updateSearchParams = () => {
|
||||||
const searchParams = new URLSearchParams(this.search);
|
if (this.#searchParams != null) {
|
||||||
|
const params = paramLists.get(this.#searchParams);
|
||||||
for (const methodName of searchParamsMethods) {
|
const newParams = core.jsonOpSync(
|
||||||
const method = searchParams[methodName];
|
"op_parse_url_search_params",
|
||||||
searchParams[methodName] = (...args) => {
|
this.search.slice(1),
|
||||||
method.apply(searchParams, args);
|
);
|
||||||
this.search = searchParams.toString();
|
params.splice(0, params.length, ...newParams);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
this.#searchParams = searchParams;
|
|
||||||
|
|
||||||
urls.set(searchParams, this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
get hash() {
|
get hash() {
|
||||||
|
@ -559,27 +243,25 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
set hash(value) {
|
set hash(value) {
|
||||||
value = unescape(String(value));
|
try {
|
||||||
if (!value) {
|
const parseArgs = { href: this.href, setHash: String(value) };
|
||||||
parts.get(this).hash = "";
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
} else {
|
} catch {
|
||||||
if (value.charAt(0) !== "#") {
|
/* pass */
|
||||||
value = `#${value}`;
|
|
||||||
}
|
|
||||||
// hashes can contain % and # unescaped
|
|
||||||
parts.get(this).hash = encodeHash(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get host() {
|
get host() {
|
||||||
return `${this.hostname}${this.port ? `:${this.port}` : ""}`;
|
return parts.get(this).host;
|
||||||
}
|
}
|
||||||
|
|
||||||
set host(value) {
|
set host(value) {
|
||||||
value = String(value);
|
try {
|
||||||
const url = new URL(`http://${value}`);
|
const parseArgs = { href: this.href, setHost: String(value) };
|
||||||
parts.get(this).hostname = url.hostname;
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
parts.get(this).port = url.port;
|
} catch {
|
||||||
|
/* pass */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get hostname() {
|
get hostname() {
|
||||||
|
@ -587,42 +269,30 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
set hostname(value) {
|
set hostname(value) {
|
||||||
value = String(value);
|
|
||||||
try {
|
try {
|
||||||
const isSpecial = specialSchemes.includes(parts.get(this).protocol);
|
const parseArgs = { href: this.href, setHostname: String(value) };
|
||||||
parts.get(this).hostname = encodeHostname(value, isSpecial);
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
} catch {
|
} catch {
|
||||||
// pass
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get href() {
|
get href() {
|
||||||
const authentication = this.username || this.password
|
return parts.get(this).href;
|
||||||
? `${this.username}${this.password ? ":" + this.password : ""}@`
|
|
||||||
: "";
|
|
||||||
const host = this.host;
|
|
||||||
const slashes = host ? "//" : parts.get(this).slashes;
|
|
||||||
let pathname = this.pathname;
|
|
||||||
if (pathname.charAt(0) != "/" && pathname != "" && host != "") {
|
|
||||||
pathname = `/${pathname}`;
|
|
||||||
}
|
|
||||||
return `${this.protocol}${slashes}${authentication}${host}${pathname}${this.search}${this.hash}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set href(value) {
|
set href(value) {
|
||||||
value = String(value);
|
try {
|
||||||
if (value !== this.href) {
|
const parseArgs = { href: String(value) };
|
||||||
const url = new URL(value);
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
parts.set(this, { ...parts.get(url) });
|
} catch {
|
||||||
this.#updateSearchParams();
|
throw new TypeError("Invalid URL");
|
||||||
}
|
}
|
||||||
|
this.#updateSearchParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
get origin() {
|
get origin() {
|
||||||
if (this.host) {
|
return parts.get(this).origin;
|
||||||
return `${this.protocol}//${this.host}`;
|
|
||||||
}
|
|
||||||
return "null";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get password() {
|
get password() {
|
||||||
|
@ -630,64 +300,65 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
set password(value) {
|
set password(value) {
|
||||||
value = String(value);
|
try {
|
||||||
parts.get(this).password = encodeUserinfo(value);
|
const parseArgs = { href: this.href, setPassword: String(value) };
|
||||||
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
|
} catch {
|
||||||
|
/* pass */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get pathname() {
|
get pathname() {
|
||||||
let path = parts.get(this).path;
|
return parts.get(this).pathname;
|
||||||
if (specialSchemes.includes(parts.get(this).protocol)) {
|
|
||||||
if (path.charAt(0) != "/") {
|
|
||||||
path = `/${path}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set pathname(value) {
|
set pathname(value) {
|
||||||
parts.get(this).path = encodePathname(String(value));
|
try {
|
||||||
|
const parseArgs = { href: this.href, setPathname: String(value) };
|
||||||
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
|
} catch {
|
||||||
|
/* pass */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get port() {
|
get port() {
|
||||||
const port = parts.get(this).port;
|
return parts.get(this).port;
|
||||||
if (schemePorts[parts.get(this).protocol] === port) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set port(value) {
|
set port(value) {
|
||||||
if (!isValidPort(value)) {
|
try {
|
||||||
return;
|
const parseArgs = { href: this.href, setPort: String(value) };
|
||||||
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
|
} catch {
|
||||||
|
/* pass */
|
||||||
}
|
}
|
||||||
parts.get(this).port = value.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get protocol() {
|
get protocol() {
|
||||||
return `${parts.get(this).protocol}:`;
|
return parts.get(this).protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
set protocol(value) {
|
set protocol(value) {
|
||||||
value = String(value);
|
try {
|
||||||
if (value) {
|
const parseArgs = { href: this.href, setProtocol: String(value) };
|
||||||
if (value.charAt(value.length - 1) === ":") {
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
value = value.slice(0, -1);
|
} catch {
|
||||||
}
|
/* pass */
|
||||||
parts.get(this).protocol = encodeURIComponent(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get search() {
|
get search() {
|
||||||
return parts.get(this).query;
|
return parts.get(this).search;
|
||||||
}
|
}
|
||||||
|
|
||||||
set search(value) {
|
set search(value) {
|
||||||
value = String(value);
|
try {
|
||||||
const query = value == "" || value.charAt(0) == "?" ? value : `?${value}`;
|
const parseArgs = { href: this.href, setSearch: String(value) };
|
||||||
const isSpecial = specialSchemes.includes(parts.get(this).protocol);
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
parts.get(this).query = encodeSearch(query, isSpecial);
|
this.#updateSearchParams();
|
||||||
this.#updateSearchParams();
|
} catch {
|
||||||
|
/* pass */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get username() {
|
get username() {
|
||||||
|
@ -695,35 +366,22 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
set username(value) {
|
set username(value) {
|
||||||
value = String(value);
|
try {
|
||||||
parts.get(this).username = encodeUserinfo(value);
|
const parseArgs = { href: this.href, setUsername: String(value) };
|
||||||
|
parts.set(this, core.jsonOpSync("op_parse_url", parseArgs));
|
||||||
|
} catch {
|
||||||
|
/* pass */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get searchParams() {
|
get searchParams() {
|
||||||
|
if (this.#searchParams == null) {
|
||||||
|
this.#searchParams = new URLSearchParams(this.search);
|
||||||
|
urls.set(this.#searchParams, this);
|
||||||
|
}
|
||||||
return this.#searchParams;
|
return this.#searchParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(url, base) {
|
|
||||||
let baseParts = null;
|
|
||||||
new.target;
|
|
||||||
if (base) {
|
|
||||||
baseParts = base instanceof URL ? parts.get(base) : parse(base);
|
|
||||||
if (baseParts == null) {
|
|
||||||
throw new TypeError("Invalid base URL.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlParts = url instanceof URL
|
|
||||||
? parts.get(url)
|
|
||||||
: parse(url, baseParts);
|
|
||||||
if (urlParts == null) {
|
|
||||||
throw new TypeError("Invalid URL.");
|
|
||||||
}
|
|
||||||
parts.set(this, urlParts);
|
|
||||||
|
|
||||||
this.#updateSearchParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return this.href;
|
return this.href;
|
||||||
}
|
}
|
||||||
|
@ -741,166 +399,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseIpv4Number(s) {
|
|
||||||
if (s.match(/^(0[Xx])[0-9A-Za-z]+$/)) {
|
|
||||||
return Number(s);
|
|
||||||
}
|
|
||||||
if (s.match(/^[0-9]+$/)) {
|
|
||||||
return Number(s.startsWith("0") ? `0o${s}` : s);
|
|
||||||
}
|
|
||||||
return NaN;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseIpv4(s) {
|
|
||||||
const parts = s.split(".");
|
|
||||||
if (parts[parts.length - 1] == "" && parts.length > 1) {
|
|
||||||
parts.pop();
|
|
||||||
}
|
|
||||||
if (parts.includes("") || parts.length > 4) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
const numbers = parts.map(parseIpv4Number);
|
|
||||||
if (numbers.includes(NaN)) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
const last = numbers.pop();
|
|
||||||
if (last >= 256 ** (4 - numbers.length) || numbers.find((n) => n >= 256)) {
|
|
||||||
throw new TypeError("Invalid hostname.");
|
|
||||||
}
|
|
||||||
const ipv4 = numbers.reduce((sum, n, i) => sum + n * 256 ** (3 - i), last);
|
|
||||||
const ipv4Hex = ipv4.toString(16).padStart(8, "0");
|
|
||||||
const ipv4HexParts = ipv4Hex.match(/(..)(..)(..)(..)$/).slice(1);
|
|
||||||
return ipv4HexParts.map((s) => String(Number(`0x${s}`))).join(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
function charInC0ControlSet(c) {
|
|
||||||
return (c >= "\u0000" && c <= "\u001F") || c > "\u007E";
|
|
||||||
}
|
|
||||||
|
|
||||||
function charInSearchSet(c, isSpecial) {
|
|
||||||
// deno-fmt-ignore
|
|
||||||
return charInC0ControlSet(c) || ["\u0020", "\u0022", "\u0023", "\u003C", "\u003E"].includes(c) || isSpecial && c == "\u0027" || c > "\u007E";
|
|
||||||
}
|
|
||||||
|
|
||||||
function charInFragmentSet(c) {
|
|
||||||
// deno-fmt-ignore
|
|
||||||
return charInC0ControlSet(c) || ["\u0020", "\u0022", "\u003C", "\u003E", "\u0060"].includes(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
function charInPathSet(c) {
|
|
||||||
// deno-fmt-ignore
|
|
||||||
return charInFragmentSet(c) || ["\u0023", "\u003F", "\u007B", "\u007D"].includes(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
function charInUserinfoSet(c) {
|
|
||||||
// "\u0027" ("'") seemingly isn't in the spec, but matches Chrome and Firefox.
|
|
||||||
// deno-fmt-ignore
|
|
||||||
return charInPathSet(c) || ["\u0027", "\u002F", "\u003A", "\u003B", "\u003D", "\u0040", "\u005B", "\u005C", "\u005D", "\u005E", "\u007C"].includes(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
function charIsForbiddenInHost(c) {
|
|
||||||
// deno-fmt-ignore
|
|
||||||
return ["\u0000", "\u0009", "\u000A", "\u000D", "\u0020", "\u0023", "\u0025", "\u002F", "\u003A", "\u003C", "\u003E", "\u003F", "\u0040", "\u005B", "\u005C", "\u005D", "\u005E"].includes(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
function charInFormUrlencodedSet(c) {
|
|
||||||
// deno-fmt-ignore
|
|
||||||
return charInUserinfoSet(c) || ["\u0021", "\u0024", "\u0025", "\u0026", "\u0027", "\u0028", "\u0029", "\u002B", "\u002C", "\u007E"].includes(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
|
|
||||||
function encodeChar(c) {
|
|
||||||
return [...encoder.encode(c)]
|
|
||||||
.map((n) => `%${n.toString(16).padStart(2, "0")}`)
|
|
||||||
.join("")
|
|
||||||
.toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeUserinfo(s) {
|
|
||||||
return [...s].map((c) => (charInUserinfoSet(c) ? encodeChar(c) : c)).join(
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeHostname(s, isSpecial = true) {
|
|
||||||
// IPv6 parsing.
|
|
||||||
if (s.startsWith("[") && s.endsWith("]")) {
|
|
||||||
if (!s.match(/^\[[0-9A-Fa-f.:]{2,}\]$/)) {
|
|
||||||
throw new TypeError("Invalid hostname.");
|
|
||||||
}
|
|
||||||
// IPv6 address compress
|
|
||||||
return s.toLowerCase().replace(/\b:?(?:0+:?){2,}/, "::");
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = s;
|
|
||||||
|
|
||||||
if (!isSpecial) {
|
|
||||||
// Check against forbidden host code points except for "%".
|
|
||||||
for (const c of result) {
|
|
||||||
if (charIsForbiddenInHost(c) && c != "\u0025") {
|
|
||||||
throw new TypeError("Invalid hostname.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Percent-encode C0 control set.
|
|
||||||
result = [...result]
|
|
||||||
.map((c) => (charInC0ControlSet(c) ? encodeChar(c) : c))
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Percent-decode.
|
|
||||||
if (result.match(/%(?![0-9A-Fa-f]{2})/) != null) {
|
|
||||||
throw new TypeError("Invalid hostname.");
|
|
||||||
}
|
|
||||||
result = result.replace(
|
|
||||||
/%(.{2})/g,
|
|
||||||
(_, hex) => String.fromCodePoint(Number(`0x${hex}`)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// IDNA domain to ASCII.
|
|
||||||
result = domainToAscii(result);
|
|
||||||
|
|
||||||
// Check against forbidden host code points.
|
|
||||||
for (const c of result) {
|
|
||||||
if (charIsForbiddenInHost(c)) {
|
|
||||||
throw new TypeError("Invalid hostname.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPv4 parsing.
|
|
||||||
if (isSpecial) {
|
|
||||||
result = parseIpv4(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodePathname(s) {
|
|
||||||
return [...s.replace(/\\/g, "/")].map((
|
|
||||||
c,
|
|
||||||
) => (charInPathSet(c) ? encodeChar(c) : c)).join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeSearch(s, isSpecial) {
|
|
||||||
return [...s].map((
|
|
||||||
c,
|
|
||||||
) => (charInSearchSet(c, isSpecial) ? encodeChar(c) : c)).join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeHash(s) {
|
|
||||||
return [...s].map((c) => (charInFragmentSet(c) ? encodeChar(c) : c)).join(
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeSearchParam(s) {
|
|
||||||
return [...s].map((c) => (charInFormUrlencodedSet(c) ? encodeChar(c) : c))
|
|
||||||
.join("").replace(/%20/g, "+");
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.url = {
|
window.__bootstrap.url = {
|
||||||
URL,
|
URL,
|
||||||
URLSearchParams,
|
URLSearchParams,
|
||||||
|
|
|
@ -15,7 +15,6 @@ path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deno_core = { version = "0.79.0", path = "../../core" }
|
deno_core = { version = "0.79.0", path = "../../core" }
|
||||||
idna = "0.2.1"
|
|
||||||
serde = { version = "1.0.123", features = ["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -1,42 +1,22 @@
|
||||||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use deno_core::error::generic_error;
|
||||||
|
use deno_core::error::type_error;
|
||||||
use deno_core::error::uri_error;
|
use deno_core::error::uri_error;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_core::serde_json::json;
|
use deno_core::serde_json::json;
|
||||||
use deno_core::serde_json::Value;
|
use deno_core::serde_json::Value;
|
||||||
|
use deno_core::url::form_urlencoded;
|
||||||
|
use deno_core::url::quirks;
|
||||||
|
use deno_core::url::Url;
|
||||||
use deno_core::JsRuntime;
|
use deno_core::JsRuntime;
|
||||||
use deno_core::ZeroCopyBuf;
|
use deno_core::ZeroCopyBuf;
|
||||||
use idna::domain_to_ascii;
|
|
||||||
use idna::domain_to_ascii_strict;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::panic::catch_unwind;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn op_domain_to_ascii(
|
|
||||||
_state: &mut deno_core::OpState,
|
|
||||||
args: Value,
|
|
||||||
_zero_copy: &mut [ZeroCopyBuf],
|
|
||||||
) -> Result<Value, AnyError> {
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct DomainToAscii {
|
|
||||||
domain: String,
|
|
||||||
be_strict: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
let args: DomainToAscii = serde_json::from_value(args)?;
|
|
||||||
if args.be_strict {
|
|
||||||
domain_to_ascii_strict(args.domain.as_str())
|
|
||||||
} else {
|
|
||||||
domain_to_ascii(args.domain.as_str())
|
|
||||||
}
|
|
||||||
.map_err(|err| {
|
|
||||||
let message = format!("Invalid IDNA encoded domain name: {:?}", err);
|
|
||||||
uri_error(message)
|
|
||||||
})
|
|
||||||
.map(|domain| json!(domain))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load and execute the javascript code.
|
/// Load and execute the javascript code.
|
||||||
pub fn init(isolate: &mut JsRuntime) {
|
pub fn init(isolate: &mut JsRuntime) {
|
||||||
let files = vec![
|
let files = vec![
|
||||||
|
@ -79,6 +59,131 @@ pub fn init(isolate: &mut JsRuntime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse `UrlParseArgs::href` with an optional `UrlParseArgs::base_href`, or an
|
||||||
|
/// optional part to "set" after parsing. Return `UrlParts`.
|
||||||
|
pub fn op_parse_url(
|
||||||
|
_state: &mut deno_core::OpState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: &mut [ZeroCopyBuf],
|
||||||
|
) -> Result<Value, AnyError> {
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct UrlParseArgs {
|
||||||
|
href: String,
|
||||||
|
base_href: Option<String>,
|
||||||
|
// If one of the following are present, this is a setter call. Apply the
|
||||||
|
// proper `Url::set_*()` method after (re)parsing `href`.
|
||||||
|
set_hash: Option<String>,
|
||||||
|
set_host: Option<String>,
|
||||||
|
set_hostname: Option<String>,
|
||||||
|
set_password: Option<String>,
|
||||||
|
set_pathname: Option<String>,
|
||||||
|
set_port: Option<String>,
|
||||||
|
set_protocol: Option<String>,
|
||||||
|
set_search: Option<String>,
|
||||||
|
set_username: Option<String>,
|
||||||
|
}
|
||||||
|
let args: UrlParseArgs = serde_json::from_value(args)?;
|
||||||
|
let base_url = args
|
||||||
|
.base_href
|
||||||
|
.as_ref()
|
||||||
|
.map(|b| Url::parse(b).map_err(|_| type_error("Invalid base URL")))
|
||||||
|
.transpose()?;
|
||||||
|
let mut url = Url::options()
|
||||||
|
.base_url(base_url.as_ref())
|
||||||
|
.parse(&args.href)
|
||||||
|
.map_err(|_| type_error("Invalid URL"))?;
|
||||||
|
|
||||||
|
if let Some(hash) = args.set_hash.as_ref() {
|
||||||
|
quirks::set_hash(&mut url, hash);
|
||||||
|
} else if let Some(host) = args.set_host.as_ref() {
|
||||||
|
quirks::set_host(&mut url, host).map_err(|_| uri_error("Invalid host"))?;
|
||||||
|
} else if let Some(hostname) = args.set_hostname.as_ref() {
|
||||||
|
quirks::set_hostname(&mut url, hostname)
|
||||||
|
.map_err(|_| uri_error("Invalid hostname"))?;
|
||||||
|
} else if let Some(password) = args.set_password.as_ref() {
|
||||||
|
quirks::set_password(&mut url, password)
|
||||||
|
.map_err(|_| uri_error("Invalid password"))?;
|
||||||
|
} else if let Some(pathname) = args.set_pathname.as_ref() {
|
||||||
|
quirks::set_pathname(&mut url, pathname);
|
||||||
|
} else if let Some(port) = args.set_port.as_ref() {
|
||||||
|
quirks::set_port(&mut url, port).map_err(|_| uri_error("Invalid port"))?;
|
||||||
|
} else if let Some(protocol) = args.set_protocol.as_ref() {
|
||||||
|
quirks::set_protocol(&mut url, protocol)
|
||||||
|
.map_err(|_| uri_error("Invalid protocol"))?;
|
||||||
|
} else if let Some(search) = args.set_search.as_ref() {
|
||||||
|
quirks::set_search(&mut url, search);
|
||||||
|
} else if let Some(username) = args.set_username.as_ref() {
|
||||||
|
quirks::set_username(&mut url, username)
|
||||||
|
.map_err(|_| uri_error("Invalid username"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct UrlParts<'a> {
|
||||||
|
href: &'a str,
|
||||||
|
hash: &'a str,
|
||||||
|
host: &'a str,
|
||||||
|
hostname: &'a str,
|
||||||
|
origin: &'a str,
|
||||||
|
password: &'a str,
|
||||||
|
pathname: &'a str,
|
||||||
|
port: &'a str,
|
||||||
|
protocol: &'a str,
|
||||||
|
search: &'a str,
|
||||||
|
username: &'a str,
|
||||||
|
}
|
||||||
|
// TODO(nayeemrmn): Panic that occurs in rust-url for the `non-spec:`
|
||||||
|
// url-constructor wpt tests: https://github.com/servo/rust-url/issues/670.
|
||||||
|
let username = catch_unwind(|| quirks::username(&url)).map_err(|_| {
|
||||||
|
generic_error(format!(
|
||||||
|
"Internal error while parsing \"{}\"{}, \
|
||||||
|
see https://github.com/servo/rust-url/issues/670",
|
||||||
|
args.href,
|
||||||
|
args
|
||||||
|
.base_href
|
||||||
|
.map(|b| format!(" against \"{}\"", b))
|
||||||
|
.unwrap_or_default()
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
Ok(json!(UrlParts {
|
||||||
|
href: quirks::href(&url),
|
||||||
|
hash: quirks::hash(&url),
|
||||||
|
host: quirks::host(&url),
|
||||||
|
hostname: quirks::hostname(&url),
|
||||||
|
origin: &quirks::origin(&url),
|
||||||
|
password: quirks::password(&url),
|
||||||
|
pathname: quirks::pathname(&url),
|
||||||
|
port: quirks::port(&url),
|
||||||
|
protocol: quirks::protocol(&url),
|
||||||
|
search: quirks::search(&url),
|
||||||
|
username,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn op_parse_url_search_params(
|
||||||
|
_state: &mut deno_core::OpState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: &mut [ZeroCopyBuf],
|
||||||
|
) -> Result<Value, AnyError> {
|
||||||
|
let search: String = serde_json::from_value(args)?;
|
||||||
|
let search_params: Vec<_> = form_urlencoded::parse(search.as_bytes())
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
Ok(json!(search_params))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn op_stringify_url_search_params(
|
||||||
|
_state: &mut deno_core::OpState,
|
||||||
|
args: Value,
|
||||||
|
_zero_copy: &mut [ZeroCopyBuf],
|
||||||
|
) -> Result<Value, AnyError> {
|
||||||
|
let search_params: Vec<(String, String)> = serde_json::from_value(args)?;
|
||||||
|
let search = form_urlencoded::Serializer::new(String::new())
|
||||||
|
.extend_pairs(search_params)
|
||||||
|
.finish();
|
||||||
|
Ok(json!(search))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_declaration() -> PathBuf {
|
pub fn get_declaration() -> PathBuf {
|
||||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_web.d.ts")
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,10 +231,16 @@ impl WebWorker {
|
||||||
);
|
);
|
||||||
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
|
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
|
||||||
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
||||||
|
ops::reg_json_sync(js_runtime, "op_parse_url", deno_web::op_parse_url);
|
||||||
ops::reg_json_sync(
|
ops::reg_json_sync(
|
||||||
js_runtime,
|
js_runtime,
|
||||||
"op_domain_to_ascii",
|
"op_parse_url_search_params",
|
||||||
deno_web::op_domain_to_ascii,
|
deno_web::op_parse_url_search_params,
|
||||||
|
);
|
||||||
|
ops::reg_json_sync(
|
||||||
|
js_runtime,
|
||||||
|
"op_stringify_url_search_params",
|
||||||
|
deno_web::op_stringify_url_search_params,
|
||||||
);
|
);
|
||||||
ops::io::init(js_runtime);
|
ops::io::init(js_runtime);
|
||||||
ops::webgpu::init(js_runtime);
|
ops::webgpu::init(js_runtime);
|
||||||
|
|
|
@ -126,10 +126,16 @@ impl MainWorker {
|
||||||
ops::crypto::init(js_runtime, options.seed);
|
ops::crypto::init(js_runtime, options.seed);
|
||||||
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
|
ops::reg_json_sync(js_runtime, "op_close", deno_core::op_close);
|
||||||
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
ops::reg_json_sync(js_runtime, "op_resources", deno_core::op_resources);
|
||||||
|
ops::reg_json_sync(js_runtime, "op_parse_url", deno_web::op_parse_url);
|
||||||
ops::reg_json_sync(
|
ops::reg_json_sync(
|
||||||
js_runtime,
|
js_runtime,
|
||||||
"op_domain_to_ascii",
|
"op_parse_url_search_params",
|
||||||
deno_web::op_domain_to_ascii,
|
deno_web::op_parse_url_search_params,
|
||||||
|
);
|
||||||
|
ops::reg_json_sync(
|
||||||
|
js_runtime,
|
||||||
|
"op_stringify_url_search_params",
|
||||||
|
deno_web::op_stringify_url_search_params,
|
||||||
);
|
);
|
||||||
ops::fs_events::init(js_runtime);
|
ops::fs_events::init(js_runtime);
|
||||||
ops::fs::init(js_runtime);
|
ops::fs::init(js_runtime);
|
||||||
|
|
|
@ -629,62 +629,8 @@
|
||||||
],
|
],
|
||||||
"idlharness.any.js": false,
|
"idlharness.any.js": false,
|
||||||
"url-constructor.any.js": [
|
"url-constructor.any.js": [
|
||||||
"Parsing: <http://example\t.\norg> against <http://example.org/foo/bar>",
|
"Parsing: <http://example.com/\ud800\udfff﷏ﷰ?\ud800\udfff﷏ﷰ> against <about:blank>",
|
||||||
"Parsing: <a:\t foo.com> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <lolscheme:x x#x x> against <about:blank>",
|
|
||||||
"Parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <http://f: /c> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <http://f: 21 / b ? d # e > against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <:#> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <#> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <?> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <http://[::127.0.0.1]> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>",
|
|
||||||
"Parsing: <file:c:\\foo\\bar.html> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: < File:c|////foo\\bar.html> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: <C|/foo/bar> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: </C|\\foo\\bar> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: <//C|/foo/bar> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: <file://localhost> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: <file://localhost/> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: <file://localhost/test> against <file:///tmp/mock/path>",
|
|
||||||
"Parsing: <http://example.com/foo/%2e> against <about:blank>",
|
|
||||||
"Parsing: <http://example.com/foo/%2e./%2e%2e/.%2e/%2e.bar> against <about:blank>",
|
|
||||||
"Parsing: <http://example.com////../..> against <about:blank>",
|
|
||||||
"Parsing: <http://example.com/foo\t%91> against <about:blank>",
|
|
||||||
"Parsing: <http://example.com/foo\tbar> against <about:blank>",
|
|
||||||
"Parsing: <http://www.google.com/foo?bar=baz#> against <about:blank>",
|
|
||||||
"Parsing: <http://www/foo/%2E/html> against <about:blank>",
|
|
||||||
"Parsing: <file:..> against <http://www.example.com/test>",
|
|
||||||
"Parsing: <\u0000\u001b\u0004\u0012 http://example.com/\u001f \r > against <about:blank>",
|
|
||||||
"Parsing: <https://%EF%BF%BD> against <about:blank>",
|
|
||||||
"Parsing: <http://[::1.2.3.]> against <http://other.com/>",
|
|
||||||
"Parsing: <http://[::1.2.]> against <http://other.com/>",
|
|
||||||
"Parsing: <http://[::1.]> against <http://other.com/>",
|
|
||||||
"Parsing: <#> against <test:test>",
|
|
||||||
"Parsing: <#> against <test:test?test>",
|
|
||||||
"Parsing: <i> against <sc:sd>",
|
|
||||||
"Parsing: <i> against <sc:sd/sd>",
|
|
||||||
"Parsing: <../i> against <sc:sd>",
|
|
||||||
"Parsing: <../i> against <sc:sd/sd>",
|
|
||||||
"Parsing: </i> against <sc:sd>",
|
|
||||||
"Parsing: </i> against <sc:sd/sd>",
|
|
||||||
"Parsing: <?i> against <sc:sd>",
|
|
||||||
"Parsing: <?i> against <sc:sd/sd>",
|
|
||||||
"Parsing: <sc://@/> against <about:blank>",
|
|
||||||
"Parsing: <sc://te@s:t@/> against <about:blank>",
|
|
||||||
"Parsing: <sc://:/> against <about:blank>",
|
|
||||||
"Parsing: <sc://:12/> against <about:blank>",
|
|
||||||
"Parsing: <sc://\\/> against <about:blank>",
|
|
||||||
"Parsing: <sc:\\../> against <about:blank>",
|
|
||||||
"Parsing: <ftp://%e2%98%83> against <about:blank>",
|
|
||||||
"Parsing: <https://%e2%98%83> against <about:blank>",
|
|
||||||
"Parsing: <h\tt\nt\rp://h\to\ns\rt:9\t0\n0\r0/p\ta\nt\rh?q\tu\ne\rry#f\tr\na\rg> against <about:blank>",
|
|
||||||
"Parsing: <https://0x.0x.0> against <about:blank>",
|
|
||||||
"Parsing: </> against <file://h/C:/a/b>",
|
"Parsing: </> against <file://h/C:/a/b>",
|
||||||
"Parsing: <//d:> against <file:///C:/a/b>",
|
|
||||||
"Parsing: <//d:/..> against <file:///C:/a/b>",
|
|
||||||
"Parsing: <file:\\\\//> against <about:blank>",
|
"Parsing: <file:\\\\//> against <about:blank>",
|
||||||
"Parsing: <file:\\\\\\\\> against <about:blank>",
|
"Parsing: <file:\\\\\\\\> against <about:blank>",
|
||||||
"Parsing: <file:\\\\\\\\?fox> against <about:blank>",
|
"Parsing: <file:\\\\\\\\?fox> against <about:blank>",
|
||||||
|
@ -696,6 +642,7 @@
|
||||||
"Parsing: </////mouse> against <file:///elephant>",
|
"Parsing: </////mouse> against <file:///elephant>",
|
||||||
"Parsing: <\\/localhost//pig> against <file://lion/>",
|
"Parsing: <\\/localhost//pig> against <file://lion/>",
|
||||||
"Parsing: <//localhost//pig> against <file://lion/>",
|
"Parsing: <//localhost//pig> against <file://lion/>",
|
||||||
|
"Parsing: </..//localhost//pig> against <file://lion/>",
|
||||||
"Parsing: <C|> against <file://host/dir/file>",
|
"Parsing: <C|> against <file://host/dir/file>",
|
||||||
"Parsing: <C|> against <file://host/D:/dir1/dir2/file>",
|
"Parsing: <C|> against <file://host/D:/dir1/dir2/file>",
|
||||||
"Parsing: <C|#> against <file://host/dir/file>",
|
"Parsing: <C|#> against <file://host/dir/file>",
|
||||||
|
@ -703,34 +650,18 @@
|
||||||
"Parsing: <C|/> against <file://host/dir/file>",
|
"Parsing: <C|/> against <file://host/dir/file>",
|
||||||
"Parsing: <C|\n/> against <file://host/dir/file>",
|
"Parsing: <C|\n/> against <file://host/dir/file>",
|
||||||
"Parsing: <C|\\> against <file://host/dir/file>",
|
"Parsing: <C|\\> against <file://host/dir/file>",
|
||||||
"Parsing: </c|/foo/bar> against <file:///c:/baz/qux>",
|
|
||||||
"Parsing: </c:/foo/bar> against <file://host/path>",
|
"Parsing: </c:/foo/bar> against <file://host/path>",
|
||||||
"Parsing: <file://example.net/C:/> against <about:blank>",
|
"Parsing: <file://example.net/C:/> against <about:blank>",
|
||||||
"Parsing: <file://1.2.3.4/C:/> against <about:blank>",
|
"Parsing: <file://1.2.3.4/C:/> against <about:blank>",
|
||||||
"Parsing: <file://[1::8]/C:/> against <about:blank>",
|
"Parsing: <file://[1::8]/C:/> against <about:blank>",
|
||||||
"Parsing: <file:/C|/> against <about:blank>",
|
|
||||||
"Parsing: <file://C|/> against <about:blank>",
|
|
||||||
"Parsing: <\\\\\\.\\Y:> against <about:blank>",
|
|
||||||
"Parsing: <\\\\\\.\\y:> against <about:blank>",
|
|
||||||
"Parsing: <file://localhost//a//../..//foo> against <about:blank>",
|
"Parsing: <file://localhost//a//../..//foo> against <about:blank>",
|
||||||
"Parsing: <file://localhost////foo> against <about:blank>",
|
"Parsing: <file://localhost////foo> against <about:blank>",
|
||||||
"Parsing: <file:////foo> against <about:blank>",
|
"Parsing: <file:////foo> against <about:blank>",
|
||||||
"Parsing: <file:////one/two> against <file:///>",
|
"Parsing: <file:////one/two> against <file:///>",
|
||||||
"Parsing: <////one/two> against <file:///>",
|
"Parsing: <////one/two> against <file:///>",
|
||||||
|
"Parsing: <file:///.//> against <file:////>",
|
||||||
"Parsing: <file:.//p> against <about:blank>",
|
"Parsing: <file:.//p> against <about:blank>",
|
||||||
"Parsing: <http://[1:0::]> against <http://example.net/>",
|
"Parsing: <file:/.//p> against <about:blank>",
|
||||||
"Parsing: <http://[0:1:2:3:4:5:6:7:8]> against <http://example.net/>",
|
|
||||||
"Parsing: <https://[0::0::0]> against <about:blank>",
|
|
||||||
"Parsing: <https://[0:.0]> against <about:blank>",
|
|
||||||
"Parsing: <https://[0:0:]> against <about:blank>",
|
|
||||||
"Parsing: <https://[0:1:2:3:4:5:6:7.0.0.0.1]> against <about:blank>",
|
|
||||||
"Parsing: <https://[0:1.00.0.0.0]> against <about:blank>",
|
|
||||||
"Parsing: <https://[0:1.290.0.0.0]> against <about:blank>",
|
|
||||||
"Parsing: <https://[0:1.23.23]> against <about:blank>",
|
|
||||||
"Parsing: <#x> against <sc://ñ>",
|
|
||||||
"Parsing: <?x> against <sc://ñ>",
|
|
||||||
"Parsing: <sc://?> against <about:blank>",
|
|
||||||
"Parsing: <sc://#> against <about:blank>",
|
|
||||||
"Parsing: <non-spec:/.//> against <about:blank>",
|
"Parsing: <non-spec:/.//> against <about:blank>",
|
||||||
"Parsing: <non-spec:/..//> against <about:blank>",
|
"Parsing: <non-spec:/..//> against <about:blank>",
|
||||||
"Parsing: <non-spec:/a/..//> against <about:blank>",
|
"Parsing: <non-spec:/a/..//> against <about:blank>",
|
||||||
|
@ -742,227 +673,32 @@
|
||||||
"Parsing: <..//path> against <non-spec:/p>",
|
"Parsing: <..//path> against <non-spec:/p>",
|
||||||
"Parsing: <a/..//path> against <non-spec:/p>",
|
"Parsing: <a/..//path> against <non-spec:/p>",
|
||||||
"Parsing: <> against <non-spec:/..//p>",
|
"Parsing: <> against <non-spec:/..//p>",
|
||||||
"Parsing: <path> against <non-spec:/..//p>",
|
"Parsing: <path> against <non-spec:/..//p>"
|
||||||
"Parsing: <non-special://[1:2:0:0:5:0:0:0]/> against <about:blank>",
|
|
||||||
"Parsing: <http://[::127.0.0.0.1]> against <about:blank>",
|
|
||||||
"Parsing: <http://example.org/test?#> against <about:blank>",
|
|
||||||
"Parsing: <a> against <about:blank>",
|
|
||||||
"Parsing: <a/> against <about:blank>",
|
|
||||||
"Parsing: <a//> against <about:blank>",
|
|
||||||
"Parsing: <test-a-colon.html> against <a:>",
|
|
||||||
"Parsing: <test-a-colon-b.html> against <a:b>",
|
|
||||||
"Parsing: <file://a%C2%ADb/p> against <about:blank>",
|
|
||||||
"Parsing: <file:///p> against <about:blank>",
|
|
||||||
"Parsing: <file://%C2%AD/p> against <about:blank>",
|
|
||||||
"Parsing: <file://xn--/p> against <about:blank>"
|
|
||||||
],
|
],
|
||||||
"url-origin.any.js": [
|
"url-origin.any.js": [
|
||||||
"Origin parsing: <http://example\t.\norg> against <http://example.org/foo/bar>",
|
"Origin parsing: <http://example.com/\ud800\udfff﷏ﷰ?\ud800\udfff﷏ﷰ> against <about:blank>"
|
||||||
"Origin parsing: <non-special://test:@test/x> against <about:blank>",
|
|
||||||
"Origin parsing: <non-special://:@test/x> against <about:blank>",
|
|
||||||
"Origin parsing: <http://f:00000000000000/c> against <http://example.org/foo/bar>",
|
|
||||||
"Origin parsing: <http://f:00000000000000000000080/c> against <http://example.org/foo/bar>",
|
|
||||||
"Origin parsing: <http://[::127.0.0.1]> against <http://example.org/foo/bar>",
|
|
||||||
"Origin parsing: <http://[0:0:0:0:0:0:13.1.68.3]> against <http://example.org/foo/bar>",
|
|
||||||
"Origin parsing: <ssh://example.com/foo/bar.git> against <http://example.org/>",
|
|
||||||
"Origin parsing: <httpa://foo:80/> against <about:blank>",
|
|
||||||
"Origin parsing: <gopher://foo:70/> against <about:blank>",
|
|
||||||
"Origin parsing: <gopher://foo:443/> against <about:blank>",
|
|
||||||
"Origin parsing: <\u0000\u001b\u0004\u0012 http://example.com/\u001f \r > against <about:blank>",
|
|
||||||
"Origin parsing: <sc://faß.ExAmPlE/> against <about:blank>",
|
|
||||||
"Origin parsing: <notspecial://host/?'> against <about:blank>",
|
|
||||||
"Origin parsing: <i> against <sc://ho/pa>",
|
|
||||||
"Origin parsing: <../i> against <sc://ho/pa>",
|
|
||||||
"Origin parsing: </i> against <sc://ho/pa>",
|
|
||||||
"Origin parsing: <?i> against <sc://ho/pa>",
|
|
||||||
"Origin parsing: <#i> against <sc://ho/pa>",
|
|
||||||
"Origin parsing: <sc://ñ.test/> against <about:blank>",
|
|
||||||
"Origin parsing: <x> against <sc://ñ>",
|
|
||||||
"Origin parsing: <sc://\u001f!\"$&'()*+,-.;=_`{|}~/> against <about:blank>",
|
|
||||||
"Origin parsing: <ftp://%e2%98%83> against <about:blank>",
|
|
||||||
"Origin parsing: <https://%e2%98%83> against <about:blank>",
|
|
||||||
"Origin parsing: <h\tt\nt\rp://h\to\ns\rt:9\t0\n0\r0/p\ta\nt\rh?q\tu\ne\rry#f\tr\na\rg> against <about:blank>",
|
|
||||||
"Origin parsing: <https://0x.0x.0> against <about:blank>",
|
|
||||||
"Origin parsing: <http://[1:0::]> against <http://example.net/>",
|
|
||||||
"Origin parsing: <sc://ñ> against <about:blank>",
|
|
||||||
"Origin parsing: <sc://ñ?x> against <about:blank>",
|
|
||||||
"Origin parsing: <sc://ñ#x> against <about:blank>",
|
|
||||||
"Origin parsing: <#x> against <sc://ñ>",
|
|
||||||
"Origin parsing: <?x> against <sc://ñ>",
|
|
||||||
"Origin parsing: <tftp://foobar.com/someconfig;mode=netascii> against <about:blank>",
|
|
||||||
"Origin parsing: <telnet://user:pass@foobar.com:23/> against <about:blank>",
|
|
||||||
"Origin parsing: <ut2004://10.10.10.10:7777/Index.ut2> against <about:blank>",
|
|
||||||
"Origin parsing: <redis://foo:bar@somehost:6379/0?baz=bam&qux=baz> against <about:blank>",
|
|
||||||
"Origin parsing: <rsync://foo@host:911/sup> against <about:blank>",
|
|
||||||
"Origin parsing: <git://github.com/foo/bar.git> against <about:blank>",
|
|
||||||
"Origin parsing: <irc://myserver.com:6999/channel?passwd> against <about:blank>",
|
|
||||||
"Origin parsing: <dns://fw.example.org:9999/foo.bar.org?type=TXT> against <about:blank>",
|
|
||||||
"Origin parsing: <ldap://localhost:389/ou=People,o=JNDITutorial> against <about:blank>",
|
|
||||||
"Origin parsing: <git+https://github.com/foo/bar> against <about:blank>"
|
|
||||||
],
|
|
||||||
"url-searchparams.any.js": [
|
|
||||||
"URL.searchParams updating, clearing",
|
|
||||||
"URL.searchParams and URL.search setters, update propagation"
|
|
||||||
],
|
],
|
||||||
|
"url-searchparams.any.js": true,
|
||||||
"url-setters-stripping.any.js": [
|
"url-setters-stripping.any.js": [
|
||||||
"Setting protocol with leading U+0000 (https:)",
|
"Setting protocol with leading U+0000 (https:)",
|
||||||
"Setting protocol with U+0000 before inserted colon (https:)",
|
"Setting protocol with U+0000 before inserted colon (https:)",
|
||||||
"Setting host with leading U+0000 (https:)",
|
"Setting port with leading U+0000 (https:)",
|
||||||
"Setting host with middle U+0000 (https:)",
|
"Setting pathname with trailing U+0000 (https:)",
|
||||||
"Setting host with trailing U+0000 (https:)",
|
|
||||||
"Setting port with middle U+0000 (https:)",
|
|
||||||
"Setting port with trailing U+0000 (https:)",
|
|
||||||
"Setting protocol with leading U+0009 (https:)",
|
|
||||||
"Setting protocol with U+0009 before inserted colon (https:)",
|
|
||||||
"Setting host with leading U+0009 (https:)",
|
|
||||||
"Setting hostname with leading U+0009 (https:)",
|
|
||||||
"Setting host with middle U+0009 (https:)",
|
|
||||||
"Setting hostname with middle U+0009 (https:)",
|
|
||||||
"Setting host with trailing U+0009 (https:)",
|
|
||||||
"Setting hostname with trailing U+0009 (https:)",
|
|
||||||
"Setting port with leading U+0009 (https:)",
|
|
||||||
"Setting port with middle U+0009 (https:)",
|
|
||||||
"Setting port with trailing U+0009 (https:)",
|
|
||||||
"Setting pathname with leading U+0009 (https:)",
|
|
||||||
"Setting pathname with middle U+0009 (https:)",
|
|
||||||
"Setting pathname with trailing U+0009 (https:)",
|
|
||||||
"Setting search with leading U+0009 (https:)",
|
|
||||||
"Setting search with middle U+0009 (https:)",
|
|
||||||
"Setting search with trailing U+0009 (https:)",
|
|
||||||
"Setting hash with leading U+0009 (https:)",
|
|
||||||
"Setting hash with middle U+0009 (https:)",
|
|
||||||
"Setting hash with trailing U+0009 (https:)",
|
|
||||||
"Setting protocol with leading U+000A (https:)",
|
|
||||||
"Setting protocol with U+000A before inserted colon (https:)",
|
|
||||||
"Setting host with leading U+000A (https:)",
|
|
||||||
"Setting hostname with leading U+000A (https:)",
|
|
||||||
"Setting host with middle U+000A (https:)",
|
|
||||||
"Setting hostname with middle U+000A (https:)",
|
|
||||||
"Setting host with trailing U+000A (https:)",
|
|
||||||
"Setting hostname with trailing U+000A (https:)",
|
|
||||||
"Setting port with leading U+000A (https:)",
|
|
||||||
"Setting port with middle U+000A (https:)",
|
|
||||||
"Setting port with trailing U+000A (https:)",
|
|
||||||
"Setting pathname with leading U+000A (https:)",
|
|
||||||
"Setting pathname with middle U+000A (https:)",
|
|
||||||
"Setting pathname with trailing U+000A (https:)",
|
|
||||||
"Setting search with leading U+000A (https:)",
|
|
||||||
"Setting search with middle U+000A (https:)",
|
|
||||||
"Setting search with trailing U+000A (https:)",
|
|
||||||
"Setting hash with leading U+000A (https:)",
|
|
||||||
"Setting hash with middle U+000A (https:)",
|
|
||||||
"Setting hash with trailing U+000A (https:)",
|
|
||||||
"Setting protocol with leading U+000D (https:)",
|
|
||||||
"Setting protocol with U+000D before inserted colon (https:)",
|
|
||||||
"Setting host with leading U+000D (https:)",
|
|
||||||
"Setting hostname with leading U+000D (https:)",
|
|
||||||
"Setting host with middle U+000D (https:)",
|
|
||||||
"Setting hostname with middle U+000D (https:)",
|
|
||||||
"Setting host with trailing U+000D (https:)",
|
|
||||||
"Setting hostname with trailing U+000D (https:)",
|
|
||||||
"Setting port with leading U+000D (https:)",
|
|
||||||
"Setting port with middle U+000D (https:)",
|
|
||||||
"Setting port with trailing U+000D (https:)",
|
|
||||||
"Setting pathname with leading U+000D (https:)",
|
|
||||||
"Setting pathname with middle U+000D (https:)",
|
|
||||||
"Setting pathname with trailing U+000D (https:)",
|
|
||||||
"Setting search with leading U+000D (https:)",
|
|
||||||
"Setting search with middle U+000D (https:)",
|
|
||||||
"Setting search with trailing U+000D (https:)",
|
|
||||||
"Setting hash with leading U+000D (https:)",
|
|
||||||
"Setting hash with middle U+000D (https:)",
|
|
||||||
"Setting hash with trailing U+000D (https:)",
|
|
||||||
"Setting protocol with leading U+001F (https:)",
|
"Setting protocol with leading U+001F (https:)",
|
||||||
"Setting protocol with U+001F before inserted colon (https:)",
|
"Setting protocol with U+001F before inserted colon (https:)",
|
||||||
"Setting host with leading U+001F (https:)",
|
"Setting port with leading U+001F (https:)",
|
||||||
"Setting host with middle U+001F (https:)",
|
"Setting pathname with trailing U+001F (https:)",
|
||||||
"Setting host with trailing U+001F (https:)",
|
|
||||||
"Setting port with middle U+001F (https:)",
|
|
||||||
"Setting port with trailing U+001F (https:)",
|
|
||||||
"Setting protocol with leading U+0000 (wpt++:)",
|
"Setting protocol with leading U+0000 (wpt++:)",
|
||||||
"Setting protocol with U+0000 before inserted colon (wpt++:)",
|
"Setting protocol with U+0000 before inserted colon (wpt++:)",
|
||||||
"Setting host with leading U+0000 (wpt++:)",
|
"Setting port with leading U+0000 (wpt++:)",
|
||||||
"Setting host with middle U+0000 (wpt++:)",
|
|
||||||
"Setting host with trailing U+0000 (wpt++:)",
|
|
||||||
"Setting port with middle U+0000 (wpt++:)",
|
|
||||||
"Setting port with trailing U+0000 (wpt++:)",
|
|
||||||
"Setting pathname with leading U+0000 (wpt++:)",
|
|
||||||
"Setting pathname with middle U+0000 (wpt++:)",
|
|
||||||
"Setting pathname with trailing U+0000 (wpt++:)",
|
"Setting pathname with trailing U+0000 (wpt++:)",
|
||||||
"Setting protocol with leading U+0009 (wpt++:)",
|
|
||||||
"Setting protocol with U+0009 before inserted colon (wpt++:)",
|
|
||||||
"Setting host with leading U+0009 (wpt++:)",
|
|
||||||
"Setting hostname with leading U+0009 (wpt++:)",
|
|
||||||
"Setting host with middle U+0009 (wpt++:)",
|
|
||||||
"Setting hostname with middle U+0009 (wpt++:)",
|
|
||||||
"Setting host with trailing U+0009 (wpt++:)",
|
|
||||||
"Setting hostname with trailing U+0009 (wpt++:)",
|
|
||||||
"Setting port with leading U+0009 (wpt++:)",
|
|
||||||
"Setting port with middle U+0009 (wpt++:)",
|
|
||||||
"Setting port with trailing U+0009 (wpt++:)",
|
|
||||||
"Setting pathname with leading U+0009 (wpt++:)",
|
|
||||||
"Setting pathname with middle U+0009 (wpt++:)",
|
|
||||||
"Setting pathname with trailing U+0009 (wpt++:)",
|
|
||||||
"Setting search with leading U+0009 (wpt++:)",
|
|
||||||
"Setting search with middle U+0009 (wpt++:)",
|
|
||||||
"Setting search with trailing U+0009 (wpt++:)",
|
|
||||||
"Setting hash with leading U+0009 (wpt++:)",
|
|
||||||
"Setting hash with middle U+0009 (wpt++:)",
|
|
||||||
"Setting hash with trailing U+0009 (wpt++:)",
|
|
||||||
"Setting protocol with leading U+000A (wpt++:)",
|
|
||||||
"Setting protocol with U+000A before inserted colon (wpt++:)",
|
|
||||||
"Setting host with leading U+000A (wpt++:)",
|
|
||||||
"Setting hostname with leading U+000A (wpt++:)",
|
|
||||||
"Setting host with middle U+000A (wpt++:)",
|
|
||||||
"Setting hostname with middle U+000A (wpt++:)",
|
|
||||||
"Setting host with trailing U+000A (wpt++:)",
|
|
||||||
"Setting hostname with trailing U+000A (wpt++:)",
|
|
||||||
"Setting port with leading U+000A (wpt++:)",
|
|
||||||
"Setting port with middle U+000A (wpt++:)",
|
|
||||||
"Setting port with trailing U+000A (wpt++:)",
|
|
||||||
"Setting pathname with leading U+000A (wpt++:)",
|
|
||||||
"Setting pathname with middle U+000A (wpt++:)",
|
|
||||||
"Setting pathname with trailing U+000A (wpt++:)",
|
|
||||||
"Setting search with leading U+000A (wpt++:)",
|
|
||||||
"Setting search with middle U+000A (wpt++:)",
|
|
||||||
"Setting search with trailing U+000A (wpt++:)",
|
|
||||||
"Setting hash with leading U+000A (wpt++:)",
|
|
||||||
"Setting hash with middle U+000A (wpt++:)",
|
|
||||||
"Setting hash with trailing U+000A (wpt++:)",
|
|
||||||
"Setting protocol with leading U+000D (wpt++:)",
|
|
||||||
"Setting protocol with U+000D before inserted colon (wpt++:)",
|
|
||||||
"Setting host with leading U+000D (wpt++:)",
|
|
||||||
"Setting hostname with leading U+000D (wpt++:)",
|
|
||||||
"Setting host with middle U+000D (wpt++:)",
|
|
||||||
"Setting hostname with middle U+000D (wpt++:)",
|
|
||||||
"Setting host with trailing U+000D (wpt++:)",
|
|
||||||
"Setting hostname with trailing U+000D (wpt++:)",
|
|
||||||
"Setting port with leading U+000D (wpt++:)",
|
|
||||||
"Setting port with middle U+000D (wpt++:)",
|
|
||||||
"Setting port with trailing U+000D (wpt++:)",
|
|
||||||
"Setting pathname with leading U+000D (wpt++:)",
|
|
||||||
"Setting pathname with middle U+000D (wpt++:)",
|
|
||||||
"Setting pathname with trailing U+000D (wpt++:)",
|
|
||||||
"Setting search with leading U+000D (wpt++:)",
|
|
||||||
"Setting search with middle U+000D (wpt++:)",
|
|
||||||
"Setting search with trailing U+000D (wpt++:)",
|
|
||||||
"Setting hash with leading U+000D (wpt++:)",
|
|
||||||
"Setting hash with middle U+000D (wpt++:)",
|
|
||||||
"Setting hash with trailing U+000D (wpt++:)",
|
|
||||||
"Setting protocol with leading U+001F (wpt++:)",
|
"Setting protocol with leading U+001F (wpt++:)",
|
||||||
"Setting protocol with U+001F before inserted colon (wpt++:)",
|
"Setting protocol with U+001F before inserted colon (wpt++:)",
|
||||||
"Setting host with leading U+001F (wpt++:)",
|
"Setting port with leading U+001F (wpt++:)",
|
||||||
"Setting host with middle U+001F (wpt++:)",
|
|
||||||
"Setting host with trailing U+001F (wpt++:)",
|
|
||||||
"Setting port with middle U+001F (wpt++:)",
|
|
||||||
"Setting port with trailing U+001F (wpt++:)",
|
|
||||||
"Setting pathname with leading U+001F (wpt++:)",
|
|
||||||
"Setting pathname with middle U+001F (wpt++:)",
|
|
||||||
"Setting pathname with trailing U+001F (wpt++:)"
|
"Setting pathname with trailing U+001F (wpt++:)"
|
||||||
],
|
],
|
||||||
"url-tojson.any.js": true,
|
"url-tojson.any.js": true,
|
||||||
"urlencoded-parser.any.js": [
|
"urlencoded-parser.any.js": [
|
||||||
"URLSearchParams constructed with: %EF%BB%BFtest=%EF%BB%BF",
|
|
||||||
"request.formData() with input: test=",
|
"request.formData() with input: test=",
|
||||||
"response.formData() with input: test=",
|
"response.formData() with input: test=",
|
||||||
"request.formData() with input: †&†=x",
|
"request.formData() with input: †&†=x",
|
||||||
|
|
Loading…
Add table
Reference in a new issue