diff --git a/cli/js/tests/url_search_params_test.ts b/cli/js/tests/url_search_params_test.ts index 3e71d29003..7b7dbab764 100644 --- a/cli/js/tests/url_search_params_test.ts +++ b/cli/js/tests/url_search_params_test.ts @@ -243,3 +243,33 @@ unitTest( assertEquals(params1.get("1"), "2"); } ); + +// If a class extends URLSearchParams, override one method should not change another's behavior. +unitTest( + function urlSearchParamsOverridingAppendNotChangeConstructorAndSet(): void { + let overridedAppendCalled = 0; + class CustomSearchParams extends URLSearchParams { + append(name: string, value: string): void { + ++overridedAppendCalled; + super.append(name, value); + } + } + new CustomSearchParams("foo=bar"); + new CustomSearchParams([["foo", "bar"]]); + new CustomSearchParams(new CustomSearchParams({ foo: "bar" })); + new CustomSearchParams().set("foo", "bar"); + assertEquals(overridedAppendCalled, 0); + } +); + +unitTest(function urlSearchParamsOverridingEntriesNotChangeForEach(): void { + class CustomSearchParams extends URLSearchParams { + *entries(): IterableIterator<[string, string]> { + yield* []; + } + } + let loopCount = 0; + const params = new CustomSearchParams({ foo: "bar" }); + params.forEach(() => void ++loopCount); + assertEquals(loopCount, 1); +}); diff --git a/cli/js/web/url_search_params.ts b/cli/js/web/url_search_params.ts index 221a70ab12..35439b0e2e 100644 --- a/cli/js/web/url_search_params.ts +++ b/cli/js/web/url_search_params.ts @@ -5,56 +5,17 @@ import { isIterable, requiredArguments } from "./util.ts"; /** @internal */ export const urls = new WeakMap(); -function handleStringInitialization( - searchParams: URLSearchParams, - init: string -): void { - // 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); - searchParams.append(decodeURIComponent(name), decodeURIComponent(value)); - } -} - -function handleArrayInitialization( - searchParams: URLSearchParams, - init: string[][] | Iterable<[string, string]> -): void { - // Overload: sequence> - 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" - ); - } - searchParams.append(tuple[0], tuple[1]); - } -} - export class URLSearchParamsImpl implements URLSearchParams { #params: Array<[string, string]> = []; constructor(init: string | string[][] | Record = "") { if (typeof init === "string") { - handleStringInitialization(this, init); + this.#handleStringInitialization(init); return; } if (Array.isArray(init) || isIterable(init)) { - handleArrayInitialization(this, init); + this.#handleArrayInitialization(init); return; } @@ -69,12 +30,47 @@ export class URLSearchParamsImpl implements URLSearchParams { // Overload: record for (const key of Object.keys(init)) { - this.append(key, init[key]); + this.#append(key, init[key]); } urls.set(this, null); } + #handleStringInitialization = (init: string): void => { + // 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(decodeURIComponent(name), decodeURIComponent(value)); + } + }; + + #handleArrayInitialization = ( + init: string[][] | Iterable<[string, string]> + ): void => { + // Overload: sequence> + 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 = (): void => { const url = urls.get(this); if (url == null) { @@ -89,9 +85,13 @@ export class URLSearchParamsImpl implements URLSearchParams { parts.get(url)!.query = query; }; + #append = (name: string, value: string): void => { + this.#params.push([String(name), String(value)]); + }; + append(name: string, value: string): void { requiredArguments("URLSearchParams.append", arguments.length, 2); - this.#params.push([String(name), String(value)]); + this.#append(name, value); this.#updateSteps(); } @@ -167,7 +167,7 @@ export class URLSearchParamsImpl implements URLSearchParams { // Otherwise, append a new name-value pair whose name is name // and value is value, to list. if (!found) { - this.append(name, value); + this.#append(name, value); } this.#updateSteps(); @@ -189,7 +189,7 @@ export class URLSearchParamsImpl implements URLSearchParams { callbackfn = callbackfn.bind(thisArg); } - for (const [key, value] of this.entries()) { + for (const [key, value] of this.#params) { callbackfn(value, key, this); } }