From c421b6113c3f5e7cef3013e77a59da8db999f2c9 Mon Sep 17 00:00:00 2001 From: Matthew Olsson Date: Sun, 2 Apr 2023 08:27:37 -0700 Subject: [PATCH] LibWeb: Implement WritableStream.abort() --- .../LibWeb/Streams/AbstractOperations.cpp | 54 +++++++++++++++++++ .../LibWeb/Streams/AbstractOperations.h | 1 + .../LibWeb/Streams/WritableStream.cpp | 15 ++++++ .../Libraries/LibWeb/Streams/WritableStream.h | 1 + .../LibWeb/Streams/WritableStream.idl | 4 +- 5 files changed, 72 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp index e9bd5bca9a7..2fb167a3bc8 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -839,6 +840,59 @@ WebIDL::ExceptionOr set_up_writable_stream_default_writer(WritableStreamDe return {}; } +// https://streams.spec.whatwg.org/#writable-stream-abort +WebIDL::ExceptionOr> writable_stream_abort(WritableStream& stream, JS::Value reason) +{ + auto& realm = stream.realm(); + + // 1. If stream.[[state]] is "closed" or "errored", return a promise resolved with undefined. + auto state = stream.state(); + if (state == WritableStream::State::Closed || state == WritableStream::State::Errored) + return WebIDL::create_resolved_promise(realm, JS::js_undefined()); + + // 2. Signal abort on stream.[[controller]].[[signal]] with reason. + stream.controller()->signal()->signal_abort(reason); + + // 3. Let state be stream.[[state]]. + state = stream.state(); + + // 4. If state is "closed" or "errored", return a promise resolved with undefined. + if (state == WritableStream::State::Closed || state == WritableStream::State::Errored) + return WebIDL::create_resolved_promise(realm, JS::js_undefined()); + + // 5. If stream.[[pendingAbortRequest]] is not undefined, return stream.[[pendingAbortRequest]]'s promise. + if (stream.pending_abort_request().has_value()) + return stream.pending_abort_request()->promise; + + // 6. Assert: state is "writable" or "erroring". + VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring); + + // 7. Let wasAlreadyErroring be false. + auto was_already_erroring = false; + + // 8. If state is "erroring", + if (state == WritableStream::State::Erroring) { + // 1. Set wasAlreadyErroring to true. + was_already_erroring = true; + + // 2. Set reason to undefined. + reason = JS::js_undefined(); + } + + // 9. Let promise be a new promise. + auto promise = WebIDL::create_promise(realm); + + // 10. Set stream.[[pendingAbortRequest]] to a new pending abort request whose promise is promise, reason is reason, and was already erroring is wasAlreadyErroring. + stream.set_pending_abort_request(PendingAbortRequest { promise, reason, was_already_erroring }); + + // 11. If wasAlreadyErroring is false, perform ! WritableStreamStartErroring(stream, reason). + if (!was_already_erroring) + TRY(writable_stream_start_erroring(stream, reason)); + + // 12. Return promise. + return promise; +} + // https://streams.spec.whatwg.org/#writable-stream-close WebIDL::ExceptionOr> writable_stream_close(WritableStream& stream) { diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h index a1153d28c83..d9a97f65cc3 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h @@ -56,6 +56,7 @@ WebIDL::ExceptionOr set_up_readable_stream_default_controller_from_underly WebIDL::ExceptionOr> acquire_writable_stream_default_writer(WritableStream&); bool is_writable_stream_locked(WritableStream const&); WebIDL::ExceptionOr set_up_writable_stream_default_writer(WritableStreamDefaultWriter&, WritableStream&); +WebIDL::ExceptionOr> writable_stream_abort(WritableStream&, JS::Value reason); WebIDL::ExceptionOr> writable_stream_close(WritableStream&); bool writable_stream_close_queued_or_in_flight(WritableStream const&); diff --git a/Userland/Libraries/LibWeb/Streams/WritableStream.cpp b/Userland/Libraries/LibWeb/Streams/WritableStream.cpp index 342f5d6abfe..54ab3dbcd3b 100644 --- a/Userland/Libraries/LibWeb/Streams/WritableStream.cpp +++ b/Userland/Libraries/LibWeb/Streams/WritableStream.cpp @@ -76,6 +76,21 @@ WebIDL::ExceptionOr> WritableStream::close() return TRY(writable_stream_close(*this))->promise(); } +// https://streams.spec.whatwg.org/#ws-abort +WebIDL::ExceptionOr> WritableStream::abort(JS::Value reason) +{ + auto& realm = this->realm(); + + // 1. If ! IsWritableStreamLocked(this) is true, return a promise rejected with a TypeError exception. + if (is_writable_stream_locked(*this)) { + auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot abort a locked stream"sv)); + return WebIDL::create_rejected_promise(realm, exception)->promise(); + } + + // 2. Return ! WritableStreamAbort(this, reason). + return TRY(writable_stream_abort(*this, reason))->promise(); +} + // https://streams.spec.whatwg.org/#ws-get-writer WebIDL::ExceptionOr> WritableStream::get_writer() { diff --git a/Userland/Libraries/LibWeb/Streams/WritableStream.h b/Userland/Libraries/LibWeb/Streams/WritableStream.h index 4f25672ffa0..9e02e9a3824 100644 --- a/Userland/Libraries/LibWeb/Streams/WritableStream.h +++ b/Userland/Libraries/LibWeb/Streams/WritableStream.h @@ -47,6 +47,7 @@ public: virtual ~WritableStream() = default; bool locked() const; + WebIDL::ExceptionOr> abort(JS::Value reason); WebIDL::ExceptionOr> close(); WebIDL::ExceptionOr> get_writer(); diff --git a/Userland/Libraries/LibWeb/Streams/WritableStream.idl b/Userland/Libraries/LibWeb/Streams/WritableStream.idl index 35bb0913a04..19977cfb853 100644 --- a/Userland/Libraries/LibWeb/Streams/WritableStream.idl +++ b/Userland/Libraries/LibWeb/Streams/WritableStream.idl @@ -7,9 +7,7 @@ interface WritableStream { readonly attribute boolean locked; - // FIXME: - // Promise abort(optional any reason); - + Promise abort(optional any reason); Promise close(); WritableStreamDefaultWriter getWriter(); };