From d4ac3a50c5833d7e783b9236e5e5db8d06a835e3 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Mon, 9 Mar 2026 16:46:08 +0100 Subject: [PATCH] lib: fix sequence argument handling in Blob constructor This uses the existing WebIDL infrastructure to handle the iteration over the argument correctly according to the specification. Note that we can't avoid looping over the input twice: we only know the value of the 'endings' option after converting the blob parts into an array. --- lib/internal/blob.js | 32 +++++++++++++++++-------------- test/wpt/status/FileAPI/blob.json | 8 -------- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/lib/internal/blob.js b/lib/internal/blob.js index e42773a8d03dcc..ad8f9d7292da5a 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -1,7 +1,6 @@ 'use strict'; const { - ArrayFrom, MathMax, MathMin, ObjectDefineProperties, @@ -15,7 +14,6 @@ const { StringPrototypeSplit, StringPrototypeToLowerCase, Symbol, - SymbolIterator, SymbolToStringTag, Uint8Array, } = primordials; @@ -54,12 +52,15 @@ const { lazyDOMException, } = require('internal/util'); const { inspect } = require('internal/util/inspect'); -const { convertToInt } = require('internal/webidl'); +const { + converters, + convertToInt, + createSequenceConverter, +} = require('internal/webidl'); const { codes: { ERR_BUFFER_TOO_LARGE, - ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE, ERR_INVALID_STATE, ERR_INVALID_THIS, @@ -112,7 +113,6 @@ function getSource(source, endings) { if (isAnyArrayBuffer(source)) { source = new Uint8Array(source); } else if (!isArrayBufferView(source)) { - source = `${source}`; if (endings === 'native') source = RegExpPrototypeSymbolReplace(/\n|\r\n/g, source, EOL); source = enc.encode(source); @@ -126,6 +126,13 @@ function getSource(source, endings) { return [byteLength, new Uint8Array(slice)]; } +const sourcesConverter = createSequenceConverter((source, opts = kEmptyObject) => { + if (isBlob(source) || isAnyArrayBuffer(source) || isArrayBufferView(source)) { + return source; + } + return converters.DOMString(source, opts); +}); + class Blob { /** * @typedef {string|ArrayBuffer|ArrayBufferView|Blob} SourcePart @@ -142,11 +149,8 @@ class Blob { constructor(sources = [], options) { markTransferMode(this, true, false); - if (sources === null || - typeof sources[SymbolIterator] !== 'function' || - typeof sources === 'string') { - throw new ERR_INVALID_ARG_TYPE('sources', 'a sequence', sources); - } + const sources_ = sourcesConverter(sources); + validateDictionary(options, 'options'); let { endings = 'transparent', @@ -158,11 +162,11 @@ class Blob { throw new ERR_INVALID_ARG_VALUE('options.endings', endings); let length = 0; - const sources_ = ArrayFrom(sources, (source) => { - const { 0: len, 1: src } = getSource(source, endings); + for (let i = 0; i < sources_.length; ++i) { + const { 0: len, 1: src } = getSource(sources_[i], endings); length += len; - return src; - }); + sources_[i] = src; + } if (length > kMaxLength) throw new ERR_BUFFER_TOO_LARGE(kMaxLength); diff --git a/test/wpt/status/FileAPI/blob.json b/test/wpt/status/FileAPI/blob.json index a82a9cb0cfdb11..498e43cbc94bdf 100644 --- a/test/wpt/status/FileAPI/blob.json +++ b/test/wpt/status/FileAPI/blob.json @@ -4,14 +4,6 @@ }, "Blob-constructor.any.js": { "fail": { - "expected": [ - "blobParts not an object: boolean with Boolean.prototype[Symbol.iterator]", - "blobParts not an object: number with Number.prototype[Symbol.iterator]", - "blobParts not an object: BigInt with BigInt.prototype[Symbol.iterator]", - "blobParts not an object: Symbol with Symbol.prototype[Symbol.iterator]", - "Getters and value conversions should happen in order until an exception is thrown.", - "Arguments should be evaluated from left to right." - ], "flaky": [ "Passing typed arrays as elements of the blobParts array should work.", "Passing a Float16Array as element of the blobParts array should work.",