summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2023-04-09 15:21:58 -0700
committerLinus Groh <mail@linusgroh.de>2023-04-10 00:45:03 +0200
commit58f3009faaf99cff532b993fe868225e0ab319f4 (patch)
tree516e0cf02e4113b1fc03d561cf060afdaf3ec5f5 /Userland
parent48b67e41f068262fbe387bfbc7f5f52b398c8008 (diff)
downloadserenity-58f3009faaf99cff532b993fe868225e0ab319f4.zip
LibWeb: Implement WritableStreamDefaultWriter.write()
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibWeb/Streams/AbstractOperations.cpp141
-rw-r--r--Userland/Libraries/LibWeb/Streams/AbstractOperations.h5
-rw-r--r--Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.cpp15
-rw-r--r--Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.h1
-rw-r--r--Userland/Libraries/LibWeb/Streams/WritableStreamDefaultWriter.idl4
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);
};