2023-07-06 19:37:12 +02:00
/*
* Copyright ( c ) 2023 , Kenneth Myhra < kennethmyhra @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibWeb/Bindings/Intrinsics.h>
2024-04-27 12:09:58 +12:00
# include <LibWeb/Bindings/TransformStreamPrototype.h>
2023-07-06 19:37:12 +02:00
# include <LibWeb/Streams/AbstractOperations.h>
2025-04-17 15:47:53 -04:00
# include <LibWeb/Streams/ReadableStream.h>
2023-07-06 19:37:12 +02:00
# include <LibWeb/Streams/TransformStream.h>
2023-07-08 20:10:32 +02:00
# include <LibWeb/Streams/TransformStreamDefaultController.h>
2023-07-06 19:37:12 +02:00
# include <LibWeb/Streams/Transformer.h>
# include <LibWeb/Streams/WritableStream.h>
2023-07-08 20:10:32 +02:00
# include <LibWeb/WebIDL/AbstractOperations.h>
2023-07-06 19:37:12 +02:00
# include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web : : Streams {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( TransformStream ) ;
2023-11-19 19:47:52 +01:00
2024-04-29 18:38:03 -04:00
// https://streams.spec.whatwg.org/#ts-constructor
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < GC : : Ref < TransformStream > > TransformStream : : construct_impl ( JS : : Realm & realm , Optional < GC : : Root < JS : : Object > > transformer_object , QueuingStrategy const & writable_strategy , QueuingStrategy const & readable_strategy )
2023-07-06 19:37:12 +02:00
{
2023-07-08 20:10:32 +02:00
auto & vm = realm . vm ( ) ;
2023-07-06 19:37:12 +02:00
2024-11-14 05:50:17 +13:00
auto stream = realm . create < TransformStream > ( realm ) ;
2023-07-08 20:10:32 +02:00
// 1. If transformer is missing, set it to null.
2023-12-18 14:01:39 -07:00
auto transformer = transformer_object . has_value ( ) ? JS : : Value { transformer_object . value ( ) } : JS : : js_null ( ) ;
2023-07-08 20:10:32 +02:00
// 2. Let transformerDict be transformer, converted to an IDL value of type Transformer.
auto transformer_dict = TRY ( Transformer : : from_value ( vm , transformer ) ) ;
// 3. If transformerDict["readableType"] exists, throw a RangeError exception.
if ( transformer_dict . readable_type . has_value ( ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " Invalid use of reserved key 'readableType' " sv } ;
// 4. If transformerDict["writableType"] exists, throw a RangeError exception.
if ( transformer_dict . writable_type . has_value ( ) )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " Invalid use of reserved key 'writableType' " sv } ;
// 5. Let readableHighWaterMark be ? ExtractHighWaterMark(readableStrategy, 0).
auto readable_high_water_mark = TRY ( extract_high_water_mark ( readable_strategy , 0 ) ) ;
// 6. Let readableSizeAlgorithm be ! ExtractSizeAlgorithm(readableStrategy).
2024-01-26 18:54:24 +13:00
auto readable_size_algorithm = extract_size_algorithm ( vm , readable_strategy ) ;
2023-07-08 20:10:32 +02:00
// 7. Let writableHighWaterMark be ? ExtractHighWaterMark(writableStrategy, 1).
auto writable_high_water_mark = TRY ( extract_high_water_mark ( writable_strategy , 1 ) ) ;
// 8. Let writableSizeAlgorithm be ! ExtractSizeAlgorithm(writableStrategy).
2024-01-26 18:54:24 +13:00
auto writable_size_algorithm = extract_size_algorithm ( vm , writable_strategy ) ;
2023-07-08 20:10:32 +02:00
// 9. Let startPromise be a new promise.
auto start_promise = WebIDL : : create_promise ( realm ) ;
2024-04-29 18:01:44 -04:00
// 10. Perform ! InitializeTransformStream(this, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm).
initialize_transform_stream ( * stream , start_promise , writable_high_water_mark , move ( writable_size_algorithm ) , readable_high_water_mark , move ( readable_size_algorithm ) ) ;
2023-07-08 20:10:32 +02:00
// 11. Perform ? SetUpTransformStreamDefaultControllerFromTransformer(this, transformer, transformerDict).
2024-04-29 18:15:07 -04:00
set_up_transform_stream_default_controller_from_transformer ( * stream , transformer , transformer_dict ) ;
2023-07-08 20:10:32 +02:00
// 12. If transformerDict["start"] exists, then resolve startPromise with the result of invoking
2024-04-29 16:17:32 -04:00
// transformerDict["start"] with argument list « this.[[controller]] » and callback this value transformer.
2023-07-08 20:10:32 +02:00
if ( transformer_dict . start ) {
2025-04-15 20:56:03 -04:00
auto result = TRY ( WebIDL : : invoke_callback ( * transformer_dict . start , transformer , { { stream - > controller ( ) } } ) ) ;
2023-07-08 20:10:32 +02:00
WebIDL : : resolve_promise ( realm , start_promise , result ) ;
}
// 13. Otherwise, resolve startPromise with undefined.
else {
WebIDL : : resolve_promise ( realm , start_promise , JS : : js_undefined ( ) ) ;
}
return stream ;
2023-07-06 19:37:12 +02:00
}
2024-12-24 12:56:59 +13:00
// https://streams.spec.whatwg.org/#transformstream-enqueue
void TransformStream : : enqueue ( JS : : Value chunk )
{
// To enqueue the JavaScript value chunk into a TransformStream stream, perform ! TransformStreamDefaultControllerEnqueue(stream.[[controller]], chunk).
MUST ( Streams : : transform_stream_default_controller_enqueue ( * controller ( ) , chunk ) ) ;
}
2024-12-08 17:35:07 +13:00
// https://streams.spec.whatwg.org/#transformstream-set-up
void TransformStream : : set_up ( GC : : Ref < TransformAlgorithm > transform_algorithm , GC : : Ptr < FlushAlgorithm > flush_algorithm , GC : : Ptr < CancelAlgorithm > cancel_algorithm )
{
auto & realm = this - > realm ( ) ;
// 1. Let writableHighWaterMark be 1.
auto writable_high_water_mark = 1.0 ;
// 2. Let writableSizeAlgorithm be an algorithm that returns 1.
auto writable_size_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( JS : : Value ) {
return JS : : normal_completion ( JS : : Value { 1 } ) ;
} ) ;
// 3. Let readableHighWaterMark be 0.
auto readable_high_water_mark = 0.0 ;
// 4. Let readableSizeAlgorithm be an algorithm that returns 1.
auto readable_size_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( JS : : Value ) {
return JS : : normal_completion ( JS : : Value { 1 } ) ;
} ) ;
// 5. Let transformAlgorithmWrapper be an algorithm that runs these steps given a value chunk:
auto transform_algorithm_wrapper = GC : : create_function ( realm . heap ( ) , [ & realm , transform_algorithm ] ( JS : : Value chunk ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Let result be the result of running transformAlgorithm given chunk. If this throws an exception e, return a promise rejected with e.
GC : : Ptr < JS : : PromiseCapability > result = nullptr ;
result = transform_algorithm - > function ( ) ( chunk ) ;
// 2. If result is a Promise, then return result.
if ( result )
return GC : : Ref { * result } ;
// 3. Return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 6. Let flushAlgorithmWrapper be an algorithm that runs these steps:
auto flush_algorithm_wrapper = GC : : create_function ( realm . heap ( ) , [ & realm , flush_algorithm ] ( ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Let result be the result of running flushAlgorithm, if flushAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e.
GC : : Ptr < JS : : PromiseCapability > result = nullptr ;
if ( flush_algorithm )
result = flush_algorithm - > function ( ) ( ) ;
// 2. If result is a Promise, then return result.
if ( result )
return GC : : Ref { * result } ;
// 3. Return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 7. Let cancelAlgorithmWrapper be an algorithm that runs these steps given a value reason:
auto cancel_algorithm_wrapper = GC : : create_function ( realm . heap ( ) , [ & realm , cancel_algorithm ] ( JS : : Value reason ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Let result be the result of running cancelAlgorithm given reason, if cancelAlgorithm was given, or null otherwise. If this throws an exception e, return a promise rejected with e.
GC : : Ptr < JS : : PromiseCapability > result = nullptr ;
if ( cancel_algorithm )
result = cancel_algorithm - > function ( ) ( reason ) ;
// 2. If result is a Promise, then return result.
if ( result )
return GC : : Ref { * result } ;
// 3. Return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 8. Let startPromise be a promise resolved with undefined.
auto start_promise = WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
// 9. Perform ! InitializeTransformStream(stream, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm).
initialize_transform_stream ( * this , start_promise , writable_high_water_mark , writable_size_algorithm , readable_high_water_mark , readable_size_algorithm ) ;
// 10. Let controller be a new TransformStreamDefaultController.
auto controller = realm . create < TransformStreamDefaultController > ( realm ) ;
// 11. Perform ! SetUpTransformStreamDefaultController(stream, controller, transformAlgorithmWrapper, flushAlgorithmWrapper, cancelAlgorithmWrapper).
set_up_transform_stream_default_controller ( * this , controller , transform_algorithm_wrapper , flush_algorithm_wrapper , cancel_algorithm_wrapper ) ;
}
2023-07-06 19:37:12 +02:00
TransformStream : : TransformStream ( JS : : Realm & realm )
: Bindings : : PlatformObject ( realm )
{
}
TransformStream : : ~ TransformStream ( ) = default ;
2023-08-07 08:41:28 +02:00
void TransformStream : : initialize ( JS : : Realm & realm )
2023-07-06 19:37:12 +02:00
{
2023-08-07 08:41:28 +02:00
Base : : initialize ( realm ) ;
2024-03-16 13:13:08 +01:00
WEB_SET_PROTOTYPE_FOR_INTERFACE ( TransformStream ) ;
2023-07-06 19:37:12 +02:00
}
void TransformStream : : visit_edges ( Cell : : Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( m_backpressure_change_promise ) ;
visitor . visit ( m_controller ) ;
visitor . visit ( m_readable ) ;
visitor . visit ( m_writable ) ;
}
}