diff --git a/lib/internal/per_context/domexception.js b/lib/internal/per_context/domexception.js index 06bb3fa3053cd5..1ea37f7062bb5d 100644 --- a/lib/internal/per_context/domexception.js +++ b/lib/internal/per_context/domexception.js @@ -10,12 +10,27 @@ const { TypeError, } = primordials; -class ERR_INVALID_THIS extends TypeError { - constructor(type) { - super('Value of "this" must be of ' + type); - } - - get code() { return 'ERR_INVALID_THIS'; } +function throwInvalidThisError(Base, type) { + const err = new Base(); + const key = 'ERR_INVALID_THIS'; + ObjectDefineProperties(err, { + message: { + value: `Value of "this" must be of ${type}`, + enumerable: false, + writable: true, + configurable: true, + }, + toString: { + value() { + return `${this.name} [${key}]: ${this.message}`; + }, + enumerable: false, + writable: true, + configurable: true, + }, + }); + err.code = key; + throw err; } let internalsMap; @@ -51,7 +66,7 @@ class DOMException extends Error { ensureInitialized(); const internals = internalsMap.get(this); if (internals === undefined) { - throw new ERR_INVALID_THIS('DOMException'); + throwInvalidThisError(TypeError, 'DOMException'); } return internals.name; } @@ -60,7 +75,7 @@ class DOMException extends Error { ensureInitialized(); const internals = internalsMap.get(this); if (internals === undefined) { - throw new ERR_INVALID_THIS('DOMException'); + throwInvalidThisError(TypeError, 'DOMException'); } return internals.message; } @@ -69,7 +84,7 @@ class DOMException extends Error { ensureInitialized(); const internals = internalsMap.get(this); if (internals === undefined) { - throw new ERR_INVALID_THIS('DOMException'); + throwInvalidThisError(TypeError, 'DOMException'); } const code = nameToCodeMap.get(internals.name); return code === undefined ? 0 : code; diff --git a/lib/internal/url.js b/lib/internal/url.js index bc5d70d8bf17c7..b3ee11a6c4808b 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -219,6 +219,7 @@ class URLSearchParams { } else { // Record // Need to use reflection APIs for full spec compliance. + const visited = {}; this[searchParams] = []; const keys = ReflectOwnKeys(init); for (let i = 0; i < keys.length; i++) { @@ -227,7 +228,15 @@ class URLSearchParams { if (desc !== undefined && desc.enumerable) { const typedKey = toUSVString(key); const typedValue = toUSVString(init[key]); - this[searchParams].push(typedKey, typedValue); + + // Two different key may result same after `toUSVString()`, we only + // leave the later one. Refers to WPT. + if (visited[typedKey] !== undefined) { + this[searchParams][visited[typedKey]] = typedValue; + } else { + visited[typedKey] = this[searchParams].push(typedKey, + typedValue) - 1; + } } } } diff --git a/test/wpt/status/url.json b/test/wpt/status/url.json index fdf562c2c89c99..bb5b0215febf69 100644 --- a/test/wpt/status/url.json +++ b/test/wpt/status/url.json @@ -12,9 +12,6 @@ "urlencoded-parser.any.js": { "fail": "missing Request and Response" }, - "urlsearchparams-constructor.any.js": { - "fail": "FormData is not defined" - }, "url-constructor.any.js": { "requires": ["small-icu"] }, diff --git a/test/wpt/test-url.js b/test/wpt/test-url.js index cca2184b47720b..f381c439fbeffd 100644 --- a/test/wpt/test-url.js +++ b/test/wpt/test-url.js @@ -11,6 +11,15 @@ runner.setScriptModifier((obj) => { // created via `document.createElement`. So we need to ignore them and just // test `URL`. obj.code = obj.code.replace(/\["url", "a", "area"\]/, '[ "url" ]'); + } else if (typeof FormData === 'undefined' && // eslint-disable-line + obj.filename.includes('urlsearchparams-constructor.any.js')) { + // TODO(XadillaX): Remove this `else if` after `FormData` is supported. + + // Ignore test named `URLSearchParams constructor, FormData.` because we do + // not have `FormData`. + obj.code = obj.code.replace( + /('URLSearchParams constructor, object\.'\);[\w\W]+)test\(function\(\) {[\w\W]*?}, 'URLSearchParams constructor, FormData\.'\);/, + '$1'); } }); runner.pretendGlobalThisAs('Window');