2025-04-17 18:21:29 -03:00
/*
* Copyright ( c ) 2022 , Linus Groh < linusg @ serenityos . org >
* Copyright ( c ) 2023 , Matthew Olsson < mattco @ serenityos . org >
* Copyright ( c ) 2023 - 2025 , Shannon Booth < shannon @ serenityos . org >
* Copyright ( c ) 2023 - 2024 , Kenneth Myhra < kennethmyhra @ serenityos . org >
* Copyright ( c ) 2025 , Tim Flynn < trflynn89 @ ladybird . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/GenericShorthands.h>
# include <LibWeb/Bindings/ExceptionOrUtils.h>
2026-04-25 16:02:49 -03:00
# include <LibWeb/Bindings/UnderlyingSink.h>
2025-04-17 18:21:29 -03:00
# include <LibWeb/DOM/AbortSignal.h>
# include <LibWeb/Streams/AbstractOperations.h>
# include <LibWeb/Streams/WritableStream.h>
# include <LibWeb/Streams/WritableStreamDefaultController.h>
# include <LibWeb/Streams/WritableStreamDefaultWriter.h>
# include <LibWeb/Streams/WritableStreamOperations.h>
# include <LibWeb/WebIDL/AbstractOperations.h>
# include <LibWeb/WebIDL/Promise.h>
namespace Web : : Streams {
// https://streams.spec.whatwg.org/#close-sentinel
static 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.
return JS : : js_special_empty_value ( ) ;
}
// https://streams.spec.whatwg.org/#close-sentinel
static bool is_close_sentinel ( JS : : Value value )
{
return value . is_special_empty_value ( ) ;
}
// https://streams.spec.whatwg.org/#acquire-writable-stream-default-writer
WebIDL : : ExceptionOr < GC : : Ref < WritableStreamDefaultWriter > > acquire_writable_stream_default_writer ( WritableStream & stream )
{
auto & realm = stream . realm ( ) ;
// 1. Let writer be a new WritableStreamDefaultWriter.
auto writer = realm . create < WritableStreamDefaultWriter > ( realm ) ;
// 2. Perform ? SetUpWritableStreamDefaultWriter(writer, stream).
TRY ( set_up_writable_stream_default_writer ( writer , stream ) ) ;
// 3. Return writer.
return writer ;
}
// https://streams.spec.whatwg.org/#create-writable-stream
WebIDL : : ExceptionOr < GC : : Ref < WritableStream > > create_writable_stream ( JS : : Realm & realm , GC : : Ref < StartAlgorithm > start_algorithm , GC : : Ref < WriteAlgorithm > write_algorithm , GC : : Ref < CloseAlgorithm > close_algorithm , GC : : Ref < AbortAlgorithm > abort_algorithm , double high_water_mark , GC : : Ref < SizeAlgorithm > size_algorithm )
{
// 1. Assert: ! IsNonNegativeNumber(highWaterMark) is true.
VERIFY ( is_non_negative_number ( JS : : Value { high_water_mark } ) ) ;
// 2. Let stream be a new WritableStream.
auto stream = realm . create < WritableStream > ( realm ) ;
// 3. Perform ! InitializeWritableStream(stream).
initialize_writable_stream ( stream ) ;
// 4. Let controller be a new WritableStreamDefaultController.
auto controller = realm . create < WritableStreamDefaultController > ( realm ) ;
// 5. Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
TRY ( set_up_writable_stream_default_controller ( stream , controller , start_algorithm , write_algorithm , close_algorithm , abort_algorithm , high_water_mark , size_algorithm ) ) ;
// 6. Return stream.
return stream ;
}
// https://streams.spec.whatwg.org/#initialize-writable-stream
void initialize_writable_stream ( WritableStream & stream )
{
// 1. Set stream.[[state]] to "writable".
stream . set_state ( WritableStream : : State : : Writable ) ;
// 2. Set stream.[[storedError]], stream.[[writer]], stream.[[controller]], stream.[[inFlightWriteRequest]],
// stream.[[closeRequest]], stream.[[inFlightCloseRequest]], and stream.[[pendingAbortRequest]] to undefined.
stream . set_stored_error ( JS : : js_undefined ( ) ) ;
stream . set_writer ( { } ) ;
stream . set_controller ( { } ) ;
stream . set_in_flight_write_request ( { } ) ;
stream . set_close_request ( { } ) ;
stream . set_in_flight_close_request ( { } ) ;
stream . set_pending_abort_request ( { } ) ;
// 3. Set stream.[[writeRequests]] to a new empty list.
stream . write_requests ( ) . clear ( ) ;
// 4. Set stream.[[backpressure]] to false.
stream . set_backpressure ( false ) ;
}
// https://streams.spec.whatwg.org/#is-writable-stream-locked
bool is_writable_stream_locked ( WritableStream const & stream )
{
// 1. If stream.[[writer]] is undefined, return false.
if ( ! stream . writer ( ) )
return false ;
// 2. Return true.
return true ;
}
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer
WebIDL : : ExceptionOr < void > set_up_writable_stream_default_writer ( WritableStreamDefaultWriter & writer , WritableStream & stream )
{
// FIXME: Exactly when we should effectively be using the relevant realm of `this` is to be clarified by the spec.
// For now, we do so as needed by WPT tests. See: https://github.com/whatwg/streams/issues/1213
auto & realm = HTML : : relevant_realm ( writer ) ;
// 1. If ! IsWritableStreamLocked(stream) is true, throw a TypeError exception.
if ( is_writable_stream_locked ( stream ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Stream is locked " sv } ;
// 2. Set writer.[[stream]] to stream.
writer . set_stream ( stream ) ;
// 3. Set stream.[[writer]] to writer.
stream . set_writer ( writer ) ;
// 4. Let state be stream.[[state]].
auto state = stream . state ( ) ;
// 5. If state is "writable",
if ( state = = WritableStream : : State : : Writable ) {
// 1. If ! WritableStreamCloseQueuedOrInFlight(stream) is false and stream.[[backpressure]] is true, set
// writer.[[readyPromise]] to a new promise.
if ( ! writable_stream_close_queued_or_in_flight ( stream ) & & stream . backpressure ( ) ) {
writer . set_ready_promise ( WebIDL : : create_promise ( realm ) ) ;
}
// 2. Otherwise, set writer.[[readyPromise]] to a promise resolved with undefined.
else {
writer . set_ready_promise ( WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ) ;
}
// 3. Set writer.[[closedPromise]] to a new promise.
writer . set_closed_promise ( WebIDL : : create_promise ( realm ) ) ;
}
// 6. Otherwise, if state is "erroring",
else if ( state = = WritableStream : : State : : Erroring ) {
// 1. Set writer.[[readyPromise]] to a promise rejected with stream.[[storedError]].
writer . set_ready_promise ( WebIDL : : create_rejected_promise ( realm , stream . stored_error ( ) ) ) ;
// 2. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * writer . ready_promise ( ) ) ;
// 3. Set writer.[[closedPromise]] to a new promise.
writer . set_closed_promise ( WebIDL : : create_promise ( realm ) ) ;
}
// 7. Otherwise, if state is "closed",
else if ( state = = WritableStream : : State : : Closed ) {
// 1. Set writer.[[readyPromise]] to a promise resolved with undefined.
writer . set_ready_promise ( WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ) ;
// 2. Set writer.[[closedPromise]] to a promise resolved with undefined.
writer . set_closed_promise ( WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ) ;
}
// 8. Otherwise,
else {
// 1. Assert: state is "errored".
VERIFY ( state = = WritableStream : : State : : Errored ) ;
// 2. Let storedError be stream.[[storedError]].
auto stored_error = stream . stored_error ( ) ;
// 3. Set writer.[[readyPromise]] to a promise rejected with storedError.
writer . set_ready_promise ( WebIDL : : create_rejected_promise ( realm , stored_error ) ) ;
// 4. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * writer . ready_promise ( ) ) ;
// 5. Set writer.[[closedPromise]] to a promise rejected with storedError.
writer . set_closed_promise ( WebIDL : : create_rejected_promise ( realm , stored_error ) ) ;
// 6. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * writer . closed_promise ( ) ) ;
}
return { } ;
}
// https://streams.spec.whatwg.org/#writable-stream-abort
GC : : Ref < WebIDL : : Promise > 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.
if ( first_is_one_of ( stream . state ( ) , WritableStream : : State : : Closed , 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]].
auto 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 )
writable_stream_start_erroring ( stream , reason ) ;
// 12. Return promise.
return promise ;
}
// https://streams.spec.whatwg.org/#writable-stream-close
GC : : Ref < 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 = 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]]).
writable_stream_default_controller_close ( * stream . controller ( ) ) ;
// 10. Return promise.
return promise ;
}
// https://streams.spec.whatwg.org/#writable-stream-add-write-request
GC : : Ref < WebIDL : : Promise > writable_stream_add_write_request ( WritableStream & stream )
{
auto & realm = stream . realm ( ) ;
// 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]].
stream . write_requests ( ) . 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 )
{
// 1. If stream.[[closeRequest]] is undefined and stream.[[inFlightCloseRequest]] is undefined, return false.
if ( ! stream . close_request ( ) & & ! stream . in_flight_close_request ( ) )
return false ;
// 2. Return true.
return true ;
}
// https://streams.spec.whatwg.org/#writable-stream-deal-with-rejection
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).
writable_stream_start_erroring ( stream , error ) ;
// 2. Return.
return ;
}
// 3. Assert: state is "erroring".
VERIFY ( state = = WritableStream : : State : : Erroring ) ;
// 4. Perform ! WritableStreamFinishErroring(stream).
writable_stream_finish_erroring ( stream ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-finish-erroring
void writable_stream_finish_erroring ( WritableStream & stream )
{
auto & realm = stream . realm ( ) ;
// 1. Assert: stream.[[state]] is "erroring".
VERIFY ( stream . state ( ) = = WritableStream : : State : : Erroring ) ;
// 2. Assert: ! WritableStreamHasOperationMarkedInFlight(stream) is false.
VERIFY ( ! writable_stream_has_operation_marked_in_flight ( stream ) ) ;
// 3. Set stream.[[state]] to "errored".
stream . set_state ( WritableStream : : State : : Errored ) ;
// 4. Perform ! stream.[[controller]].[[ErrorSteps]]().
stream . controller ( ) - > error_steps ( ) ;
// 5. Let storedError be stream.[[storedError]].
auto stored_error = stream . stored_error ( ) ;
// 6. For each writeRequest of stream.[[writeRequests]]:
for ( auto write_request : stream . write_requests ( ) ) {
// 1. Reject writeRequest with storedError.
WebIDL : : reject_promise ( realm , * write_request , stored_error ) ;
}
// 7. Set stream.[[writeRequests]] to an empty list.
stream . write_requests ( ) . clear ( ) ;
// 8. If stream.[[pendingAbortRequest]] is undefined,
if ( ! stream . pending_abort_request ( ) . has_value ( ) ) {
// 1. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
writable_stream_reject_close_and_closed_promise_if_needed ( stream ) ;
// 2. Return.
return ;
}
// 9. Let abortRequest be stream.[[pendingAbortRequest]].
// 10. Set stream.[[pendingAbortRequest]] to undefined.
auto abort_request = stream . pending_abort_request ( ) . release_value ( ) ;
// 11. If abortRequest’ s was already erroring is true,
if ( abort_request . was_already_erroring ) {
// 1. Reject abortRequest’ s promise with storedError.
WebIDL : : reject_promise ( realm , abort_request . promise , stored_error ) ;
// 2. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
writable_stream_reject_close_and_closed_promise_if_needed ( stream ) ;
// 3. Return.
return ;
}
// 12. Let promise be ! stream.[[controller]].[[AbortSteps]](abortRequest’ s reason).
auto promise = stream . controller ( ) - > abort_steps ( abort_request . reason ) ;
WebIDL : : react_to_promise ( promise ,
// 13. Upon fulfillment of promise,
GC : : create_function ( realm . heap ( ) , [ & realm , & stream , abort_promise = abort_request . promise ] ( JS : : Value ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Resolve abortRequest’ s promise with undefined.
WebIDL : : resolve_promise ( realm , abort_promise , JS : : js_undefined ( ) ) ;
// 2. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
writable_stream_reject_close_and_closed_promise_if_needed ( stream ) ;
return JS : : js_undefined ( ) ;
} ) ,
// 14. Upon rejection of promise with reason reason,
GC : : create_function ( realm . heap ( ) , [ & realm , & stream , abort_promise = abort_request . promise ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Reject abortRequest’ s promise with reason.
WebIDL : : reject_promise ( realm , abort_promise , reason ) ;
// 2. Perform ! WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream).
writable_stream_reject_close_and_closed_promise_if_needed ( stream ) ;
return JS : : js_undefined ( ) ;
} ) ) ;
}
// 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
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".
VERIFY ( first_is_one_of ( stream . state ( ) , WritableStream : : State : : Writable , 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).
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
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".
VERIFY ( first_is_one_of ( stream . state ( ) , WritableStream : : State : : Writable , WritableStream : : State : : Erroring ) ) ;
// 5. Perform ! WritableStreamDealWithRejection(stream, error).
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 )
{
// 1. If stream.[[inFlightWriteRequest]] is undefined and stream.[[inFlightCloseRequest]] is undefined, return false.
if ( ! stream . in_flight_write_request ( ) & & ! stream . in_flight_close_request ( ) )
return false ;
// 2. Return true.
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 )
{
auto & realm = stream . realm ( ) ;
// 1. Assert: stream.[[state]] is "errored".
VERIFY ( stream . state ( ) = = WritableStream : : State : : Errored ) ;
// 2. If stream.[[closeRequest]] is not undefined,
if ( stream . close_request ( ) ) {
// 1. Assert: stream.[[inFlightCloseRequest]] is undefined.
VERIFY ( ! stream . in_flight_close_request ( ) ) ;
// 2. Reject stream.[[closeRequest]] with stream.[[storedError]].
WebIDL : : reject_promise ( realm , * stream . close_request ( ) , stream . stored_error ( ) ) ;
// 3. Set stream.[[closeRequest]] to undefined.
stream . set_close_request ( { } ) ;
}
// 3. Let writer be stream.[[writer]].
auto writer = stream . writer ( ) ;
// 4. If writer is not undefined,
if ( writer ) {
// 1. Reject writer.[[closedPromise]] with stream.[[storedError]].
WebIDL : : reject_promise ( realm , * writer - > closed_promise ( ) , stream . stored_error ( ) ) ;
// 2. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * writer - > closed_promise ( ) ) ;
}
}
// https://streams.spec.whatwg.org/#writable-stream-start-erroring
void writable_stream_start_erroring ( WritableStream & stream , JS : : Value reason )
{
// 1. Assert: stream.[[storedError]] is undefined.
VERIFY ( stream . stored_error ( ) . is_undefined ( ) ) ;
// 2. Assert: stream.[[state]] is "writable".
VERIFY ( stream . state ( ) = = WritableStream : : State : : Writable ) ;
// 3. Let controller be stream.[[controller]].
auto controller = stream . controller ( ) ;
// 4. Assert: controller is not undefined.
VERIFY ( controller ) ;
// 5. Set stream.[[state]] to "erroring".
stream . set_state ( WritableStream : : State : : Erroring ) ;
// 6. Set stream.[[storedError]] to reason.
stream . set_stored_error ( reason ) ;
// 7. Let writer be stream.[[writer]].
auto writer = stream . writer ( ) ;
// 8. If writer is not undefined, perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason).
if ( writer )
writable_stream_default_writer_ensure_ready_promise_rejected ( * writer , reason ) ;
// 9. If ! WritableStreamHasOperationMarkedInFlight(stream) is false and controller.[[started]] is true,
// perform ! WritableStreamFinishErroring(stream).
if ( ! writable_stream_has_operation_marked_in_flight ( stream ) & & controller - > started ( ) )
writable_stream_finish_erroring ( stream ) ;
}
// 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-abort
GC : : Ref < WebIDL : : Promise > writable_stream_default_writer_abort ( WritableStreamDefaultWriter & writer , JS : : Value reason )
{
// 1. Let stream be writer.[[stream]].
auto stream = writer . stream ( ) ;
// 2. Assert: stream is not undefined.
VERIFY ( stream ) ;
// 3. Return ! WritableStreamAbort(stream, reason).
return writable_stream_abort ( * stream , reason ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-writer-close
GC : : Ref < WebIDL : : Promise > writable_stream_default_writer_close ( WritableStreamDefaultWriter & writer )
{
// 1. Let stream be writer.[[stream]].
auto stream = writer . stream ( ) ;
// 2. Assert: stream is not undefined.
VERIFY ( stream ) ;
// 3. Return ! WritableStreamClose(stream).
return writable_stream_close ( * stream ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-writer-close-with-error-propagation
GC : : Ref < WebIDL : : Promise > writable_stream_default_writer_close_with_error_propagation ( WritableStreamDefaultWriter & writer )
{
auto & realm = writer . realm ( ) ;
// 1. Let stream be writer.[[stream]].
auto stream = writer . stream ( ) ;
// 2. Assert: stream is not undefined.
VERIFY ( stream ) ;
// 3. Let state be stream.[[state]].
auto state = stream - > state ( ) ;
// 4. If ! WritableStreamCloseQueuedOrInFlight(stream) is true or state is "closed", return a promise resolved with undefined.
if ( writable_stream_close_queued_or_in_flight ( * stream ) | | state = = WritableStream : : State : : Closed )
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
// 5. 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 ( ) ) ;
// 6. Assert: state is "writable" or "erroring".
VERIFY ( state = = WritableStream : : State : : Writable | | state = = WritableStream : : State : : Erroring ) ;
// 7. Return ! WritableStreamDefaultWriterClose(writer).
return writable_stream_default_writer_close ( writer ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-writer-ensure-closed-promise-rejected
void writable_stream_default_writer_ensure_closed_promise_rejected ( WritableStreamDefaultWriter & writer , JS : : Value error )
{
auto & realm = writer . realm ( ) ;
// 1. If writer.[[closedPromise]].[[PromiseState]] is "pending", reject writer.[[closedPromise]] with error.
if ( as < JS : : Promise > ( * writer . closed_promise ( ) - > promise ( ) ) . state ( ) = = JS : : Promise : : State : : Pending ) {
WebIDL : : reject_promise ( realm , * writer . closed_promise ( ) , error ) ;
}
// 2. Otherwise, set writer.[[closedPromise]] to a promise rejected with error.
else {
writer . set_closed_promise ( WebIDL : : create_rejected_promise ( realm , error ) ) ;
}
// 3. Set writer.[[closedPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * writer . closed_promise ( ) ) ;
}
// 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 )
{
auto & realm = writer . realm ( ) ;
// 1. If writer.[[readyPromise]].[[PromiseState]] is "pending", reject writer.[[readyPromise]] with error.
if ( as < JS : : Promise > ( * writer . ready_promise ( ) - > promise ( ) ) . state ( ) = = JS : : Promise : : State : : Pending ) {
WebIDL : : reject_promise ( realm , * writer . ready_promise ( ) , error ) ;
}
// 2. Otherwise, set writer.[[readyPromise]] to a promise rejected with error.
else {
writer . set_ready_promise ( WebIDL : : create_rejected_promise ( realm , error ) ) ;
}
// 3. Set writer.[[readyPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * writer . ready_promise ( ) ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-writer-get-desired-size
Optional < double > writable_stream_default_writer_get_desired_size ( WritableStreamDefaultWriter const & writer )
{
// 1. Let stream be writer.[[stream]].
auto stream = writer . stream ( ) ;
// 2. Let state be stream.[[state]].
auto state = stream - > state ( ) ;
// 3. If state is "errored" or "erroring", return null.
if ( state = = WritableStream : : State : : Errored | | state = = WritableStream : : State : : Erroring )
return { } ;
// 4. If state is "closed", return 0.
if ( state = = WritableStream : : State : : Closed )
return 0.0 ;
// 5. Return ! WritableStreamDefaultControllerGetDesiredSize(stream.[[controller]]).
return writable_stream_default_controller_get_desired_size ( * stream - > controller ( ) ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-writer-release
void writable_stream_default_writer_release ( WritableStreamDefaultWriter & writer )
{
auto & realm = writer . realm ( ) ;
// 1. Let stream be writer.[[stream]].
auto stream = writer . stream ( ) ;
// 2. Assert: stream is not undefined.
VERIFY ( stream ) ;
// 3. Assert: stream.[[writer]] is writer.
VERIFY ( stream - > writer ( ) . ptr ( ) = = & writer ) ;
// 4. Let releasedError be a new TypeError.
auto released_error = JS : : TypeError : : create ( realm , " Writer's stream lock has been released " sv ) ;
// 5. Perform ! WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError).
writable_stream_default_writer_ensure_ready_promise_rejected ( writer , released_error ) ;
// 6. Perform ! WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError).
writable_stream_default_writer_ensure_closed_promise_rejected ( writer , released_error ) ;
// 7. Set stream.[[writer]] to undefined.
stream - > set_writer ( { } ) ;
// 8. Set writer.[[stream]] to undefined.
writer . set_stream ( { } ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-writer-write
GC : : Ref < 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 = 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 = 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 = 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).
writable_stream_default_controller_write ( * controller , chunk , chunk_size ) ;
// 13. Return promise.
return promise ;
}
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller
WebIDL : : ExceptionOr < void > set_up_writable_stream_default_controller ( WritableStream & stream , WritableStreamDefaultController & controller , GC : : Ref < StartAlgorithm > start_algorithm , GC : : Ref < WriteAlgorithm > write_algorithm , GC : : Ref < CloseAlgorithm > close_algorithm , GC : : Ref < AbortAlgorithm > abort_algorithm , double high_water_mark , GC : : Ref < SizeAlgorithm > size_algorithm )
{
auto & realm = stream . realm ( ) ;
// 1. Assert: stream implements WritableStream.
// 2. Assert: stream.[[controller]] is undefined.
VERIFY ( ! stream . controller ( ) ) ;
// 3. Set controller.[[stream]] to stream.
controller . set_stream ( stream ) ;
// 4. Set stream.[[controller]] to controller.
stream . set_controller ( controller ) ;
// 5. Perform ! ResetQueue(controller).
reset_queue ( controller ) ;
// 6. Set controller.[[signal]] to a new AbortSignal.
controller . set_signal ( realm . create < DOM : : AbortSignal > ( realm ) ) ;
// 7. Set controller.[[started]] to false.
controller . set_started ( false ) ;
// 8. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm.
controller . set_strategy_size_algorithm ( size_algorithm ) ;
// 9. Set controller.[[strategyHWM]] to highWaterMark.
controller . set_strategy_hwm ( high_water_mark ) ;
// 10. Set controller.[[writeAlgorithm]] to writeAlgorithm.
controller . set_write_algorithm ( write_algorithm ) ;
// 11. Set controller.[[closeAlgorithm]] to closeAlgorithm.
controller . set_close_algorithm ( close_algorithm ) ;
// 12. Set controller.[[abortAlgorithm]] to abortAlgorithm.
controller . set_abort_algorithm ( abort_algorithm ) ;
// 13. Let backpressure be ! WritableStreamDefaultControllerGetBackpressure(controller).
auto backpressure = writable_stream_default_controller_get_backpressure ( controller ) ;
// 14. Perform ! WritableStreamUpdateBackpressure(stream, backpressure).
writable_stream_update_backpressure ( stream , backpressure ) ;
// 15. Let startResult be the result of performing startAlgorithm. (This may throw an exception.)
auto start_result = TRY ( start_algorithm - > function ( ) ( ) ) ;
// 16. Let startPromise be a promise resolved with startResult.
auto start_promise = WebIDL : : create_resolved_promise ( realm , start_result ) ;
WebIDL : : react_to_promise ( start_promise ,
// 17. Upon fulfillment of startPromise,
GC : : create_function ( realm . heap ( ) , [ & controller , & stream ] ( JS : : Value ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Assert: stream.[[state]] is "writable" or "erroring".
VERIFY ( first_is_one_of ( stream . state ( ) , WritableStream : : State : : Writable , WritableStream : : State : : Erroring ) ) ;
// 2. Set controller.[[started]] to true.
controller . set_started ( true ) ;
// 3. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
writable_stream_default_controller_advance_queue_if_needed ( controller ) ;
return JS : : js_undefined ( ) ;
} ) ,
// 18. Upon rejection of startPromise with reason r,
GC : : create_function ( realm . heap ( ) , [ & stream , & controller ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Assert: stream.[[state]] is "writable" or "erroring".
VERIFY ( first_is_one_of ( stream . state ( ) , WritableStream : : State : : Writable , WritableStream : : State : : Erroring ) ) ;
// 2. Set controller.[[started]] to true.
controller . set_started ( true ) ;
// 3. Perform ! WritableStreamDealWithRejection(stream, r).
writable_stream_deal_with_rejection ( stream , reason ) ;
return JS : : js_undefined ( ) ;
} ) ) ;
return { } ;
}
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
2026-04-25 16:02:49 -03:00
WebIDL : : ExceptionOr < void > set_up_writable_stream_default_controller_from_underlying_sink ( WritableStream & stream , JS : : Value underlying_sink_value , Bindings : : UnderlyingSink & underlying_sink , double high_water_mark , GC : : Ref < SizeAlgorithm > size_algorithm )
2025-04-17 18:21:29 -03:00
{
auto & realm = stream . realm ( ) ;
// 1. Let controller be a new WritableStreamDefaultController.
auto controller = realm . create < WritableStreamDefaultController > ( realm ) ;
// 2. Let startAlgorithm be an algorithm that returns undefined.
auto start_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( ) - > WebIDL : : ExceptionOr < JS : : Value > { return JS : : js_undefined ( ) ; } ) ;
// 3. Let writeAlgorithm be an algorithm that returns a promise resolved with undefined.
auto write_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm ] ( JS : : Value ) {
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 4. Let closeAlgorithm be an algorithm that returns a promise resolved with undefined.
auto close_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm ] ( ) {
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 5. Let abortAlgorithm be an algorithm that returns a promise resolved with undefined.
auto abort_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm ] ( JS : : Value ) {
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 6. If underlyingSinkDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of
// invoking underlyingSinkDict["start"] with argument list « controller », exception behavior "rethrow", and
// callback this value underlyingSink.
if ( underlying_sink . start ) {
start_algorithm = GC : : create_function ( realm . heap ( ) , [ controller , underlying_sink_value , callback = underlying_sink . start ] ( ) - > WebIDL : : ExceptionOr < JS : : Value > {
return TRY ( WebIDL : : invoke_callback ( * callback , underlying_sink_value , WebIDL : : ExceptionBehavior : : Rethrow , { { controller } } ) ) ;
} ) ;
}
// 7. If underlyingSinkDict["write"] exists, then set writeAlgorithm to an algorithm which takes an argument chunk
// and returns the result of invoking underlyingSinkDict["write"] with argument list « chunk, controller » and
// callback this value underlyingSink.
if ( underlying_sink . write ) {
write_algorithm = GC : : create_function ( realm . heap ( ) , [ controller , underlying_sink_value , callback = underlying_sink . write ] ( JS : : Value chunk ) {
return WebIDL : : invoke_promise_callback ( * callback , underlying_sink_value , { { chunk , controller } } ) ;
} ) ;
}
// 8. If underlyingSinkDict["close"] exists, then set closeAlgorithm to an algorithm which returns the result of
// invoking underlyingSinkDict["close"] with argument list «» and callback this value underlyingSink.
if ( underlying_sink . close ) {
close_algorithm = GC : : create_function ( realm . heap ( ) , [ underlying_sink_value , callback = underlying_sink . close ] ( ) {
return WebIDL : : invoke_promise_callback ( * callback , underlying_sink_value , { } ) ;
} ) ;
}
// 9. If underlyingSinkDict["abort"] exists, then set abortAlgorithm to an algorithm which takes an argument reason
// and returns the result of invoking underlyingSinkDict["abort"] with argument list « reason » and callback this
// value underlyingSink.
if ( underlying_sink . abort ) {
abort_algorithm = GC : : create_function ( realm . heap ( ) , [ underlying_sink_value , callback = underlying_sink . abort ] ( JS : : Value reason ) {
return WebIDL : : invoke_promise_callback ( * callback , underlying_sink_value , { { reason } } ) ;
} ) ;
}
// 10. Perform ? SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm).
TRY ( set_up_writable_stream_default_controller ( stream , controller , start_algorithm , write_algorithm , close_algorithm , abort_algorithm , high_water_mark , size_algorithm ) ) ;
return { } ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-advance-queue-if-needed
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).
writable_stream_finish_erroring ( stream ) ;
// 2. Return.
return ;
}
// 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 ) ) {
writable_stream_default_controller_process_close ( controller ) ;
}
// 10. Otherwise, perform ! WritableStreamDefaultControllerProcessWrite(controller, value).
else {
writable_stream_default_controller_process_write ( controller , value ) ;
}
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-clear-algorithms
void writable_stream_default_controller_clear_algorithms ( WritableStreamDefaultController & controller )
{
// 1. Set controller.[[writeAlgorithm]] to undefined.
controller . set_write_algorithm ( { } ) ;
// 2. Set controller.[[closeAlgorithm]] to undefined.
controller . set_close_algorithm ( { } ) ;
// 3. Set controller.[[abortAlgorithm]] to undefined.
controller . set_abort_algorithm ( { } ) ;
// 4. Set controller.[[strategySizeAlgorithm]] to undefined.
controller . set_strategy_size_algorithm ( { } ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-close
void writable_stream_default_controller_close ( WritableStreamDefaultController & controller )
{
// 1. Perform ! EnqueueValueWithSize(controller, close sentinel, 0).
MUST ( enqueue_value_with_size ( controller , create_close_sentinel ( ) , JS : : Value ( 0.0 ) ) ) ;
// 2. Perform ! WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller).
writable_stream_default_controller_advance_queue_if_needed ( controller ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error
void writable_stream_default_controller_error ( WritableStreamDefaultController & controller , JS : : Value error )
{
// 1. Let stream be controller.[[stream]].
auto stream = controller . stream ( ) ;
// 2. Assert: stream.[[state]] is "writable".
VERIFY ( stream - > state ( ) = = WritableStream : : State : : Writable ) ;
// 3. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
writable_stream_default_controller_clear_algorithms ( controller ) ;
// 4. Perform ! WritableStreamStartErroring(stream, error).
writable_stream_start_erroring ( stream , error ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-error-if-needed
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 )
writable_stream_default_controller_error ( controller , 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-chunk-size
JS : : Value writable_stream_default_controller_get_chunk_size ( WritableStreamDefaultController & controller , JS : : Value chunk )
{
// 1. If controller.[[strategySizeAlgorithm]] is undefined, then:
if ( ! controller . strategy_size_algorithm ( ) ) {
// 1. Assert: controller.[[stream]].[[state]] is not "writable".
VERIFY ( controller . stream ( ) - > state ( ) ! = WritableStream : : State : : Writable ) ;
// 2. Return 1.
return JS : : Value { 1.0 } ;
}
// 2. 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 ( ) - > function ( ) ( chunk ) ;
// 3. If returnValue is an abrupt completion,
if ( return_value . is_abrupt ( ) ) {
// 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, returnValue.[[Value]]).
writable_stream_default_controller_error_if_needed ( controller , return_value . release_value ( ) ) ;
// 2. Return 1.
return JS : : Value { 1.0 } ;
}
// 4. 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 )
{
// 1. Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
return controller . strategy_hwm ( ) - controller . queue_total_size ( ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-close
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 = controller . close_algorithm ( ) - > function ( ) ( ) ;
// 6. Perform ! WritableStreamDefaultControllerClearAlgorithms(controller).
writable_stream_default_controller_clear_algorithms ( controller ) ;
WebIDL : : react_to_promise ( sink_close_promise ,
// 7. Upon fulfillment of sinkClosePromise,
GC : : create_function ( controller . heap ( ) , [ stream ] ( JS : : Value ) - > 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,
GC : : create_function ( controller . heap ( ) , [ stream ] ( JS : : Value reason ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Perform ! WritableStreamFinishInFlightCloseWithError(stream, reason).
writable_stream_finish_in_flight_close_with_error ( stream , reason ) ;
return JS : : js_undefined ( ) ;
} ) ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-process-write
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 = controller . write_algorithm ( ) - > function ( ) ( chunk ) ;
WebIDL : : react_to_promise ( sink_write_promise ,
// 4. Upon fulfillment of sinkWritePromise,
GC : : create_function ( controller . heap ( ) , [ & controller , stream ] ( JS : : Value ) - > 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).
writable_stream_default_controller_advance_queue_if_needed ( controller ) ;
return JS : : js_undefined ( ) ;
} ) ,
// 5. Upon rejection of sinkWritePromise with reason,
GC : : create_function ( controller . heap ( ) , [ & controller , stream ] ( JS : : Value 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).
writable_stream_finish_in_flight_write_with_error ( stream , reason ) ;
return JS : : js_undefined ( ) ;
} ) ) ;
}
// https://streams.spec.whatwg.org/#writable-stream-default-controller-write
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]]).
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).
writable_stream_default_controller_advance_queue_if_needed ( controller ) ;
}
}