From ee89ba1919431a91389a5c0e64f1382688890afc Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Sun, 1 Oct 2023 18:22:42 +0530 Subject: [PATCH 1/7] stream: implement TransformStream cleanup using "transformer.cancel" Fixes: https://github.com/nodejs/node/issues/49971 --- lib/internal/webstreams/transformstream.js | 115 +++++++++++++++++++-- 1 file changed, 105 insertions(+), 10 deletions(-) diff --git a/lib/internal/webstreams/transformstream.js b/lib/internal/webstreams/transformstream.js index 4d47856a6a12de..597d38f5d8fed5 100644 --- a/lib/internal/webstreams/transformstream.js +++ b/lib/internal/webstreams/transformstream.js @@ -47,6 +47,7 @@ const { nonOpFlush, kType, kState, + nonOpCancel, } = require('internal/webstreams/util'); const { @@ -384,8 +385,7 @@ function initializeTransformStream( return transformStreamDefaultSourcePullAlgorithm(stream); }, cancel(reason) { - transformStreamErrorWritableAndUnblockWrite(stream, reason); - return PromiseResolve(); + return transformStreamDefaultSourceCancelAlgorithm(stream, reason); }, }, { highWaterMark: readableHighWaterMark, @@ -427,6 +427,10 @@ function transformStreamErrorWritableAndUnblockWrite(stream, error) { writableStreamDefaultControllerErrorIfNeeded( writable[kState].controller, error); + transformStreamUnblockWrite(stream); +} + +function transformStreamUnblockWrite(stream) { if (stream[kState].backpressure) transformStreamSetBackpressure(stream, false); } @@ -443,13 +447,15 @@ function setupTransformStreamDefaultController( stream, controller, transformAlgorithm, - flushAlgorithm) { + flushAlgorithm, + cancelAlgorithm) { assert(isTransformStream(stream)); assert(stream[kState].controller === undefined); controller[kState] = { stream, transformAlgorithm, flushAlgorithm, + cancelAlgorithm, }; stream[kState].controller = controller; } @@ -460,21 +466,26 @@ function setupTransformStreamDefaultControllerFromTransformer( const controller = new TransformStreamDefaultController(kSkipThrow); const transform = transformer?.transform || defaultTransformAlgorithm; const flush = transformer?.flush || nonOpFlush; + const cancel = transformer?.cancel || nonOpCancel; const transformAlgorithm = FunctionPrototypeBind(transform, transformer); const flushAlgorithm = FunctionPrototypeBind(flush, transformer); + const cancelAlgorithm = + FunctionPrototypeBind(cancel, transformer); setupTransformStreamDefaultController( stream, controller, transformAlgorithm, - flushAlgorithm); + flushAlgorithm, + cancelAlgorithm); } function transformStreamDefaultControllerClearAlgorithms(controller) { controller[kState].transformAlgorithm = undefined; controller[kState].flushAlgorithm = undefined; + controller[kState].cancelAlgorithm = undefined; } function transformStreamDefaultControllerEnqueue(controller, chunk) { @@ -563,7 +574,40 @@ function transformStreamDefaultSinkWriteAlgorithm(stream, chunk) { } async function transformStreamDefaultSinkAbortAlgorithm(stream, reason) { - transformStreamError(stream, reason); + const { + controller, + readable, + } = stream[kState]; + + if (controller[kState].finishPromise !== undefined) { + return controller[kState].finishPromise + } + + const { promise, resolve, reject } = createDeferredPromise(); + controller[kState].finishPromise = promise; + const cancelPromise = ensureIsPromise( + controller[kState].cancelAlgorithm, + controller, + reason); + transformStreamDefaultControllerClearAlgorithms(controller); + + PromisePrototypeThen( + cancelPromise, + () => { + if (readable[kState].state === 'errored') + reject(readable[kState].storedError); + else { + readableStreamDefaultControllerError(readable[kState].controller, reason); + resolve(); + } + }, + (error) => { + readableStreamDefaultControllerError(readable[kState].controller, error); + reject(error); + } + ); + + return controller[kState].finishPromise; } function transformStreamDefaultSinkCloseAlgorithm(stream) { @@ -572,23 +616,32 @@ function transformStreamDefaultSinkCloseAlgorithm(stream) { controller, } = stream[kState]; + if (controller[kState].finishPromise !== undefined) { + return controller[kState].finishPromise + } + const { promise, resolve, reject } = createDeferredPromise(); + controller[kState].finishPromise = promise; const flushPromise = ensureIsPromise( controller[kState].flushAlgorithm, controller, controller); transformStreamDefaultControllerClearAlgorithms(controller); - return PromisePrototypeThen( + PromisePrototypeThen( flushPromise, () => { if (readable[kState].state === 'errored') - throw readable[kState].storedError; - readableStreamDefaultControllerClose(readable[kState].controller); + reject(readable[kState].storedError); + else { + readableStreamDefaultControllerClose(readable[kState].controller); + resolve(); + } }, (error) => { - transformStreamError(stream, error); - throw readable[kState].storedError; + readableStreamDefaultControllerError(readable[kState].controller, error); + reject(error); }); + return controller[kState].finishPromise; } function transformStreamDefaultSourcePullAlgorithm(stream) { @@ -598,6 +651,48 @@ function transformStreamDefaultSourcePullAlgorithm(stream) { return stream[kState].backpressureChange.promise; } +function transformStreamDefaultSourceCancelAlgorithm(stream, reason) { + const { + controller, + writable, + } = stream[kState]; + + if (controller[kState].finishPromise !== undefined) { + return controller[kState].finishPromise; + } + + const { promise, resolve, reject } = createDeferredPromise(); + controller[kState].finishPromise = promise; + const cancelPromise = ensureIsPromise( + controller[kState].cancelAlgorithm, + controller, + reason); + transformStreamDefaultControllerClearAlgorithms(controller); + + PromisePrototypeThen(cancelPromise, + () => { + if (writable[kState].state === 'errored') + reject(writable[kState].storedError); + else { + writableStreamDefaultControllerErrorIfNeeded( + writable[kState].controller, + reason); + transformStreamUnblockWrite(stream); + resolve(); + } + }, + (error) => { + writableStreamDefaultControllerErrorIfNeeded( + writable[kState].controller, + error); + transformStreamUnblockWrite(stream); + reject(error); + } + ); + + return controller[kState].finishPromise +} + module.exports = { TransformStream, TransformStreamDefaultController, From 867f64e663498a3d448d4d54f7b3e985d0b9dd69 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Sun, 1 Oct 2023 18:28:33 +0530 Subject: [PATCH 2/7] test: update wpts --- test/fixtures/wpt/README.md | 2 +- test/fixtures/wpt/streams/piping/abort.any.js | 2 +- .../piping/close-propagation-backward.any.js | 2 +- .../piping/close-propagation-forward.any.js | 2 +- .../piping/crashtests/cross-piping.html | 12 ++ .../piping/error-propagation-backward.any.js | 2 +- .../piping/error-propagation-forward.any.js | 2 +- .../wpt/streams/piping/flow-control.any.js | 2 +- .../streams/piping/general-addition.any.js | 2 +- .../wpt/streams/piping/general.any.js | 2 +- .../piping/multiple-propagation.any.js | 2 +- .../wpt/streams/piping/pipe-through.any.js | 2 +- .../streams/piping/then-interception.any.js | 2 +- .../streams/piping/throwing-options.any.js | 2 +- .../streams/piping/transform-streams.any.js | 2 +- .../wpt/streams/queuing-strategies.any.js | 2 +- .../bad-buffers-and-views.any.js | 2 +- .../construct-byob-request.any.js | 2 +- .../enqueue-with-detached-buffer.any.js | 2 +- .../readable-byte-streams/general.any.js | 2 +- .../non-transferable-buffers.any.js | 2 +- .../respond-after-enqueue.any.js | 2 +- .../streams/readable-byte-streams/tee.any.js | 2 +- .../readable-streams/async-iterator.any.js | 2 +- .../readable-streams/bad-strategies.any.js | 43 ++++++- .../bad-underlying-sources.any.js | 2 +- .../streams/readable-streams/cancel.any.js | 2 +- .../readable-streams/constructor.any.js | 2 +- .../count-queuing-strategy-integration.any.js | 2 +- .../readable-streams/default-reader.any.js | 2 +- .../floating-point-total-queue-size.any.js | 2 +- .../wpt/streams/readable-streams/from.any.js | 2 +- .../garbage-collection.any.js | 2 +- .../streams/readable-streams/general.any.js | 2 +- .../owning-type-message-port.any.js | 2 +- .../owning-type-video-frame.any.js | 2 +- .../readable-streams/owning-type.any.js | 2 +- .../readable-streams/patched-global.any.js | 2 +- .../reentrant-strategies.any.js | 2 +- .../wpt/streams/readable-streams/tee.any.js | 2 +- .../streams/readable-streams/templated.any.js | 2 +- .../transform-stream-members.any.js | 2 + .../transform-streams/backpressure.any.js | 2 +- .../streams/transform-streams/cancel.any.js | 115 ++++++++++++++++++ .../streams/transform-streams/errors.any.js | 45 +++++-- .../streams/transform-streams/flush.any.js | 17 ++- .../streams/transform-streams/general.any.js | 21 +++- .../streams/transform-streams/lipfuzz.any.js | 2 +- .../transform-streams/patched-global.any.js | 2 +- .../transform-streams/properties.any.js | 2 +- .../reentrant-strategies.any.js | 6 +- .../transform-streams/strategies.any.js | 2 +- .../transform-streams/terminate.any.js | 2 +- .../streams/writable-streams/aborting.any.js | 2 +- .../writable-streams/bad-strategies.any.js | 2 +- .../bad-underlying-sinks.any.js | 2 +- .../byte-length-queuing-strategy.any.js | 2 +- .../wpt/streams/writable-streams/close.any.js | 2 +- .../writable-streams/constructor.any.js | 2 +- .../count-queuing-strategy.any.js | 2 +- .../wpt/streams/writable-streams/error.any.js | 2 +- .../floating-point-total-queue-size.any.js | 2 +- .../streams/writable-streams/general.any.js | 2 +- .../writable-streams/properties.any.js | 2 +- .../reentrant-strategy.any.js | 2 +- .../wpt/streams/writable-streams/start.any.js | 2 +- .../wpt/streams/writable-streams/write.any.js | 2 +- test/fixtures/wpt/versions.json | 2 +- 68 files changed, 301 insertions(+), 80 deletions(-) create mode 100644 test/fixtures/wpt/streams/piping/crashtests/cross-piping.html create mode 100644 test/fixtures/wpt/streams/transform-streams/cancel.any.js diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 26dd520f8f3eed..6d635668f81a1c 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -27,7 +27,7 @@ Last update: - performance-timeline: https://github.com/web-platform-tests/wpt/tree/17ebc3aea0/performance-timeline - resource-timing: https://github.com/web-platform-tests/wpt/tree/22d38586d0/resource-timing - resources: https://github.com/web-platform-tests/wpt/tree/1e140d63ec/resources -- streams: https://github.com/web-platform-tests/wpt/tree/517e945bbf/streams +- streams: https://github.com/web-platform-tests/wpt/tree/a8872d92b1/streams - url: https://github.com/web-platform-tests/wpt/tree/c2d7e70b52/url - user-timing: https://github.com/web-platform-tests/wpt/tree/5ae85bf826/user-timing - wasm/jsapi: https://github.com/web-platform-tests/wpt/tree/cde25e7e3c/wasm/jsapi diff --git a/test/fixtures/wpt/streams/piping/abort.any.js b/test/fixtures/wpt/streams/piping/abort.any.js index 503de9dcaf0893..c9929df91cf688 100644 --- a/test/fixtures/wpt/streams/piping/abort.any.js +++ b/test/fixtures/wpt/streams/piping/abort.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/test-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/close-propagation-backward.any.js b/test/fixtures/wpt/streams/piping/close-propagation-backward.any.js index 5ea47ab85c0c1f..25bd475ed13e77 100644 --- a/test/fixtures/wpt/streams/piping/close-propagation-backward.any.js +++ b/test/fixtures/wpt/streams/piping/close-propagation-backward.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/close-propagation-forward.any.js b/test/fixtures/wpt/streams/piping/close-propagation-forward.any.js index 71b6e262840090..0ec94f80abf04b 100644 --- a/test/fixtures/wpt/streams/piping/close-propagation-forward.any.js +++ b/test/fixtures/wpt/streams/piping/close-propagation-forward.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/crashtests/cross-piping.html b/test/fixtures/wpt/streams/piping/crashtests/cross-piping.html new file mode 100644 index 00000000000000..712d5ecebef8ff --- /dev/null +++ b/test/fixtures/wpt/streams/piping/crashtests/cross-piping.html @@ -0,0 +1,12 @@ + + diff --git a/test/fixtures/wpt/streams/piping/error-propagation-backward.any.js b/test/fixtures/wpt/streams/piping/error-propagation-backward.any.js index ec74592f86effe..f786469d6c1d40 100644 --- a/test/fixtures/wpt/streams/piping/error-propagation-backward.any.js +++ b/test/fixtures/wpt/streams/piping/error-propagation-backward.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/error-propagation-forward.any.js b/test/fixtures/wpt/streams/piping/error-propagation-forward.any.js index 482da2f8a88e18..e9260f9ea22c41 100644 --- a/test/fixtures/wpt/streams/piping/error-propagation-forward.any.js +++ b/test/fixtures/wpt/streams/piping/error-propagation-forward.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/flow-control.any.js b/test/fixtures/wpt/streams/piping/flow-control.any.js index 09c4420f872adc..e2318da375ab90 100644 --- a/test/fixtures/wpt/streams/piping/flow-control.any.js +++ b/test/fixtures/wpt/streams/piping/flow-control.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js // META: script=../resources/recording-streams.js diff --git a/test/fixtures/wpt/streams/piping/general-addition.any.js b/test/fixtures/wpt/streams/piping/general-addition.any.js index 2562b7064338ee..cf4aa9bea6a1a4 100644 --- a/test/fixtures/wpt/streams/piping/general-addition.any.js +++ b/test/fixtures/wpt/streams/piping/general-addition.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; promise_test(async t => { diff --git a/test/fixtures/wpt/streams/piping/general.any.js b/test/fixtures/wpt/streams/piping/general.any.js index bec3480f653944..09e01536325cca 100644 --- a/test/fixtures/wpt/streams/piping/general.any.js +++ b/test/fixtures/wpt/streams/piping/general.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/multiple-propagation.any.js b/test/fixtures/wpt/streams/piping/multiple-propagation.any.js index a78652fc06795e..9be828a23269a5 100644 --- a/test/fixtures/wpt/streams/piping/multiple-propagation.any.js +++ b/test/fixtures/wpt/streams/piping/multiple-propagation.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/pipe-through.any.js b/test/fixtures/wpt/streams/piping/pipe-through.any.js index 26b1cd26a3c82f..339cee19993346 100644 --- a/test/fixtures/wpt/streams/piping/pipe-through.any.js +++ b/test/fixtures/wpt/streams/piping/pipe-through.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js diff --git a/test/fixtures/wpt/streams/piping/then-interception.any.js b/test/fixtures/wpt/streams/piping/then-interception.any.js index 543f916d940d9a..fc48c368311478 100644 --- a/test/fixtures/wpt/streams/piping/then-interception.any.js +++ b/test/fixtures/wpt/streams/piping/then-interception.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/piping/throwing-options.any.js b/test/fixtures/wpt/streams/piping/throwing-options.any.js index b9f906778f632b..186f8ded1968a4 100644 --- a/test/fixtures/wpt/streams/piping/throwing-options.any.js +++ b/test/fixtures/wpt/streams/piping/throwing-options.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; class ThrowingOptions { diff --git a/test/fixtures/wpt/streams/piping/transform-streams.any.js b/test/fixtures/wpt/streams/piping/transform-streams.any.js index caae9fbad8848a..e079bb637cad0d 100644 --- a/test/fixtures/wpt/streams/piping/transform-streams.any.js +++ b/test/fixtures/wpt/streams/piping/transform-streams.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; promise_test(() => { diff --git a/test/fixtures/wpt/streams/queuing-strategies.any.js b/test/fixtures/wpt/streams/queuing-strategies.any.js index fa959ebba28338..9efc4570cf2ee2 100644 --- a/test/fixtures/wpt/streams/queuing-strategies.any.js +++ b/test/fixtures/wpt/streams/queuing-strategies.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const highWaterMarkConversions = new Map([ diff --git a/test/fixtures/wpt/streams/readable-byte-streams/bad-buffers-and-views.any.js b/test/fixtures/wpt/streams/readable-byte-streams/bad-buffers-and-views.any.js index 3322116b191840..afcc61e6800a28 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/bad-buffers-and-views.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/bad-buffers-and-views.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; promise_test(() => { diff --git a/test/fixtures/wpt/streams/readable-byte-streams/construct-byob-request.any.js b/test/fixtures/wpt/streams/readable-byte-streams/construct-byob-request.any.js index 8d460a1c81b7bd..a26f949ee29d94 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/construct-byob-request.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/construct-byob-request.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js b/test/fixtures/wpt/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js index d2b37f00a9d697..92bd0a26a0ec05 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/enqueue-with-detached-buffer.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm promise_test(async t => { const error = new Error('cannot proceed'); diff --git a/test/fixtures/wpt/streams/readable-byte-streams/general.any.js b/test/fixtures/wpt/streams/readable-byte-streams/general.any.js index dd4fdc855786f2..cdce2244c3c84b 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/general.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/general.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-byte-streams/non-transferable-buffers.any.js b/test/fixtures/wpt/streams/readable-byte-streams/non-transferable-buffers.any.js index e8ea3c4f966763..47d7b2e653e5ec 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/non-transferable-buffers.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/non-transferable-buffers.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; promise_test(async t => { diff --git a/test/fixtures/wpt/streams/readable-byte-streams/respond-after-enqueue.any.js b/test/fixtures/wpt/streams/readable-byte-streams/respond-after-enqueue.any.js index b93cec97391e13..e51efa061a6511 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/respond-after-enqueue.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/respond-after-enqueue.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js b/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js index 85844669cd9088..7dd5ba3f3fb013 100644 --- a/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js +++ b/test/fixtures/wpt/streams/readable-byte-streams/tee.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js diff --git a/test/fixtures/wpt/streams/readable-streams/async-iterator.any.js b/test/fixtures/wpt/streams/readable-streams/async-iterator.any.js index 3ccaca17bc1ac7..4b674bea8430f3 100644 --- a/test/fixtures/wpt/streams/readable-streams/async-iterator.any.js +++ b/test/fixtures/wpt/streams/readable-streams/async-iterator.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js diff --git a/test/fixtures/wpt/streams/readable-streams/bad-strategies.any.js b/test/fixtures/wpt/streams/readable-streams/bad-strategies.any.js index 521fbffe3ab479..49fa4bdbece5b5 100644 --- a/test/fixtures/wpt/streams/readable-streams/bad-strategies.any.js +++ b/test/fixtures/wpt/streams/readable-streams/bad-strategies.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; test(() => { @@ -149,7 +149,9 @@ promise_test(() => { } ); - promises.push(rs.getReader().closed.catch(e => { + promises.push(rs.getReader().closed.then(() => { + assert_unreached('closed didn\'t throw'); + }, e => { assert_equals(e, theError, 'closed should reject with the error for ' + size); })); } @@ -157,3 +159,40 @@ promise_test(() => { return Promise.all(promises); }, 'Readable stream: invalid strategy.size return value'); + +promise_test(() => { + + const promises = []; + for (const size of [NaN, -Infinity, Infinity, -1]) { + let theError; + const rs = new ReadableStream( + { + pull(c) { + try { + c.enqueue('hi'); + assert_unreached('enqueue didn\'t throw'); + } catch (error) { + assert_equals(error.name, 'RangeError', 'enqueue should throw a RangeError for ' + size); + theError = error; + } + } + }, + { + size() { + return size; + }, + highWaterMark: 5 + } + ); + + promises.push(rs.getReader().closed.then(() => { + assert_unreached('closed didn\'t throw'); + }, e => { + assert_equals(e, theError, 'closed should reject with the error for ' + size); + })); + } + + return Promise.all(promises); + +}, 'Readable stream: invalid strategy.size return value when pulling'); + diff --git a/test/fixtures/wpt/streams/readable-streams/bad-underlying-sources.any.js b/test/fixtures/wpt/streams/readable-streams/bad-underlying-sources.any.js index e9cf4c924930b2..3d77b923d178a0 100644 --- a/test/fixtures/wpt/streams/readable-streams/bad-underlying-sources.any.js +++ b/test/fixtures/wpt/streams/readable-streams/bad-underlying-sources.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/cancel.any.js b/test/fixtures/wpt/streams/readable-streams/cancel.any.js index 800bd994417241..9915c1fb6330c1 100644 --- a/test/fixtures/wpt/streams/readable-streams/cancel.any.js +++ b/test/fixtures/wpt/streams/readable-streams/cancel.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/constructor.any.js b/test/fixtures/wpt/streams/readable-streams/constructor.any.js index 608dc48cfa39d7..0b995f0cb16bde 100644 --- a/test/fixtures/wpt/streams/readable-streams/constructor.any.js +++ b/test/fixtures/wpt/streams/readable-streams/constructor.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const error1 = new Error('error1'); diff --git a/test/fixtures/wpt/streams/readable-streams/count-queuing-strategy-integration.any.js b/test/fixtures/wpt/streams/readable-streams/count-queuing-strategy-integration.any.js index 02ac5bae5c2f8a..a8c1b91d0068e3 100644 --- a/test/fixtures/wpt/streams/readable-streams/count-queuing-strategy-integration.any.js +++ b/test/fixtures/wpt/streams/readable-streams/count-queuing-strategy-integration.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; test(() => { diff --git a/test/fixtures/wpt/streams/readable-streams/default-reader.any.js b/test/fixtures/wpt/streams/readable-streams/default-reader.any.js index 59d7ab2f74db63..f92862719e47c1 100644 --- a/test/fixtures/wpt/streams/readable-streams/default-reader.any.js +++ b/test/fixtures/wpt/streams/readable-streams/default-reader.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/floating-point-total-queue-size.any.js b/test/fixtures/wpt/streams/readable-streams/floating-point-total-queue-size.any.js index 50cca3d951a942..8b88c21d7f0b53 100644 --- a/test/fixtures/wpt/streams/readable-streams/floating-point-total-queue-size.any.js +++ b/test/fixtures/wpt/streams/readable-streams/floating-point-total-queue-size.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; // Due to the limitations of floating-point precision, the calculation of desiredSize sometimes gives different answers diff --git a/test/fixtures/wpt/streams/readable-streams/from.any.js b/test/fixtures/wpt/streams/readable-streams/from.any.js index 04a03545ad5fe3..58ad4d4add127d 100644 --- a/test/fixtures/wpt/streams/readable-streams/from.any.js +++ b/test/fixtures/wpt/streams/readable-streams/from.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker,jsshell +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js b/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js index e578176777adaf..13bd1fb3437877 100644 --- a/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js +++ b/test/fixtures/wpt/streams/readable-streams/garbage-collection.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=/common/gc.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/general.any.js b/test/fixtures/wpt/streams/readable-streams/general.any.js index 2a32b27943c82f..eee3f62215eeb0 100644 --- a/test/fixtures/wpt/streams/readable-streams/general.any.js +++ b/test/fixtures/wpt/streams/readable-streams/general.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/owning-type-message-port.any.js b/test/fixtures/wpt/streams/readable-streams/owning-type-message-port.any.js index e9961ce042256a..282c1f411485c0 100644 --- a/test/fixtures/wpt/streams/readable-streams/owning-type-message-port.any.js +++ b/test/fixtures/wpt/streams/readable-streams/owning-type-message-port.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js b/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js index ec01fda0b3c737..b652f9c5fcb4b6 100644 --- a/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js +++ b/test/fixtures/wpt/streams/readable-streams/owning-type-video-frame.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/owning-type.any.js b/test/fixtures/wpt/streams/readable-streams/owning-type.any.js index 27a3dda894e4d6..34c2a55d5134a2 100644 --- a/test/fixtures/wpt/streams/readable-streams/owning-type.any.js +++ b/test/fixtures/wpt/streams/readable-streams/owning-type.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/readable-streams/patched-global.any.js b/test/fixtures/wpt/streams/readable-streams/patched-global.any.js index a64a054a97f1f5..c208824c864469 100644 --- a/test/fixtures/wpt/streams/readable-streams/patched-global.any.js +++ b/test/fixtures/wpt/streams/readable-streams/patched-global.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; // Tests which patch the global environment are kept separate to avoid diff --git a/test/fixtures/wpt/streams/readable-streams/reentrant-strategies.any.js b/test/fixtures/wpt/streams/readable-streams/reentrant-strategies.any.js index b4988bc2433fd5..8ae7b98e8d5eef 100644 --- a/test/fixtures/wpt/streams/readable-streams/reentrant-strategies.any.js +++ b/test/fixtures/wpt/streams/readable-streams/reentrant-strategies.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js diff --git a/test/fixtures/wpt/streams/readable-streams/tee.any.js b/test/fixtures/wpt/streams/readable-streams/tee.any.js index 00397932f4b6e3..c2c2e482307211 100644 --- a/test/fixtures/wpt/streams/readable-streams/tee.any.js +++ b/test/fixtures/wpt/streams/readable-streams/tee.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js diff --git a/test/fixtures/wpt/streams/readable-streams/templated.any.js b/test/fixtures/wpt/streams/readable-streams/templated.any.js index ecae3f4d8b129f..dc75b805a10d63 100644 --- a/test/fixtures/wpt/streams/readable-streams/templated.any.js +++ b/test/fixtures/wpt/streams/readable-streams/templated.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-test-templates.js 'use strict'; diff --git a/test/fixtures/wpt/streams/transferable/transform-stream-members.any.js b/test/fixtures/wpt/streams/transferable/transform-stream-members.any.js index fca060b0c05fa3..05914e12ccbe57 100644 --- a/test/fixtures/wpt/streams/transferable/transform-stream-members.any.js +++ b/test/fixtures/wpt/streams/transferable/transform-stream-members.any.js @@ -1,3 +1,5 @@ +// META: global=window,dedicatedworker,shadowrealm + const combinations = [ (t => [t, t.readable])(new TransformStream()), (t => [t.readable, t])(new TransformStream()), diff --git a/test/fixtures/wpt/streams/transform-streams/backpressure.any.js b/test/fixtures/wpt/streams/transform-streams/backpressure.any.js index 6befba41b79542..47a21fb7e71fed 100644 --- a/test/fixtures/wpt/streams/transform-streams/backpressure.any.js +++ b/test/fixtures/wpt/streams/transform-streams/backpressure.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/test-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/transform-streams/cancel.any.js b/test/fixtures/wpt/streams/transform-streams/cancel.any.js new file mode 100644 index 00000000000000..5c7fc4eae5d55b --- /dev/null +++ b/test/fixtures/wpt/streams/transform-streams/cancel.any.js @@ -0,0 +1,115 @@ +// META: global=window,worker,shadowrealm +// META: script=../resources/test-utils.js +'use strict'; + +const thrownError = new Error('bad things are happening!'); +thrownError.name = 'error1'; + +const originalReason = new Error('original reason'); +originalReason.name = 'error2'; + +promise_test(async t => { + let cancelled = undefined; + const ts = new TransformStream({ + cancel(reason) { + cancelled = reason; + } + }); + const res = await ts.readable.cancel(thrownError); + assert_equals(res, undefined, 'readable.cancel() should return undefined'); + assert_equals(cancelled, thrownError, 'transformer.cancel() should be called with the passed reason'); +}, 'cancelling the readable side should call transformer.cancel()'); + +promise_test(async t => { + const ts = new TransformStream({ + cancel(reason) { + assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason'); + throw thrownError; + } + }); + const writer = ts.writable.getWriter(); + const cancelPromise = ts.readable.cancel(originalReason); + await promise_rejects_exactly(t, thrownError, cancelPromise, 'readable.cancel() should reject with thrownError'); + await promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject with thrownError'); +}, 'cancelling the readable side should reject if transformer.cancel() throws'); + +promise_test(async t => { + let aborted = undefined; + const ts = new TransformStream({ + cancel(reason) { + aborted = reason; + }, + flush: t.unreached_func('flush should not be called') + }); + const res = await ts.writable.abort(thrownError); + assert_equals(res, undefined, 'writable.abort() should return undefined'); + assert_equals(aborted, thrownError, 'transformer.abort() should be called with the passed reason'); +}, 'aborting the writable side should call transformer.abort()'); + +promise_test(async t => { + const ts = new TransformStream({ + cancel(reason) { + assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason'); + throw thrownError; + }, + flush: t.unreached_func('flush should not be called') + }); + const reader = ts.readable.getReader(); + const abortPromise = ts.writable.abort(originalReason); + await promise_rejects_exactly(t, thrownError, abortPromise, 'writable.abort() should reject with thrownError'); + await promise_rejects_exactly(t, thrownError, reader.closed, 'reader.closed should reject with thrownError'); +}, 'aborting the writable side should reject if transformer.cancel() throws'); + +promise_test(async t => { + const ts = new TransformStream({ + async cancel(reason) { + assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason'); + throw thrownError; + }, + flush: t.unreached_func('flush should not be called') + }); + const cancelPromise = ts.readable.cancel(originalReason); + const closePromise = ts.writable.close(); + await Promise.all([ + promise_rejects_exactly(t, thrownError, cancelPromise, 'cancelPromise should reject with thrownError'), + promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError'), + ]); +}, 'closing the writable side should reject if a parallel transformer.cancel() throws'); + +promise_test(async t => { + let controller; + const ts = new TransformStream({ + start(c) { + controller = c; + }, + async cancel(reason) { + assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason'); + controller.error(thrownError); + }, + flush: t.unreached_func('flush should not be called') + }); + const cancelPromise = ts.readable.cancel(originalReason); + const closePromise = ts.writable.close(); + await Promise.all([ + promise_rejects_exactly(t, thrownError, cancelPromise, 'cancelPromise should reject with thrownError'), + promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError'), + ]); +}, 'readable.cancel() and a parallel writable.close() should reject if a transformer.cancel() calls controller.error()'); + +promise_test(async t => { + let controller; + const ts = new TransformStream({ + start(c) { + controller = c; + }, + async cancel(reason) { + assert_equals(reason, originalReason, 'transformer.cancel() should be called with the passed reason'); + controller.error(thrownError); + }, + flush: t.unreached_func('flush should not be called') + }); + const cancelPromise = ts.writable.abort(originalReason); + await promise_rejects_exactly(t, thrownError, cancelPromise, 'cancelPromise should reject with thrownError'); + const closePromise = ts.readable.cancel(1); + await promise_rejects_exactly(t, thrownError, closePromise, 'closePromise should reject with thrownError'); +}, 'writable.abort() and readable.cancel() should reject if a transformer.cancel() calls controller.error()'); diff --git a/test/fixtures/wpt/streams/transform-streams/errors.any.js b/test/fixtures/wpt/streams/transform-streams/errors.any.js index 0cca4c75479d6d..bea060b6590818 100644 --- a/test/fixtures/wpt/streams/transform-streams/errors.any.js +++ b/test/fixtures/wpt/streams/transform-streams/errors.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js 'use strict'; @@ -9,7 +9,8 @@ promise_test(t => { const ts = new TransformStream({ transform() { throw thrownError; - } + }, + cancel: t.unreached_func('cancel should not be called') }); const reader = ts.readable.getReader(); @@ -34,7 +35,8 @@ promise_test(t => { }, flush() { throw thrownError; - } + }, + cancel: t.unreached_func('cancel should not be called') }); const reader = ts.readable.getReader(); @@ -54,13 +56,14 @@ promise_test(t => { ]); }, 'TransformStream errors thrown in flush put the writable and readable in an errored state'); -test(() => { +test(t => { new TransformStream({ start(c) { c.enqueue('a'); c.error(new Error('generic error')); assert_throws_js(TypeError, () => c.enqueue('b'), 'enqueue() should throw'); - } + }, + cancel: t.unreached_func('cancel should not be called') }); }, 'errored TransformStream should not enqueue new chunks'); @@ -72,7 +75,8 @@ promise_test(t => { }); }, transform: t.unreached_func('transform should not be called'), - flush: t.unreached_func('flush should not be called') + flush: t.unreached_func('flush should not be called'), + cancel: t.unreached_func('cancel should not be called') }); const writer = ts.writable.getWriter(); @@ -96,7 +100,8 @@ promise_test(t => { }); }, transform: t.unreached_func('transform should never be called if start() fails'), - flush: t.unreached_func('flush should never be called if start() fails') + flush: t.unreached_func('flush should never be called if start() fails'), + cancel: t.unreached_func('cancel should never be called if start() fails') }); const writer = ts.writable.getWriter(); @@ -202,9 +207,10 @@ promise_test(t => { return Promise.all([ abortPromise, cancelPromise, - promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject with thrownError')]); -}, 'abort should set the close reason for the writable when it happens before cancel during start, but cancel should ' + - 'still succeed'); + promise_rejects_exactly(t, thrownError, writer.closed, 'writer.closed should reject'), + ]); +}, 'abort should set the close reason for the writable when it happens before cancel during start, and cancel should ' + + 'reject'); promise_test(t => { let resolveTransform; @@ -251,13 +257,26 @@ promise_test(t => { controller = c; } }); - const cancelPromise = ts.readable.cancel(thrownError); - controller.error(ignoredError); + const cancelPromise = ts.readable.cancel(ignoredError); + controller.error(thrownError); return Promise.all([ cancelPromise, promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError') ]); -}, 'controller.error() should do nothing after readable.cancel()'); +}, 'controller.error() should close writable immediately after readable.cancel()'); + +promise_test(t => { + let controller; + const ts = new TransformStream({ + start(c) { + controller = c; + } + }); + return ts.readable.cancel(thrownError).then(() => { + controller.error(ignoredError); + return promise_rejects_exactly(t, thrownError, ts.writable.getWriter().closed, 'closed should reject with thrownError'); + }); +}, 'controller.error() should do nothing after readable.cancel() resolves'); promise_test(t => { let controller; diff --git a/test/fixtures/wpt/streams/transform-streams/flush.any.js b/test/fixtures/wpt/streams/transform-streams/flush.any.js index 9287f6f5eb78eb..c95d8ae1186518 100644 --- a/test/fixtures/wpt/streams/transform-streams/flush.any.js +++ b/test/fixtures/wpt/streams/transform-streams/flush.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js 'use strict'; @@ -129,3 +129,18 @@ promise_test(t => { }); return promise_rejects_exactly(t, error1, ts.writable.getWriter().close(), 'close() should reject'); }, 'error() during flush should cause writer.close() to reject'); + +promise_test(async t => { + let flushed = false; + const ts = new TransformStream({ + flush() { + flushed = true; + }, + cancel: t.unreached_func('cancel should not be called') + }); + const closePromise = ts.writable.close(); + await delay(0); + const cancelPromise = ts.readable.cancel(error1); + await Promise.all([closePromise, cancelPromise]); + assert_equals(flushed, true, 'transformer.flush() should be called'); +}, 'closing the writable side should call transformer.flush() and a parallel readable.cancel() should not reject'); diff --git a/test/fixtures/wpt/streams/transform-streams/general.any.js b/test/fixtures/wpt/streams/transform-streams/general.any.js index c95691f7bf49df..a40ef30843ec4d 100644 --- a/test/fixtures/wpt/streams/transform-streams/general.any.js +++ b/test/fixtures/wpt/streams/transform-streams/general.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/rs-utils.js 'use strict'; @@ -388,9 +388,24 @@ promise_test(t => { controller.terminate(); return Promise.all([ cancelPromise, - promise_rejects_exactly(t, cancelReason, ts.writable.getWriter().closed, 'closed should reject with cancelReason') + promise_rejects_js(t, TypeError, ts.writable.getWriter().closed, 'closed should reject with TypeError') ]); -}, 'terminate() should do nothing after readable.cancel()'); +}, 'terminate() should abort writable immediately after readable.cancel()'); + +promise_test(t => { + let controller; + const ts = new TransformStream({ + start(c) { + controller = c; + } + }); + const cancelReason = { name: 'cancelReason' }; + return ts.readable.cancel(cancelReason).then(() => { + controller.terminate(); + return promise_rejects_exactly(t, cancelReason, ts.writable.getWriter().closed, 'closed should reject with TypeError'); + }) +}, 'terminate() should do nothing after readable.cancel() resolves'); + promise_test(() => { let calls = 0; diff --git a/test/fixtures/wpt/streams/transform-streams/lipfuzz.any.js b/test/fixtures/wpt/streams/transform-streams/lipfuzz.any.js index f9f148aaf1c6a4..e334705db44354 100644 --- a/test/fixtures/wpt/streams/transform-streams/lipfuzz.any.js +++ b/test/fixtures/wpt/streams/transform-streams/lipfuzz.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; class LipFuzzTransformer { diff --git a/test/fixtures/wpt/streams/transform-streams/patched-global.any.js b/test/fixtures/wpt/streams/transform-streams/patched-global.any.js index 2d04e3b948b324..cc111eda452f0f 100644 --- a/test/fixtures/wpt/streams/transform-streams/patched-global.any.js +++ b/test/fixtures/wpt/streams/transform-streams/patched-global.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; // Tests which patch the global environment are kept separate to avoid diff --git a/test/fixtures/wpt/streams/transform-streams/properties.any.js b/test/fixtures/wpt/streams/transform-streams/properties.any.js index 02981b8bc76a5f..dbfd1aa372b907 100644 --- a/test/fixtures/wpt/streams/transform-streams/properties.any.js +++ b/test/fixtures/wpt/streams/transform-streams/properties.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const transformerMethods = { diff --git a/test/fixtures/wpt/streams/transform-streams/reentrant-strategies.any.js b/test/fixtures/wpt/streams/transform-streams/reentrant-strategies.any.js index fc2f91886659f6..a6d459655856a7 100644 --- a/test/fixtures/wpt/streams/transform-streams/reentrant-strategies.any.js +++ b/test/fixtures/wpt/streams/transform-streams/reentrant-strategies.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/rs-utils.js // META: script=../resources/test-utils.js @@ -314,6 +314,10 @@ promise_test(t => { // call to TransformStreamDefaultSink. return delay(0).then(() => { controller.enqueue('a'); + return reader.read(); + }).then(({ value, done }) => { + assert_false(done, 'done should be false'); + assert_equals(value, 'a', 'value should be correct'); return Promise.all([promise_rejects_exactly(t, error1, reader.read(), 'read() should reject'), abortPromise]); }); }, 'writer.abort() inside size() should work'); diff --git a/test/fixtures/wpt/streams/transform-streams/strategies.any.js b/test/fixtures/wpt/streams/transform-streams/strategies.any.js index 94055ad99dc94b..57e113e668ce24 100644 --- a/test/fixtures/wpt/streams/transform-streams/strategies.any.js +++ b/test/fixtures/wpt/streams/transform-streams/strategies.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/test-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/transform-streams/terminate.any.js b/test/fixtures/wpt/streams/transform-streams/terminate.any.js index 670006366db2af..a959e70fe69339 100644 --- a/test/fixtures/wpt/streams/transform-streams/terminate.any.js +++ b/test/fixtures/wpt/streams/transform-streams/terminate.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/recording-streams.js // META: script=../resources/test-utils.js 'use strict'; diff --git a/test/fixtures/wpt/streams/writable-streams/aborting.any.js b/test/fixtures/wpt/streams/writable-streams/aborting.any.js index e016cd191b876f..9171dbe158f00c 100644 --- a/test/fixtures/wpt/streams/writable-streams/aborting.any.js +++ b/test/fixtures/wpt/streams/writable-streams/aborting.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/writable-streams/bad-strategies.any.js b/test/fixtures/wpt/streams/writable-streams/bad-strategies.any.js index 63fa443065ee41..a1ef0791168f99 100644 --- a/test/fixtures/wpt/streams/writable-streams/bad-strategies.any.js +++ b/test/fixtures/wpt/streams/writable-streams/bad-strategies.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const error1 = new Error('a unique string'); diff --git a/test/fixtures/wpt/streams/writable-streams/bad-underlying-sinks.any.js b/test/fixtures/wpt/streams/writable-streams/bad-underlying-sinks.any.js index d0b3467978ea05..3c434ffe60ccb9 100644 --- a/test/fixtures/wpt/streams/writable-streams/bad-underlying-sinks.any.js +++ b/test/fixtures/wpt/streams/writable-streams/bad-underlying-sinks.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/writable-streams/byte-length-queuing-strategy.any.js b/test/fixtures/wpt/streams/writable-streams/byte-length-queuing-strategy.any.js index ce1962e8917f32..eed86ee7004ea1 100644 --- a/test/fixtures/wpt/streams/writable-streams/byte-length-queuing-strategy.any.js +++ b/test/fixtures/wpt/streams/writable-streams/byte-length-queuing-strategy.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; promise_test(t => { diff --git a/test/fixtures/wpt/streams/writable-streams/close.any.js b/test/fixtures/wpt/streams/writable-streams/close.any.js index 88855a92efd550..45261e7ca7e01d 100644 --- a/test/fixtures/wpt/streams/writable-streams/close.any.js +++ b/test/fixtures/wpt/streams/writable-streams/close.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/writable-streams/constructor.any.js b/test/fixtures/wpt/streams/writable-streams/constructor.any.js index eaac90e48b8f86..0abc7ef545ea0b 100644 --- a/test/fixtures/wpt/streams/writable-streams/constructor.any.js +++ b/test/fixtures/wpt/streams/writable-streams/constructor.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const error1 = new Error('error1'); diff --git a/test/fixtures/wpt/streams/writable-streams/count-queuing-strategy.any.js b/test/fixtures/wpt/streams/writable-streams/count-queuing-strategy.any.js index 064e16e81506f1..8211757530d323 100644 --- a/test/fixtures/wpt/streams/writable-streams/count-queuing-strategy.any.js +++ b/test/fixtures/wpt/streams/writable-streams/count-queuing-strategy.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; test(() => { diff --git a/test/fixtures/wpt/streams/writable-streams/error.any.js b/test/fixtures/wpt/streams/writable-streams/error.any.js index faf3fdd9521430..d08c8a54863bb9 100644 --- a/test/fixtures/wpt/streams/writable-streams/error.any.js +++ b/test/fixtures/wpt/streams/writable-streams/error.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const error1 = new Error('error1'); diff --git a/test/fixtures/wpt/streams/writable-streams/floating-point-total-queue-size.any.js b/test/fixtures/wpt/streams/writable-streams/floating-point-total-queue-size.any.js index bd34cc53a69579..20a14fc19a51e0 100644 --- a/test/fixtures/wpt/streams/writable-streams/floating-point-total-queue-size.any.js +++ b/test/fixtures/wpt/streams/writable-streams/floating-point-total-queue-size.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; // Due to the limitations of floating-point precision, the calculation of desiredSize sometimes gives different answers diff --git a/test/fixtures/wpt/streams/writable-streams/general.any.js b/test/fixtures/wpt/streams/writable-streams/general.any.js index cede7fd0845b74..48f8eeb89e41ee 100644 --- a/test/fixtures/wpt/streams/writable-streams/general.any.js +++ b/test/fixtures/wpt/streams/writable-streams/general.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; test(() => { diff --git a/test/fixtures/wpt/streams/writable-streams/properties.any.js b/test/fixtures/wpt/streams/writable-streams/properties.any.js index c95bd7d0c080ba..ae0549f0871b48 100644 --- a/test/fixtures/wpt/streams/writable-streams/properties.any.js +++ b/test/fixtures/wpt/streams/writable-streams/properties.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm 'use strict'; const sinkMethods = { diff --git a/test/fixtures/wpt/streams/writable-streams/reentrant-strategy.any.js b/test/fixtures/wpt/streams/writable-streams/reentrant-strategy.any.js index eb05cc068043ea..18ce9e84759a11 100644 --- a/test/fixtures/wpt/streams/writable-streams/reentrant-strategy.any.js +++ b/test/fixtures/wpt/streams/writable-streams/reentrant-strategy.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/writable-streams/start.any.js b/test/fixtures/wpt/streams/writable-streams/start.any.js index 82d869430dd700..17972b568ceb24 100644 --- a/test/fixtures/wpt/streams/writable-streams/start.any.js +++ b/test/fixtures/wpt/streams/writable-streams/start.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/streams/writable-streams/write.any.js b/test/fixtures/wpt/streams/writable-streams/write.any.js index f0246f6cad39fd..20a2885bf3512a 100644 --- a/test/fixtures/wpt/streams/writable-streams/write.any.js +++ b/test/fixtures/wpt/streams/writable-streams/write.any.js @@ -1,4 +1,4 @@ -// META: global=window,worker +// META: global=window,worker,shadowrealm // META: script=../resources/test-utils.js // META: script=../resources/recording-streams.js 'use strict'; diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index 8ec93059e8ea69..c9ad58bfce7d86 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -68,7 +68,7 @@ "path": "resources" }, "streams": { - "commit": "517e945bbfaf903f37a35c11700eb96662efbdd3", + "commit": "a8872d92b147fc87200eb0c14fe7a4a9e7cd4f73", "path": "streams" }, "url": { From 02205e782ce904068af96cdcc972de5599ff41e2 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Sun, 26 Nov 2023 20:03:18 +0530 Subject: [PATCH 3/7] fixup! replace promise resolve --- lib/internal/webstreams/readablestream.js | 5 +++-- lib/internal/webstreams/writablestream.js | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/internal/webstreams/readablestream.js b/lib/internal/webstreams/readablestream.js index a5f95596e3082a..a3b22afd2f2ea0 100644 --- a/lib/internal/webstreams/readablestream.js +++ b/lib/internal/webstreams/readablestream.js @@ -14,6 +14,7 @@ const { ObjectCreate, ObjectDefineProperties, ObjectSetPrototypeOf, + Promise, PromisePrototypeThen, PromiseResolve, PromiseReject, @@ -2440,7 +2441,7 @@ function setupReadableStreamDefaultController( const startResult = startAlgorithm(); PromisePrototypeThen( - PromiseResolve(startResult), + new Promise((r) => r(startResult)), () => { controller[kState].started = true; assert(!controller[kState].pulling); @@ -3239,7 +3240,7 @@ function setupReadableByteStreamController( const startResult = startAlgorithm(); PromisePrototypeThen( - PromiseResolve(startResult), + new Promise((r) => r(startResult)), () => { controller[kState].started = true; assert(!controller[kState].pulling); diff --git a/lib/internal/webstreams/writablestream.js b/lib/internal/webstreams/writablestream.js index 2115aba36e927b..6dd7bc65566db6 100644 --- a/lib/internal/webstreams/writablestream.js +++ b/lib/internal/webstreams/writablestream.js @@ -7,6 +7,7 @@ const { FunctionPrototypeCall, ObjectDefineProperties, ObjectSetPrototypeOf, + Promise, PromisePrototypeThen, PromiseResolve, PromiseReject, @@ -1295,7 +1296,7 @@ function setupWritableStreamDefaultController( const startResult = startAlgorithm(); PromisePrototypeThen( - PromiseResolve(startResult), + new Promise((r) => r(startResult)), () => { assert(stream[kState].state === 'writable' || stream[kState].state === 'erroring'); From 67b094424b762bdd1a617192b08636631504ad54 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Sun, 26 Nov 2023 20:04:55 +0530 Subject: [PATCH 4/7] fixup! useless import --- lib/internal/webstreams/transformstream.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/internal/webstreams/transformstream.js b/lib/internal/webstreams/transformstream.js index 597d38f5d8fed5..bd6a2dbad1e3dc 100644 --- a/lib/internal/webstreams/transformstream.js +++ b/lib/internal/webstreams/transformstream.js @@ -5,8 +5,6 @@ const { FunctionPrototypeCall, ObjectDefineProperties, ObjectSetPrototypeOf, - PromisePrototypeThen, - PromiseResolve, SymbolToStringTag, Symbol, } = primordials; From c627596c6237996e279a67f918a2356c0d8f2e77 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Wed, 29 Nov 2023 11:34:40 +0530 Subject: [PATCH 5/7] fixup! wrong import remove --- lib/internal/webstreams/transformstream.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/webstreams/transformstream.js b/lib/internal/webstreams/transformstream.js index bd6a2dbad1e3dc..516a6ac0116862 100644 --- a/lib/internal/webstreams/transformstream.js +++ b/lib/internal/webstreams/transformstream.js @@ -5,6 +5,7 @@ const { FunctionPrototypeCall, ObjectDefineProperties, ObjectSetPrototypeOf, + PromisePrototypeThen, SymbolToStringTag, Symbol, } = primordials; From 23e6183f2b732fad2e8ab1d930289b8772fc0c18 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Tue, 5 Dec 2023 16:31:19 +0530 Subject: [PATCH 6/7] expect a failure --- test/wpt/status/streams.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/wpt/status/streams.json b/test/wpt/status/streams.json index fb0064a810503b..51303c0582943e 100644 --- a/test/wpt/status/streams.json +++ b/test/wpt/status/streams.json @@ -64,5 +64,12 @@ }, "readable-streams/read-task-handling.window.js": { "skip": "Browser-specific test" + }, + "transform-streams/cancel.any.js": { + "fail": { + "expected": [ + "readable.cancel() and a parallel writable.close() should reject if a transformer.cancel() calls controller.error()" + ] + } } } From 2557e8523dc4d89f6a487ae55639861243589b10 Mon Sep 17 00:00:00 2001 From: Debadree Chatterjee Date: Tue, 5 Dec 2023 16:42:34 +0530 Subject: [PATCH 7/7] linting --- lib/internal/webstreams/transformstream.js | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/internal/webstreams/transformstream.js b/lib/internal/webstreams/transformstream.js index 516a6ac0116862..3dc85b79a8273e 100644 --- a/lib/internal/webstreams/transformstream.js +++ b/lib/internal/webstreams/transformstream.js @@ -579,7 +579,7 @@ async function transformStreamDefaultSinkAbortAlgorithm(stream, reason) { } = stream[kState]; if (controller[kState].finishPromise !== undefined) { - return controller[kState].finishPromise + return controller[kState].finishPromise; } const { promise, resolve, reject } = createDeferredPromise(); @@ -603,7 +603,7 @@ async function transformStreamDefaultSinkAbortAlgorithm(stream, reason) { (error) => { readableStreamDefaultControllerError(readable[kState].controller, error); reject(error); - } + }, ); return controller[kState].finishPromise; @@ -616,7 +616,7 @@ function transformStreamDefaultSinkCloseAlgorithm(stream) { } = stream[kState]; if (controller[kState].finishPromise !== undefined) { - return controller[kState].finishPromise + return controller[kState].finishPromise; } const { promise, resolve, reject } = createDeferredPromise(); controller[kState].finishPromise = promise; @@ -669,27 +669,27 @@ function transformStreamDefaultSourceCancelAlgorithm(stream, reason) { transformStreamDefaultControllerClearAlgorithms(controller); PromisePrototypeThen(cancelPromise, - () => { - if (writable[kState].state === 'errored') - reject(writable[kState].storedError); - else { - writableStreamDefaultControllerErrorIfNeeded( - writable[kState].controller, - reason); - transformStreamUnblockWrite(stream); - resolve(); - } - }, - (error) => { - writableStreamDefaultControllerErrorIfNeeded( - writable[kState].controller, - error); - transformStreamUnblockWrite(stream); - reject(error); - } + () => { + if (writable[kState].state === 'errored') + reject(writable[kState].storedError); + else { + writableStreamDefaultControllerErrorIfNeeded( + writable[kState].controller, + reason); + transformStreamUnblockWrite(stream); + resolve(); + } + }, + (error) => { + writableStreamDefaultControllerErrorIfNeeded( + writable[kState].controller, + error); + transformStreamUnblockWrite(stream); + reject(error); + }, ); - return controller[kState].finishPromise + return controller[kState].finishPromise; } module.exports = {