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 >
2025-01-18 00:43:11 +13:00
* Copyright ( c ) 2023 - 2025 , Shannon Booth < shannon @ serenityos . org >
2024-04-06 18:56:42 +02:00
* Copyright ( c ) 2023 - 2024 , Kenneth Myhra < kennethmyhra @ serenityos . org >
2025-04-17 15:47:53 -04:00
* Copyright ( c ) 2025 , Tim Flynn < trflynn89 @ ladybird . org >
2022-09-21 23:27:13 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-06-13 07:26:59 +12:00
# include <LibJS/Runtime/ArrayBuffer.h>
2025-05-20 17:18:20 -04:00
# include <LibJS/Runtime/NativeFunction.h>
2023-11-19 12:31:20 +13:00
# include <LibJS/Runtime/TypedArray.h>
2025-05-20 17:18:20 -04:00
# include <LibWeb/Bindings/ExceptionOrUtils.h>
# include <LibWeb/DOM/IDLEventListener.h>
# include <LibWeb/HTML/EventNames.h>
# include <LibWeb/HTML/MessageEvent.h>
# include <LibWeb/HTML/MessagePort.h>
2024-01-28 10:22:52 -05:00
# include <LibWeb/HTML/StructuredSerialize.h>
2025-05-20 17:18:20 -04:00
# include <LibWeb/HTML/StructuredSerializeOptions.h>
2022-09-21 23:27:13 +01:00
# include <LibWeb/Streams/AbstractOperations.h>
2023-06-18 21:45:39 +12:00
# include <LibWeb/Streams/QueuingStrategy.h>
2025-05-20 17:18:20 -04:00
# include <LibWeb/Streams/ReadableStream.h>
# include <LibWeb/Streams/ReadableStreamDefaultController.h>
# include <LibWeb/Streams/ReadableStreamOperations.h>
# include <LibWeb/Streams/WritableStream.h>
# include <LibWeb/Streams/WritableStreamDefaultController.h>
# include <LibWeb/Streams/WritableStreamOperations.h>
2023-03-28 19:11:22 -07:00
# include <LibWeb/WebIDL/AbstractOperations.h>
2023-11-23 21:48:28 +13:00
# include <LibWeb/WebIDL/Buffers.h>
2025-05-20 17:18:20 -04:00
# include <LibWeb/WebIDL/CallbackType.h>
# include <LibWeb/WebIDL/DOMException.h>
2022-09-21 23:27:13 +01:00
namespace Web : : Streams {
2025-04-17 15:47:53 -04:00
// https://streams.spec.whatwg.org/#validate-and-normalize-high-water-mark
WebIDL : : ExceptionOr < double > extract_high_water_mark ( QueuingStrategy const & strategy , double default_hwm )
2024-01-27 09:08:46 -05:00
{
2025-04-17 15:47:53 -04:00
// 1. If strategy["highWaterMark"] does not exist, return defaultHWM.
if ( ! strategy . high_water_mark . has_value ( ) )
return default_hwm ;
2024-01-27 09:08:46 -05:00
2025-04-17 15:47:53 -04:00
// 2. Let highWaterMark be strategy["highWaterMark"].
auto high_water_mark = strategy . high_water_mark . value ( ) ;
2024-01-27 09:08:46 -05:00
2025-04-17 15:47:53 -04:00
// 3. If highWaterMark is NaN or highWaterMark < 0, throw a RangeError exception.
if ( isnan ( high_water_mark ) | | high_water_mark < 0 )
return WebIDL : : SimpleException { WebIDL : : SimpleExceptionType : : RangeError , " Invalid value for high water mark " sv } ;
2024-01-27 09:08:46 -05:00
2025-04-17 15:47:53 -04:00
// 4. Return highWaterMark.
return high_water_mark ;
2024-01-27 09:08:46 -05:00
}
2025-04-17 20:09:43 -04:00
// https://streams.spec.whatwg.org/#make-size-algorithm-from-size-function
GC : : Ref < SizeAlgorithm > extract_size_algorithm ( JS : : VM & vm , QueuingStrategy const & strategy )
2023-06-13 07:26:59 +12:00
{
2025-04-17 20:09:43 -04:00
// 1. If strategy["size"] does not exist, return an algorithm that returns 1.
if ( ! strategy . size )
return GC : : create_function ( vm . heap ( ) , [ ] ( JS : : Value ) { return JS : : normal_completion ( JS : : Value ( 1 ) ) ; } ) ;
2023-06-13 07:26:59 +12:00
2025-04-17 20:09:43 -04:00
// 2. Return an algorithm that performs the following steps, taking a chunk argument:
return GC : : create_function ( vm . heap ( ) , [ size = strategy . size ] ( JS : : Value chunk ) {
// 1. Return the result of invoking strategy["size"] with argument list « chunk ».
return WebIDL : : invoke_callback ( * size , { } , { { chunk } } ) ;
} ) ;
}
2023-06-13 07:26:59 +12:00
2025-05-20 17:18:20 -04:00
struct PromiseHolder : public JS : : Cell {
GC_CELL ( PromiseHolder , JS : : Cell ) ;
GC_DECLARE_ALLOCATOR ( PromiseHolder ) ;
explicit PromiseHolder ( GC : : Ptr < WebIDL : : Promise > promise )
: promise ( promise )
{
}
virtual void visit_edges ( JS : : Cell : : Visitor & visitor ) override
{
Base : : visit_edges ( visitor ) ;
visitor . visit ( promise ) ;
}
GC : : Ptr < WebIDL : : Promise > promise ;
} ;
GC_DEFINE_ALLOCATOR ( PromiseHolder ) ;
static void add_message_event_listener ( JS : : Realm & realm , HTML : : MessagePort & port , FlyString const & name , Function < void ( JS : : VM & , HTML : : MessageEvent const & ) > handler )
{
auto behavior = [ handler = GC : : create_function ( realm . heap ( ) , move ( handler ) ) ] ( JS : : VM & vm ) {
auto event = vm . argument ( 0 ) ;
VERIFY ( event . is_object ( ) ) ;
auto & message_event = as < HTML : : MessageEvent > ( event . as_object ( ) ) ;
handler - > function ( ) ( vm , message_event ) ;
return JS : : js_undefined ( ) ;
} ;
2025-08-02 19:27:29 -04:00
auto function = JS : : NativeFunction : : create ( realm , move ( behavior ) , 1 , Utf16FlyString { } , & realm ) ;
2025-05-20 17:18:20 -04:00
auto callback = realm . heap ( ) . allocate < WebIDL : : CallbackType > ( function , realm ) ;
auto listener = DOM : : IDLEventListener : : create ( realm , callback ) ;
port . add_event_listener_without_options ( name , listener ) ;
}
// https://streams.spec.whatwg.org/#abstract-opdef-crossrealmtransformsenderror
void cross_realm_transform_send_error ( JS : : Realm & realm , HTML : : MessagePort & port , JS : : Value error )
{
// 1. Perform PackAndPostMessage(port, "error", error), discarding the result.
( void ) pack_and_post_message ( realm , port , " error " sv , error ) ;
}
// https://streams.spec.whatwg.org/#abstract-opdef-packandpostmessagehandlingerror
WebIDL : : ExceptionOr < void > pack_and_post_message ( JS : : Realm & realm , HTML : : MessagePort & port , StringView type , JS : : Value value )
{
auto & vm = realm . vm ( ) ;
// 1. Let message be OrdinaryObjectCreate(null).
auto message = JS : : Object : : create ( realm , nullptr ) ;
// 2. Perform ! CreateDataProperty(message, "type", type).
MUST ( message - > create_data_property ( vm . names . type , JS : : PrimitiveString : : create ( vm , type ) ) ) ;
// 3. Perform ! CreateDataProperty(message, "value", value).
MUST ( message - > create_data_property ( vm . names . value , value ) ) ;
// 4. Let targetPort be the port with which port is entangled, if any; otherwise let it be null.
auto target_port = port . entangled_port ( ) ;
// 5. Let options be «[ "transfer" → « » ]».
HTML : : StructuredSerializeOptions options { . transfer = { } } ;
// 6. Run the message port post message steps providing targetPort, message, and options.
return port . message_port_post_message_steps ( target_port , message , options ) ;
}
// https://streams.spec.whatwg.org/#abstract-opdef-packandpostmessagehandlingerror
WebIDL : : ExceptionOr < void > pack_and_post_message_handling_error ( JS : : Realm & realm , HTML : : MessagePort & port , StringView type , JS : : Value value )
{
// 1. Let result be PackAndPostMessage(port, type, value).
auto result = pack_and_post_message ( realm , port , type , value ) ;
// 2. If result is an abrupt completion,
if ( result . is_exception ( ) ) {
auto error = Bindings : : exception_to_throw_completion ( realm . vm ( ) , result . release_error ( ) ) ;
// 1. Perform ! CrossRealmTransformSendError(port, result.[[Value]]).
cross_realm_transform_send_error ( realm , port , error . value ( ) ) ;
}
// 3. Return result as a completion record.
return result ;
}
// https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformreadable
void set_up_cross_realm_transform_readable ( JS : : Realm & realm , ReadableStream & stream , HTML : : MessagePort & port )
{
// 1. Perform ! InitializeReadableStream(stream).
initialize_readable_stream ( stream ) ;
// 2. Let controller be a new ReadableStreamDefaultController.
auto controller = realm . create < ReadableStreamDefaultController > ( realm ) ;
// 3. Add a handler for port’ s message event with the following steps:
add_message_event_listener ( realm , port , HTML : : EventNames : : message ,
[ & port , controller ] ( JS : : VM & vm , HTML : : MessageEvent const & message ) {
// 1. Let data be the data of the message.
auto data = message . data ( ) ;
// 2. Assert: data is an Object.
VERIFY ( data . is_object ( ) ) ;
// 3. Let type be ! Get(data, "type").
auto type = MUST ( data . get ( vm , vm . names . type ) ) ;
// 4. Let value be ! Get(data, "value").
auto value = MUST ( data . get ( vm , vm . names . value ) ) ;
// 5. Assert: type is a String.
auto type_string = type . as_string ( ) . utf8_string_view ( ) ;
// 6. If type is "chunk",
if ( type_string = = " chunk " sv ) {
// 1. Perform ! ReadableStreamDefaultControllerEnqueue(controller, value).
MUST ( readable_stream_default_controller_enqueue ( controller , value ) ) ;
}
// 7. Otherwise, if type is "close",
else if ( type_string = = " close " sv ) {
// 1. Perform ! ReadableStreamDefaultControllerClose(controller).
readable_stream_default_controller_close ( controller ) ;
// 2. Disentangle port.
port . disentangle ( ) ;
}
// 8. Otherwise, if type is "error",
else if ( type_string = = " error " sv ) {
// 1. Perform ! ReadableStreamDefaultControllerError(controller, value).
readable_stream_default_controller_error ( controller , value ) ;
// 2. Disentangle port.
port . disentangle ( ) ;
}
} ) ;
// 4. Add a handler for port’ s messageerror event with the following steps:
add_message_event_listener ( realm , port , HTML : : EventNames : : messageerror ,
[ & realm , & port , controller ] ( JS : : VM & , HTML : : MessageEvent const & ) {
// 1. Let error be a new "DataCloneError" DOMException.
2025-08-07 19:31:52 -04:00
auto error = WebIDL : : DataCloneError : : create ( realm , " Unable to transfer stream " _utf16 ) ;
2025-05-20 17:18:20 -04:00
// 2. Perform ! CrossRealmTransformSendError(port, error).
cross_realm_transform_send_error ( realm , port , error ) ;
// 3. Perform ! ReadableStreamDefaultControllerError(controller, error).
readable_stream_default_controller_error ( controller , error ) ;
// 4. Disentangle port.
port . disentangle ( ) ;
} ) ;
2025-06-07 23:57:17 +02:00
// 5. Enable port’ s port message queue.
port . enable ( ) ;
2025-05-20 17:18:20 -04:00
// 6. Let startAlgorithm be an algorithm that returns undefined.
auto start_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( ) - > WebIDL : : ExceptionOr < JS : : Value > {
return JS : : js_undefined ( ) ;
} ) ;
// 7. Let pullAlgorithm be the following steps:
auto pull_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm , & port ] ( ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Perform ! PackAndPostMessage(port, "pull", undefined).
MUST ( pack_and_post_message ( realm , port , " pull " sv , JS : : js_undefined ( ) ) ) ;
// 2. Return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 8. Let cancelAlgorithm be the following steps, taking a reason argument:
auto cancel_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm , & port ] ( JS : : Value reason ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Let result be PackAndPostMessageHandlingError(port, "error", reason).
auto result = pack_and_post_message_handling_error ( realm , port , " error " sv , reason ) ;
// 2. Disentangle port.
port . disentangle ( ) ;
// 3. If result is an abrupt completion, return a promise rejected with result.[[Value]].
if ( result . is_error ( ) )
return WebIDL : : create_rejected_promise_from_exception ( realm , result . release_error ( ) ) ;
// 4. Otherwise, return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 9. Let sizeAlgorithm be an algorithm that returns 1.
auto size_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( JS : : Value ) - > JS : : Completion {
return JS : : Value { 1 } ;
} ) ;
// 10. Perform ! SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, sizeAlgorithm).
MUST ( set_up_readable_stream_default_controller ( stream , controller , start_algorithm , pull_algorithm , cancel_algorithm , 0 , size_algorithm ) ) ;
}
// https://streams.spec.whatwg.org/#abstract-opdef-setupcrossrealmtransformwritable
void set_up_cross_realm_transform_writable ( JS : : Realm & realm , WritableStream & stream , HTML : : MessagePort & port )
{
// 1. Perform ! InitializeWritableStream(stream).
initialize_writable_stream ( stream ) ;
// 2. Let controller be a new WritableStreamDefaultController.
auto controller = realm . create < WritableStreamDefaultController > ( realm ) ;
// 3. Let backpressurePromise be a new promise.
auto backpressure_promise = realm . heap ( ) . allocate < PromiseHolder > ( WebIDL : : create_promise ( realm ) ) ;
// 4. Add a handler for port’ s message event with the following steps:
add_message_event_listener ( realm , port , HTML : : EventNames : : message ,
[ & realm , controller , backpressure_promise ] ( JS : : VM & vm , HTML : : MessageEvent const & message ) {
// 1. Let data be the data of the message.
auto data = message . data ( ) ;
// 2. Assert: data is an Object.
VERIFY ( data . is_object ( ) ) ;
// 3. Let type be ! Get(data, "type").
auto type = MUST ( data . get ( vm , vm . names . type ) ) ;
// 4. Let value be ! Get(data, "value").
auto value = MUST ( data . get ( vm , vm . names . value ) ) ;
// 5. Assert: type is a String.
auto type_string = type . as_string ( ) . utf8_string_view ( ) ;
// 6. If type is "pull",
if ( type_string = = " pull " sv ) {
// 1. If backpressurePromise is not undefined,
if ( backpressure_promise - > promise ) {
// 1. Resolve backpressurePromise with undefined.
WebIDL : : resolve_promise ( realm , * backpressure_promise - > promise , JS : : js_undefined ( ) ) ;
// 2. Set backpressurePromise to undefined.
backpressure_promise - > promise = nullptr ;
}
}
// 7. Otherwise, if type is "error",
else if ( type_string = = " error " sv ) {
// 1. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, value).
writable_stream_default_controller_error_if_needed ( controller , value ) ;
// 2. If backpressurePromise is not undefined,
if ( backpressure_promise - > promise ) {
// 1. Resolve backpressurePromise with undefined.
WebIDL : : resolve_promise ( realm , * backpressure_promise - > promise , JS : : js_undefined ( ) ) ;
// 2. Set backpressurePromise to undefined.
backpressure_promise - > promise = nullptr ;
}
}
} ) ;
// 5. Add a handler for port’ s messageerror event with the following steps:
add_message_event_listener ( realm , port , HTML : : EventNames : : messageerror ,
[ & realm , & port , controller ] ( JS : : VM & , HTML : : MessageEvent const & ) {
// 1. Let error be a new "DataCloneError" DOMException
2025-08-07 19:31:52 -04:00
auto error = WebIDL : : DataCloneError : : create ( realm , " Unable to transfer stream " _utf16 ) ;
2025-05-20 17:18:20 -04:00
// 2. Perform ! CrossRealmTransformSendError(port, error).
cross_realm_transform_send_error ( realm , port , error ) ;
// 3. Perform ! WritableStreamDefaultControllerErrorIfNeeded(controller, error).
writable_stream_default_controller_error_if_needed ( controller , error ) ;
// 4. Disentangle port.
port . disentangle ( ) ;
} ) ;
2025-06-07 23:57:17 +02:00
// 6. Enable port’ s port message queue.
port . enable ( ) ;
2025-05-20 17:18:20 -04:00
// 7. Let startAlgorithm be an algorithm that returns undefined.
auto start_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( ) - > WebIDL : : ExceptionOr < JS : : Value > {
return JS : : js_undefined ( ) ;
} ) ;
// 8. Let writeAlgorithm be the following steps, taking a chunk argument:
auto write_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm , & port , backpressure_promise ] ( JS : : Value chunk ) - > GC : : Ref < WebIDL : : Promise > {
// 1. If backpressurePromise is undefined, set backpressurePromise to a promise resolved with undefined.
if ( ! backpressure_promise - > promise )
backpressure_promise - > promise = WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
// FIXME: The steps below ("Return a promise rejected/resolved with ...") seem to indicate we should be creating
// a promise on-the-fly. But in order for the error from PackAndPostMessageHandlingError to be propagated
// back to the original ReadableStream, we must actually fulfill the promise created from reacting to the
// backpressure promise. This is explicitly tested by WPT.
auto reaction_promise = realm . heap ( ) . allocate < PromiseHolder > ( nullptr ) ;
// 2. Return the result of reacting to backpressurePromise with the following fulfillment steps:
reaction_promise - > promise = WebIDL : : upon_fulfillment ( * backpressure_promise - > promise ,
GC : : create_function ( realm . heap ( ) , [ & realm , & port , backpressure_promise , reaction_promise , chunk ] ( JS : : Value ) - > WebIDL : : ExceptionOr < JS : : Value > {
// 1. Set backpressurePromise to a new promise.
backpressure_promise - > promise = WebIDL : : create_promise ( realm ) ;
// 2. Let result be PackAndPostMessageHandlingError(port, "chunk", chunk).
auto result = pack_and_post_message_handling_error ( realm , port , " chunk " sv , chunk ) ;
// 3. If result is an abrupt completion,
if ( result . is_error ( ) ) {
// 1. Disentangle port.
port . disentangle ( ) ;
// 2. Return a promise rejected with result.[[Value]].
auto error = Bindings : : exception_to_throw_completion ( realm . vm ( ) , result . release_error ( ) ) ;
WebIDL : : reject_promise ( realm , * reaction_promise - > promise , error . value ( ) ) ;
}
// 4. Otherwise, return a promise resolved with undefined.
else {
WebIDL : : resolve_promise ( realm , * reaction_promise - > promise , JS : : js_undefined ( ) ) ;
}
return reaction_promise - > promise ;
} ) ) ;
return * reaction_promise - > promise ;
} ) ;
// 9. Let closeAlgorithm be the folowing steps:
auto close_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm , & port ] ( ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Perform ! PackAndPostMessage(port, "close", undefined).
MUST ( pack_and_post_message ( realm , port , " close " sv , JS : : js_undefined ( ) ) ) ;
// 2. Disentangle port.
port . disentangle ( ) ;
// 3. Return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 10. Let abortAlgorithm be the following steps, taking a reason argument:
auto abort_algorithm = GC : : create_function ( realm . heap ( ) , [ & realm , & port ] ( JS : : Value reason ) - > GC : : Ref < WebIDL : : Promise > {
// 1. Let result be PackAndPostMessageHandlingError(port, "error", reason).
auto result = pack_and_post_message_handling_error ( realm , port , " error " sv , reason ) ;
// 2. Disentangle port.
port . disentangle ( ) ;
// 3. If result is an abrupt completion, return a promise rejected with result.[[Value]].
if ( result . is_error ( ) )
return WebIDL : : create_rejected_promise_from_exception ( realm , result . release_error ( ) ) ;
// 4. Otherwise, return a promise resolved with undefined.
return WebIDL : : create_resolved_promise ( realm , JS : : js_undefined ( ) ) ;
} ) ;
// 11. Let sizeAlgorithm be an algorithm that returns 1.
auto size_algorithm = GC : : create_function ( realm . heap ( ) , [ ] ( JS : : Value ) - > JS : : Completion {
return JS : : Value { 1 } ;
} ) ;
// 12. Perform ! SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, 1, sizeAlgorithm).
MUST ( set_up_writable_stream_default_controller ( stream , controller , start_algorithm , write_algorithm , close_algorithm , abort_algorithm , 1 , size_algorithm ) ) ;
}
2025-04-17 20:09:43 -04:00
// https://streams.spec.whatwg.org/#can-transfer-array-buffer
bool can_transfer_array_buffer ( JS : : ArrayBuffer const & array_buffer )
{
// 1. Assert: O is an Object.
// 2. Assert: O has an [[ArrayBufferData]] internal slot.
2023-06-13 07:26:59 +12:00
2025-04-17 20:09:43 -04:00
// 3. If ! IsDetachedBuffer(O) is true, return false.
if ( array_buffer . is_detached ( ) )
return false ;
2023-06-13 07:26:59 +12:00
2025-04-17 20:09:43 -04:00
// 4. If SameValue(O.[[ArrayBufferDetachKey]], undefined) is false, return false.
if ( ! JS : : same_value ( array_buffer . detach_key ( ) , JS : : js_undefined ( ) ) )
return false ;
// 5. Return true.
return true ;
2023-06-13 07:26:59 +12:00
}
2023-04-09 15:21:54 -07:00
// https://streams.spec.whatwg.org/#is-non-negative-number
bool is_non_negative_number ( JS : : Value value )
{
2024-10-31 11:34:22 +00:00
// 1. If v is not a Number, return false.
2023-04-09 15:21:54 -07:00
if ( ! value . is_number ( ) )
return false ;
// 2. If v is NaN, return false.
if ( value . is_nan ( ) )
return false ;
// 3. If v < 0, return false.
if ( value . as_double ( ) < 0.0 )
return false ;
// 4. Return true.
return true ;
}
2025-04-17 20:09:43 -04:00
// https://streams.spec.whatwg.org/#transfer-array-buffer
WebIDL : : ExceptionOr < GC : : Ref < JS : : ArrayBuffer > > transfer_array_buffer ( JS : : Realm & realm , JS : : ArrayBuffer & buffer )
2024-12-01 21:36:06 +01:00
{
2025-04-17 20:09:43 -04:00
auto & vm = realm . vm ( ) ;
2024-12-01 21:36:06 +01:00
2025-04-17 20:09:43 -04:00
// 1. Assert: ! IsDetachedBuffer(O) is false.
VERIFY ( ! buffer . is_detached ( ) ) ;
2023-12-02 20:28:17 +13:00
2025-04-17 20:09:43 -04:00
// 2. Let arrayBufferData be O.[[ArrayBufferData]].
// 3. Let arrayBufferByteLength be O.[[ArrayBufferByteLength]].
auto array_buffer = buffer . buffer ( ) ;
2023-12-02 20:28:17 +13:00
2025-04-17 20:09:43 -04:00
// 4. Perform ? DetachArrayBuffer(O).
TRY ( JS : : detach_array_buffer ( vm , buffer ) ) ;
2023-12-02 20:28:17 +13:00
2025-04-17 20:09:43 -04:00
// 5. Return a new ArrayBuffer object, created in the current Realm, whose [[ArrayBufferData]] internal slot value is arrayBufferData and whose [[ArrayBufferByteLength]] internal slot value is arrayBufferByteLength.
return JS : : ArrayBuffer : : create ( realm , move ( array_buffer ) ) ;
2023-12-02 20:28:17 +13:00
}
2024-01-28 12:20:55 -05:00
// https://streams.spec.whatwg.org/#abstract-opdef-cloneasuint8array
WebIDL : : ExceptionOr < JS : : Value > clone_as_uint8_array ( JS : : Realm & realm , WebIDL : : ArrayBufferView & view )
{
auto & vm = realm . vm ( ) ;
2024-10-31 11:34:22 +00:00
// 1. Assert: O is an Object.
2024-01-28 12:20:55 -05:00
// 2. Assert: O has an [[ViewedArrayBuffer]] internal slot.
// 3. Assert: ! IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is false.
VERIFY ( ! view . viewed_array_buffer ( ) - > is_detached ( ) ) ;
// 4. Let buffer be ? CloneArrayBuffer(O.[[ViewedArrayBuffer]], O.[[ByteOffset]], O.[[ByteLength]], %ArrayBuffer%).
auto * buffer = TRY ( JS : : clone_array_buffer ( vm , * view . viewed_array_buffer ( ) , view . byte_offset ( ) , view . byte_length ( ) ) ) ;
// 5. Let array be ! Construct(%Uint8Array%, « buffer »).
auto array = MUST ( JS : : construct ( vm , * realm . intrinsics ( ) . uint8_array_constructor ( ) , buffer ) ) ;
// 5. Return array.
return array ;
}
2024-01-28 10:22:52 -05:00
// https://streams.spec.whatwg.org/#abstract-opdef-structuredclone
WebIDL : : ExceptionOr < JS : : Value > structured_clone ( JS : : Realm & realm , JS : : Value value )
{
auto & vm = realm . vm ( ) ;
// 1. Let serialized be ? StructuredSerialize(v).
auto serialized = TRY ( HTML : : structured_serialize ( vm , value ) ) ;
// 2. Return ? StructuredDeserialize(serialized, the current Realm).
2024-11-23 19:24:57 +13:00
return TRY ( HTML : : structured_deserialize ( vm , serialized , realm ) ) ;
2024-01-28 10:22:52 -05:00
}
2025-04-17 20:09:43 -04:00
// https://streams.spec.whatwg.org/#abstract-opdef-cancopydatablockbytes
bool can_copy_data_block_bytes_buffer ( JS : : ArrayBuffer const & to_buffer , u64 to_index , JS : : ArrayBuffer const & from_buffer , u64 from_index , u64 count )
{
// 1. Assert: toBuffer is an Object.
// 2. Assert: toBuffer has an [[ArrayBufferData]] internal slot.
// 3. Assert: fromBuffer is an Object.
// 4. Assert: fromBuffer has an [[ArrayBufferData]] internal slot.
// 5. If toBuffer is fromBuffer, return false.
if ( & to_buffer = = & from_buffer )
return false ;
// 6. If ! IsDetachedBuffer(toBuffer) is true, return false.
if ( to_buffer . is_detached ( ) )
return false ;
// 7. If ! IsDetachedBuffer(fromBuffer) is true, return false.
if ( from_buffer . is_detached ( ) )
return false ;
// 8. If toIndex + count > toBuffer.[[ArrayBufferByteLength]], return false.
if ( to_index + count > to_buffer . byte_length ( ) )
return false ;
// 9. If fromIndex + count > fromBuffer.[[ArrayBufferByteLength]], return false.
if ( from_index + count > from_buffer . byte_length ( ) )
return false ;
// 10. Return true.
return true ;
}
2022-09-21 23:27:13 +01:00
}