Skip to content

Commit

Permalink
ReadableStream.pipeTo should reuse AbortSignal reason when needed
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=248591
rdar://problem/102849934

Reviewed by Alex Christensen.

Makr the AbortAlgorithm take the abort reason.
Use this value in ReadableStream code.
Covered by rebased tests.

* LayoutTests/imported/w3c/web-platform-tests/streams/piping/abort.any-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/streams/piping/abort.any.js:
(Object.create):
(promise_test.t.string_appeared_here.then):
(async promise_test):
(const.reason.of.null.promise_test.async t): Deleted.
* LayoutTests/imported/w3c/web-platform-tests/streams/piping/abort.any.serviceworker-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/streams/piping/abort.any.sharedworker-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/streams/piping/abort.any.worker-expected.txt:
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/streams/piping/abort.any-expected.txt:
* Source/WebCore/Modules/streams/ReadableStreamInternals.js:
(readableStreamPipeToWritableStream):
(readableStreamReaderGenericRelease):
* Source/WebCore/dom/AbortAlgorithm.h:
* Source/WebCore/dom/AbortAlgorithm.idl:
* Source/WebCore/dom/AbortSignal.cpp:
(WebCore::AbortSignal::addAbortAlgorithmToSignal):

Canonical link: https://commits.webkit.org/257464@main
  • Loading branch information
youennf committed Dec 7, 2022
1 parent 0a22679 commit 2ce995e
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 47 deletions.
Expand Up @@ -7,18 +7,18 @@ PASS a signal argument 'true' should cause pipeTo() to reject
PASS a signal argument '-1' should cause pipeTo() to reject
PASS a signal argument '[object AbortSignal]' should cause pipeTo() to reject
PASS an aborted signal should cause the writable stream to reject with an AbortError
FAIL (reason: 'null') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all the error objects should be the same object assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') all the error objects should be the same object
PASS (reason: 'undefined') all the error objects should be the same object
PASS (reason: 'error1: error1') all the error objects should be the same object
PASS preventCancel should prevent canceling the readable
PASS preventAbort should prevent aborting the readable
PASS preventCancel and preventAbort should prevent canceling the readable and aborting the readable
FAIL (reason: 'null') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') abort should prevent further reads assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
FAIL (reason: 'null') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all pending writes should complete on abort assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') abort should prevent further reads
PASS (reason: 'undefined') abort should prevent further reads
PASS (reason: 'error1: error1') abort should prevent further reads
PASS (reason: 'null') all pending writes should complete on abort
PASS (reason: 'undefined') all pending writes should complete on abort
PASS (reason: 'error1: error1') all pending writes should complete on abort
PASS a rejection from underlyingSource.cancel() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be preferred to one from underlyingSource.cancel()
Expand Down
Expand Up @@ -7,18 +7,18 @@ PASS a signal argument 'true' should cause pipeTo() to reject
PASS a signal argument '-1' should cause pipeTo() to reject
PASS a signal argument '[object AbortSignal]' should cause pipeTo() to reject
PASS an aborted signal should cause the writable stream to reject with an AbortError
FAIL (reason: 'null') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all the error objects should be the same object assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') all the error objects should be the same object
PASS (reason: 'undefined') all the error objects should be the same object
PASS (reason: 'error1: error1') all the error objects should be the same object
PASS preventCancel should prevent canceling the readable
PASS preventAbort should prevent aborting the readable
PASS preventCancel and preventAbort should prevent canceling the readable and aborting the readable
FAIL (reason: 'null') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') abort should prevent further reads assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
FAIL (reason: 'null') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all pending writes should complete on abort assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') abort should prevent further reads
PASS (reason: 'undefined') abort should prevent further reads
PASS (reason: 'error1: error1') abort should prevent further reads
PASS (reason: 'null') all pending writes should complete on abort
PASS (reason: 'undefined') all pending writes should complete on abort
PASS (reason: 'error1: error1') all pending writes should complete on abort
PASS a rejection from underlyingSource.cancel() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be preferred to one from underlyingSource.cancel()
Expand Down
Expand Up @@ -7,18 +7,18 @@ PASS a signal argument 'true' should cause pipeTo() to reject
PASS a signal argument '-1' should cause pipeTo() to reject
PASS a signal argument '[object AbortSignal]' should cause pipeTo() to reject
PASS an aborted signal should cause the writable stream to reject with an AbortError
FAIL (reason: 'null') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all the error objects should be the same object assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') all the error objects should be the same object
PASS (reason: 'undefined') all the error objects should be the same object
PASS (reason: 'error1: error1') all the error objects should be the same object
PASS preventCancel should prevent canceling the readable
PASS preventAbort should prevent aborting the readable
PASS preventCancel and preventAbort should prevent canceling the readable and aborting the readable
FAIL (reason: 'null') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') abort should prevent further reads assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
FAIL (reason: 'null') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all pending writes should complete on abort assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') abort should prevent further reads
PASS (reason: 'undefined') abort should prevent further reads
PASS (reason: 'error1: error1') abort should prevent further reads
PASS (reason: 'null') all pending writes should complete on abort
PASS (reason: 'undefined') all pending writes should complete on abort
PASS (reason: 'error1: error1') all pending writes should complete on abort
PASS a rejection from underlyingSource.cancel() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be preferred to one from underlyingSource.cancel()
Expand Down
Expand Up @@ -10,18 +10,18 @@ PASS a signal argument 'true' should cause pipeTo() to reject
PASS a signal argument '-1' should cause pipeTo() to reject
PASS a signal argument '[object AbortSignal]' should cause pipeTo() to reject
PASS an aborted signal should cause the writable stream to reject with an AbortError
FAIL (reason: 'null') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all the error objects should be the same object assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all the error objects should be the same object promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') all the error objects should be the same object
PASS (reason: 'undefined') all the error objects should be the same object
PASS (reason: 'error1: error1') all the error objects should be the same object
PASS preventCancel should prevent canceling the readable
PASS preventAbort should prevent aborting the readable
PASS preventCancel and preventAbort should prevent canceling the readable and aborting the readable
FAIL (reason: 'null') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') abort should prevent further reads assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') abort should prevent further reads promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
FAIL (reason: 'null') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw null
FAIL (reason: 'undefined') all pending writes should complete on abort assert_equals: signal.reason should be error expected object "AbortError: abort pipeTo from signal" but got object "AbortError: The operation was aborted."
FAIL (reason: 'error1: error1') all pending writes should complete on abort promise_rejects_exactly: pipeTo rejects with abort reason function "function () { throw e }" threw object "AbortError: abort pipeTo from signal" but we expected it to throw object "error1: error1"
PASS (reason: 'null') abort should prevent further reads
PASS (reason: 'undefined') abort should prevent further reads
PASS (reason: 'error1: error1') abort should prevent further reads
PASS (reason: 'null') all pending writes should complete on abort
PASS (reason: 'undefined') all pending writes should complete on abort
PASS (reason: 'error1: error1') all pending writes should complete on abort
PASS a rejection from underlyingSource.cancel() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be returned by pipeTo()
PASS a rejection from underlyingSink.abort() should be preferred to one from underlyingSource.cancel()
Expand Down
10 changes: 4 additions & 6 deletions Source/WebCore/Modules/streams/ReadableStreamInternals.js
Expand Up @@ -163,15 +163,13 @@ function readableStreamPipeToWritableStream(source, destination, preventClose, p
pipeState.pendingWritePromise = @Promise.@resolve();

if (signal !== @undefined) {
const algorithm = () => {
const error = @makeDOMException("AbortError", "abort pipeTo from signal");

const algorithm = (reason) => {
@pipeToShutdownWithAction(pipeState, () => {
const shouldAbortDestination = !pipeState.preventAbort && @getByIdDirectPrivate(pipeState.destination, "state") === "writable";
const promiseDestination = shouldAbortDestination ? @writableStreamAbort(pipeState.destination, error) : @Promise.@resolve();
const promiseDestination = shouldAbortDestination ? @writableStreamAbort(pipeState.destination, reason) : @Promise.@resolve();

const shouldAbortSource = !pipeState.preventCancel && @getByIdDirectPrivate(pipeState.source, "state") === @streamReadable;
const promiseSource = shouldAbortSource ? @readableStreamCancel(pipeState.source, error) : @Promise.@resolve();
const promiseSource = shouldAbortSource ? @readableStreamCancel(pipeState.source, reason) : @Promise.@resolve();

let promiseCapability = @newPromiseCapability(@Promise);
let shouldWait = true;
Expand All @@ -188,7 +186,7 @@ function readableStreamPipeToWritableStream(source, destination, preventClose, p
promiseDestination.@then(handleResolvedPromise, handleRejectedPromise);
promiseSource.@then(handleResolvedPromise, handleRejectedPromise);
return promiseCapability.@promise;
}, error);
}, reason);
};
pipeState.abortAlgorithmIdentifier = @addAbortAlgorithmToSignal(signal, algorithm)
if (!pipeState.abortAlgorithmIdentifier)
Expand Down
6 changes: 5 additions & 1 deletion Source/WebCore/dom/AbortAlgorithm.h
Expand Up @@ -29,13 +29,17 @@
#include "CallbackResult.h"
#include <wtf/ThreadSafeRefCounted.h>

namespace JSC {
class JSValue;
} // namespace JSC

namespace WebCore {

class AbortAlgorithm : public ThreadSafeRefCounted<AbortAlgorithm>, public ActiveDOMCallback {
public:
using ActiveDOMCallback::ActiveDOMCallback;

virtual CallbackResult<void> handleEvent() = 0;
virtual CallbackResult<void> handleEvent(JSC::JSValue) = 0;
};

} // namespace WebCore
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/dom/AbortAlgorithm.idl
Expand Up @@ -23,4 +23,4 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/

callback AbortAlgorithm = undefined ();
callback AbortAlgorithm = undefined (any value);
6 changes: 3 additions & 3 deletions Source/WebCore/dom/AbortSignal.cpp
Expand Up @@ -137,11 +137,11 @@ void AbortSignal::eventListenersDidChange()
uint32_t AbortSignal::addAbortAlgorithmToSignal(AbortSignal& signal, Ref<AbortAlgorithm>&& algorithm)
{
if (signal.aborted()) {
algorithm->handleEvent();
algorithm->handleEvent(signal.m_reason.getValue());
return 0;
}
return signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue) mutable {
algorithm->handleEvent();
return signal.addAlgorithm([algorithm = WTFMove(algorithm)](JSC::JSValue value) mutable {
algorithm->handleEvent(value);
});
}

Expand Down

0 comments on commit 2ce995e

Please sign in to comment.