1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-08 23:28:18 -05:00

fix(extensions/web): aborting a FileReader should not affect later reads (#11381)

Currently, calling the `abort()` method on a `FileReader` object aborts
any current read operation, but it also prevents any read operation
started at some later point from starting. The File API instead
specifies that calling `abort()` should reset the `FileReader`'s state
and result, as well as removing any queued tasks from the current
operation that haven't yet run.
This commit is contained in:
Andreu Botella 2021-07-14 12:08:42 +02:00 committed by GitHub
parent 56635d3b52
commit 5b771e7e83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 18 additions and 10 deletions

View file

@ -52,8 +52,8 @@
[result] = null; [result] = null;
/** @type {null | DOMException} */ /** @type {null | DOMException} */
[error] = null; [error] = null;
/** @type {null | {aborted: boolean}} */
[aborted] = false; [aborted] = null;
/** /**
* @param {Blob} blob * @param {Blob} blob
@ -74,6 +74,12 @@
// 4. Set frs error to null. // 4. Set frs error to null.
this[error] = null; this[error] = null;
// We set this[aborted] to a new object, and keep track of it in a
// separate variable, so if a new read operation starts while there are
// remaining tasks from a previous aborted operation, the new operation
// will run while the tasks from the previous one are still aborted.
const abortedState = this[aborted] = { aborted: false };
// 5. Let stream be the result of calling get stream on blob. // 5. Let stream be the result of calling get stream on blob.
const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream(); const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
@ -92,17 +98,17 @@
// 10 in parallel while true // 10 in parallel while true
(async () => { (async () => {
while (!this[aborted]) { while (!abortedState.aborted) {
// 1. Wait for chunkPromise to be fulfilled or rejected. // 1. Wait for chunkPromise to be fulfilled or rejected.
try { try {
const chunk = await chunkPromise; const chunk = await chunkPromise;
if (this[aborted]) return; if (abortedState.aborted) return;
// 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr. // 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.
if (isFirstChunk) { if (isFirstChunk) {
// TODO(lucacasonato): this is wrong, should be HTML "queue a task" // TODO(lucacasonato): this is wrong, should be HTML "queue a task"
queueMicrotask(() => { queueMicrotask(() => {
if (this[aborted]) return; if (abortedState.aborted) return;
// fire a progress event for loadstart // fire a progress event for loadstart
const ev = new ProgressEvent("loadstart", {}); const ev = new ProgressEvent("loadstart", {});
this.dispatchEvent(ev); this.dispatchEvent(ev);
@ -128,7 +134,7 @@
}); });
// TODO(lucacasonato): this is wrong, should be HTML "queue a task" // TODO(lucacasonato): this is wrong, should be HTML "queue a task"
queueMicrotask(() => { queueMicrotask(() => {
if (this[aborted]) return; if (abortedState.aborted) return;
this.dispatchEvent(ev); this.dispatchEvent(ev);
}); });
} }
@ -138,7 +144,7 @@
else if (chunk.done === true) { else if (chunk.done === true) {
// TODO(lucacasonato): this is wrong, should be HTML "queue a task" // TODO(lucacasonato): this is wrong, should be HTML "queue a task"
queueMicrotask(() => { queueMicrotask(() => {
if (this[aborted]) return; if (abortedState.aborted) return;
// 1. Set frs state to "done". // 1. Set frs state to "done".
this[state] = "done"; this[state] = "done";
// 2. Let result be the result of package data given bytes, type, blobs type, and encodingName. // 2. Let result be the result of package data given bytes, type, blobs type, and encodingName.
@ -232,7 +238,7 @@
} catch (err) { } catch (err) {
// TODO(lucacasonato): this is wrong, should be HTML "queue a task" // TODO(lucacasonato): this is wrong, should be HTML "queue a task"
queueMicrotask(() => { queueMicrotask(() => {
if (this[aborted]) return; if (abortedState.aborted) return;
// chunkPromise rejected // chunkPromise rejected
this[state] = "done"; this[state] = "done";
@ -303,7 +309,9 @@
} }
// If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue. // If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue.
// Terminate the algorithm for the read method being processed. // Terminate the algorithm for the read method being processed.
this[aborted] = true; if (this[aborted] !== null) {
this[aborted].aborted = true;
}
// Fire a progress event called abort at the context object. // Fire a progress event called abort at the context object.
const ev = new ProgressEvent("abort", {}); const ev = new ProgressEvent("abort", {});

View file

@ -2794,7 +2794,7 @@
"reading-data-section": { "reading-data-section": {
"Determining-Encoding.any.html": true, "Determining-Encoding.any.html": true,
"FileReader-event-handler-attributes.any.html": true, "FileReader-event-handler-attributes.any.html": true,
"FileReader-multiple-reads.any.html": false, "FileReader-multiple-reads.any.html": true,
"filereader_abort.any.html": true, "filereader_abort.any.html": true,
"filereader_error.any.html": true, "filereader_error.any.html": true,
"filereader_events.any.html": false, "filereader_events.any.html": false,