diff --git a/lib/internal/url.js b/lib/internal/url.js index 0fe819fe129..35abca64e93 100644 --- a/lib/internal/url.js +++ b/lib/internal/url.js @@ -1065,7 +1065,8 @@ class URL { } set protocol(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kProtocol, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kProtocol, value); if (href) { this.#updateContext(href); } @@ -1079,7 +1080,8 @@ class URL { } set username(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kUsername, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kUsername, value); if (href) { this.#updateContext(href); } @@ -1093,7 +1095,8 @@ class URL { } set password(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kPassword, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kPassword, value); if (href) { this.#updateContext(href); } @@ -1113,7 +1116,8 @@ class URL { } set host(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kHost, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kHost, value); if (href) { this.#updateContext(href); } @@ -1129,7 +1133,8 @@ class URL { } set hostname(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kHostname, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kHostname, value); if (href) { this.#updateContext(href); } @@ -1143,7 +1148,8 @@ class URL { } set port(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kPort, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kPort, value); if (href) { this.#updateContext(href); } @@ -1160,7 +1166,8 @@ class URL { } set pathname(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kPathname, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kPathname, value); if (href) { this.#updateContext(href); } @@ -1173,7 +1180,8 @@ class URL { } set search(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kSearch, StringPrototypeToWellFormed(`${value}`)); + value = StringPrototypeToWellFormed(`${value}`); + const href = bindingUrl.update(this.#context.href, updateActions.kSearch, value); if (href) { this.#updateContext(href, true); } @@ -1198,7 +1206,8 @@ class URL { } set hash(value) { - const href = bindingUrl.update(this.#context.href, updateActions.kHash, `${value}`); + value = `${value}`; + const href = bindingUrl.update(this.#context.href, updateActions.kHash, value); if (href) { this.#updateContext(href); } diff --git a/test/parallel/test-whatwg-url-setters-coercion-order.js b/test/parallel/test-whatwg-url-setters-coercion-order.js new file mode 100644 index 00000000000..364f2354185 --- /dev/null +++ b/test/parallel/test-whatwg-url-setters-coercion-order.js @@ -0,0 +1,34 @@ +'use strict'; + +// Tests below are not from WPT. +// +// Web IDL converts an assigned value to the attribute's type before the +// setter's steps run. A `toString()` that mutates the URL during conversion +// must be observed by the setter: the new component value applies to the +// mutated URL, not to stale state captured before the conversion. +// Refs: https://github.com/nodejs/node/issues/67345 +require('../common'); +const assert = require('assert'); + +const cases = [ + ['protocol', 'http', 'http://after.example/base'], + ['username', 'user', 'https://user@after.example/base'], + ['password', 'pass', 'https://:pass@after.example/base'], + ['host', 'other.example:8080', 'https://other.example:8080/base'], + ['hostname', 'other.example', 'https://other.example/base'], + ['port', '8080', 'https://after.example:8080/base'], + ['pathname', '/new', 'https://after.example/new'], + ['search', '?q=1', 'https://after.example/base?q=1'], + ['hash', '#frag', 'https://after.example/base#frag'], +]; + +for (const [setter, value, expected] of cases) { + const url = new URL('https://before.example/old'); + url[setter] = { + toString() { + url.href = 'https://after.example/base'; + return value; + }, + }; + assert.strictEqual(url.href, expected, `${setter} setter`); +}