2022-09-21 23:27:13 +01:00
/*
* Copyright ( c ) 2022 , Linus Groh < linusg @ serenityos . org >
2023-03-28 17:43:34 -07:00
* Copyright ( c ) 2023 , Matthew Olsson < mattco @ serenityos . org >
2022-09-21 23:27:13 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-03-28 18:56:11 -07:00
# include <LibJS/Runtime/PromiseCapability.h>
# include <LibJS/Runtime/PromiseConstructor.h>
# include <LibWeb/Bindings/ExceptionOrUtils.h>
2022-09-21 23:27:13 +01:00
# include <LibWeb/Streams/AbstractOperations.h>
# include <LibWeb/Streams/ReadableStream.h>
2023-03-28 18:56:11 -07:00
# include <LibWeb/Streams/ReadableStreamDefaultController.h>
2023-03-28 18:30:22 -07:00
# include <LibWeb/Streams/ReadableStreamDefaultReader.h>
2023-03-28 17:43:34 -07:00
# include <LibWeb/Streams/ReadableStreamGenericReader.h>
2023-04-02 15:15:53 -07:00
# include <LibWeb/Streams/UnderlyingSink.h>
2023-03-28 19:11:22 -07:00
# include <LibWeb/Streams/UnderlyingSource.h>
2023-04-02 15:15:53 -07:00
# include <LibWeb/Streams/WritableStream.h>
2023-03-30 19:27:45 -07:00
# include <LibWeb/Streams/WritableStreamDefaultWriter.h>
2023-03-28 19:11:22 -07:00
# include <LibWeb/WebIDL/AbstractOperations.h>
2023-03-28 17:43:34 -07:00
# include <LibWeb/WebIDL/ExceptionOr.h>
2023-03-28 18:30:22 -07:00
# include <LibWeb/WebIDL/Promise.h>
2022-09-21 23:27:13 +01:00
namespace Web : : Streams {
2023-03-28 20:01:04 -07:00
// https://streams.spec.whatwg.org/#acquire-readable-stream-reader
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < ReadableStreamDefaultReader > > acquire_readable_stream_default_reader ( ReadableStream & stream )
{
auto & realm = stream . realm ( ) ;
// 1. Let reader be a new ReadableStreamDefaultReader.
auto reader = TRY ( realm . heap ( ) . allocate < ReadableStreamDefaultReader > ( realm , realm ) ) ;
// 2. Perform ? SetUpReadableStreamDefaultReader(reader, stream).
TRY ( set_up_readable_stream_default_reader ( reader , stream ) ) ;
// 3. Return reader.
return reader ;
}
2022-09-21 23:27:13 +01:00
// https://streams.spec.whatwg.org/#is-readable-stream-locked
bool is_readable_stream_locked ( ReadableStream const & stream )
{
// 1. If stream.[[reader]] is undefined, return false.
2023-03-28 18:30:22 -07:00
if ( ! stream . reader ( ) )
2022-09-21 23:27:13 +01:00
return false ;
// 2. Return true.
return true ;
}
2023-03-28 17:43:34 -07:00
// https://streams.spec.whatwg.org/#readable-stream-cancel
WebIDL : : ExceptionOr < JS : : NonnullGCPtr < WebIDL : : Promise > > readable_stream_cancel ( ReadableStream & stream , JS : : Value reason )
{
auto & realm = stream . realm ( ) ;
// 1. Set stream.[[disturbed]] to true.
stream . set_disturbed ( true ) ;
// 2. If stream.[[state]] is "closed", return a promise resolved with undefined.
if ( stream . is_closed ( ) )
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
// 3. If stream.[[state]] is "errored", return a promise rejected with stream.[[storedError]].
if ( stream . is_errored ( ) )
return WebIDL : : create_rejected_promise ( realm , stream . stored_error ( ) ) ;
// 4. Perform ! ReadableStreamClose(stream).
readable_stream_close ( stream ) ;
// 5. Let reader be stream.[[reader]].
auto reader = stream . reader ( ) ;
// FIXME:
// 6. If reader is not undefined and reader implements ReadableStreamBYOBReader,
// 1. Let readIntoRequests be reader.[[readIntoRequests]].
// 2. Set reader.[[readIntoRequests]] to an empty list.
// 3. For each readIntoRequest of readIntoRequests,
// 1. Perform readIntoRequest’ s close steps, given undefined.
2023-03-28 18:56:11 -07:00
( void ) reader ;
2023-03-28 17:43:34 -07:00
// 7. Let sourceCancelPromise be ! stream.[[controller]].[[CancelSteps]](reason).
2023-03-28 18:56:11 -07:00
auto source_cancel_promise = MUST ( stream . controller ( ) - > cancel_steps ( reason ) ) ;
2023-03-28 17:43:34 -07:00
// 8. Return the result of reacting to sourceCancelPromise with a fulfillment step that returns undefined.
2023-03-28 18:56:11 -07:00
auto react_result = WebIDL : : react_to_promise ( * source_cancel_promise ,
[ ] ( auto const & ) - > WebIDL : : ExceptionOr < JS : : Value > { return JS : : js_undefined ( ) ; } ,
{ } ) ;
return WebIDL : : create_resolved_promise ( realm , react_result ) ;
}
// https://streams.spec.whatwg.org/#readable-stream-fulfill-read-request
void readable_stream_fulfill_read_request ( ReadableStream & stream , JS : : Value chunk , bool done )
{
// 1. Assert: ! ReadableStreamHasDefaultReader(stream) is true.
VERIFY ( readable_stream_has_default_reader ( stream ) ) ;
// 2. Let reader be stream.[[reader]].
auto & reader = * stream . reader ( ) ;
// 3. Assert: reader.[[readRequests]] is not empty.
VERIFY ( ! reader . read_requests ( ) . is_empty ( ) ) ;
// 4. Let readRequest be reader.[[readRequests]][0].
// 5. Remove readRequest from reader.[[readRequests]].
auto read_request = reader . read_requests ( ) . take_first ( ) ;
// 6. If done is true, perform readRequest’ s close steps.
if ( done ) {
read_request - > on_close ( ) ;
}
// 7. Otherwise, perform readRequest’ s chunk steps, given chunk.
else {
read_request - > on_chunk ( chunk ) ;
}
}
// https://streams.spec.whatwg.org/#readable-stream-get-num-read-requests
size_t readable_stream_get_num_read_requests ( ReadableStream & stream )
{
// 1. Assert: ! ReadableStreamHasDefaultReader(stream) is true.
VERIFY ( readable_stream_has_default_reader ( stream ) ) ;
2023-03-28 17:43:34 -07:00
2023-03-28 18:56:11 -07:00
// 2. Return stream.[[reader]].[[readRequests]]'s size.
return stream . reader ( ) - > read_requests ( ) . size ( ) ;
}
// https://streams.spec.whatwg.org/#readable-stream-has-default-reader
bool readable_stream_has_default_reader ( ReadableStream & stream )
{
// 1. Let reader be stream.[[reader]].
auto reader = stream . reader ( ) ;
// 2. If reader is undefined, return false.
if ( ! reader )
return false ;
// 3. If reader implements ReadableStreamDefaultReader, return true.
if ( reader - > is_default_reader ( ) )
return true ;
// 4. Return false.
return false ;
2023-03-28 17:43:34 -07:00
}
// https://streams.spec.whatwg.org/#readable-stream-close
void readable_stream_close ( ReadableStream & stream )
{
auto & realm = stream . realm ( ) ;
// 1. Assert: stream.[[state]] is "readable".
VERIFY ( stream . is_readable ( ) ) ;
// 2. Set stream.[[state]] to "closed".
stream . set_stream_state ( ReadableStream : : State : : Closed ) ;
// 3. Let reader be stream.[[reader]].
auto reader = stream . reader ( ) ;
// 4. If reader is undefined, return.
if ( ! reader )
return ;
// 5. Resolve reader.[[closedPromise]] with undefined.
WebIDL : : resolve_promise ( realm , * reader - > closed_promise_capability ( ) ) ;
// 6. If reader implements ReadableStreamDefaultReader,
2023-03-28 18:30:22 -07:00
if ( reader - > is_default_reader ( ) ) {
// 1. Let readRequests be reader.[[readRequests]].
// 2. Set reader.[[readRequests]] to an empty list.
auto read_requests = move ( reader - > read_requests ( ) ) ;
// 3. For each readRequest of readRequests,
for ( auto & read_request : read_requests ) {
// 1. Perform readRequest’ s close steps.
read_request - > on_close ( ) ;
}
}
2023-03-28 17:43:34 -07:00
}
2023-03-28 18:56:11 -07:00
// https://streams.spec.whatwg.org/#readable-stream-error
void readable_stream_error ( ReadableStream & stream , JS : : Value error )
{
auto & realm = stream . realm ( ) ;
// 1. Assert: stream.[[state]] is "readable".
VERIFY ( stream . is_readable ( ) ) ;
// 2. Set stream.[[state]] to "errored".
stream . set_stream_state ( ReadableStream : : State : : Errored ) ;
// 3. Set stream.[[storedError]] to e.
stream . set_stored_error ( error ) ;
// 4. Let reader be stream.[[reader]].
auto reader = stream . reader ( ) ;
// 5. If reader is undefined, return.
if ( ! reader )
return ;
// 6. Reject reader.[[closedPromise]] with e.
WebIDL : : reject_promise ( realm , * reader - > closed_promise_capability ( ) , error ) ;
// 7. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * reader - > closed_promise_capability ( ) ) ;
// 8. If reader implements ReadableStreamDefaultReader,
if ( reader - > is_default_reader ( ) ) {
// 1. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
readable_stream_default_reader_error_read_requests ( * reader , error ) ;
}
// 9. Otherwise,
else {
// 1. Assert: reader implements ReadableStreamBYOBReader.
// 2. Perform ! ReadableStreamBYOBReaderErrorReadIntoRequests(reader, e).
// FIXME: Handle BYOBReader
TODO ( ) ;
}
}
// https://streams.spec.whatwg.org/#readable-stream-add-read-request
void readable_stream_add_read_request ( ReadableStream & stream , ReadRequest const & read_request )
{
// FIXME: Check implementation type
// 1. Assert: stream.[[reader]] implements ReadableStreamDefaultReader.
VERIFY ( stream . reader ( ) ) ;
// 2. Assert: stream.[[state]] is "readable".
VERIFY ( stream . is_readable ( ) ) ;
// 3. Append readRequest to stream.[[reader]].[[readRequests]].
stream . reader ( ) - > read_requests ( ) . append ( read_request ) ;
}
2023-03-28 17:43:34 -07:00
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-cancel
JS : : NonnullGCPtr < WebIDL : : Promise > readable_stream_reader_generic_cancel ( ReadableStreamGenericReaderMixin & reader , JS : : Value reason )
{
// 1. Let stream be reader.[[stream]]
auto stream = reader . stream ( ) ;
// 2. Assert: stream is not undefined
VERIFY ( stream ) ;
// 3. Return ! ReadableStreamCancel(stream, reason)
return MUST ( readable_stream_cancel ( * stream , reason ) ) ;
}
2023-03-28 18:30:22 -07:00
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-initialize
void readable_stream_reader_generic_initialize ( ReadableStreamGenericReaderMixin & reader , ReadableStream & stream )
{
auto & realm = stream . realm ( ) ;
// 1. Set reader.[[stream]] to stream.
reader . set_stream ( stream ) ;
// 2. Set stream.[[reader]] to reader.
if ( reader . is_default_reader ( ) ) {
stream . set_reader ( static_cast < ReadableStreamDefaultReader & > ( reader ) ) ;
} else {
// FIXME: Handle other descendents of ReadableStreamGenericReaderMixin (i.e. BYOBReader)
TODO ( ) ;
}
// 3. If stream.[[state]] is "readable",
if ( stream . is_readable ( ) ) {
// 1. Set reader.[[closedPromise]] to a new promise.
reader . set_closed_promise_capability ( WebIDL : : create_promise ( realm ) ) ;
}
// 4. Otherwise, if stream.[[state]] is "closed",
else if ( stream . is_closed ( ) ) {
// 1. Set reader.[[closedPromise]] to a promise resolved with undefined.
reader . set_closed_promise_capability ( WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ) ;
}
// 5. Otherwise,
else {
// 1. Assert: stream.[[state]] is "errored".
VERIFY ( stream . is_errored ( ) ) ;
// 2. Set reader.[[closedPromise]] to a promise rejected with stream.[[storedError]].
reader . set_closed_promise_capability ( WebIDL : : create_rejected_promise ( realm , stream . stored_error ( ) ) ) ;
// 3. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * reader . closed_promise_capability ( ) ) ;
}
}
// https://streams.spec.whatwg.org/#readable-stream-reader-generic-release
WebIDL : : ExceptionOr < void > readable_stream_reader_generic_release ( ReadableStreamGenericReaderMixin & reader )
{
// 1. Let stream be reader.[[stream]].
auto stream = reader . stream ( ) ;
// 2. Assert: stream is not undefined.
VERIFY ( stream ) ;
// 3. Assert: stream.[[reader]] is reader.
VERIFY ( stream - > reader ( ) . ptr ( ) = = & reader ) ;
// 4. If stream.[[state]] is "readable", reject reader.[[closedPromise]] with a TypeError exception.
auto exception = TRY ( JS : : TypeError : : create ( stream - > realm ( ) , " Released readable stream " sv ) ) ;
if ( stream - > is_readable ( ) ) {
WebIDL : : reject_promise ( stream - > realm ( ) , * reader . closed_promise_capability ( ) , exception ) ;
}
// 5. Otherwise, set reader.[[closedPromise]] to a promise rejected with a TypeError exception.
else {
reader . set_closed_promise_capability ( WebIDL : : create_rejected_promise ( stream - > realm ( ) , exception ) ) ;
}
// 6. Set reader.[[closedPromise]].[[PromiseIsHandled]] to true.
WebIDL : : mark_promise_as_handled ( * reader . closed_promise_capability ( ) ) ;
2023-03-28 18:56:11 -07:00
// 7. Perform ! stream.[[controller]].[[ReleaseSteps]]().
stream - > controller ( ) - > release_steps ( ) ;
2023-03-28 18:30:22 -07:00
// 8. Set stream.[[reader]] to undefined.
stream - > set_reader ( { } ) ;
// 9. Set reader.[[stream]] to undefined.
reader . set_stream ( { } ) ;
return { } ;
}
// https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreadererrorreadrequests
void readable_stream_default_reader_error_read_requests ( ReadableStreamDefaultReader & reader , JS : : Value error )
{
// 1. Let readRequests be reader.[[readRequests]].
// 2. Set reader.[[readRequests]] to a new empty list.
auto read_requests = move ( reader . read_requests ( ) ) ;
// 3. For each readRequest of readRequests,
for ( auto & read_request : read_requests ) {
// 1. Perform readRequest’ s error steps, given e.
read_request - > on_error ( error ) ;
}
}
// https://streams.spec.whatwg.org/#readable-stream-default-reader-read
void readable_stream_default_reader_read ( ReadableStreamDefaultReader & reader , ReadRequest & read_request )
{
// 1. Let stream be reader.[[stream]].
auto stream = reader . stream ( ) ;
// 2. Assert: stream is not undefined.
VERIFY ( stream ) ;
// 3. Set stream.[[disturbed]] to true.
stream - > set_disturbed ( true ) ;
// 4. If stream.[[state]] is "closed", perform readRequest’ s close steps.
if ( stream - > is_closed ( ) ) {
read_request . on_close ( ) ;
}
// 5. Otherwise, if stream.[[state]] is "errored", perform readRequest’ s error steps given stream.[[storedError]].
else if ( stream - > is_errored ( ) ) {
read_request . on_error ( stream - > stored_error ( ) ) ;
}
// 6. Otherwise,
else {
// 1. Assert: stream.[[state]] is "readable".
VERIFY ( stream - > is_readable ( ) ) ;
2023-03-28 18:56:11 -07:00
// 2. Perform ! stream.[[controller]].[[PullSteps]](readRequest).
MUST ( stream - > controller ( ) - > pull_steps ( read_request ) ) ;
2023-03-28 18:30:22 -07:00
}
}
// https://streams.spec.whatwg.org/#abstract-opdef-readablestreamdefaultreaderrelease
WebIDL : : ExceptionOr < void > readable_stream_default_reader_release ( ReadableStreamDefaultReader & reader )
{
// 1. Perform ! ReadableStreamReaderGenericRelease(reader).
TRY ( readable_stream_reader_generic_release ( reader ) ) ;
// 2. Let e be a new TypeError exception.
auto e = TRY ( JS : : TypeError : : create ( reader . realm ( ) , " Reader has been released " sv ) ) ;
// 3. Perform ! ReadableStreamDefaultReaderErrorReadRequests(reader, e).
readable_stream_default_reader_error_read_requests ( reader , e ) ;
return { } ;
}
// https://streams.spec.whatwg.org/#set-up-readable-stream-default-reader
WebIDL : : ExceptionOr < void > set_up_readable_stream_default_reader ( ReadableStreamDefaultReader & reader , ReadableStream & stream )
{
// 1. If ! IsReadableStreamLocked(stream) is true, throw a TypeError exception.
if ( is_readable_stream_locked ( stream ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : TypeError , " Cannot create stream reader for a locked stream " sv } ;
// 2. Perform ! ReadableStreamReaderGenericInitialize(reader, stream).
// 3. Set reader.[[readRequests]] to a new empty list.
readable_stream_reader_generic_initialize ( reader , stream ) ;
return { } ;
}
2023-03-28 18:56:11 -07:00
// https://streams.spec.whatwg.org/#readable-stream-default-controller-close
void readable_stream_default_controller_close ( ReadableStreamDefaultController & controller )
{
// 1. If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is false, return.
if ( ! readable_stream_default_controller_can_close_or_enqueue ( controller ) )
return ;
// 2. Let stream be controller.[[stream]].
auto stream = controller . stream ( ) ;
// 3. Set controller.[[closeRequested]] to true.
controller . set_close_requested ( true ) ;
// 4. If controller.[[queue]] is empty,
if ( controller . queue ( ) . is_empty ( ) ) {
// 1. Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller).
readable_stream_default_controller_clear_algorithms ( controller ) ;
// 2. Perform ! ReadableStreamClose(stream).
readable_stream_close ( * stream ) ;
}
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-enqueue
WebIDL : : ExceptionOr < void > readable_stream_default_controller_enqueue ( ReadableStreamDefaultController & controller , JS : : Value chunk )
{
auto & vm = controller . vm ( ) ;
// 1. If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is false, return.
if ( ! readable_stream_default_controller_can_close_or_enqueue ( controller ) )
return { } ;
// 2. Let stream be controller.[[stream]].
auto stream = controller . stream ( ) ;
// 3. If ! IsReadableStreamLocked(stream) is true and ! ReadableStreamGetNumReadRequests(stream) > 0, perform ! ReadableStreamFulfillReadRequest(stream, chunk, false).
if ( is_readable_stream_locked ( * stream ) & & readable_stream_get_num_read_requests ( * stream ) > 0 ) {
readable_stream_fulfill_read_request ( * stream , chunk , false ) ;
}
// 4. Otherwise,
else {
// 1. Let result be the result of performing controller.[[strategySizeAlgorithm]], passing in chunk, and interpreting the result as a completion record.
auto result = ( * controller . strategy_size_algorithm ( ) ) ( chunk ) ;
// 2. If result is an abrupt completion,
if ( result . is_abrupt ( ) ) {
// 1. Perform ! ReadableStreamDefaultControllerError(controller, result.[[Value]]).
readable_stream_default_controller_error ( controller , result . value ( ) . value ( ) ) ;
// 2. Return result.
return result ;
}
// 3. Let chunkSize be result.[[Value]].
auto chunk_size = TRY ( result . release_value ( ) . release_value ( ) . to_double ( vm ) ) ;
// 4. Let enqueueResult be EnqueueValueWithSize(controller, chunk, chunkSize).
auto enqueue_result = enqueue_value_with_size ( controller , chunk , chunk_size ) ;
// 5. If enqueueResult is an abrupt completion,
if ( enqueue_result . is_error ( ) ) {
auto throw_completion = Bindings : : throw_dom_exception_if_needed ( vm , [ & ] { return enqueue_result ; } ) . throw_completion ( ) ;
// 1. Perform ! ReadableStreamDefaultControllerError(controller, enqueueResult.[[Value]]).
readable_stream_default_controller_error ( controller , throw_completion . value ( ) . value ( ) ) ;
// 2. Return enqueueResult.
return enqueue_result ;
}
}
// 5. Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
return readable_stream_default_controller_can_pull_if_needed ( controller ) ;
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-call-pull-if-needed
WebIDL : : ExceptionOr < void > readable_stream_default_controller_can_pull_if_needed ( ReadableStreamDefaultController & controller )
{
// 1. Let shouldPull be ! ReadableStreamDefaultControllerShouldCallPull(controller).
auto should_pull = readable_stream_default_controller_should_call_pull ( controller ) ;
// 2. If shouldPull is false, return.
if ( ! should_pull )
return { } ;
// 3. If controller.[[pulling]] is true,
if ( controller . pulling ( ) ) {
// 1. Set controller.[[pullAgain]] to true.
controller . set_pull_again ( true ) ;
// 2. Return.
return { } ;
}
// 4. Assert: controller.[[pullAgain]] is false.
VERIFY ( ! controller . pull_again ( ) ) ;
// 5. Set controller.[[pulling]] to true.
controller . set_pulling ( true ) ;
// 6. Let pullPromise be the result of performing controller.[[pullAlgorithm]].
auto pull_promise = TRY ( ( * controller . pull_algorithm ( ) ) ( ) ) ;
// 7. Upon fulfillment of pullPromise,
WebIDL : : upon_fulfillment ( * pull_promise , [ & ] ( auto const & ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Set controller.[[pulling]] to false.
controller . set_pulling ( false ) ;
// 2. If controller.[[pullAgain]] is true,
if ( controller . pull_again ( ) ) {
// 1. Set controller.[[pullAgain]] to false.
controller . set_pull_again ( false ) ;
// 2. Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
TRY ( readable_stream_default_controller_can_pull_if_needed ( controller ) ) ;
}
return JS : : js_undefined ( ) ;
} ) ;
// 8. Upon rejection of pullPromise with reason e,
WebIDL : : upon_rejection ( * pull_promise , [ & ] ( auto const & e ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Perform ! ReadableStreamDefaultControllerError(controller, e).
readable_stream_default_controller_error ( controller , e ) ;
return JS : : js_undefined ( ) ;
} ) ;
return { } ;
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-should-call-pull
bool readable_stream_default_controller_should_call_pull ( ReadableStreamDefaultController & controller )
{
// 1. Let stream be controller.[[stream]].
auto stream = controller . stream ( ) ;
// 2. If ! ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) is false, return false.
if ( ! readable_stream_default_controller_can_close_or_enqueue ( controller ) )
return false ;
// 3. If controller.[[started]] is false, return false.
if ( ! controller . started ( ) )
return false ;
// 4. If ! IsReadableStreamLocked(stream) is true and ! ReadableStreamGetNumReadRequests(stream) > 0, return true.
if ( is_readable_stream_locked ( * stream ) & & readable_stream_get_num_read_requests ( * stream ) > 0 )
return true ;
// 5. Let desiredSize be ! ReadableStreamDefaultControllerGetDesiredSize(controller).
auto desired_size = readable_stream_default_controller_get_desired_size ( controller ) ;
// 6. Assert: desiredSize is not null.
VERIFY ( desired_size . has_value ( ) ) ;
// 7. If desiredSize > 0, return true.
if ( desired_size . release_value ( ) > 0 )
return true ;
// 8. Return false.
return false ;
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-clear-algorithms
void readable_stream_default_controller_clear_algorithms ( ReadableStreamDefaultController & controller )
{
// 1. Set controller.[[pullAlgorithm]] to undefined.
controller . set_pull_algorithm ( { } ) ;
// 2. Set controller.[[cancelAlgorithm]] to undefined.
controller . set_cancel_algorithm ( { } ) ;
// 3. Set controller.[[strategySizeAlgorithm]] to undefined.
controller . set_strategy_size_algorithm ( { } ) ;
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-error
void readable_stream_default_controller_error ( ReadableStreamDefaultController & controller , JS : : Value error )
{
// 1. Let stream be controller.[[stream]].
auto stream = controller . stream ( ) ;
// 2. If stream.[[state]] is not "readable", return.
if ( ! stream - > is_readable ( ) )
return ;
// 3. Perform ! ResetQueue(controller).
reset_queue ( controller ) ;
// 4. Perform ! ReadableStreamDefaultControllerClearAlgorithms(controller).
readable_stream_default_controller_clear_algorithms ( controller ) ;
// 5. Perform ! ReadableStreamError(stream, e).
readable_stream_error ( * stream , error ) ;
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-get-desired-size
Optional < float > readable_stream_default_controller_get_desired_size ( ReadableStreamDefaultController & controller )
{
auto stream = controller . stream ( ) ;
// 1. Let state be controller.[[stream]].[[state]].
// 2. If state is "errored", return null.
if ( stream - > is_errored ( ) )
return { } ;
// 3. If state is "closed", return 0.
if ( stream - > is_closed ( ) )
return 0.0f ;
// 4. Return controller.[[strategyHWM]] − controller.[[queueTotalSize]].
return controller . strategy_hwm ( ) - controller . queue_total_size ( ) ;
}
// https://streams.spec.whatwg.org/#readable-stream-default-controller-can-close-or-enqueue
bool readable_stream_default_controller_can_close_or_enqueue ( ReadableStreamDefaultController & controller )
{
// 1. Let state be controller.[[stream]].[[state]].
// 2. If controller.[[closeRequested]] is false and state is "readable", return true.
// 3. Otherwise, return false.
return ! controller . close_requested ( ) & & controller . stream ( ) - > is_readable ( ) ;
}
2023-03-28 19:11:22 -07:00
// https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller
WebIDL : : ExceptionOr < void > set_up_readable_stream_default_controller ( ReadableStream & stream , ReadableStreamDefaultController & controller , StartAlgorithm & & start_algorithm , PullAlgorithm & & pull_algorithm , CancelAlgorithm & & cancel_algorithm , double high_water_mark , SizeAlgorithm & & size_algorithm )
{
auto & realm = stream . realm ( ) ;
// 1. Assert: stream.[[controller]] is undefined.
VERIFY ( ! stream . controller ( ) ) ;
// 2. Set controller.[[stream]] to stream.
controller . set_stream ( stream ) ;
// 3. Perform ! ResetQueue(controller).
reset_queue ( controller ) ;
// 4. Set controller.[[started]], controller.[[closeRequested]], controller.[[pullAgain]], and controller.[[pulling]] to false.
controller . set_started ( false ) ;
controller . set_close_requested ( false ) ;
controller . set_pull_again ( false ) ;
controller . set_pulling ( false ) ;
// 5. Set controller.[[strategySizeAlgorithm]] to sizeAlgorithm and controller.[[strategyHWM]] to highWaterMark.
controller . set_strategy_size_algorithm ( move ( size_algorithm ) ) ;
controller . set_strategy_hwm ( high_water_mark ) ;
// 6. Set controller.[[pullAlgorithm]] to pullAlgorithm.
controller . set_pull_algorithm ( move ( pull_algorithm ) ) ;
// 7. Set controller.[[cancelAlgorithm]] to cancelAlgorithm.
controller . set_cancel_algorithm ( move ( cancel_algorithm ) ) ;
// 8. Set stream.[[controller]] to controller.
stream . set_controller ( controller ) ;
// 9. Let startResult be the result of performing startAlgorithm. (This might throw an exception.)
auto start_result = TRY ( start_algorithm ( ) ) ;
// 10. Let startPromise be a promise resolved with startResult.
auto start_promise = WebIDL : : create_resolved_promise ( realm , start_result ? start_result - > promise ( ) : JS : : js_undefined ( ) ) ;
// 11. Upon fulfillment of startPromise,
WebIDL : : upon_fulfillment ( start_promise , [ & ] ( auto const & ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Set controller.[[started]] to true.
controller . set_started ( true ) ;
// 2. Assert: controller.[[pulling]] is false.
VERIFY ( ! controller . pulling ( ) ) ;
// 3. Assert: controller.[[pullAgain]] is false.
VERIFY ( ! controller . pull_again ( ) ) ;
// 4. Perform ! ReadableStreamDefaultControllerCallPullIfNeeded(controller).
TRY ( readable_stream_default_controller_can_pull_if_needed ( controller ) ) ;
return JS : : js_undefined ( ) ;
} ) ;
// 12. Upon rejection of startPromise with reason r,
WebIDL : : upon_rejection ( start_promise , [ & ] ( auto const & r ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Perform ! ReadableStreamDefaultControllerError(controller, r).
readable_stream_default_controller_error ( controller , r ) ;
return JS : : js_undefined ( ) ;
} ) ;
return { } ;
}
// https://streams.spec.whatwg.org/#set-up-readable-stream-default-controller-from-underlying-source
WebIDL : : ExceptionOr < void > set_up_readable_stream_default_controller_from_underlying_source ( ReadableStream & stream , JS : : Value underlying_source_value , UnderlyingSource underlying_source , double high_water_mark , SizeAlgorithm & & size_algorithm )
{
auto & realm = stream . realm ( ) ;
// 1. Let controller be a new ReadableStreamDefaultController.
auto controller = MUST_OR_THROW_OOM ( stream . heap ( ) . allocate < ReadableStreamDefaultController > ( realm , realm ) ) ;
// 2. Let startAlgorithm be an algorithm that returns undefined.
StartAlgorithm start_algorithm = [ ] { return JS : : GCPtr < WebIDL : : Promise > { } ; } ;
// 3. Let pullAlgorithm be an algorithm that returns a promise resolved with undefined.
PullAlgorithm pull_algorithm = [ & realm ] ( ) {
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ;
// 4. Let cancelAlgorithm be an algorithm that returns a promise resolved with undefined.
CancelAlgorithm cancel_algorithm = [ & realm ] ( auto const & ) {
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ;
// 5. If underlyingSourceDict["start"] exists, then set startAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["start"] with argument list « controller » and callback this value underlyingSource.
if ( underlying_source . start ) {
start_algorithm = [ & , start = underlying_source . start ] ( ) - > WebIDL : : ExceptionOr < JS : : GCPtr < WebIDL : : Promise > > {
auto result = TRY ( WebIDL : : invoke_callback ( * start , underlying_source_value , controller ) ) . release_value ( ) ;
return WebIDL : : create_resolved_promise ( realm , result ) ;
} ;
}
// 6. If underlyingSourceDict["pull"] exists, then set pullAlgorithm to an algorithm which returns the result of invoking underlyingSourceDict["pull"] with argument list « controller » and callback this value underlyingSource.
if ( underlying_source . pull ) {
pull_algorithm = [ & , pull = underlying_source . pull ] ( ) - > WebIDL : : ExceptionOr < JS : : GCPtr < WebIDL : : Promise > > {
auto result = TRY ( WebIDL : : invoke_callback ( * pull , underlying_source_value , controller ) ) . release_value ( ) ;
return WebIDL : : create_resolved_promise ( realm , result ) ;
} ;
}
// 7. If underlyingSourceDict["cancel"] exists, then set cancelAlgorithm to an algorithm which takes an argument reason and returns the result of invoking underlyingSourceDict["cancel"] with argument list « reason » and callback this value underlyingSource.
if ( underlying_source . cancel ) {
cancel_algorithm = [ & , cancel = underlying_source . cancel ] ( auto const & reason ) - > WebIDL : : ExceptionOr < JS : : GCPtr < WebIDL : : Promise > > {
auto result = TRY ( WebIDL : : invoke_callback ( * cancel , underlying_source_value , reason ) ) . release_value ( ) ;
return WebIDL : : create_resolved_promise ( realm , result ) ;
} ;
}
// 8. Perform ? SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm).
return set_up_readable_stream_default_controller ( stream , controller , move ( start_algorithm ) , move ( pull_algorithm ) , move ( cancel_algorithm ) , high_water_mark , move ( size_algorithm ) ) ;
}
2023-04-02 15:15:53 -07:00
// 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 ;
}
2023-03-30 19:27:45 -07:00
// https://streams.spec.whatwg.org/#set-up-writable-stream-default-writer
WebIDL : : ExceptionOr < void > set_up_writable_stream_default_writer ( WritableStreamDefaultWriter & writer , WritableStream & stream )
{
auto & realm = writer . realm ( ) ;
// 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-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_write_request ( ) )
return false ;
// 2. Return true.
return true ;
}
// 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 ;
// FIXME: 5. Return ! WritableStreamDefaultControllerGetDesiredSize(stream.[[controller]]).
return 0.0 ;
}
2023-04-02 15:14:21 -07:00
// 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 )
{
auto property = TRY ( value . get ( vm , property_key ) ) ;
if ( property . is_undefined ( ) )
return JS : : Handle < WebIDL : : CallbackType > { } ;
if ( ! property . is_function ( ) )
return vm . throw_completion < JS : : TypeError > ( JS : : ErrorType : : NotAFunction , TRY_OR_THROW_OOM ( vm , property . to_string_without_side_effects ( ) ) ) ;
return vm . heap ( ) . allocate_without_realm < WebIDL : : CallbackType > ( property . as_object ( ) , HTML : : incumbent_settings_object ( ) ) ;
}
2022-09-21 23:27:13 +01:00
}