mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-06 19:19:44 +03:00
LibWeb: Implement WritableStream.close()
This commit is contained in:
parent
d4e48db1e1
commit
5faa0014f2
Notes:
sideshowbarker
2024-07-17 04:49:48 +09:00
Author: https://github.com/mattco98 Commit: https://github.com/SerenityOS/serenity/commit/5faa0014f2 Pull-request: https://github.com/SerenityOS/serenity/pull/18263 Reviewed-by: https://github.com/linusg
@ -824,6 +824,47 @@ WebIDL::ExceptionOr<void> set_up_writable_stream_default_writer(WritableStreamDe
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-close
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_close(WritableStream& stream)
|
||||
{
|
||||
auto& realm = stream.realm();
|
||||
|
||||
// 1. Let state be stream.[[state]].
|
||||
auto state = stream.state();
|
||||
|
||||
// 2. If state is "closed" or "errored", return a promise rejected with a TypeError exception.
|
||||
if (state == WritableStream::State::Closed || state == WritableStream::State::Errored) {
|
||||
auto message = state == WritableStream::State::Closed ? "Cannot close a closed stream"sv : "Cannot close an errored stream"sv;
|
||||
auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, message));
|
||||
return WebIDL::create_rejected_promise(realm, exception);
|
||||
}
|
||||
|
||||
// 3. Assert: state is "writable" or "erroring".
|
||||
VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
|
||||
|
||||
// 4. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
|
||||
VERIFY(!writable_stream_close_queued_or_in_flight(stream));
|
||||
|
||||
// 5. Let promise be a new promise.
|
||||
auto promise = WebIDL::create_promise(realm);
|
||||
|
||||
// 6. Set stream.[[closeRequest]] to promise.
|
||||
stream.set_close_request(promise);
|
||||
|
||||
// 7. Let writer be stream.[[writer]].
|
||||
auto writer = stream.writer();
|
||||
|
||||
// 8. If writer is not undefined, and stream.[[backpressure]] is true, and state is "writable", resolve writer.[[readyPromise]] with undefined.
|
||||
if (writer && stream.backpressure() && state == WritableStream::State::Writable)
|
||||
WebIDL::resolve_promise(realm, *writer->ready_promise(), JS::js_undefined());
|
||||
|
||||
// 9. Perform ! WritableStreamDefaultControllerClose(stream.[[controller]]).
|
||||
TRY(writable_stream_default_controller_close(*stream.controller()));
|
||||
|
||||
// 10. 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)
|
||||
{
|
||||
@ -835,6 +876,26 @@ bool writable_stream_close_queued_or_in_flight(WritableStream const& stream)
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection
|
||||
WebIDL::ExceptionOr<void> writable_stream_deal_with_rejection(WritableStream& stream, JS::Value error)
|
||||
{
|
||||
// 1. Let state be stream.[[state]].
|
||||
auto state = stream.state();
|
||||
|
||||
// 2. If state is "writable",
|
||||
if (state == WritableStream::State::Writable) {
|
||||
// 1. Perform ! WritableStreamStartErroring(stream, error).
|
||||
// 2. Return.
|
||||
return writable_stream_start_erroring(stream, error);
|
||||
}
|
||||
|
||||
// 3. Assert: state is "erroring".
|
||||
VERIFY(state == WritableStream::State::Erroring);
|
||||
|
||||
// 4. Perform ! WritableStreamFinishErroring(stream).
|
||||
return writable_stream_finish_erroring(stream);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-finish-erroring
|
||||
WebIDL::ExceptionOr<void> writable_stream_finish_erroring(WritableStream& stream)
|
||||
{
|
||||
@ -917,6 +978,122 @@ WebIDL::ExceptionOr<void> writable_stream_finish_erroring(WritableStream& stream
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close
|
||||
void writable_stream_finish_in_flight_close(WritableStream& stream)
|
||||
{
|
||||
auto& realm = stream.realm();
|
||||
|
||||
// 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
|
||||
VERIFY(stream.in_flight_close_request());
|
||||
|
||||
// 2. Resolve stream.[[inFlightCloseRequest]] with undefined.
|
||||
WebIDL::resolve_promise(realm, *stream.in_flight_close_request(), JS::js_undefined());
|
||||
|
||||
// 3. Set stream.[[inFlightCloseRequest]] to undefined.
|
||||
stream.set_in_flight_close_request({});
|
||||
|
||||
// 4. Let state be stream.[[state]].
|
||||
auto state = stream.state();
|
||||
|
||||
// 5. Assert: stream.[[state]] is "writable" or "erroring".
|
||||
VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
|
||||
|
||||
// 6. If state is "erroring",
|
||||
if (state == WritableStream::State::Erroring) {
|
||||
// 1. Set stream.[[storedError]] to undefined.
|
||||
stream.set_stored_error(JS::js_undefined());
|
||||
|
||||
// 2. If stream.[[pendingAbortRequest]] is not undefined,
|
||||
if (stream.pending_abort_request().has_value()) {
|
||||
// 1. Resolve stream.[[pendingAbortRequest]]'s promise with undefined.
|
||||
// 2. Set stream.[[pendingAbortRequest]] to undefined.
|
||||
WebIDL::resolve_promise(realm, stream.pending_abort_request().release_value().promise, JS::js_undefined());
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Set stream.[[state]] to "closed".
|
||||
stream.set_state(WritableStream::State::Closed);
|
||||
|
||||
// 8. Let writer be stream.[[writer]].
|
||||
auto writer = stream.writer();
|
||||
|
||||
// 9. If writer is not undefined, resolve writer.[[closedPromise]] with undefined.
|
||||
if (writer)
|
||||
WebIDL::resolve_promise(realm, *writer->closed_promise(), JS::js_undefined());
|
||||
|
||||
// 10. Assert: stream.[[pendingAbortRequest]] is undefined.
|
||||
VERIFY(!stream.pending_abort_request().has_value());
|
||||
|
||||
// 11. Assert: stream.[[storedError]] is undefined.
|
||||
VERIFY(stream.stored_error().is_undefined());
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-close-with-error
|
||||
WebIDL::ExceptionOr<void> writable_stream_finish_in_flight_close_with_error(WritableStream& stream, JS::Value error)
|
||||
{
|
||||
auto& realm = stream.realm();
|
||||
|
||||
// 1. Assert: stream.[[inFlightCloseRequest]] is not undefined.
|
||||
VERIFY(stream.in_flight_close_request());
|
||||
|
||||
// 2. Reject stream.[[inFlightCloseRequest]] with error.
|
||||
WebIDL::reject_promise(realm, *stream.in_flight_close_request(), error);
|
||||
|
||||
// 3. Set stream.[[inFlightCloseRequest]] to undefined.
|
||||
stream.set_in_flight_close_request({});
|
||||
|
||||
// 4. Assert: stream.[[state]] is "writable" or "erroring".
|
||||
auto state = stream.state();
|
||||
VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
|
||||
|
||||
// 5. If stream.[[pendingAbortRequest]] is not undefined,
|
||||
if (stream.pending_abort_request().has_value()) {
|
||||
// 1. Reject stream.[[pendingAbortRequest]]'s promise with error.
|
||||
// 2. Set stream.[[pendingAbortRequest]] to undefined.
|
||||
WebIDL::reject_promise(realm, stream.pending_abort_request().release_value().promise, error);
|
||||
}
|
||||
|
||||
// 6. Perform ! WritableStreamDealWithRejection(stream, error).
|
||||
return writable_stream_deal_with_rejection(stream, error);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write
|
||||
void writable_stream_finish_in_flight_write(WritableStream& stream)
|
||||
{
|
||||
auto& realm = stream.realm();
|
||||
|
||||
// 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
|
||||
VERIFY(stream.in_flight_write_request());
|
||||
|
||||
// 2. Resolve stream.[[inFlightWriteRequest]] with undefined.
|
||||
WebIDL::resolve_promise(realm, *stream.in_flight_write_request(), JS::js_undefined());
|
||||
|
||||
// 3. Set stream.[[inFlightWriteRequest]] to undefined.
|
||||
stream.set_in_flight_write_request({});
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-finish-in-flight-write-with-error
|
||||
WebIDL::ExceptionOr<void> writable_stream_finish_in_flight_write_with_error(WritableStream& stream, JS::Value error)
|
||||
{
|
||||
auto& realm = stream.realm();
|
||||
|
||||
// 1. Assert: stream.[[inFlightWriteRequest]] is not undefined.
|
||||
VERIFY(stream.in_flight_write_request());
|
||||
|
||||
// 2. Reject stream.[[inFlightWriteRequest]] with error.
|
||||
WebIDL::reject_promise(realm, *stream.in_flight_write_request(), error);
|
||||
|
||||
// 3. Set stream.[[inFlightWriteRequest]] to undefined.
|
||||
stream.set_in_flight_write_request({});
|
||||
|
||||
// 4. Assert: stream.[[state]] is "writable" or "erroring".
|
||||
auto state = stream.state();
|
||||
VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
|
||||
|
||||
// 5. Perform ! WritableStreamDealWithRejection(stream, error).
|
||||
return writable_stream_deal_with_rejection(stream, error);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-has-operation-marked-in-flight
|
||||
bool writable_stream_has_operation_marked_in_flight(WritableStream const& stream)
|
||||
{
|
||||
@ -928,6 +1105,39 @@ bool writable_stream_has_operation_marked_in_flight(WritableStream const& stream
|
||||
return true;
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-mark-close-request-in-flight
|
||||
void writable_stream_mark_close_request_in_flight(WritableStream& stream)
|
||||
{
|
||||
// 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
|
||||
VERIFY(!stream.in_flight_close_request());
|
||||
|
||||
// 2. Assert: stream.[[closeRequest]] is not undefined.
|
||||
VERIFY(stream.close_request());
|
||||
|
||||
// 3. Set stream.[[inFlightCloseRequest]] to stream.[[closeRequest]].
|
||||
stream.set_in_flight_close_request(stream.close_request());
|
||||
|
||||
// 4. Set stream.[[closeRequest]] to undefined.
|
||||
stream.set_close_request({});
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-mark-first-write-request-in-flight
|
||||
void writable_stream_mark_first_write_request_in_flight(WritableStream& stream)
|
||||
{
|
||||
// 1. Assert: stream.[[inFlightWriteRequest]] is undefined.
|
||||
VERIFY(!stream.in_flight_write_request());
|
||||
|
||||
// 2. Assert: stream.[[writeRequests]] is not empty.
|
||||
VERIFY(!stream.write_requests().is_empty());
|
||||
|
||||
// 3. Let writeRequest be stream.[[writeRequests]][0].
|
||||
// 4. Remove writeRequest from stream.[[writeRequests]].
|
||||
auto write_request = stream.write_requests().take_first();
|
||||
|
||||
// 5. Set stream.[[inFlightWriteRequest]] to writeRequest.
|
||||
stream.set_in_flight_write_request(write_request);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-reject-close-and-closed-promise-if-needed
|
||||
void writable_stream_reject_close_and_closed_promise_if_needed(WritableStream& stream)
|
||||
{
|
||||
@ -996,6 +1206,39 @@ WebIDL::ExceptionOr<void> writable_stream_start_erroring(WritableStream& stream,
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-update-backpressure
|
||||
void writable_stream_update_backpressure(WritableStream& stream, bool backpressure)
|
||||
{
|
||||
auto& realm = stream.realm();
|
||||
|
||||
// 1. Assert: stream.[[state]] is "writable".
|
||||
VERIFY(stream.state() == WritableStream::State::Writable);
|
||||
|
||||
// 2. Assert: ! WritableStreamCloseQueuedOrInFlight(stream) is false.
|
||||
VERIFY(!writable_stream_close_queued_or_in_flight(stream));
|
||||
|
||||
// 3. Let writer be stream.[[writer]].
|
||||
auto writer = stream.writer();
|
||||
|
||||
// 4. If writer is not undefined and backpressure is not stream.[[backpressure]],
|
||||
if (writer && backpressure != stream.backpressure()) {
|
||||
// 1. If backpressure is true, set writer.[[readyPromise]] to a new promise.
|
||||
if (backpressure) {
|
||||
writer->set_ready_promise(WebIDL::create_promise(realm));
|
||||
}
|
||||
// 2. Otherwise,
|
||||
else {
|
||||
// 1. Assert: backpressure is false.
|
||||
|
||||
// 2. Resolve writer.[[readyPromise]] with undefined.
|
||||
WebIDL::resolve_promise(realm, *writer->ready_promise(), JS::js_undefined());
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Set stream.[[backpressure]] to backpressure.
|
||||
stream.set_backpressure(backpressure);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-ready-promise-rejected
|
||||
void writable_stream_default_writer_ensure_ready_promise_rejected(WritableStreamDefaultWriter& writer, JS::Value error)
|
||||
{
|
||||
@ -1036,6 +1279,52 @@ Optional<double> writable_stream_default_writer_get_desired_size(WritableStreamD
|
||||
return writable_stream_default_controller_get_desired_size(*stream->controller());
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 1. Let stream be controller.[[stream]].
|
||||
auto stream = controller.stream();
|
||||
|
||||
// 2. If controller.[[started]] is false, return.
|
||||
if (!controller.started())
|
||||
return {};
|
||||
|
||||
// 3. If stream.[[inFlightWriteRequest]] is not undefined, return.
|
||||
if (stream->in_flight_write_request())
|
||||
return {};
|
||||
|
||||
// 4. Let state be stream.[[state]].
|
||||
auto state = stream->state();
|
||||
|
||||
// 5. Assert: state is not "closed" or "errored".
|
||||
VERIFY(state != WritableStream::State::Closed && state != WritableStream::State::Errored);
|
||||
|
||||
// 6. If state is "erroring",
|
||||
if (state == WritableStream::State::Erroring) {
|
||||
// 1. Perform ! WritableStreamFinishErroring(stream).
|
||||
// 2. Return.
|
||||
return writable_stream_finish_erroring(*stream);
|
||||
}
|
||||
|
||||
// 7. If controller.[[queue]] is empty, return.
|
||||
if (controller.queue().is_empty())
|
||||
return {};
|
||||
|
||||
// 8. Let value be ! PeekQueueValue(controller).
|
||||
auto value = peek_queue_value(controller);
|
||||
|
||||
// 9. If value is the close sentinel, perform ! WritableStreamDefaultControllerProcessClose(controller).
|
||||
if (is_close_sentinel(value)) {
|
||||
TRY(writable_stream_default_controller_process_close(controller));
|
||||
}
|
||||
// 10. Otherwise, perform ! WritableStreamDefaultControllerProcessWrite(controller, value).
|
||||
else {
|
||||
TRY(writable_stream_default_controller_process_write(controller, value));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-clear-algorithms
|
||||
void writable_stream_default_controller_clear_algorithms(WritableStreamDefaultController& controller)
|
||||
{
|
||||
@ -1052,6 +1341,18 @@ void writable_stream_default_controller_clear_algorithms(WritableStreamDefaultCo
|
||||
controller.set_strategy_size_algorithm({});
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-close
|
||||
WebIDL::ExceptionOr<void> writable_stream_default_controller_close(WritableStreamDefaultController& controller)
|
||||
{
|
||||
// 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0).
|
||||
TRY(enqueue_value_with_size(controller, create_close_sentinel(), 0.0));
|
||||
|
||||
// 2. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
|
||||
TRY(writable_stream_default_controller_advance_queue_if_needed(controller));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error
|
||||
WebIDL::ExceptionOr<void> writable_stream_default_controller_error(WritableStreamDefaultController& controller, JS::Value error)
|
||||
{
|
||||
@ -1068,6 +1369,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-get-backpressure
|
||||
bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultController const& controller)
|
||||
{
|
||||
// 1. Let desiredSize be ! WritableStreamDefaultControllerGetDesiredSize(controller).
|
||||
auto desired_size = writable_stream_default_controller_get_desired_size(controller);
|
||||
|
||||
// 2. Return true if desiredSize ≤ 0, or false otherwise.
|
||||
return desired_size <= 0.0;
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-get-desired-size
|
||||
double writable_stream_default_controller_get_desired_size(WritableStreamDefaultController const& controller)
|
||||
{
|
||||
@ -1075,6 +1386,118 @@ double writable_stream_default_controller_get_desired_size(WritableStreamDefault
|
||||
return controller.strategy_hwm() - controller.queue_total_size();
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
|
||||
WebIDL::ExceptionOr<void> writable_stream_default_controller_process_close(WritableStreamDefaultController& controller)
|
||||
{
|
||||
// 1. Let stream be controller.[[stream]].
|
||||
auto stream = controller.stream();
|
||||
|
||||
// 2. Perform ! WritableStreamMarkCloseRequestInFlight(stream).
|
||||
writable_stream_mark_close_request_in_flight(*stream);
|
||||
|
||||
// 3. Perform ! DequeueValue(controller).
|
||||
dequeue_value(controller);
|
||||
|
||||
// 4. Assert: controller.[[queue]] is empty.
|
||||
VERIFY(controller.queue().is_empty());
|
||||
|
||||
// 5. Let sinkClosePromise be the result of performing controller.[[closeAlgorithm]].
|
||||
auto sink_close_promise = TRY((*controller.close_algorithm())());
|
||||
|
||||
// 6. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
|
||||
writable_stream_default_controller_clear_algorithms(controller);
|
||||
|
||||
// 7. Upon fulfillment of sinkClosePromise,
|
||||
WebIDL::upon_fulfillment(*sink_close_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
// 1. Perform ! WritableStreamFinishInFlightClose(stream).
|
||||
writable_stream_finish_in_flight_close(*stream);
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
// 8. Upon rejection of sinkClosePromise with reason reason,
|
||||
WebIDL::upon_rejection(*sink_close_promise, [&, stream = stream](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
// 1. Perform ! WritableStreamFinishInFlightCloseWithError(stream, reason).
|
||||
TRY(writable_stream_finish_in_flight_close_with_error(*stream, reason));
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
|
||||
WebIDL::ExceptionOr<void> writable_stream_default_controller_process_write(WritableStreamDefaultController& controller, JS::Value chunk)
|
||||
{
|
||||
// 1. Let stream be controller.[[stream]].
|
||||
auto stream = controller.stream();
|
||||
|
||||
// 2. Perform ! WritableStreamMarkFirstWriteRequestInFlight(stream).
|
||||
writable_stream_mark_first_write_request_in_flight(*stream);
|
||||
|
||||
// 3. Let sinkWritePromise be the result of performing controller.[[writeAlgorithm]], passing in chunk.
|
||||
auto sink_write_promise = TRY((*controller.write_algorithm())(chunk));
|
||||
|
||||
// 4. Upon fulfillment of sinkWritePromise,
|
||||
WebIDL::upon_fulfillment(*sink_write_promise, [&, stream = stream](auto const&) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
// 1. Perform ! WritableStreamFinishInFlightWrite(stream).
|
||||
writable_stream_finish_in_flight_write(*stream);
|
||||
|
||||
// 2. Let state be stream.[[state]].
|
||||
auto state = stream->state();
|
||||
|
||||
// 3. Assert: state is "writable" or "erroring".
|
||||
VERIFY(state == WritableStream::State::Writable || state == WritableStream::State::Erroring);
|
||||
|
||||
// 4. Perform ! DequeueValue(controller).
|
||||
dequeue_value(controller);
|
||||
|
||||
// 5. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and state is "writable",
|
||||
if (!writable_stream_close_queued_or_in_flight(*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);
|
||||
}
|
||||
|
||||
// 6 .Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
|
||||
TRY(writable_stream_default_controller_advance_queue_if_needed(controller));
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
// 5. Upon rejection of sinkWritePromise with reason,
|
||||
WebIDL::upon_rejection(*sink_write_promise, [&, stream = stream](auto const& reason) -> WebIDL::ExceptionOr<JS::Value> {
|
||||
// 1. If stream.[[state]] is "writable", perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
|
||||
if (stream->state() == WritableStream::State::Writable)
|
||||
writable_stream_default_controller_clear_algorithms(controller);
|
||||
|
||||
// 2. Perform ! WritableStreamFinishInFlightWriteWithError(stream, reason).
|
||||
TRY(writable_stream_finish_in_flight_write_with_error(*stream, reason));
|
||||
|
||||
return JS::js_undefined();
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#close-sentinel
|
||||
// Non-standard function that implements the "close sentinel" value.
|
||||
JS::Value create_close_sentinel()
|
||||
{
|
||||
// The close sentinel is a unique value enqueued into [[queue]], in lieu of a chunk, to signal that the stream is closed. It is only used internally, and is never exposed to web developers.
|
||||
// Note: We use the empty Value to signal this as, similarly to the note above, the empty value is not exposed to nor creatable by web developers.
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#close-sentinel
|
||||
// Non-standard function that implements the "If value is a close sentinel" check.
|
||||
bool is_close_sentinel(JS::Value value)
|
||||
{
|
||||
return value.is_empty();
|
||||
}
|
||||
|
||||
// Non-standard function to aid in converting a user-provided function into a WebIDL::Callback. This is essentially
|
||||
// what the Bindings generator would do at compile time, but at runtime instead.
|
||||
JS::ThrowCompletionOr<JS::Handle<WebIDL::CallbackType>> property_to_callback(JS::VM& vm, JS::Value value, JS::PropertyKey const& property_key)
|
||||
|
@ -55,21 +55,37 @@ WebIDL::ExceptionOr<void> set_up_readable_stream_default_controller_from_underly
|
||||
|
||||
bool is_writable_stream_locked(WritableStream const&);
|
||||
WebIDL::ExceptionOr<void> set_up_writable_stream_default_writer(WritableStreamDefaultWriter&, WritableStream&);
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<WebIDL::Promise>> writable_stream_close(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&);
|
||||
void writable_stream_finish_in_flight_close(WritableStream&);
|
||||
WebIDL::ExceptionOr<void> writable_stream_finish_in_flight_close_with_error(WritableStream&, JS::Value error);
|
||||
void writable_stream_finish_in_flight_write(WritableStream&);
|
||||
WebIDL::ExceptionOr<void> writable_stream_finish_in_flight_write_with_error(WritableStream&, JS::Value error);
|
||||
bool writable_stream_has_operation_marked_in_flight(WritableStream const&);
|
||||
void writable_stream_mark_close_request_in_flight(WritableStream&);
|
||||
void writable_stream_mark_first_write_request_in_flight(WritableStream&);
|
||||
void writable_stream_reject_close_and_closed_promise_if_needed(WritableStream&);
|
||||
WebIDL::ExceptionOr<void> writable_stream_start_erroring(WritableStream&, JS::Value reason);
|
||||
void writable_stream_update_backpressure(WritableStream&, bool backpressure);
|
||||
|
||||
Optional<double> writable_stream_default_writer_get_desired_size(WritableStreamDefaultWriter const&);
|
||||
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_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);
|
||||
bool writable_stream_default_controller_get_backpressure(WritableStreamDefaultController const&);
|
||||
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);
|
||||
|
||||
JS::Value create_close_sentinel();
|
||||
bool is_close_sentinel(JS::Value);
|
||||
JS::ThrowCompletionOr<JS::Handle<WebIDL::CallbackType>> property_to_callback(JS::VM& vm, JS::Value value, JS::PropertyKey const& property_key);
|
||||
|
||||
}
|
||||
|
@ -55,6 +55,27 @@ bool WritableStream::locked() const
|
||||
return is_writable_stream_locked(*this);
|
||||
}
|
||||
|
||||
// https://streams.spec.whatwg.org/#ws-close
|
||||
WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> WritableStream::close()
|
||||
{
|
||||
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 close a locked stream"sv));
|
||||
return WebIDL::create_rejected_promise(realm, exception)->promise();
|
||||
}
|
||||
|
||||
// 2. If ! WritableStreamCloseQueuedOrInFlight(this) is true, return a promise rejected with a TypeError exception.
|
||||
if (writable_stream_close_queued_or_in_flight(*this)) {
|
||||
auto exception = MUST_OR_THROW_OOM(JS::TypeError::create(realm, "Cannot close a stream that is already closed or errored"sv));
|
||||
return WebIDL::create_rejected_promise(realm, exception)->promise();
|
||||
}
|
||||
|
||||
// 3. Return ! WritableStreamClose(this).
|
||||
return TRY(writable_stream_close(*this))->promise();
|
||||
}
|
||||
|
||||
WritableStream::WritableStream(JS::Realm& realm)
|
||||
: Bindings::PlatformObject(realm)
|
||||
{
|
||||
|
@ -47,11 +47,13 @@ public:
|
||||
virtual ~WritableStream() = default;
|
||||
|
||||
bool locked() const;
|
||||
WebIDL::ExceptionOr<JS::GCPtr<JS::Object>> close();
|
||||
|
||||
bool backpressure() const { return m_backpressure; }
|
||||
void set_backpressure(bool value) { m_backpressure = value; }
|
||||
|
||||
JS::GCPtr<WebIDL::Promise const> close_request() const { return m_close_request; }
|
||||
JS::GCPtr<WebIDL::Promise> close_request() { return m_close_request; }
|
||||
void set_close_request(JS::GCPtr<WebIDL::Promise> value) { m_close_request = value; }
|
||||
|
||||
JS::GCPtr<WritableStreamDefaultController const> controller() const { return m_controller; }
|
||||
|
@ -7,6 +7,7 @@ interface WritableStream {
|
||||
|
||||
// FIXME:
|
||||
// Promise<undefined> abort(optional any reason);
|
||||
// Promise<undefined> close();
|
||||
// WritableStreamDefaultWriter getWriter();
|
||||
|
||||
Promise<undefined> close();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user