0
0
Fork 0
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:
Luca Casonato 2021-06-16 18:40:35 +02:00 committed by GitHub
parent 718cb6dad7
commit 2a66d5de01
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 350 additions and 219 deletions

1
Cargo.lock generated
View file

@ -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",

View file

@ -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);
} }

View file

@ -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,

View file

@ -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"

View file

@ -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![(

View file

@ -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,

View file

@ -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",