From f28ef24b40419b0488e0b4f13fb734af22285755 Mon Sep 17 00:00:00 2001 From: Khafra Date: Thu, 6 Jun 2024 13:27:48 -0400 Subject: [PATCH] buffer: extract Blob's .arrayBuffer() & webidl changes - Extracts Blob.prototype.arrayBuffer so it cannot be overridden in .text(), etc. - Make .bytes() enumerable. I guess the WPT runner is not running the idlharness tests? - Make .text() return a Promise, rather than being explicitly async. This is a non-documented part of the webidl spec. Refs: #49936 --- lib/internal/blob.js | 69 +++++++++++++++++++++----------------- test/parallel/test-blob.js | 14 ++++++++ 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/lib/internal/blob.js b/lib/internal/blob.js index 450ad593f44507..7e8d98b6708ead 100644 --- a/lib/internal/blob.js +++ b/lib/internal/blob.js @@ -270,46 +270,21 @@ class Blob { * @returns {Promise} */ arrayBuffer() { - if (!isBlob(this)) - return PromiseReject(new ERR_INVALID_THIS('Blob')); - - const { promise, resolve, reject } = createDeferredPromise(); - const reader = this[kHandle].getReader(); - const buffers = []; - const readNext = () => { - reader.pull((status, buffer) => { - if (status === 0) { - // EOS, concat & resolve - // buffer should be undefined here - resolve(concat(buffers)); - return; - } else if (status < 0) { - // The read could fail for many different reasons when reading - // from a non-memory resident blob part (e.g. file-backed blob). - // The error details the system error code. - const error = lazyDOMException('The blob could not be read', 'NotReadableError'); - reject(error); - return; - } - if (buffer !== undefined) - buffers.push(buffer); - queueMicrotask(() => readNext()); - }); - }; - readNext(); - return promise; + return arrayBuffer(this); } /** * @returns {Promise} */ - async text() { + text() { if (!isBlob(this)) throw new ERR_INVALID_THIS('Blob'); dec ??= new TextDecoder(); - return dec.decode(await this.arrayBuffer()); + return PromisePrototypeThen( + arrayBuffer(this), + (buffer) => dec.decode(buffer)); } bytes() { @@ -317,7 +292,7 @@ class Blob { throw new ERR_INVALID_THIS('Blob'); return PromisePrototypeThen( - this.arrayBuffer(), + arrayBuffer(this), (buffer) => new Uint8Array(buffer)); } @@ -436,6 +411,7 @@ ObjectDefineProperties(Blob.prototype, { stream: kEnumerableProperty, text: kEnumerableProperty, arrayBuffer: kEnumerableProperty, + bytes: kEnumerableProperty, }); function resolveObjectURL(url) { @@ -487,6 +463,37 @@ function createBlobFromFilePath(path, options) { return res; } +function arrayBuffer(blob) { + if (!isBlob(blob)) + return PromiseReject(new ERR_INVALID_THIS('Blob')); + + const { promise, resolve, reject } = createDeferredPromise(); + const reader = blob[kHandle].getReader(); + const buffers = []; + const readNext = () => { + reader.pull((status, buffer) => { + if (status === 0) { + // EOS, concat & resolve + // buffer should be undefined here + resolve(concat(buffers)); + return; + } else if (status < 0) { + // The read could fail for many different reasons when reading + // from a non-memory resident blob part (e.g. file-backed blob). + // The error details the system error code. + const error = lazyDOMException('The blob could not be read', 'NotReadableError'); + reject(error); + return; + } + if (buffer !== undefined) + buffers.push(buffer); + queueMicrotask(() => readNext()); + }); + }; + readNext(); + return promise; +} + module.exports = { Blob, createBlob, diff --git a/test/parallel/test-blob.js b/test/parallel/test-blob.js index 330fd79c742d77..2c0c0bae828a9d 100644 --- a/test/parallel/test-blob.js +++ b/test/parallel/test-blob.js @@ -197,6 +197,7 @@ assert.throws(() => new Blob({}), { 'stream', 'text', 'arrayBuffer', + 'bytes', ]; for (const prop of enumerable) { @@ -490,3 +491,16 @@ assert.throws(() => new Blob({}), { assert.ok(structuredClone(blob).size === blob.size); assert.ok((await structuredClone(blob).text()) === (await blob.text())); })().then(common.mustCall()); + +(async () => { + const blob = new Blob(['hello']); + const { arrayBuffer } = Blob.prototype; + + Blob.prototype.arrayBuffer = common.mustNotCall(); + + try { + assert.strictEqual(await blob.text(), 'hello'); + } finally { + Blob.prototype.arrayBuffer = arrayBuffer; + } +})().then(common.mustCall());