mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 12:16:11 -05:00
fix: align URL / URLSearchParams to spec (#11005)
This commit is contained in:
parent
718cb6dad7
commit
2a66d5de01
7 changed files with 350 additions and 219 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -766,6 +766,7 @@ version = "0.9.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deno_bench_util",
|
"deno_bench_util",
|
||||||
"deno_core",
|
"deno_core",
|
||||||
|
"deno_webidl",
|
||||||
"idna",
|
"idna",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -387,18 +387,9 @@
|
||||||
|
|
||||||
webidl.configurePrototype(Headers);
|
webidl.configurePrototype(Headers);
|
||||||
|
|
||||||
webidl.converters["sequence<ByteString>"] = webidl
|
|
||||||
.createSequenceConverter(webidl.converters["ByteString"]);
|
|
||||||
webidl.converters["sequence<sequence<ByteString>>"] = webidl
|
|
||||||
.createSequenceConverter(webidl.converters["sequence<ByteString>"]);
|
|
||||||
webidl.converters["record<ByteString, ByteString>"] = webidl
|
|
||||||
.createRecordConverter(
|
|
||||||
webidl.converters["ByteString"],
|
|
||||||
webidl.converters["ByteString"],
|
|
||||||
);
|
|
||||||
webidl.converters["HeadersInit"] = (V, opts) => {
|
webidl.converters["HeadersInit"] = (V, opts) => {
|
||||||
// Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
|
// Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
|
||||||
if (typeof V === "object" && V !== null) {
|
if (webidl.type(V) === "Object" && V !== null) {
|
||||||
if (V[Symbol.iterator] !== undefined) {
|
if (V[Symbol.iterator] !== undefined) {
|
||||||
return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
|
return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,23 +3,24 @@
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
|
const webidl = window.__bootstrap.webidl;
|
||||||
|
|
||||||
function requiredArguments(name, length, required) {
|
const _list = Symbol("list");
|
||||||
if (length < required) {
|
const _urlObject = Symbol("url object");
|
||||||
const errMsg = `${name} requires at least ${required} argument${
|
|
||||||
required === 1 ? "" : "s"
|
|
||||||
}, but only ${length} present`;
|
|
||||||
throw new TypeError(errMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const paramLists = new WeakMap();
|
|
||||||
const urls = new WeakMap();
|
|
||||||
|
|
||||||
class URLSearchParams {
|
class URLSearchParams {
|
||||||
#params = [];
|
[_list];
|
||||||
|
[_urlObject] = null;
|
||||||
|
|
||||||
constructor(init = "") {
|
constructor(init = "") {
|
||||||
|
const prefix = "Failed to construct 'URL'";
|
||||||
|
init = webidl.converters
|
||||||
|
["sequence<sequence<USVString>> or record<USVString, USVString> or USVString"](
|
||||||
|
init,
|
||||||
|
{ prefix, context: "Argument 1" },
|
||||||
|
);
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
|
||||||
if (typeof init === "string") {
|
if (typeof init === "string") {
|
||||||
// Overload: USVString
|
// Overload: USVString
|
||||||
// If init is a string and starts with U+003F (?),
|
// If init is a string and starts with U+003F (?),
|
||||||
|
@ -27,59 +28,65 @@
|
||||||
if (init[0] == "?") {
|
if (init[0] == "?") {
|
||||||
init = init.slice(1);
|
init = init.slice(1);
|
||||||
}
|
}
|
||||||
|
this[_list] = core.opSync("op_url_parse_search_params", init);
|
||||||
this.#params = core.opSync("op_url_parse_search_params", init);
|
} else if (Array.isArray(init)) {
|
||||||
} else if (
|
|
||||||
Array.isArray(init) ||
|
|
||||||
typeof init?.[Symbol.iterator] == "function"
|
|
||||||
) {
|
|
||||||
// Overload: sequence<sequence<USVString>>
|
// Overload: sequence<sequence<USVString>>
|
||||||
for (const pair of init) {
|
this[_list] = init.map((pair, i) => {
|
||||||
// If pair does not contain exactly two items, then throw a TypeError.
|
|
||||||
if (pair.length !== 2) {
|
if (pair.length !== 2) {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
"URLSearchParams.constructor sequence argument must only contain pair elements",
|
`${prefix}: Item ${i +
|
||||||
|
0} in the parameter list does have length 2 exactly.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.#params.push([String(pair[0]), String(pair[1])]);
|
return [pair[0], pair[1]];
|
||||||
}
|
});
|
||||||
} else if (Object(init) !== init) {
|
|
||||||
// pass
|
|
||||||
} else if (init instanceof URLSearchParams) {
|
|
||||||
this.#params = [...init.#params];
|
|
||||||
} else {
|
} else {
|
||||||
// Overload: record<USVString, USVString>
|
// Overload: record<USVString, USVString>
|
||||||
for (const key of Object.keys(init)) {
|
this[_list] = Object.keys(init).map((key) => [key, init[key]]);
|
||||||
this.#params.push([key, String(init[key])]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
paramLists.set(this, this.#params);
|
|
||||||
urls.set(this, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateUrlSearch() {
|
#updateUrlSearch() {
|
||||||
const url = urls.get(this);
|
const url = this[_urlObject];
|
||||||
if (url == null) {
|
if (url === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const parseArgs = { href: url.href, setSearch: this.toString() };
|
const parts = core.opSync("op_url_parse", {
|
||||||
parts.set(url, core.opSync("op_url_parse", parseArgs));
|
href: url.href,
|
||||||
|
setSearch: this.toString(),
|
||||||
|
});
|
||||||
|
url[_url] = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
append(name, value) {
|
append(name, value) {
|
||||||
requiredArguments("URLSearchParams.append", arguments.length, 2);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
this.#params.push([String(name), String(value)]);
|
const prefix = "Failed to execute 'append' on 'URLSearchParams'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
name = webidl.converters.USVString(name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
this[_list].push([name, value]);
|
||||||
this.#updateUrlSearch();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(name) {
|
delete(name) {
|
||||||
requiredArguments("URLSearchParams.delete", arguments.length, 1);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
name = String(name);
|
const prefix = "Failed to execute 'append' on 'URLSearchParams'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters.USVString(name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
const list = this[_list];
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < this.#params.length) {
|
while (i < list.length) {
|
||||||
if (this.#params[i][0] === name) {
|
if (list[i][0] === name) {
|
||||||
this.#params.splice(i, 1);
|
list.splice(i, 1);
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -88,54 +95,77 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
getAll(name) {
|
getAll(name) {
|
||||||
requiredArguments("URLSearchParams.getAll", arguments.length, 1);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
name = String(name);
|
const prefix = "Failed to execute 'getAll' on 'URLSearchParams'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters.USVString(name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
const values = [];
|
const values = [];
|
||||||
for (const entry of this.#params) {
|
for (const entry of this[_list]) {
|
||||||
if (entry[0] === name) {
|
if (entry[0] === name) {
|
||||||
values.push(entry[1]);
|
values.push(entry[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(name) {
|
get(name) {
|
||||||
requiredArguments("URLSearchParams.get", arguments.length, 1);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
name = String(name);
|
const prefix = "Failed to execute 'get' on 'URLSearchParams'";
|
||||||
for (const entry of this.#params) {
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters.USVString(name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
for (const entry of this[_list]) {
|
||||||
if (entry[0] === name) {
|
if (entry[0] === name) {
|
||||||
return entry[1];
|
return entry[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
has(name) {
|
has(name) {
|
||||||
requiredArguments("URLSearchParams.has", arguments.length, 1);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
name = String(name);
|
const prefix = "Failed to execute 'has' on 'URLSearchParams'";
|
||||||
return this.#params.some((entry) => entry[0] === name);
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters.USVString(name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
return this[_list].some((entry) => entry[0] === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(name, value) {
|
set(name, value) {
|
||||||
requiredArguments("URLSearchParams.set", arguments.length, 2);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
|
const prefix = "Failed to execute 'set' on 'URLSearchParams'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
name = webidl.converters.USVString(name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
|
const list = this[_list];
|
||||||
|
|
||||||
// If there are any name-value pairs whose name is name, in list,
|
// If there are any name-value pairs whose name is name, in list,
|
||||||
// set the value of the first such name-value pair to value
|
// set the value of the first such name-value pair to value
|
||||||
// and remove the others.
|
// and remove the others.
|
||||||
name = String(name);
|
|
||||||
value = String(value);
|
|
||||||
let found = false;
|
let found = false;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < this.#params.length) {
|
while (i < list.length) {
|
||||||
if (this.#params[i][0] === name) {
|
if (list[i][0] === name) {
|
||||||
if (!found) {
|
if (!found) {
|
||||||
this.#params[i][1] = value;
|
list[i][1] = value;
|
||||||
found = true;
|
found = true;
|
||||||
i++;
|
i++;
|
||||||
} else {
|
} else {
|
||||||
this.#params.splice(i, 1);
|
list.splice(i, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
|
@ -145,69 +175,51 @@
|
||||||
// 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.#params.push([String(name), String(value)]);
|
list.push([name, value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#updateUrlSearch();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
sort() {
|
sort() {
|
||||||
this.#params.sort((a, b) => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1));
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
|
this[_list].sort((a, b) => (a[0] === b[0] ? 0 : a[0] > b[0] ? 1 : -1));
|
||||||
this.#updateUrlSearch();
|
this.#updateUrlSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
forEach(callbackfn, thisArg) {
|
|
||||||
requiredArguments("URLSearchParams.forEach", arguments.length, 1);
|
|
||||||
|
|
||||||
if (typeof thisArg !== "undefined") {
|
|
||||||
callbackfn = callbackfn.bind(thisArg);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [key, value] of this.#params) {
|
|
||||||
callbackfn(value, key, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*keys() {
|
|
||||||
for (const [key] of this.#params) {
|
|
||||||
yield key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*values() {
|
|
||||||
for (const [, value] of this.#params) {
|
|
||||||
yield value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*entries() {
|
|
||||||
yield* this.#params;
|
|
||||||
}
|
|
||||||
|
|
||||||
*[Symbol.iterator]() {
|
|
||||||
yield* this.#params;
|
|
||||||
}
|
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return core.opSync("op_url_stringify_search_params", this.#params);
|
webidl.assertBranded(this, URLSearchParams);
|
||||||
|
return core.opSync("op_url_stringify_search_params", this[_list]);
|
||||||
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "URLSearchParams";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = new WeakMap();
|
webidl.mixinPairIterable("URLSearchParams", URLSearchParams, _list, 0, 1);
|
||||||
|
|
||||||
|
webidl.configurePrototype(URLSearchParams);
|
||||||
|
|
||||||
|
const _url = Symbol("url");
|
||||||
|
|
||||||
class URL {
|
class URL {
|
||||||
#searchParams = null;
|
[_url];
|
||||||
|
#queryObject = null;
|
||||||
|
|
||||||
constructor(url, base) {
|
constructor(url, base = undefined) {
|
||||||
new.target;
|
const prefix = "Failed to construct 'URL'";
|
||||||
|
url = webidl.converters.USVString(url, { prefix, context: "Argument 1" });
|
||||||
if (url instanceof URL && base === undefined) {
|
if (base !== undefined) {
|
||||||
parts.set(this, parts.get(url));
|
base = webidl.converters.USVString(base, {
|
||||||
} else {
|
prefix,
|
||||||
base = base !== undefined ? String(base) : base;
|
context: "Argument 2",
|
||||||
const parseArgs = { href: String(url), baseHref: base };
|
});
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
|
||||||
}
|
}
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
|
||||||
|
const parts = core.opSync("op_url_parse", { href: url, baseHref: base });
|
||||||
|
this[_url] = parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.for("Deno.customInspect")](inspect) {
|
[Symbol.for("Deno.customInspect")](inspect) {
|
||||||
|
@ -228,8 +240,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateSearchParams() {
|
#updateSearchParams() {
|
||||||
if (this.#searchParams != null) {
|
if (this.#queryObject !== null) {
|
||||||
const params = paramLists.get(this.#searchParams);
|
const params = this.#queryObject[_list];
|
||||||
const newParams = core.opSync(
|
const newParams = core.opSync(
|
||||||
"op_url_parse_search_params",
|
"op_url_parse_search_params",
|
||||||
this.search.slice(1),
|
this.search.slice(1),
|
||||||
|
@ -239,122 +251,208 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
get hash() {
|
get hash() {
|
||||||
return parts.get(this).hash;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
set hash(value) {
|
set hash(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'hash' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setHash: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setHash: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get host() {
|
get host() {
|
||||||
return parts.get(this).host;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].host;
|
||||||
}
|
}
|
||||||
|
|
||||||
set host(value) {
|
set host(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'host' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setHost: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setHost: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get hostname() {
|
get hostname() {
|
||||||
return parts.get(this).hostname;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
set hostname(value) {
|
set hostname(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'hostname' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setHostname: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setHostname: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get href() {
|
get href() {
|
||||||
return parts.get(this).href;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].href;
|
||||||
}
|
}
|
||||||
|
|
||||||
set href(value) {
|
set href(value) {
|
||||||
try {
|
webidl.assertBranded(this, URL);
|
||||||
const parseArgs = { href: String(value) };
|
const prefix = "Failed to set 'href' on 'URL'";
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
} catch {
|
value = webidl.converters.USVString(value, {
|
||||||
throw new TypeError("Invalid URL");
|
prefix,
|
||||||
}
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
|
href: value,
|
||||||
|
});
|
||||||
this.#updateSearchParams();
|
this.#updateSearchParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
get origin() {
|
get origin() {
|
||||||
return parts.get(this).origin;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
get password() {
|
get password() {
|
||||||
return parts.get(this).password;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].password;
|
||||||
}
|
}
|
||||||
|
|
||||||
set password(value) {
|
set password(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'password' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setPassword: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setPassword: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get pathname() {
|
get pathname() {
|
||||||
return parts.get(this).pathname;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].pathname;
|
||||||
}
|
}
|
||||||
|
|
||||||
set pathname(value) {
|
set pathname(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'pathname' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setPathname: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setPathname: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get port() {
|
get port() {
|
||||||
return parts.get(this).port;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].port;
|
||||||
}
|
}
|
||||||
|
|
||||||
set port(value) {
|
set port(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'port' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setPort: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setPort: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get protocol() {
|
get protocol() {
|
||||||
return parts.get(this).protocol;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].protocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
set protocol(value) {
|
set protocol(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'protocol' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setProtocol: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setProtocol: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get search() {
|
get search() {
|
||||||
return parts.get(this).search;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].search;
|
||||||
}
|
}
|
||||||
|
|
||||||
set search(value) {
|
set search(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'search' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setSearch: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setSearch: value,
|
||||||
|
});
|
||||||
this.#updateSearchParams();
|
this.#updateSearchParams();
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
|
@ -362,35 +460,53 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
get username() {
|
get username() {
|
||||||
return parts.get(this).username;
|
webidl.assertBranded(this, URL);
|
||||||
|
return this[_url].username;
|
||||||
}
|
}
|
||||||
|
|
||||||
set username(value) {
|
set username(value) {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
|
const prefix = "Failed to set 'username' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
value = webidl.converters.USVString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
const parseArgs = { href: this.href, setUsername: String(value) };
|
this[_url] = core.opSync("op_url_parse", {
|
||||||
parts.set(this, core.opSync("op_url_parse", parseArgs));
|
href: this.href,
|
||||||
|
setUsername: value,
|
||||||
|
});
|
||||||
} catch {
|
} catch {
|
||||||
/* pass */
|
/* pass */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get searchParams() {
|
get searchParams() {
|
||||||
if (this.#searchParams == null) {
|
if (this.#queryObject == null) {
|
||||||
this.#searchParams = new URLSearchParams(this.search);
|
this.#queryObject = new URLSearchParams(this.search);
|
||||||
urls.set(this.#searchParams, this);
|
this.#queryObject[_urlObject] = this;
|
||||||
}
|
}
|
||||||
return this.#searchParams;
|
return this.#queryObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
return this.href;
|
return this.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
|
webidl.assertBranded(this, URL);
|
||||||
return this.href;
|
return this.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get [Symbol.toStringTag]() {
|
||||||
|
return "URL";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(URL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function implements application/x-www-form-urlencoded parsing.
|
* This function implements application/x-www-form-urlencoded parsing.
|
||||||
* https://url.spec.whatwg.org/#concept-urlencoded-parser
|
* https://url.spec.whatwg.org/#concept-urlencoded-parser
|
||||||
|
@ -401,6 +517,20 @@
|
||||||
return core.opSync("op_url_parse_search_params", null, bytes);
|
return core.opSync("op_url_parse_search_params", null, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
webidl
|
||||||
|
.converters[
|
||||||
|
"sequence<sequence<USVString>> or record<USVString, USVString> or USVString"
|
||||||
|
] = (V, opts) => {
|
||||||
|
// Union for (sequence<sequence<USVString>> or record<USVString, USVString> or USVString)
|
||||||
|
if (webidl.type(V) === "Object" && V !== null) {
|
||||||
|
if (V[Symbol.iterator] !== undefined) {
|
||||||
|
return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters["record<ByteString, ByteString>"](V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters.USVString(V, opts);
|
||||||
|
};
|
||||||
|
|
||||||
window.__bootstrap.url = {
|
window.__bootstrap.url = {
|
||||||
URL,
|
URL,
|
||||||
URLSearchParams,
|
URLSearchParams,
|
||||||
|
|
|
@ -21,6 +21,7 @@ serde = { version = "1.0.125", features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
deno_bench_util = { version = "0.3.0", path = "../../bench_util" }
|
deno_bench_util = { version = "0.3.0", path = "../../bench_util" }
|
||||||
|
deno_webidl = { version = "0.9.0", path = "../webidl" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "url_ops"
|
name = "url_ops"
|
||||||
|
|
|
@ -6,6 +6,7 @@ use deno_core::Extension;
|
||||||
|
|
||||||
fn setup() -> Vec<Extension> {
|
fn setup() -> Vec<Extension> {
|
||||||
vec![
|
vec![
|
||||||
|
deno_webidl::init(),
|
||||||
deno_url::init(),
|
deno_url::init(),
|
||||||
Extension::builder()
|
Extension::builder()
|
||||||
.js(vec![(
|
.js(vec![(
|
||||||
|
|
|
@ -568,6 +568,17 @@
|
||||||
);
|
);
|
||||||
converters["Promise<undefined>"] = createPromiseConverter(() => undefined);
|
converters["Promise<undefined>"] = createPromiseConverter(() => undefined);
|
||||||
|
|
||||||
|
converters["sequence<ByteString>"] = createSequenceConverter(
|
||||||
|
converters.ByteString,
|
||||||
|
);
|
||||||
|
converters["sequence<sequence<ByteString>>"] = createSequenceConverter(
|
||||||
|
converters["sequence<ByteString>"],
|
||||||
|
);
|
||||||
|
converters["record<ByteString, ByteString>"] = createRecordConverter(
|
||||||
|
converters.ByteString,
|
||||||
|
converters.ByteString,
|
||||||
|
);
|
||||||
|
|
||||||
function requiredArguments(length, required, opts = {}) {
|
function requiredArguments(length, required, opts = {}) {
|
||||||
if (length < required) {
|
if (length < required) {
|
||||||
const errMsg = `${
|
const errMsg = `${
|
||||||
|
@ -708,7 +719,7 @@
|
||||||
// https://heycam.github.io/webidl/#es-sequence
|
// https://heycam.github.io/webidl/#es-sequence
|
||||||
function createSequenceConverter(converter) {
|
function createSequenceConverter(converter) {
|
||||||
return function (V, opts = {}) {
|
return function (V, opts = {}) {
|
||||||
if (typeof V !== "object") {
|
if (type(V) !== "Object") {
|
||||||
throw makeException(
|
throw makeException(
|
||||||
TypeError,
|
TypeError,
|
||||||
"can not be converted to sequence.",
|
"can not be converted to sequence.",
|
||||||
|
@ -746,7 +757,7 @@
|
||||||
|
|
||||||
function createRecordConverter(keyConverter, valueConverter) {
|
function createRecordConverter(keyConverter, valueConverter) {
|
||||||
return (V, opts) => {
|
return (V, opts) => {
|
||||||
if (typeof V !== "object") {
|
if (type(V) !== "Object") {
|
||||||
throw makeException(
|
throw makeException(
|
||||||
TypeError,
|
TypeError,
|
||||||
"can not be converted to dictionary.",
|
"can not be converted to dictionary.",
|
||||||
|
@ -879,41 +890,64 @@
|
||||||
return iterator;
|
return iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
const methods = {
|
function entries() {
|
||||||
entries() {
|
assertBranded(this, prototype);
|
||||||
assertBranded(this, prototype);
|
return createDefaultIterator(this, "key+value");
|
||||||
return createDefaultIterator(this, "key+value");
|
}
|
||||||
|
|
||||||
|
const properties = {
|
||||||
|
entries: {
|
||||||
|
value: entries,
|
||||||
|
writable: true,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
},
|
},
|
||||||
[Symbol.iterator]() {
|
[Symbol.iterator]: {
|
||||||
assertBranded(this, prototype);
|
value: entries,
|
||||||
return createDefaultIterator(this, "key+value");
|
writable: true,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
},
|
},
|
||||||
keys() {
|
keys: {
|
||||||
assertBranded(this, prototype);
|
value: function keys() {
|
||||||
return createDefaultIterator(this, "key");
|
assertBranded(this, prototype);
|
||||||
|
return createDefaultIterator(this, "key");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
},
|
},
|
||||||
values() {
|
values: {
|
||||||
assertBranded(this, prototype);
|
value: function values() {
|
||||||
return createDefaultIterator(this, "value");
|
assertBranded(this, prototype);
|
||||||
|
return createDefaultIterator(this, "value");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
},
|
},
|
||||||
forEach(idlCallback, thisArg) {
|
forEach: {
|
||||||
assertBranded(this, prototype);
|
value: function forEach(idlCallback, thisArg = undefined) {
|
||||||
const prefix = `Failed to execute 'forEach' on '${name}'`;
|
assertBranded(this, prototype);
|
||||||
requiredArguments(arguments.length, 1, { prefix });
|
const prefix = `Failed to execute 'forEach' on '${name}'`;
|
||||||
idlCallback = converters["Function"](idlCallback, {
|
requiredArguments(arguments.length, 1, { prefix });
|
||||||
prefix,
|
idlCallback = converters["Function"](idlCallback, {
|
||||||
context: "Argument 1",
|
prefix,
|
||||||
});
|
context: "Argument 1",
|
||||||
idlCallback = idlCallback.bind(thisArg ?? globalThis);
|
});
|
||||||
const pairs = this[dataSymbol];
|
idlCallback = idlCallback.bind(thisArg ?? globalThis);
|
||||||
for (let i = 0; i < pairs.length; i++) {
|
const pairs = this[dataSymbol];
|
||||||
const entry = pairs[i];
|
for (let i = 0; i < pairs.length; i++) {
|
||||||
idlCallback(entry[valueKey], entry[keyKey], this);
|
const entry = pairs[i];
|
||||||
}
|
idlCallback(entry[valueKey], entry[keyKey], this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
return Object.defineProperties(prototype.prototype, properties);
|
||||||
return Object.assign(prototype.prototype, methods);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function configurePrototype(prototype) {
|
function configurePrototype(prototype) {
|
||||||
|
@ -938,6 +972,7 @@
|
||||||
|
|
||||||
window.__bootstrap ??= {};
|
window.__bootstrap ??= {};
|
||||||
window.__bootstrap.webidl = {
|
window.__bootstrap.webidl = {
|
||||||
|
type,
|
||||||
makeException,
|
makeException,
|
||||||
converters,
|
converters,
|
||||||
requiredArguments,
|
requiredArguments,
|
||||||
|
|
|
@ -586,34 +586,7 @@
|
||||||
"historical.any.html": [
|
"historical.any.html": [
|
||||||
"<a> and <area>.searchParams should be undefined"
|
"<a> and <area>.searchParams should be undefined"
|
||||||
],
|
],
|
||||||
"idlharness.any.html": [
|
"idlharness.any.html": true,
|
||||||
"URL interface object length",
|
|
||||||
"URL interface: attribute href",
|
|
||||||
"URL interface: stringifier",
|
|
||||||
"URL interface: attribute origin",
|
|
||||||
"URL interface: attribute protocol",
|
|
||||||
"URL interface: attribute username",
|
|
||||||
"URL interface: attribute password",
|
|
||||||
"URL interface: attribute host",
|
|
||||||
"URL interface: attribute hostname",
|
|
||||||
"URL interface: attribute port",
|
|
||||||
"URL interface: attribute pathname",
|
|
||||||
"URL interface: attribute search",
|
|
||||||
"URL interface: attribute searchParams",
|
|
||||||
"URL interface: attribute hash",
|
|
||||||
"URL interface: operation toJSON()",
|
|
||||||
"Stringification of new URL(\"http://foo\")",
|
|
||||||
"URLSearchParams interface: operation append(USVString, USVString)",
|
|
||||||
"URLSearchParams interface: operation delete(USVString)",
|
|
||||||
"URLSearchParams interface: operation get(USVString)",
|
|
||||||
"URLSearchParams interface: operation getAll(USVString)",
|
|
||||||
"URLSearchParams interface: operation has(USVString)",
|
|
||||||
"URLSearchParams interface: operation set(USVString, USVString)",
|
|
||||||
"URLSearchParams interface: operation sort()",
|
|
||||||
"URLSearchParams interface: iterable<USVString, USVString>",
|
|
||||||
"URLSearchParams interface: stringifier",
|
|
||||||
"Stringification of new URLSearchParams(\"hi=there&thank=you\")"
|
|
||||||
],
|
|
||||||
"url-constructor.any.html": [
|
"url-constructor.any.html": [
|
||||||
"Parsing: <file://%43%7C> against <about:blank>",
|
"Parsing: <file://%43%7C> against <about:blank>",
|
||||||
"Parsing: <file://%43|> against <about:blank>",
|
"Parsing: <file://%43|> against <about:blank>",
|
||||||
|
@ -879,7 +852,6 @@
|
||||||
"redirect-to-dataurl.any.html": true
|
"redirect-to-dataurl.any.html": true
|
||||||
},
|
},
|
||||||
"idlharness.any.html": [
|
"idlharness.any.html": [
|
||||||
"Headers interface: iterable<ByteString, ByteString>",
|
|
||||||
"Request interface: attribute destination",
|
"Request interface: attribute destination",
|
||||||
"Request interface: attribute referrer",
|
"Request interface: attribute referrer",
|
||||||
"Request interface: attribute referrerPolicy",
|
"Request interface: attribute referrerPolicy",
|
||||||
|
|
Loading…
Add table
Reference in a new issue