diff options
author | Matthew Olsson <matthewcolsson@gmail.com> | 2023-04-09 15:21:58 -0700 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2023-04-10 00:45:03 +0200 |
commit | 58f3009faaf99cff532b993fe868225e0ab319f4 (patch) | |
tree | 516e0cf02e4113b1fc03d561cf060afdaf3ec5f5 /Userland | |
parent | 48b67e41f068262fbe387bfbc7f5f52b398c8008 (diff) | |
download | serenity-58f3009faaf99cff532b993fe868225e0ab319f4.zip |
LibWeb: Implement WritableStreamDefaultWriter.write()
Diffstat (limited to 'Userland')
5 files changed, 163 insertions, 3 deletions
diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp index 5e7f424f90..e45c93091c 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp @@ -934,6 +934,28 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_close(Wri return promise; } +// https://streams.spec.whatwg.org/#writable-stream-add-write-request +WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_add_write_request(WritableStream& stream) +{ + auto& realm = stream.realm(); + auto& vm = stream.vm(); + + // 1. Assert: ! IsWritableStreamLocked(stream) is true. + VERIFY(is_writable_stream_locked(stream)); + + // 2. Assert: stream.[[state]] is "writable". + VERIFY(stream.state() == WritableStream::State::Writable); + + // 3. Let promise be a new promise. + auto promise = WebIDL::create_promise(realm); + + // 4. Append promise to stream.[[writeRequests]]. + TRY_OR_THROW_OOM(vm, stream.write_requests().try_append(promise)); + + // 5. Return promise. + return promise; +} + // https://streams.spec.whatwg.org/#writable-stream-close-queued-or-in-flight bool writable_stream_close_queued_or_in_flight(WritableStream const& stream) { @@ -1423,6 +1445,59 @@ WebIDL::ExceptionOr<void> writable_stream_default_writer_release(WritableStreamD return {}; } +// https://streams.spec.whatwg.org/#writable-stream-default-writer-write +WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_default_writer_write(WritableStreamDefaultWriter& writer, JS::Value chunk) +{ + auto& realm = writer.realm(); + + // 1. Let stream be writer.[[stream]]. + auto stream = writer.stream(); + + // 2. Assert: stream is not undefined. + VERIFY(stream); + + // 3. Let controller be stream.[[controller]]. + auto controller = stream->controller(); + + // 4. Let chunkSize be ! WritableStreamDefaultControllerGetChunkSize(controller, chunk). + auto chunk_size = TRY(writable_stream_default_controller_get_chunk_size(*controller, chunk)); + + // 5. If stream is not equal to writer.[[stream]], return a promise rejected with a TypeError exception. + if (stream.ptr() != writer.stream().ptr()) { + auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Writer's locked stream changed during write"sv)); + return WebIDL::create_rejected_promise(realm, exception); + } + + // 6. Let state be stream.[[state]]. + auto state = stream->state(); + + // 7. If state is "errored", return a promise rejected with stream.[[storedError]]. + if (state == WritableStream::State::Errored) + return WebIDL::create_rejected_promise(realm, stream->stored_error()); + + // 8. If ! WritableStreamCloseQueuedOrInFlight(stream) is true or state is "closed", return a promise rejected with a TypeError exception indicating that the stream is closing or closed. + if (writable_stream_close_queued_or_in_flight(*stream) || state == WritableStream::State::Closed) { + auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot write to a writer whose stream is closing or already closed"sv)); + return WebIDL::create_rejected_promise(realm, exception); + } + + // 9. If state is "erroring", return a promise rejected with stream.[[storedError]]. + if (state == WritableStream::State::Erroring) + return WebIDL::create_rejected_promise(realm, stream->stored_error()); + + // 10. Assert: state is "writable". + VERIFY(state == WritableStream::State::Writable); + + // 11. Let promise be ! WritableStreamAddWriteRequest(stream). + auto promise = writable_stream_add_write_request(*stream); + + // 12. Perform ! WritableStreamDefaultControllerWrite(controller, chunk, chunkSize). + TRY(writable_stream_default_controller_write(*controller, chunk, chunk_size)); + + // 13. Return promise. + return promise; +} + // https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed WebIDL::ExceptionOr<void> writable_stream_default_controller_advance_queue_if_needed(WritableStreamDefaultController& controller) { @@ -1513,6 +1588,16 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_error(WritableStrea return writable_stream_start_erroring(stream, error); } +// https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed +WebIDL::ExceptionOr<void> writable_stream_default_controller_error_if_needed(WritableStreamDefaultController& controller, JS::Value error) +{ + // 1. If controller.[[stream]].[[state]] is "writable", perform ! WritableStreamDefaultControllerError(controller, error). + if (controller.stream()->state() == WritableStream::State::Writable) + TRY(writable_stream_default_controller_error(controller, error)); + + return {}; +} + // https://streams.spec.whatwg.org/#writable-stream-default-controller-get-backpressure bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultController const& controller) { @@ -1523,6 +1608,25 @@ bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultCo return desired_size <= 0.0; } +// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-chunk-size +WebIDL::ExceptionOr<JS::Value> writable_stream_default_controller_get_chunk_size(WritableStreamDefaultController& controller, JS::Value chunk) +{ + // 1. Let returnValue be the result of performing controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting the result as a completion record. + auto return_value = (*controller.strategy_size_algorithm())(chunk); + + // 2. If returnValue is an abrupt completion, + if (return_value.is_abrupt()) { + // 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, returnValue.[[Value]]). + TRY(writable_stream_default_controller_error_if_needed(controller, *return_value.release_value())); + + // 2. Return 1. + return 1.0; + } + + // 3. Return returnValue.[[Value]]. + return *return_value.release_value(); +} + // https://streams.spec.whatwg.org/#writable-stream-default-controller-get-desired-size double writable_stream_default_controller_get_desired_size(WritableStreamDefaultController const& controller) { @@ -1626,6 +1730,43 @@ WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(Writa return {}; } +// https://streams.spec.whatwg.org/#writable-stream-default-controller-write +WebIDL::ExceptionOr<void> writable_stream_default_controller_write(WritableStreamDefaultController& controller, JS::Value chunk, JS::Value chunk_size) +{ + auto& vm = controller.vm(); + + // 1. Let enqueueResult be EnqueueValueWithSize(controller, chunk, chunkSize). + auto enqueue_result = enqueue_value_with_size(controller, chunk, chunk_size); + + // 2. If enqueueResult is an abrupt completion, + if (enqueue_result.is_exception()) { + auto throw_completion = Bindings::throw_dom_exception_if_needed(vm, [&] { return enqueue_result; }).throw_completion(); + + // 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueResult.[[Value]]). + TRY(writable_stream_default_controller_error_if_needed(controller, *throw_completion.release_value())); + + // 2. Return. + return {}; + } + + // 3. Let stream be controller.[[stream]]. + auto stream = controller.stream(); + + // 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and stream.[[state]] is "writable", + if (!writable_stream_close_queued_or_in_flight(*stream) && stream->state() == WritableStream::State::Writable) { + // 1. Let backpressure be ! WritableStreamDefaultControllerGetBackpressure(controller). + auto backpressure = writable_stream_default_controller_get_backpressure(controller); + + // 2. Perform ! WritableStreamUpdateBackpressure(stream, backpressure). + writable_stream_update_backpressure(*stream, backpressure); + } + + // 5. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller). + TRY(writable_stream_default_controller_advance_queue_if_needed(controller)); + + return {}; +} + // https://streams.spec.whatwg.org/#is-non-negative-number bool is_non_negative_number(JS::Value value) { diff --git a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h index c10fdca560..5b4a29dddf 100644 --- a/Userland/Libraries/LibWeb/Streams/AbstractOperations.h +++ b/Userland/Libraries/LibWeb/Streams/AbstractOperations.h @@ -59,6 +59,7 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_writer(WritableStreamDe WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_abort(WritableStream&, JS::Value reason); WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_close(WritableStream&); +WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_add_write_request(WritableStream&); bool writable_stream_close_queued_or_in_flight(WritableStream const&); WebIDL::ExceptionOr<void> writable_stream_deal_with_rejection(WritableStream&, JS::Value error); WebIDL::ExceptionOr<void> writable_stream_finish_erroring(WritableStream&); @@ -79,15 +80,19 @@ void writable_stream_default_writer_ensure_closed_promise_rejected(WritableStrea void writable_stream_default_writer_ensure_ready_promise_rejected(WritableStreamDefaultWriter&, JS::Value error); Optional<double> writable_stream_default_writer_get_desired_size(WritableStreamDefaultWriter const&); WebIDL::ExceptionOr<void> writable_stream_default_writer_release(WritableStreamDefaultWriter&); +WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_default_writer_write(WritableStreamDefaultWriter&, JS::Value chunk); WebIDL::ExceptionOr<void> writable_stream_default_controller_advance_queue_if_needed(WritableStreamDefaultController&); void writable_stream_default_controller_clear_algorithms(WritableStreamDefaultController&); WebIDL::ExceptionOr<void> writable_stream_default_controller_close(WritableStreamDefaultController&); WebIDL::ExceptionOr<void> writable_stream_default_controller_error(WritableStreamDefaultController&, JS::Value error); +WebIDL::ExceptionOr<void> writable_stream_default_controller_error_if_needed(WritableStreamDefaultController&, JS::Value error); bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultController const&); +WebIDL::ExceptionOr<JS::Value> writable_stream_default_controller_get_chunk_size(WritableStreamDefaultController&, JS::Value chunk); double writable_stream_default_controller_get_desired_size(WritableStreamDefaultController const&); WebIDL::ExceptionOr<void> writable_stream_default_controller_process_close(WritableStreamDefaultController&); WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(WritableStreamDefaultController&, JS::Value chunk); +WebIDL::ExceptionOr<void> writable_stream_default_controller_write(WritableStreamDefaultController&, JS::Value chunk, JS::Value chunk_size); bool is_non_negative_number(JS::Value); diff --git a/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.cpp b/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.cpp index ae98f43037..60c31ccabf 100644 --- a/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.cpp +++ b/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.cpp @@ -103,6 +103,21 @@ WebIDL::ExceptionOr<void> WritableStreamDefaultWriter::release_lock() return writable_stream_default_writer_release(*this); } +// https://streams.spec.whatwg.org/#default-writer-write +WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> WritableStreamDefaultWriter::write(JS::Value chunk) +{ + auto& realm = this->realm(); + + // 1. If this.[[stream]] is undefined, return a promise rejected with a TypeError exception. + if (!m_stream) { + auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot write to a writer that has no locked stream"sv)); + return WebIDL::create_rejected_promise(realm, exception)->promise(); + } + + // 2. Return ! WritableStreamDefaultWriterWrite(this, chunk). + return TRY(writable_stream_default_writer_write(*this, chunk))->promise(); +} + WritableStreamDefaultWriter::WritableStreamDefaultWriter(JS::Realm& realm) : Bindings::PlatformObject(realm) { diff --git a/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.h b/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.h index dbd725051b..4abda8cec9 100644 --- a/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.h +++ b/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.h @@ -30,6 +30,7 @@ public: WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> abort(JS::Value reason); WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> close(); WebIDL::ExceptionOr<void> release_lock(); + WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> write(JS::Value chunk); JS::GCPtr<WebIDL::Promise> closed_promise() { return m_closed_promise; } void set_closed_promise(JS::GCPtr<WebIDL::Promise> value) { m_closed_promise = value; } diff --git a/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.idl b/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.idl index 25092d4070..fd8a32e2f9 100644 --- a/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.idl +++ b/Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.idl @@ -11,7 +11,5 @@ interface WritableStreamDefaultWriter { Promise<undefined> abort(optional any reason); Promise<undefined> close(); undefined releaseLock(); - - // FIXME: - // Promise<undefined> write(optional any chunk); + Promise<undefined> write(optional any chunk); }; |