diff --git a/lib/fetch/headers.js b/lib/fetch/headers.js index 43860c5d98a..41ae9b02368 100644 --- a/lib/fetch/headers.js +++ b/lib/fetch/headers.js @@ -528,8 +528,10 @@ Object.defineProperties(Headers.prototype, { webidl.converters.HeadersInit = function (V) { if (webidl.util.Type(V) === 'Object') { - if (V[Symbol.iterator]) { - return webidl.converters['sequence>'](V) + const iterator = Reflect.get(V, Symbol.iterator) + + if (typeof iterator === 'function') { + return webidl.converters['sequence>'](V, iterator.bind(V)) } return webidl.converters['record'](V) diff --git a/lib/fetch/webidl.js b/lib/fetch/webidl.js index a93a25505fc..0978e67f26f 100644 --- a/lib/fetch/webidl.js +++ b/lib/fetch/webidl.js @@ -218,7 +218,7 @@ webidl.util.IntegerPart = function (n) { // https://webidl.spec.whatwg.org/#es-sequence webidl.sequenceConverter = function (converter) { - return (V) => { + return (V, Iterable) => { // 1. If Type(V) is not Object, throw a TypeError. if (webidl.util.Type(V) !== 'Object') { throw webidl.errors.exception({ @@ -229,7 +229,7 @@ webidl.sequenceConverter = function (converter) { // 2. Let method be ? GetMethod(V, @@iterator). /** @type {Generator} */ - const method = V?.[Symbol.iterator]?.() + const method = typeof Iterable === 'function' ? Iterable() : V?.[Symbol.iterator]?.() const seq = [] // 3. If method is undefined, throw a TypeError. @@ -273,8 +273,8 @@ webidl.recordConverter = function (keyConverter, valueConverter) { const result = {} if (!types.isProxy(O)) { - // Object.keys only returns enumerable properties - const keys = Object.keys(O) + // 1. Let desc be ? O.[[GetOwnProperty]](key). + const keys = [...Object.getOwnPropertyNames(O), ...Object.getOwnPropertySymbols(O)] for (const key of keys) { // 1. Let typedKey be key converted to an IDL value of type K. diff --git a/test/fetch/headers.js b/test/fetch/headers.js index fcdf4b7a820..d3e27fa348d 100644 --- a/test/fetch/headers.js +++ b/test/fetch/headers.js @@ -719,3 +719,30 @@ test('When the value is updated, update the cache', (t) => { headers.append('d', 'd') deepStrictEqual([...headers], [...expected, ['d', 'd']]) }) + +test('Symbol.iterator is only accessed once', (t) => { + const { ok } = tspl(t, { plan: 1 }) + + const dict = new Proxy({}, { + get () { + ok(true) + + return function * () {} + } + }) + + new Headers(dict) // eslint-disable-line no-new +}) + +test('Invalid Symbol.iterators', (t) => { + const { throws } = tspl(t, { plan: 3 }) + + throws(() => new Headers({ [Symbol.iterator]: null }), TypeError) + throws(() => new Headers({ [Symbol.iterator]: undefined }), TypeError) + throws(() => { + const obj = { [Symbol.iterator]: null } + Object.defineProperty(obj, Symbol.iterator, { enumerable: false }) + + new Headers(obj) // eslint-disable-line no-new + }, TypeError) +}) diff --git a/types/webidl.d.ts b/types/webidl.d.ts index 40cfe064f8f..f29bebbb1e8 100644 --- a/types/webidl.d.ts +++ b/types/webidl.d.ts @@ -5,7 +5,7 @@ */ type Converter = (object: unknown) => T -type SequenceConverter = (object: unknown) => T[] +type SequenceConverter = (object: unknown, iterable?: IterableIterator) => T[] type RecordConverter = (object: unknown) => Record