Skip to content

Commit 9b82b15

Browse files
MattiasBuelensmarco-ippolito
authored andcommitted
stream: update ongoing promise in async iterator return() method
PR-URL: #52657 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Debadree Chatterjee <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 4a3ecbf commit 9b82b15

File tree

7 files changed

+139
-7
lines changed

7 files changed

+139
-7
lines changed

lib/internal/webstreams/readablestream.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,14 @@ class ReadableStream {
548548
},
549549

550550
return(error) {
551-
return state.current ?
551+
started = true;
552+
state.current = state.current !== undefined ?
552553
PromisePrototypeThen(
553554
state.current,
554555
() => returnSteps(error),
555556
() => returnSteps(error)) :
556557
returnSteps(error);
558+
return state.current;
557559
},
558560

559561
[SymbolAsyncIterator]() { return this; },

test/fixtures/wpt/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ Last update:
2525
- interfaces: https://github.com/web-platform-tests/wpt/tree/df731dab88/interfaces
2626
- performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline
2727
- resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing
28-
- resources: https://github.com/web-platform-tests/wpt/tree/919874f84f/resources
29-
- streams: https://github.com/web-platform-tests/wpt/tree/3df6d94318/streams
30-
- url: https://github.com/web-platform-tests/wpt/tree/c2d7e70b52/url
28+
- resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources
29+
- streams: https://github.com/web-platform-tests/wpt/tree/9b03282a99/streams
30+
- url: https://github.com/web-platform-tests/wpt/tree/0f550ab9f5/url
3131
- user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing
3232
- wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi
3333
- wasm/webapi: https://github.com/web-platform-tests/wpt/tree/fd1b23eeaa/wasm/webapi
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<script type="module">
3+
let a = self.open()
4+
let d = await a.navigator.storage.getDirectory()
5+
let h = await d.getFileHandle("c5c9960b-a637-4232-be3d-3ccc5704852f", {"create": true})
6+
let r = new ReadableStream({
7+
start(c) {
8+
c.enqueue(h)
9+
c.close();
10+
}
11+
});
12+
let w = await h.createWritable({ })
13+
r.pipeThrough({"readable": r, "writable": w}, {})
14+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<body>
3+
<script>
4+
window.onload = () => {
5+
const i = document.createElement("iframe");
6+
i.src = "about:blank";
7+
document.body.appendChild(i);
8+
9+
const rs = new i.contentWindow.ReadableStream({
10+
start(controller) { controller.error(); }
11+
});
12+
const ws = new i.contentWindow.WritableStream();
13+
14+
i.remove();
15+
16+
// pipeTo() should not crash with a ReadableStream or WritableStream from
17+
// a detached iframe.
18+
rs.pipeTo(ws);
19+
};
20+
</script>
21+
</body>

test/fixtures/wpt/streams/readable-streams/async-iterator.any.js

+84-2
Original file line numberDiff line numberDiff line change
@@ -475,15 +475,85 @@ promise_test(async () => {
475475
const rs = new ReadableStream();
476476
const it = rs.values();
477477

478-
const iterResults = await Promise.allSettled([it.return('return value'), it.next()]);
478+
const resolveOrder = [];
479+
const iterResults = await Promise.allSettled([
480+
it.return('return value').then(result => {
481+
resolveOrder.push('return');
482+
return result;
483+
}),
484+
it.next().then(result => {
485+
resolveOrder.push('next');
486+
return result;
487+
})
488+
]);
479489

480490
assert_equals(iterResults[0].status, 'fulfilled', 'return() promise status');
481491
assert_iter_result(iterResults[0].value, 'return value', true, 'return()');
482492

483493
assert_equals(iterResults[1].status, 'fulfilled', 'next() promise status');
484494
assert_iter_result(iterResults[1].value, undefined, true, 'next()');
495+
496+
assert_array_equals(resolveOrder, ['return', 'next'], 'next() resolves after return()');
485497
}, 'return(); next() [no awaiting]');
486498

499+
promise_test(async () => {
500+
let resolveCancelPromise;
501+
const rs = recordingReadableStream({
502+
cancel(reason) {
503+
return new Promise(r => resolveCancelPromise = r);
504+
}
505+
});
506+
const it = rs.values();
507+
508+
let returnResolved = false;
509+
const returnPromise = it.return('return value').then(result => {
510+
returnResolved = true;
511+
return result;
512+
});
513+
await flushAsyncEvents();
514+
assert_false(returnResolved, 'return() should not resolve while cancel() promise is pending');
515+
516+
resolveCancelPromise();
517+
const iterResult1 = await returnPromise;
518+
assert_iter_result(iterResult1, 'return value', true, 'return()');
519+
520+
const iterResult2 = await it.next();
521+
assert_iter_result(iterResult2, undefined, true, 'next()');
522+
}, 'return(); next() with delayed cancel()');
523+
524+
promise_test(async () => {
525+
let resolveCancelPromise;
526+
const rs = recordingReadableStream({
527+
cancel(reason) {
528+
return new Promise(r => resolveCancelPromise = r);
529+
}
530+
});
531+
const it = rs.values();
532+
533+
const resolveOrder = [];
534+
const returnPromise = it.return('return value').then(result => {
535+
resolveOrder.push('return');
536+
return result;
537+
});
538+
const nextPromise = it.next().then(result => {
539+
resolveOrder.push('next');
540+
return result;
541+
});
542+
543+
assert_array_equals(rs.events, ['cancel', 'return value'], 'return() should call cancel()');
544+
assert_array_equals(resolveOrder, [], 'return() should not resolve before cancel() resolves');
545+
546+
resolveCancelPromise();
547+
const iterResult1 = await returnPromise;
548+
assert_iter_result(iterResult1, 'return value', true, 'return() should resolve with original reason');
549+
const iterResult2 = await nextPromise;
550+
assert_iter_result(iterResult2, undefined, true, 'next() should resolve with done result');
551+
552+
assert_array_equals(rs.events, ['cancel', 'return value'], 'no pull() after cancel()');
553+
assert_array_equals(resolveOrder, ['return', 'next'], 'next() should resolve after return() resolves');
554+
555+
}, 'return(); next() with delayed cancel() [no awaiting]');
556+
487557
promise_test(async () => {
488558
const rs = new ReadableStream();
489559
const it = rs.values();
@@ -499,13 +569,25 @@ promise_test(async () => {
499569
const rs = new ReadableStream();
500570
const it = rs.values();
501571

502-
const iterResults = await Promise.allSettled([it.return('return value 1'), it.return('return value 2')]);
572+
const resolveOrder = [];
573+
const iterResults = await Promise.allSettled([
574+
it.return('return value 1').then(result => {
575+
resolveOrder.push('return 1');
576+
return result;
577+
}),
578+
it.return('return value 2').then(result => {
579+
resolveOrder.push('return 2');
580+
return result;
581+
})
582+
]);
503583

504584
assert_equals(iterResults[0].status, 'fulfilled', '1st return() promise status');
505585
assert_iter_result(iterResults[0].value, 'return value 1', true, '1st return()');
506586

507587
assert_equals(iterResults[1].status, 'fulfilled', '2nd return() promise status');
508588
assert_iter_result(iterResults[1].value, 'return value 2', true, '1st return()');
589+
590+
assert_array_equals(resolveOrder, ['return 1', 'return 2'], '2nd return() resolves after 1st return()');
509591
}, 'return(); return() [no awaiting]');
510592

511593
test(() => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<body>
3+
<script>
4+
const i = document.createElement("iframe");
5+
document.body.appendChild(i);
6+
7+
const rs = new i.contentWindow.ReadableStream();
8+
i.remove();
9+
10+
// tee() on a ReadableStream from a detached iframe should not crash.
11+
rs.tee();
12+
</script>
13+
</body>

test/fixtures/wpt/versions.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
"path": "resources"
6565
},
6666
"streams": {
67-
"commit": "3df6d94318b225845a0c8e4c7718484f41c9b8ce",
67+
"commit": "9b03282a99ef2314c1c2d5050a105a74a2940019",
6868
"path": "streams"
6969
},
7070
"url": {

0 commit comments

Comments
 (0)