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
- Have .text(), .arrayBuffer(), and .bytes() reject
  for an invalid this instead of throwing. Fix the
  tests regarding this.

PR-URL: https://github.com/nodejs/node/pull/53372
Refs: https://github.com/nodejs/node/pull/49936
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
This commit is contained in:
Khafra 2024-09-26 01:53:58 -04:00 committed by GitHub
parent 9bb6a1a790
commit da5887d8e9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 32 deletions

View File

@ -273,43 +273,21 @@ class Blob {
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<string>}
*/
async text() {
text() {
if (!isBlob(this))
throw new ERR_INVALID_THIS('Blob');
return PromiseReject(new ERR_INVALID_THIS('Blob'));
dec ??= new TextDecoder();
return dec.decode(await this.arrayBuffer());
return PromisePrototypeThen(
arrayBuffer(this),
(buffer) => dec.decode(buffer));
}
/**
@ -317,10 +295,10 @@ class Blob {
*/
bytes() {
if (!isBlob(this))
throw new ERR_INVALID_THIS('Blob');
return PromiseReject(new ERR_INVALID_THIS('Blob'));
return PromisePrototypeThen(
this.arrayBuffer(),
arrayBuffer(this),
(buffer) => new Uint8Array(buffer));
}
@ -439,6 +417,7 @@ ObjectDefineProperties(Blob.prototype, {
stream: kEnumerableProperty,
text: kEnumerableProperty,
arrayBuffer: kEnumerableProperty,
bytes: kEnumerableProperty,
});
function resolveObjectURL(url) {
@ -490,6 +469,34 @@ function createBlobFromFilePath(path, options) {
return res;
}
function arrayBuffer(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,

View File

@ -197,6 +197,7 @@ assert.throws(() => new Blob({}), {
'stream',
'text',
'arrayBuffer',
'bytes',
];
for (const prop of enumerable) {
@ -409,10 +410,13 @@ assert.throws(() => new Blob({}), {
}
(async () => {
await assert.rejects(async () => Blob.prototype.arrayBuffer.call(), {
await assert.rejects(() => Blob.prototype.arrayBuffer.call(), {
code: 'ERR_INVALID_THIS',
});
await assert.rejects(async () => Blob.prototype.text.call(), {
await assert.rejects(() => Blob.prototype.text.call(), {
code: 'ERR_INVALID_THIS',
});
await assert.rejects(() => Blob.prototype.bytes.call(), {
code: 'ERR_INVALID_THIS',
});
})().then(common.mustCall());
@ -490,3 +494,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());