2022-04-05 02:24:10 +02:00
/*
* Copyright ( c ) 2022 , Daniel Ehrenberg < dan @ littledan . dev >
* Copyright ( c ) 2022 , Andrew Kaster < akaster @ serenityos . org >
2024-02-23 18:30:50 +01:00
* Copyright ( c ) 2023 - 2024 , Kenneth Myhra < kennethmyhra @ serenityos . org >
2023-11-11 20:14:46 +02:00
* Copyright ( c ) 2023 , Idan Horowitz < idan . horowitz @ serenityos . org >
2022-04-05 02:24:10 +02:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2023-07-30 18:34:56 +02:00
# include <AK/StdLibExtras.h>
2023-07-21 21:19:52 +02:00
# include <AK/String.h>
2023-12-07 15:53:38 -07:00
# include <LibIPC/File.h>
2023-11-11 20:14:46 +02:00
# include <LibJS/Runtime/Array.h>
2023-09-11 18:06:43 -06:00
# include <LibJS/Runtime/ArrayBuffer.h>
2023-07-21 21:19:52 +02:00
# include <LibJS/Runtime/BigInt.h>
2023-08-21 17:39:44 -06:00
# include <LibJS/Runtime/BigIntObject.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/BooleanObject.h>
2023-09-12 16:56:35 -06:00
# include <LibJS/Runtime/DataView.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/Date.h>
2023-11-11 20:14:46 +02:00
# include <LibJS/Runtime/Map.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/NumberObject.h>
2023-07-21 21:19:52 +02:00
# include <LibJS/Runtime/PrimitiveString.h>
2023-09-11 16:32:13 -06:00
# include <LibJS/Runtime/RegExpObject.h>
2023-11-11 20:14:46 +02:00
# include <LibJS/Runtime/Set.h>
2023-07-24 21:03:21 +02:00
# include <LibJS/Runtime/StringObject.h>
2023-09-12 16:56:35 -06:00
# include <LibJS/Runtime/TypedArray.h>
2023-07-09 11:40:17 +03:30
# include <LibJS/Runtime/VM.h>
2025-07-14 17:15:09 +01:00
# include <LibWeb/Bindings/DOMExceptionPrototype.h>
# include <LibWeb/Bindings/DOMMatrixPrototype.h>
# include <LibWeb/Bindings/DOMMatrixReadOnlyPrototype.h>
# include <LibWeb/Bindings/DOMPointPrototype.h>
# include <LibWeb/Bindings/DOMPointReadOnlyPrototype.h>
# include <LibWeb/Bindings/DOMQuadPrototype.h>
# include <LibWeb/Bindings/DOMRectPrototype.h>
# include <LibWeb/Bindings/DOMRectReadOnlyPrototype.h>
# include <LibWeb/Bindings/FileListPrototype.h>
# include <LibWeb/Bindings/FilePrototype.h>
# include <LibWeb/Bindings/ImageBitmapPrototype.h>
2023-12-07 15:53:38 -07:00
# include <LibWeb/Bindings/Intrinsics.h>
2025-07-14 17:15:09 +01:00
# include <LibWeb/Bindings/MessagePortPrototype.h>
# include <LibWeb/Bindings/ReadableStreamPrototype.h>
2024-02-23 18:32:27 +01:00
# include <LibWeb/Bindings/Serializable.h>
2023-12-07 15:53:38 -07:00
# include <LibWeb/Bindings/Transferable.h>
2025-07-14 17:15:09 +01:00
# include <LibWeb/Bindings/TransformStreamPrototype.h>
# include <LibWeb/Bindings/WritableStreamPrototype.h>
2024-03-10 20:48:00 +01:00
# include <LibWeb/Crypto/CryptoKey.h>
2024-02-23 18:32:27 +01:00
# include <LibWeb/FileAPI/Blob.h>
2024-02-23 22:14:18 +01:00
# include <LibWeb/FileAPI/File.h>
2024-03-18 21:58:25 +01:00
# include <LibWeb/FileAPI/FileList.h>
2024-03-09 22:41:26 +01:00
# include <LibWeb/Geometry/DOMMatrix.h>
2024-03-09 22:07:22 +01:00
# include <LibWeb/Geometry/DOMMatrixReadOnly.h>
2024-03-09 23:10:11 +01:00
# include <LibWeb/Geometry/DOMPoint.h>
2024-03-09 23:02:22 +01:00
# include <LibWeb/Geometry/DOMPointReadOnly.h>
2024-03-12 21:08:46 +01:00
# include <LibWeb/Geometry/DOMQuad.h>
2024-03-10 09:43:00 +01:00
# include <LibWeb/Geometry/DOMRect.h>
2024-03-10 09:02:54 +01:00
# include <LibWeb/Geometry/DOMRectReadOnly.h>
2025-07-14 17:15:09 +01:00
# include <LibWeb/HTML/ImageBitmap.h>
2025-05-04 13:45:51 +02:00
# include <LibWeb/HTML/ImageData.h>
2023-12-07 15:53:38 -07:00
# include <LibWeb/HTML/MessagePort.h>
2025-07-17 11:53:20 -04:00
# include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
2022-04-05 02:24:10 +02:00
# include <LibWeb/HTML/StructuredSerialize.h>
2025-05-20 17:20:22 -04:00
# include <LibWeb/Streams/ReadableStream.h>
2025-05-20 18:05:52 -04:00
# include <LibWeb/Streams/TransformStream.h>
2025-05-20 17:27:51 -04:00
# include <LibWeb/Streams/WritableStream.h>
2024-11-22 12:17:18 +01:00
# include <LibWeb/WebIDL/DOMException.h>
2022-04-05 02:24:10 +02:00
namespace Web : : HTML {
2025-07-17 09:51:04 -04:00
enum class ValueTag : u8 {
Empty , // Unused, for ease of catching bugs.
2023-07-21 21:19:52 +02:00
2025-07-17 09:51:04 -04:00
UndefinedPrimitive ,
2023-07-21 21:19:52 +02:00
NullPrimitive ,
BooleanPrimitive ,
2022-04-05 02:24:10 +02:00
NumberPrimitive ,
2023-07-21 21:19:52 +02:00
StringPrimitive ,
2025-07-17 09:51:04 -04:00
BigIntPrimitive ,
2023-07-21 21:19:52 +02:00
2023-07-24 21:03:21 +02:00
BooleanObject ,
NumberObject ,
StringObject ,
2025-07-17 09:51:04 -04:00
BigIntObject ,
2023-07-24 21:03:21 +02:00
DateObject ,
2023-09-11 16:32:13 -06:00
RegExpObject ,
2025-07-17 09:51:04 -04:00
MapObject ,
SetObject ,
ArrayObject ,
ErrorObject ,
Object ,
ObjectReference ,
2023-09-11 16:32:13 -06:00
2023-09-11 18:06:43 -06:00
GrowableSharedArrayBuffer ,
SharedArrayBuffer ,
ResizeableArrayBuffer ,
ArrayBuffer ,
2023-09-12 16:56:35 -06:00
ArrayBufferView ,
2024-02-23 18:32:27 +01:00
SerializableObject ,
2022-04-05 02:24:10 +02:00
// TODO: Define many more types
} ;
2023-11-11 01:14:56 +02:00
enum ErrorType {
Error ,
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ClassName ,
JS_ENUMERATE_NATIVE_ERRORS
# undef __JS_ENUMERATE
} ;
static ErrorType error_name_to_type ( String const & name )
{
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if ( name = = # ClassName # # sv ) \
return ErrorType : : ClassName ;
JS_ENUMERATE_NATIVE_ERRORS
# undef __JS_ENUMERATE
return Error ;
}
2025-07-17 11:08:09 -04:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
static WebIDL : : ExceptionOr < void > serialize_array_buffer ( JS : : VM & vm , TransferDataEncoder & data_holder , JS : : ArrayBuffer const & array_buffer , bool for_storage )
{
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
// 1. If IsSharedArrayBuffer(value) is true, then:
if ( array_buffer . is_shared_array_buffer ( ) ) {
// 1. If the current principal settings object's cross-origin isolated capability is false, then throw a "DataCloneError" DOMException.
// NOTE: This check is only needed when serializing (and not when deserializing) as the cross-origin isolated capability cannot change
// over time and a SharedArrayBuffer cannot leave an agent cluster.
if ( current_principal_settings_object ( ) . cross_origin_isolated_capability ( ) = = CanUseCrossOriginIsolatedAPIs : : No )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot serialize SharedArrayBuffer when cross-origin isolated " _utf16 ) ;
2025-07-17 11:08:09 -04:00
// 2. If forStorage is true, then throw a "DataCloneError" DOMException.
if ( for_storage )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot serialize SharedArrayBuffer for storage " _utf16 ) ;
2025-07-17 11:08:09 -04:00
if ( ! array_buffer . is_fixed_length ( ) ) {
// 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer",
// [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]],
// [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]],
// FIXME: [[AgentCluster]]: the surrounding agent's agent cluster }.
data_holder . encode ( ValueTag : : GrowableSharedArrayBuffer ) ;
data_holder . encode ( array_buffer . buffer ( ) ) ;
data_holder . encode ( array_buffer . max_byte_length ( ) ) ;
} else {
// 4. Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]],
// [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]],
// FIXME: [[AgentCluster]]: the surrounding agent's agent cluster }.
data_holder . encode ( ValueTag : : SharedArrayBuffer ) ;
data_holder . encode ( array_buffer . buffer ( ) ) ;
}
}
// 2. Otherwise:
else {
// 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
if ( array_buffer . is_detached ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot serialize detached ArrayBuffer " _utf16 ) ;
2025-07-17 11:08:09 -04:00
// 2. Let size be value.[[ArrayBufferByteLength]].
auto size = array_buffer . byte_length ( ) ;
// 3. Let dataCopy be ? CreateByteDataBlock(size).
// NOTE: This can throw a RangeError exception upon allocation failure.
auto data_copy = TRY ( JS : : create_byte_data_block ( vm , size ) ) ;
// 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
JS : : copy_data_block_bytes ( data_copy . buffer ( ) , 0 , array_buffer . buffer ( ) , 0 , size ) ;
// 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
// [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
if ( ! array_buffer . is_fixed_length ( ) ) {
data_holder . encode ( ValueTag : : ResizeableArrayBuffer ) ;
data_holder . encode ( data_copy . buffer ( ) ) ;
data_holder . encode ( array_buffer . max_byte_length ( ) ) ;
}
// 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
else {
data_holder . encode ( ValueTag : : ArrayBuffer ) ;
data_holder . encode ( data_copy . buffer ( ) ) ;
}
}
return { } ;
}
2025-07-17 09:51:04 -04:00
2025-07-17 11:08:09 -04:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
2025-07-17 09:51:04 -04:00
template < OneOf < JS : : TypedArrayBase , JS : : DataView > ViewType >
2025-07-17 11:08:09 -04:00
static WebIDL : : ExceptionOr < void > serialize_viewed_array_buffer ( JS : : VM & vm , TransferDataEncoder & data_holder , ViewType const & view , bool for_storage , SerializationMemory & memory )
{
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
auto view_record = [ & ] ( ) {
if constexpr ( IsSame < ViewType , JS : : DataView > )
return JS : : make_data_view_with_buffer_witness_record ( view , JS : : ArrayBuffer : : Order : : SeqCst ) ;
else
return JS : : make_typed_array_with_buffer_witness_record ( view , JS : : ArrayBuffer : : Order : : SeqCst ) ;
} ( ) ;
// 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
if constexpr ( IsSame < ViewType , JS : : DataView > ) {
if ( JS : : is_view_out_of_bounds ( view_record ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , Utf16String : : formatted ( JS : : ErrorType : : BufferOutOfBounds . format ( ) , " DataView " sv ) ) ;
2025-07-17 11:08:09 -04:00
} else {
if ( JS : : is_typed_array_out_of_bounds ( view_record ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , Utf16String : : formatted ( JS : : ErrorType : : BufferOutOfBounds . format ( ) , " TypedArray " sv ) ) ;
2025-07-17 11:08:09 -04:00
}
// 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
JS : : Value buffer = view . viewed_array_buffer ( ) ;
// 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
auto buffer_serialized = TRY ( structured_serialize_internal ( vm , buffer , for_storage , memory ) ) ;
// 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
// NOTE: Object reference + memory check is required when ArrayBuffer is transferred.
auto tag = TransferDataDecoder { buffer_serialized } . decode < ValueTag > ( ) ;
VERIFY ( first_is_one_of ( tag , ValueTag : : ArrayBuffer , ValueTag : : ResizeableArrayBuffer , ValueTag : : SharedArrayBuffer , ValueTag : : GrowableSharedArrayBuffer )
| | ( tag = = ValueTag : : ObjectReference & & memory . contains ( buffer ) ) ) ;
// 5. If value has a [[DataView]] internal slot, then set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView",
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
if constexpr ( IsSame < ViewType , JS : : DataView > ) {
data_holder . encode ( ValueTag : : ArrayBufferView ) ;
data_holder . append ( move ( buffer_serialized ) ) ; // [[ArrayBufferSerialized]]
2025-08-02 19:27:29 -04:00
data_holder . encode ( " DataView " _utf16 ) ; // [[Constructor]]
2025-07-17 11:08:09 -04:00
data_holder . encode ( JS : : get_view_byte_length ( view_record ) ) ;
data_holder . encode ( view . byte_offset ( ) ) ;
}
// 6. Otherwise:
else {
// 1. Assert: value has a [[TypedArrayName]] internal slot.
// NOTE: Handled by constexpr check and template constraints
// 2. Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]],
// [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
// [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
data_holder . encode ( ValueTag : : ArrayBufferView ) ;
2025-08-02 19:27:29 -04:00
data_holder . append ( move ( buffer_serialized ) ) ; // [[ArrayBufferSerialized]]
data_holder . encode ( view . element_name ( ) . to_utf16_string ( ) ) ; // [[Constructor]]
2025-07-17 11:08:09 -04:00
data_holder . encode ( JS : : typed_array_byte_length ( view_record ) ) ;
data_holder . encode ( view . byte_offset ( ) ) ;
data_holder . encode ( JS : : typed_array_length ( view_record ) ) ;
}
return { } ;
}
2025-07-17 09:51:04 -04:00
2022-04-05 02:24:10 +02:00
// Serializing and deserializing are each two passes:
// 1. Fill up the memory with all the values, but without translating references
// 2. Translate all the references into the appropriate form
class Serializer {
public :
2023-09-11 18:06:43 -06:00
Serializer ( JS : : VM & vm , SerializationMemory & memory , bool for_storage )
2022-04-05 02:24:10 +02:00
: m_vm ( vm )
2023-08-21 15:15:05 -06:00
, m_memory ( memory )
2024-11-24 14:20:57 +01:00
, m_next_id ( memory . size ( ) )
2023-09-11 18:06:43 -06:00
, m_for_storage ( for_storage )
2022-04-05 02:24:10 +02:00
{
}
2023-08-21 15:15:05 -06:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
2024-10-21 13:35:24 +13:00
// https://whatpr.org/html/9893/structured-data.html#structuredserializeinternal
2023-08-21 15:15:05 -06:00
WebIDL : : ExceptionOr < SerializationRecord > serialize ( JS : : Value value )
2022-04-05 02:24:10 +02:00
{
2025-07-17 09:51:04 -04:00
TransferDataEncoder serialized ;
2023-08-21 15:15:05 -06:00
// 2. If memory[value] exists, then return memory[value].
if ( m_memory . contains ( value ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : ObjectReference ) ;
serialized . encode ( m_memory . get ( value ) . value ( ) ) ;
return serialized . take_buffer ( ) . take_data ( ) ;
2023-08-21 15:15:05 -06:00
}
// 3. Let deep be false.
2023-11-11 20:14:46 +02:00
auto deep = false ;
2023-08-21 15:15:05 -06:00
2024-10-31 11:34:22 +00:00
// 4. If value is undefined, null, a Boolean, a Number, a BigInt, or a String, then return { [[Type]]: "primitive", [[Value]]: value }.
2025-07-17 09:51:04 -04:00
bool return_primitive_type = true ;
2023-07-21 21:19:52 +02:00
if ( value . is_undefined ( ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : UndefinedPrimitive ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_null ( ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : NullPrimitive ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_boolean ( ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : BooleanPrimitive ) ;
serialized . encode ( value . as_bool ( ) ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_number ( ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : NumberPrimitive ) ;
serialized . encode ( value . as_double ( ) ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_bigint ( ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : BigIntPrimitive ) ;
serialized . encode ( MUST ( value . as_bigint ( ) . big_integer ( ) . to_base ( 10 ) ) ) ;
2023-07-21 21:19:52 +02:00
} else if ( value . is_string ( ) ) {
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : StringPrimitive ) ;
serialized . encode ( value . as_string ( ) . utf8_string ( ) ) ;
2023-08-21 15:15:05 -06:00
} else {
return_primitive_type = false ;
}
if ( return_primitive_type )
2025-07-17 09:51:04 -04:00
return serialized . take_buffer ( ) . take_data ( ) ;
2023-08-21 15:15:05 -06:00
2024-10-31 11:34:22 +00:00
// 5. If value is a Symbol, then throw a "DataCloneError" DOMException.
2023-08-21 17:39:44 -06:00
if ( value . is_symbol ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize Symbol " _utf16 ) ;
2023-08-21 15:15:05 -06:00
// 6. Let serialized be an uninitialized value.
2025-07-17 09:51:04 -04:00
// NOTE: We created the serialized value above.
2023-08-21 15:15:05 -06:00
2025-07-17 11:36:22 -04:00
if ( value . is_object ( ) ) {
auto const & object = value . as_object ( ) ;
2023-08-21 15:15:05 -06:00
2025-07-17 11:36:22 -04:00
// 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
2025-07-17 09:51:04 -04:00
if ( auto const * boolean_object = as_if < JS : : BooleanObject > ( object ) ) {
serialized . encode ( ValueTag : : BooleanObject ) ;
serialized . encode ( boolean_object - > boolean ( ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-08-21 15:15:05 -06:00
2025-07-17 11:36:22 -04:00
// 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }.
2025-07-17 09:51:04 -04:00
else if ( auto const * number_object = as_if < JS : : NumberObject > ( object ) ) {
serialized . encode ( ValueTag : : NumberObject ) ;
serialized . encode ( number_object - > number ( ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-08-21 15:15:05 -06:00
2025-07-17 11:36:22 -04:00
// 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }.
2025-07-17 09:51:04 -04:00
else if ( auto const * big_int_object = as_if < JS : : BigIntObject > ( object ) ) {
serialized . encode ( ValueTag : : BigIntObject ) ;
serialized . encode ( MUST ( big_int_object - > bigint ( ) . big_integer ( ) . to_base ( 10 ) ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-08-21 15:15:05 -06:00
2025-07-17 11:36:22 -04:00
// 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }.
2025-07-17 09:51:04 -04:00
else if ( auto const * string_object = as_if < JS : : StringObject > ( object ) ) {
serialized . encode ( ValueTag : : StringObject ) ;
serialized . encode ( string_object - > primitive_string ( ) . utf8_string ( ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-07-21 21:19:52 +02:00
2025-07-17 11:36:22 -04:00
// 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
2025-07-17 09:51:04 -04:00
else if ( auto const * date = as_if < JS : : Date > ( object ) ) {
serialized . encode ( ValueTag : : DateObject ) ;
serialized . encode ( date - > date_value ( ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-09-11 16:32:13 -06:00
2025-07-17 11:36:22 -04:00
// 12. Otherwise, if value has a [[RegExpMatcher]] internal slot, then set serialized to
// { [[Type]]: "RegExp", [[RegExpMatcher]]: value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]],
// [[OriginalFlags]]: value.[[OriginalFlags]] }.
2025-07-17 09:51:04 -04:00
else if ( auto const * reg_exp_object = as_if < JS : : RegExpObject > ( object ) ) {
// NOTE: A Regex<ECMA262> object is perfectly happy to be reconstructed with just the source+flags.
// In the future, we could optimize the work being done on the deserialize step by serializing
// more of the internal state (the [[RegExpMatcher]] internal slot).
serialized . encode ( ValueTag : : RegExpObject ) ;
serialized . encode ( reg_exp_object - > pattern ( ) ) ;
serialized . encode ( reg_exp_object - > flags ( ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-09-11 18:06:43 -06:00
2025-07-17 11:36:22 -04:00
// 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
else if ( auto const * array_buffer = as_if < JS : : ArrayBuffer > ( object ) ) {
2025-07-17 09:51:04 -04:00
TRY ( serialize_array_buffer ( m_vm , serialized , * array_buffer , m_for_storage ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-09-12 16:56:35 -06:00
2025-07-17 11:36:22 -04:00
// 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
else if ( auto const * typed_array_base = as_if < JS : : TypedArrayBase > ( object ) ) {
2025-07-17 09:51:04 -04:00
TRY ( serialize_viewed_array_buffer ( m_vm , serialized , * typed_array_base , m_for_storage , m_memory ) ) ;
2025-07-17 11:36:22 -04:00
} else if ( auto const * data_view = as_if < JS : : DataView > ( object ) ) {
2025-07-17 09:51:04 -04:00
TRY ( serialize_viewed_array_buffer ( m_vm , serialized , * data_view , m_for_storage , m_memory ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 15. Otherwise, if value has a [[MapData]] internal slot, then:
else if ( is < JS : : Map > ( object ) ) {
// 1. Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }.
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : MapObject ) ;
2025-07-17 11:36:22 -04:00
// 2. Set deep to true.
deep = true ;
}
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 16. Otherwise, if value has a [[SetData]] internal slot, then:
else if ( is < JS : : Set > ( object ) ) {
// 1. Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }.
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : SetObject ) ;
2025-07-17 11:36:22 -04:00
// 2. Set deep to true.
deep = true ;
}
2023-11-11 01:14:56 +02:00
2025-07-17 11:36:22 -04:00
// 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then:
else if ( is < JS : : Error > ( object ) & & ! is < Bindings : : PlatformObject > ( object ) ) {
// 1. Let name be ? Get(value, "name").
auto name_property = TRY ( object . get ( m_vm . names . name ) ) ;
// FIXME: Spec bug - https://github.com/whatwg/html/issues/9923
// MISSING STEP: Set name to ? ToString(name).
auto name = TRY ( name_property . to_string ( m_vm ) ) ;
// 2. If name is not one of "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", or "URIError", then set name to "Error".
auto type = error_name_to_type ( name ) ;
// 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
auto value_message_descriptor = TRY ( object . internal_get_own_property ( m_vm . names . message ) ) ;
// 4. Let message be undefined if IsDataDescriptor(valueMessageDesc) is false, and ? ToString(valueMessageDesc.[[Value]]) otherwise.
2025-08-07 19:31:52 -04:00
Optional < Utf16String > message ;
2025-07-17 11:36:22 -04:00
if ( value_message_descriptor . has_value ( ) & & value_message_descriptor - > is_data_descriptor ( ) )
2025-08-07 19:31:52 -04:00
message = TRY ( value_message_descriptor - > value - > to_utf16_string ( m_vm ) ) ;
2025-07-17 11:36:22 -04:00
// 5. Set serialized to { [[Type]]: "Error", [[Name]]: name, [[Message]]: message }.
// FIXME: 6. User agents should attach a serialized representation of any interesting accompanying data which are not yet specified, notably the stack property, to serialized.
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : ErrorObject ) ;
serialized . encode ( type ) ;
serialized . encode ( message ) ;
2025-07-17 11:36:22 -04:00
}
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 18. Otherwise, if value is an Array exotic object, then:
else if ( is < JS : : Array > ( object ) ) {
// 1. Let valueLenDescriptor be ? OrdinaryGetOwnProperty(value, "length").
// 2. Let valueLen be valueLenDescriptor.[[Value]].
// NON-STANDARD: Array objects in LibJS do not have a real length property, so it must be accessed the usual way
u64 length = MUST ( JS : : length_of_array_like ( m_vm , object ) ) ;
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 3. Set serialized to { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: a new empty List }.
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : ArrayObject ) ;
serialized . encode ( length ) ;
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 4. Set deep to true.
deep = true ;
}
2024-02-23 18:32:27 +01:00
2025-07-17 11:36:22 -04:00
// 19. Otherwise, if value is a platform object that is a serializable object:
else if ( auto const * serializable = as_if < Bindings : : Serializable > ( object ) ) {
// FIXME: 1. If value has a [[Detached]] internal slot whose value is true, then throw a "DataCloneError" DOMException.
2024-02-23 18:32:27 +01:00
2025-07-17 11:36:22 -04:00
// 2. Let typeString be the identifier of the primary interface of value.
// 3. Set serialized to { [[Type]]: typeString }.
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : SerializableObject ) ;
serialized . encode ( serializable - > serialize_type ( ) ) ;
2024-03-02 22:32:50 +01:00
2025-07-17 11:36:22 -04:00
// 4. Set deep to true
deep = true ;
}
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 20. Otherwise, if value is a platform object, then throw a "DataCloneError" DOMException.
else if ( is < Bindings : : PlatformObject > ( object ) ) {
2025-08-07 19:31:52 -04:00
return throw_completion ( WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize platform objects " _utf16 ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-11-11 01:23:09 +02:00
2025-07-17 11:36:22 -04:00
// 21. Otherwise, if IsCallable(value) is true, then throw a "DataCloneError" DOMException.
else if ( value . is_function ( ) ) {
2025-08-07 19:31:52 -04:00
return throw_completion ( WebIDL : : DataCloneError : : create ( * m_vm . current_realm ( ) , " Cannot serialize functions " _utf16 ) ) ;
2025-07-17 11:36:22 -04:00
}
2023-11-11 01:23:09 +02:00
2025-07-17 11:36:22 -04:00
// FIXME: 22. Otherwise, if value has any internal slot other than [[Prototype]] or [[Extensible]], then throw a "DataCloneError" DOMException.
2023-11-11 01:23:09 +02:00
2025-07-17 11:36:22 -04:00
// FIXME: 23. Otherwise, if value is an exotic object and value is not the %Object.prototype% intrinsic object associated with any realm, then throw a "DataCloneError" DOMException.
2023-11-11 01:23:09 +02:00
2025-07-17 11:36:22 -04:00
// 24. Otherwise:
else {
// 1. Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }.
2025-07-17 09:51:04 -04:00
serialized . encode ( ValueTag : : Object ) ;
2023-11-11 20:14:46 +02:00
2025-07-17 11:36:22 -04:00
// 2. Set deep to true.
deep = true ;
}
2023-08-21 15:15:05 -06:00
}
2023-07-21 21:19:52 +02:00
2023-08-21 15:15:05 -06:00
// 25. Set memory[value] to serialized.
2024-11-15 04:01:23 +13:00
m_memory . set ( make_root ( value ) , m_next_id + + ) ;
2023-11-11 20:14:46 +02:00
// 26. If deep is true, then:
if ( deep ) {
2025-07-17 11:36:22 -04:00
auto & object = value . as_object ( ) ;
2023-11-11 20:14:46 +02:00
// 1. If value has a [[MapData]] internal slot, then:
2025-07-17 11:36:22 -04:00
if ( auto const * map = as_if < JS : : Map > ( object ) ) {
2023-11-11 20:14:46 +02:00
// 1. Let copiedList be a new empty List.
Vector < JS : : Value > copied_list ;
2025-07-17 11:36:22 -04:00
copied_list . ensure_capacity ( map - > map_size ( ) * 2 ) ;
2023-11-11 20:14:46 +02:00
// 2. For each Record { [[Key]], [[Value]] } entry of value.[[MapData]]:
2025-07-17 11:36:22 -04:00
for ( auto const & entry : * map ) {
2023-11-11 20:14:46 +02:00
// 1. Let copiedEntry be a new Record { [[Key]]: entry.[[Key]], [[Value]]: entry.[[Value]] }.
// 2. If copiedEntry.[[Key]] is not the special value empty, append copiedEntry to copiedList.
copied_list . append ( entry . key ) ;
copied_list . append ( entry . value ) ;
}
2025-07-17 09:51:04 -04:00
serialized . encode ( map - > map_size ( ) ) ;
2023-11-11 20:14:46 +02:00
// 3. For each Record { [[Key]], [[Value]] } entry of copiedList:
for ( auto copied_value : copied_list ) {
// 1. Let serializedKey be ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory).
// 2. Let serializedValue be ? StructuredSerializeInternal(entry.[[Value]], forStorage, memory).
auto serialized_value = TRY ( structured_serialize_internal ( m_vm , copied_value , m_for_storage , m_memory ) ) ;
// 3. Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]].
2025-07-17 09:51:04 -04:00
serialized . append ( move ( serialized_value ) ) ;
2023-11-11 20:14:46 +02:00
}
}
// 2. Otherwise, if value has a [[SetData]] internal slot, then:
2025-07-17 11:36:22 -04:00
else if ( auto const * set = as_if < JS : : Set > ( object ) ) {
2023-11-11 20:14:46 +02:00
// 1. Let copiedList be a new empty List.
Vector < JS : : Value > copied_list ;
2025-07-17 11:36:22 -04:00
copied_list . ensure_capacity ( set - > set_size ( ) ) ;
2023-11-11 20:14:46 +02:00
// 2. For each entry of value.[[SetData]]:
2025-07-17 11:36:22 -04:00
for ( auto const & entry : * set ) {
2023-11-11 20:14:46 +02:00
// 1. If entry is not the special value empty, append entry to copiedList.
copied_list . append ( entry . key ) ;
}
2025-07-17 09:51:04 -04:00
serialized . encode ( set - > set_size ( ) ) ;
2023-11-11 20:14:46 +02:00
// 3. For each entry of copiedList:
for ( auto copied_value : copied_list ) {
// 1. Let serializedEntry be ? StructuredSerializeInternal(entry, forStorage, memory).
auto serialized_value = TRY ( structured_serialize_internal ( m_vm , copied_value , m_for_storage , m_memory ) ) ;
// 2. Append serializedEntry to serialized.[[SetData]].
2025-07-17 09:51:04 -04:00
serialized . append ( move ( serialized_value ) ) ;
2023-11-11 20:14:46 +02:00
}
}
2024-03-27 07:56:00 +01:00
// 3. Otherwise, if value is a platform object that is a serializable object, then perform the serialization steps for value's primary interface, given value, serialized, and forStorage.
2025-07-17 11:36:22 -04:00
else if ( auto * serializable = as_if < Bindings : : Serializable > ( object ) ) {
2025-07-17 09:51:04 -04:00
TRY ( serializable - > serialization_steps ( serialized , m_for_storage , m_memory ) ) ;
2024-03-27 07:56:00 +01:00
}
2023-11-11 20:14:46 +02:00
// 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key):
else {
u64 property_count = 0 ;
2025-07-17 09:51:04 -04:00
auto count_offset = serialized . buffer ( ) . data ( ) . size ( ) ;
serialized . encode ( property_count ) ;
2025-07-17 11:36:22 -04:00
for ( auto key : MUST ( object . enumerable_own_property_names ( JS : : Object : : PropertyKind : : Key ) ) ) {
2023-11-11 20:14:46 +02:00
auto property_key = MUST ( JS : : PropertyKey : : from_value ( m_vm , key ) ) ;
// 1. If ! HasOwnProperty(value, key) is true, then:
2025-07-17 11:36:22 -04:00
if ( MUST ( object . has_own_property ( property_key ) ) ) {
2023-11-11 20:14:46 +02:00
// 1. Let inputValue be ? value.[[Get]](key, value).
2025-07-17 11:36:22 -04:00
auto input_value = TRY ( object . internal_get ( property_key , value ) ) ;
2022-04-05 02:24:10 +02:00
2023-11-11 20:14:46 +02:00
// 2. Let outputValue be ? StructuredSerializeInternal(inputValue, forStorage, memory).
auto output_value = TRY ( structured_serialize_internal ( m_vm , input_value , m_for_storage , m_memory ) ) ;
2023-08-21 15:15:05 -06:00
2023-11-11 20:14:46 +02:00
// 3. Append { [[Key]]: key, [[Value]]: outputValue } to serialized.[[Properties]].
2025-08-02 19:27:29 -04:00
serialized . encode ( key . as_string ( ) . utf16_string ( ) ) ;
2025-07-17 09:51:04 -04:00
serialized . append ( move ( output_value ) ) ;
2023-11-11 20:14:46 +02:00
2025-07-17 09:51:04 -04:00
+ + property_count ;
2023-11-11 20:14:46 +02:00
}
}
2025-07-17 09:51:04 -04:00
if ( property_count ) {
auto * data = const_cast < u8 * > ( serialized . buffer ( ) . data ( ) . data ( ) ) ;
memcpy ( data + count_offset , & property_count , sizeof ( property_count ) ) ;
}
2023-11-11 20:14:46 +02:00
}
}
// 27. Return serialized.
2025-07-17 09:51:04 -04:00
return serialized . take_buffer ( ) . take_data ( ) ;
2022-04-05 02:24:10 +02:00
}
private :
JS : : VM & m_vm ;
2023-08-21 15:15:05 -06:00
SerializationMemory & m_memory ; // JS value -> index
2023-11-11 20:14:46 +02:00
u32 m_next_id { 0 } ;
2023-09-11 18:06:43 -06:00
bool m_for_storage { false } ;
2024-02-23 18:30:50 +01:00
} ;
2023-07-21 21:19:52 +02:00
2022-04-05 02:24:10 +02:00
class Deserializer {
public :
2025-07-17 09:51:04 -04:00
Deserializer ( JS : : VM & vm , TransferDataDecoder & serialized , JS : : Realm & target_realm , DeserializationMemory & memory )
2022-04-05 02:24:10 +02:00
: m_vm ( vm )
2023-11-11 20:14:46 +02:00
, m_serialized ( serialized )
, m_memory ( memory )
2022-04-05 02:24:10 +02:00
{
2023-09-12 16:41:13 -06:00
VERIFY ( vm . current_realm ( ) = = & target_realm ) ;
2022-04-05 02:24:10 +02:00
}
2023-11-11 20:14:46 +02:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
WebIDL : : ExceptionOr < JS : : Value > deserialize ( )
2022-04-05 02:24:10 +02:00
{
2025-07-17 09:51:04 -04:00
auto & realm = * m_vm . current_realm ( ) ;
auto tag = m_serialized . decode < ValueTag > ( ) ;
2023-11-11 20:14:46 +02:00
// 2. If memory[serialized] exists, then return memory[serialized].
if ( tag = = ValueTag : : ObjectReference ) {
2025-07-17 09:51:04 -04:00
auto index = m_serialized . decode < u32 > ( ) ;
if ( index = = NumericLimits < u32 > : : max ( ) )
2023-12-19 15:25:33 -07:00
return JS : : Object : : create ( * m_vm . current_realm ( ) , nullptr ) ;
2023-11-11 20:14:46 +02:00
return m_memory [ index ] ;
}
// 3. Let deep be false.
auto deep = false ;
// 4. Let value be an uninitialized value.
JS : : Value value ;
auto is_primitive = false ;
2025-07-17 09:51:04 -04:00
auto decode_string = [ & ] ( ) {
auto string = m_serialized . decode < String > ( ) ;
return JS : : PrimitiveString : : create ( m_vm , string ) ;
} ;
2025-08-06 11:28:18 -04:00
auto decode_utf16_string = [ & ] ( ) {
auto string = m_serialized . decode < Utf16String > ( ) ;
return JS : : PrimitiveString : : create ( m_vm , string ) ;
} ;
2025-07-17 09:51:04 -04:00
auto decode_big_int = [ & ] ( ) {
auto string = m_serialized . decode < String > ( ) ;
return JS : : BigInt : : create ( m_vm , MUST ( : : Crypto : : SignedBigInteger : : from_base ( 10 , string ) ) ) ;
} ;
2023-11-11 20:14:46 +02:00
switch ( tag ) {
// 5. If serialized.[[Type]] is "primitive", then set value to serialized.[[Value]].
2025-07-17 09:51:04 -04:00
case ValueTag : : UndefinedPrimitive :
2023-11-11 20:14:46 +02:00
value = JS : : js_undefined ( ) ;
is_primitive = true ;
break ;
2025-07-17 09:51:04 -04:00
case ValueTag : : NullPrimitive :
2023-11-11 20:14:46 +02:00
value = JS : : js_null ( ) ;
is_primitive = true ;
break ;
2025-07-17 09:51:04 -04:00
case ValueTag : : BooleanPrimitive :
value = JS : : Value { m_serialized . decode < bool > ( ) } ;
2023-11-11 20:14:46 +02:00
is_primitive = true ;
break ;
2025-07-17 09:51:04 -04:00
case ValueTag : : NumberPrimitive :
value = JS : : Value { m_serialized . decode < double > ( ) } ;
2023-11-11 20:14:46 +02:00
is_primitive = true ;
break ;
2025-07-17 09:51:04 -04:00
case ValueTag : : BigIntPrimitive :
value = decode_big_int ( ) ;
2023-11-11 20:14:46 +02:00
is_primitive = true ;
break ;
2025-07-17 09:51:04 -04:00
case ValueTag : : StringPrimitive :
value = decode_string ( ) ;
2023-11-11 20:14:46 +02:00
is_primitive = true ;
break ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 6. Otherwise, if serialized.[[Type]] is "Boolean", then set value to a new Boolean object in targetRealm whose [[BooleanData]] internal slot value is serialized.[[BooleanData]].
2025-07-17 09:51:04 -04:00
case ValueTag : : BooleanObject :
value = JS : : BooleanObject : : create ( realm , m_serialized . decode < bool > ( ) ) ;
2023-11-11 20:14:46 +02:00
break ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 7. Otherwise, if serialized.[[Type]] is "Number", then set value to a new Number object in targetRealm whose [[NumberData]] internal slot value is serialized.[[NumberData]].
2025-07-17 09:51:04 -04:00
case ValueTag : : NumberObject :
value = JS : : NumberObject : : create ( realm , m_serialized . decode < double > ( ) ) ;
2023-11-11 20:14:46 +02:00
break ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 8. Otherwise, if serialized.[[Type]] is "BigInt", then set value to a new BigInt object in targetRealm whose [[BigIntData]] internal slot value is serialized.[[BigIntData]].
2025-07-17 09:51:04 -04:00
case ValueTag : : BigIntObject :
value = JS : : BigIntObject : : create ( realm , decode_big_int ( ) ) ;
2023-11-11 20:14:46 +02:00
break ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 9. Otherwise, if serialized.[[Type]] is "String", then set value to a new String object in targetRealm whose [[StringData]] internal slot value is serialized.[[StringData]].
2025-07-17 09:51:04 -04:00
case ValueTag : : StringObject :
value = JS : : StringObject : : create ( realm , decode_string ( ) , realm . intrinsics ( ) . string_prototype ( ) ) ;
2023-11-11 20:14:46 +02:00
break ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 10. Otherwise, if serialized.[[Type]] is "Date", then set value to a new Date object in targetRealm whose [[DateValue]] internal slot value is serialized.[[DateValue]].
2025-07-17 09:51:04 -04:00
case ValueTag : : DateObject :
value = JS : : Date : : create ( realm , m_serialized . decode < double > ( ) ) ;
2023-11-11 20:14:46 +02:00
break ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 11. Otherwise, if serialized.[[Type]] is "RegExp", then set value to a new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is serialized.[[RegExpMatcher]],
// whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]].
case ValueTag : : RegExpObject : {
2025-08-06 11:28:18 -04:00
auto pattern = decode_utf16_string ( ) ;
auto flags = decode_utf16_string ( ) ;
2025-07-17 09:51:04 -04:00
value = MUST ( JS : : regexp_create ( m_vm , pattern , flags ) ) ;
2023-11-11 20:14:46 +02:00
break ;
}
2025-07-17 09:51:04 -04:00
2024-12-04 21:33:13 +01:00
// 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:
case ValueTag : : SharedArrayBuffer : {
// FIXME: 1. If targetRealm's corresponding agent cluster is not serialized.[[AgentCluster]], then throw a "DataCloneError" DOMException.
2025-07-17 09:51:04 -04:00
2024-12-04 21:33:13 +01:00
// 2. Otherwise, set value to a new SharedArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]]
// and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]].
2025-07-17 09:51:04 -04:00
auto buffer = TRY ( m_serialized . decode_buffer ( realm ) ) ;
value = JS : : ArrayBuffer : : create ( realm , move ( buffer ) , JS : : DataBlock : : Shared : : Yes ) ;
2024-12-04 21:33:13 +01:00
break ;
}
2025-07-17 09:51:04 -04:00
2024-12-04 21:33:13 +01:00
// 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then:
case ValueTag : : GrowableSharedArrayBuffer : {
// FIXME: 1. If targetRealm's corresponding agent cluster is not serialized.[[AgentCluster]], then throw a "DataCloneError" DOMException.
2025-07-17 09:51:04 -04:00
2024-12-04 21:33:13 +01:00
// 2. Otherwise, set value to a new SharedArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]],
// whose [[ArrayBufferByteLengthData]] internal slot value is serialized.[[ArrayBufferByteLengthData]],
// and whose [[ArrayBufferMaxByteLength]] internal slot value is serialized.[[ArrayBufferMaxByteLength]].
2025-07-17 09:51:04 -04:00
auto buffer = TRY ( m_serialized . decode_buffer ( realm ) ) ;
auto max_byte_length = m_serialized . decode < size_t > ( ) ;
auto data = JS : : ArrayBuffer : : create ( realm , move ( buffer ) , JS : : DataBlock : : Shared : : Yes ) ;
data - > set_max_byte_length ( max_byte_length ) ;
value = data ;
2024-12-04 21:33:13 +01:00
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 14. Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]].
case ValueTag : : ArrayBuffer : {
2025-07-17 09:51:04 -04:00
auto buffer = TRY ( m_serialized . decode_buffer ( realm ) ) ;
value = JS : : ArrayBuffer : : create ( realm , move ( buffer ) ) ;
2023-11-11 20:14:46 +02:00
break ;
}
2025-07-17 09:51:04 -04:00
2024-12-04 21:33:13 +01:00
// 15. Otherwise, if serialized.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]], and whose [[ArrayBufferMaxByteLength]] internal slot value is a serialized.[[ArrayBufferMaxByteLength]].
case ValueTag : : ResizeableArrayBuffer : {
2025-07-17 09:51:04 -04:00
auto buffer = TRY ( m_serialized . decode_buffer ( realm ) ) ;
auto max_byte_length = m_serialized . decode < size_t > ( ) ;
auto data = JS : : ArrayBuffer : : create ( realm , move ( buffer ) ) ;
data - > set_max_byte_length ( max_byte_length ) ;
value = data ;
2024-12-04 21:33:13 +01:00
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then:
case ValueTag : : ArrayBufferView : {
auto array_buffer_value = TRY ( deserialize ( ) ) ;
2025-01-21 09:12:05 -05:00
auto & array_buffer = as < JS : : ArrayBuffer > ( array_buffer_value . as_object ( ) ) ;
2025-07-17 09:51:04 -04:00
2025-08-02 19:27:29 -04:00
auto constructor_name = m_serialized . decode < Utf16String > ( ) ;
2025-07-17 09:51:04 -04:00
auto byte_length = m_serialized . decode < u32 > ( ) ;
auto byte_offset = m_serialized . decode < u32 > ( ) ;
2023-11-11 20:14:46 +02:00
if ( constructor_name = = " DataView " sv ) {
2025-07-17 09:51:04 -04:00
value = JS : : DataView : : create ( realm , & array_buffer , byte_length , byte_offset ) ;
2023-11-11 20:14:46 +02:00
} else {
2025-07-17 09:51:04 -04:00
auto array_length = m_serialized . decode < u32 > ( ) ;
GC : : Ptr < JS : : TypedArrayBase > typed_array ;
2023-09-12 16:56:35 -06:00
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
2025-07-17 09:51:04 -04:00
if ( constructor_name = = # ClassName # # sv ) \
typed_array = JS : : ClassName : : create ( realm , array_length , array_buffer ) ;
2023-11-11 20:14:46 +02:00
JS_ENUMERATE_TYPED_ARRAYS
2023-09-12 16:56:35 -06:00
# undef __JS_ENUMERATE
# undef CREATE_TYPED_ARRAY
2025-07-17 09:51:04 -04:00
VERIFY ( typed_array ) ; // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
typed_array - > set_byte_length ( byte_length ) ;
typed_array - > set_byte_offset ( byte_offset ) ;
value = typed_array ;
2023-09-12 16:56:35 -06:00
}
2023-11-11 20:14:46 +02:00
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 17. Otherwise, if serialized.[[Type]] is "Map", then:
case ValueTag : : MapObject : {
// 1. Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.
value = JS : : Map : : create ( realm ) ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 2. Set deep to true.
deep = true ;
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 18. Otherwise, if serialized.[[Type]] is "Set", then:
case ValueTag : : SetObject : {
// 1. Set value to a new Set object in targetRealm whose [[SetData]] internal slot value is a new empty List.
value = JS : : Set : : create ( realm ) ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 2. Set deep to true.
deep = true ;
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 19. Otherwise, if serialized.[[Type]] is "Array", then:
case ValueTag : : ArrayObject : {
// 1. Let outputProto be targetRealm.[[Intrinsics]].[[%Array.prototype%]].
// 2. Set value to ! ArrayCreate(serialized.[[Length]], outputProto).
2025-07-17 09:51:04 -04:00
value = MUST ( JS : : Array : : create ( realm , m_serialized . decode < u64 > ( ) , realm . intrinsics ( ) . array_prototype ( ) ) ) ;
2023-11-11 20:14:46 +02:00
// 3. Set deep to true.
deep = true ;
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 20. Otherwise, if serialized.[[Type]] is "Object", then:
case ValueTag : : Object : {
// 1. Set value to a new Object in targetRealm.
value = JS : : Object : : create ( realm , realm . intrinsics ( ) . object_prototype ( ) ) ;
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 2. Set deep to true.
deep = true ;
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 21. Otherwise, if serialized.[[Type]] is "Error", then:
case ValueTag : : ErrorObject : {
2025-07-17 09:51:04 -04:00
auto type = m_serialized . decode < ErrorType > ( ) ;
2025-08-07 19:31:52 -04:00
auto message = m_serialized . decode < Optional < Utf16String > > ( ) ;
2025-07-17 09:51:04 -04:00
GC : : Ptr < JS : : Error > error ;
switch ( type ) {
case ErrorType : : Error :
error = JS : : Error : : create ( realm ) ;
break ;
2023-11-11 01:14:56 +02:00
# define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
case ErrorType : : ClassName : \
2025-07-17 09:51:04 -04:00
error = JS : : ClassName : : create ( realm ) ; \
2023-11-11 01:14:56 +02:00
break ;
2025-07-17 09:51:04 -04:00
JS_ENUMERATE_NATIVE_ERRORS
2023-11-11 01:14:56 +02:00
# undef __JS_ENUMERATE
}
2025-07-17 09:51:04 -04:00
VERIFY ( error ) ;
if ( message . has_value ( ) )
error - > set_message ( message . release_value ( ) ) ;
value = error ;
2023-11-11 20:14:46 +02:00
break ;
}
2025-07-17 09:51:04 -04:00
2023-11-11 20:14:46 +02:00
// 22. Otherwise:
default :
2024-02-23 18:32:27 +01:00
VERIFY ( tag = = ValueTag : : SerializableObject ) ;
// 1. Let interfaceName be serialized.[[Type]].
2025-07-17 09:51:04 -04:00
auto interface_name = m_serialized . decode < SerializeType > ( ) ;
2024-02-23 18:32:27 +01:00
// 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
2025-07-14 17:15:09 +01:00
if ( ! is_serializable_interface_exposed_on_target_realm ( interface_name , realm ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( realm , " Unsupported type " _utf16 ) ;
2024-02-23 18:32:27 +01:00
// 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
2024-11-23 15:36:22 +01:00
value = create_serialized_type ( interface_name , realm ) ;
2024-02-23 18:32:27 +01:00
// 4. Set deep to true.
deep = true ;
2023-11-11 20:14:46 +02:00
}
// 23. Set memory[serialized] to value.
// IMPLEMENTATION DEFINED: We don't add primitive values to the memory to match the serialization indices (which also doesn't add them)
if ( ! is_primitive )
m_memory . append ( value ) ;
// 24. If deep is true, then:
if ( deep ) {
// 1. If serialized.[[Type]] is "Map", then:
if ( tag = = ValueTag : : MapObject ) {
2025-07-17 09:51:04 -04:00
auto & map = as < JS : : Map > ( value . as_object ( ) ) ;
auto length = m_serialized . decode < u64 > ( ) ;
2023-11-11 20:14:46 +02:00
// 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[MapData]]:
for ( u64 i = 0u ; i < length ; + + i ) {
// 1. Let deserializedKey be ? StructuredDeserialize(entry.[[Key]], targetRealm, memory).
auto deserialized_key = TRY ( deserialize ( ) ) ;
// 2. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
auto deserialized_value = TRY ( deserialize ( ) ) ;
// 3. Append { [[Key]]: deserializedKey, [[Value]]: deserializedValue } to value.[[MapData]].
map . map_set ( deserialized_key , deserialized_value ) ;
}
}
// 2. Otherwise, if serialized.[[Type]] is "Set", then:
else if ( tag = = ValueTag : : SetObject ) {
2025-07-17 09:51:04 -04:00
auto & set = as < JS : : Set > ( value . as_object ( ) ) ;
auto length = m_serialized . decode < u64 > ( ) ;
2023-11-11 20:14:46 +02:00
// 1. For each entry of serialized.[[SetData]]:
for ( u64 i = 0u ; i < length ; + + i ) {
// 1. Let deserializedEntry be ? StructuredDeserialize(entry, targetRealm, memory).
auto deserialized_entry = TRY ( deserialize ( ) ) ;
// 2. Append deserializedEntry to value.[[SetData]].
set . set_add ( deserialized_entry ) ;
}
}
// 3. Otherwise, if serialized.[[Type]] is "Array" or "Object", then:
else if ( tag = = ValueTag : : ArrayObject | | tag = = ValueTag : : Object ) {
auto & object = value . as_object ( ) ;
2025-07-17 09:51:04 -04:00
auto length = m_serialized . decode < u64 > ( ) ;
2023-11-11 20:14:46 +02:00
// 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[Properties]]:
for ( u64 i = 0u ; i < length ; + + i ) {
2025-08-02 19:27:29 -04:00
auto key = m_serialized . decode < Utf16String > ( ) ;
2023-11-11 20:14:46 +02:00
// 1. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
auto deserialized_value = TRY ( deserialize ( ) ) ;
// 2. Let result be ! CreateDataProperty(value, entry.[[Key]], deserializedValue).
2025-03-16 20:45:02 -05:00
auto result = MUST ( object . create_data_property ( key , deserialized_value ) ) ;
2023-11-11 20:14:46 +02:00
// 3. Assert: result is true.
VERIFY ( result ) ;
}
}
// 4. Otherwise:
else {
2024-02-23 18:32:27 +01:00
// 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm.
2025-08-22 11:59:47 +01:00
auto & serializable = as < Bindings : : Serializable > ( value . as_object ( ) ) ;
2025-07-17 09:51:04 -04:00
TRY ( serializable . deserialization_steps ( m_serialized , m_memory ) ) ;
2022-04-05 02:24:10 +02:00
}
}
2023-11-11 20:14:46 +02:00
// 25. Return value.
return value ;
2022-04-05 02:24:10 +02:00
}
private :
2025-07-14 17:15:09 +01:00
static bool is_serializable_interface_exposed_on_target_realm ( SerializeType name , JS : : Realm & realm )
2024-02-23 18:32:27 +01:00
{
2025-07-14 17:15:09 +01:00
auto const & intrinsics = Bindings : : host_defined_intrinsics ( realm ) ;
switch ( name ) {
case SerializeType : : Blob :
return intrinsics . is_interface_exposed < Bindings : : BlobPrototype > ( realm ) ;
case SerializeType : : File :
return intrinsics . is_interface_exposed < Bindings : : FilePrototype > ( realm ) ;
case SerializeType : : FileList :
return intrinsics . is_interface_exposed < Bindings : : FileListPrototype > ( realm ) ;
case SerializeType : : DOMException :
return intrinsics . is_interface_exposed < Bindings : : DOMExceptionPrototype > ( realm ) ;
case SerializeType : : DOMMatrixReadOnly :
return intrinsics . is_interface_exposed < Bindings : : DOMMatrixReadOnlyPrototype > ( realm ) ;
case SerializeType : : DOMMatrix :
return intrinsics . is_interface_exposed < Bindings : : DOMMatrixPrototype > ( realm ) ;
case SerializeType : : DOMPointReadOnly :
return intrinsics . is_interface_exposed < Bindings : : DOMPointReadOnlyPrototype > ( realm ) ;
case SerializeType : : DOMPoint :
return intrinsics . is_interface_exposed < Bindings : : DOMPointPrototype > ( realm ) ;
case SerializeType : : DOMRectReadOnly :
return intrinsics . is_interface_exposed < Bindings : : DOMRectReadOnlyPrototype > ( realm ) ;
case SerializeType : : DOMRect :
return intrinsics . is_interface_exposed < Bindings : : DOMRectPrototype > ( realm ) ;
case SerializeType : : CryptoKey :
return intrinsics . is_interface_exposed < Bindings : : CryptoKeyPrototype > ( realm ) ;
case SerializeType : : DOMQuad :
return intrinsics . is_interface_exposed < Bindings : : DOMQuadPrototype > ( realm ) ;
case SerializeType : : ImageData :
return intrinsics . is_interface_exposed < Bindings : : ImageDataPrototype > ( realm ) ;
case SerializeType : : ImageBitmap :
return intrinsics . is_interface_exposed < Bindings : : ImageBitmapPrototype > ( realm ) ;
case SerializeType : : Unknown :
dbgln ( " Unknown interface type for serialization: {} " , to_underlying ( name ) ) ;
break ;
default :
VERIFY_NOT_REACHED ( ) ;
}
return false ;
}
static GC : : Ref < Bindings : : PlatformObject > create_serialized_type ( SerializeType serialize_type , JS : : Realm & realm )
{
switch ( serialize_type ) {
case SerializeType : : Blob :
2024-02-23 18:32:27 +01:00
return FileAPI : : Blob : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : File :
2024-02-23 22:14:18 +01:00
return FileAPI : : File : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : FileList :
2024-03-18 21:58:25 +01:00
return FileAPI : : FileList : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMException :
2024-11-22 12:17:18 +01:00
return WebIDL : : DOMException : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMMatrixReadOnly :
2024-03-09 22:07:22 +01:00
return Geometry : : DOMMatrixReadOnly : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMMatrix :
2024-03-09 22:41:26 +01:00
return Geometry : : DOMMatrix : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMPointReadOnly :
2024-03-09 23:02:22 +01:00
return Geometry : : DOMPointReadOnly : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMPoint :
2024-03-09 23:10:11 +01:00
return Geometry : : DOMPoint : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMRectReadOnly :
2024-03-10 09:02:54 +01:00
return Geometry : : DOMRectReadOnly : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMRect :
2024-03-10 09:43:00 +01:00
return Geometry : : DOMRect : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : CryptoKey :
2024-03-10 20:48:00 +01:00
return Crypto : : CryptoKey : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : DOMQuad :
2024-03-12 21:08:46 +01:00
return Geometry : : DOMQuad : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : ImageData :
2025-05-04 13:45:51 +02:00
return ImageData : : create ( realm ) ;
2025-07-14 17:15:09 +01:00
case SerializeType : : ImageBitmap :
return ImageBitmap : : create ( realm ) ;
case SerializeType : : Unknown :
default :
VERIFY_NOT_REACHED ( ) ;
}
2024-02-23 18:32:27 +01:00
}
2023-11-11 20:14:46 +02:00
2025-07-17 09:51:04 -04:00
JS : : VM & m_vm ;
TransferDataDecoder & m_serialized ;
GC : : RootVector < JS : : Value > m_memory ;
} ;
2022-04-05 02:24:10 +02:00
2023-12-07 15:53:38 -07:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializewithtransfer
2024-11-15 04:01:23 +13:00
WebIDL : : ExceptionOr < SerializedTransferRecord > structured_serialize_with_transfer ( JS : : VM & vm , JS : : Value value , Vector < GC : : Root < JS : : Object > > const & transfer_list )
2023-12-07 15:53:38 -07:00
{
// 1. Let memory be an empty map.
SerializationMemory memory = { } ;
// 2. For each transferable of transferList:
for ( auto const & transferable : transfer_list ) {
2025-07-17 09:51:04 -04:00
auto const * as_array_buffer = as_if < JS : : ArrayBuffer > ( * transferable ) ;
2023-12-07 15:53:38 -07:00
// 1. If transferable has neither an [[ArrayBufferData]] internal slot nor a [[Detached]] internal slot, then throw a "DataCloneError" DOMException.
2024-11-24 14:20:57 +01:00
// FIXME: Handle transferring objects with [[Detached]] internal slot.
2025-07-17 09:51:04 -04:00
if ( ! as_array_buffer & & ! is < Bindings : : Transferable > ( * transferable ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer type " _utf16 ) ;
2023-12-07 15:53:38 -07:00
2024-11-24 14:20:57 +01:00
// 2. If transferable has an [[ArrayBufferData]] internal slot and IsSharedArrayBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
2025-07-17 09:51:04 -04:00
if ( as_array_buffer & & as_array_buffer - > is_shared_array_buffer ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer shared array buffer " _utf16 ) ;
2025-07-17 09:51:04 -04:00
JS : : Value transferable_value { transferable } ;
2023-12-07 15:53:38 -07:00
// 3. If memory[transferable] exists, then throw a "DataCloneError" DOMException.
2025-07-17 09:51:04 -04:00
if ( memory . contains ( transferable_value ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer value twice " _utf16 ) ;
2023-12-07 15:53:38 -07:00
// 4. Set memory[transferable] to { [[Type]]: an uninitialized value }.
2024-11-24 14:20:57 +01:00
memory . set ( GC : : make_root ( transferable_value ) , memory . size ( ) ) ;
2023-12-07 15:53:38 -07:00
}
// 3. Let serialized be ? StructuredSerializeInternal(value, false, memory).
auto serialized = TRY ( structured_serialize_internal ( vm , value , false , memory ) ) ;
// 4. Let transferDataHolders be a new empty List.
2025-07-17 09:51:04 -04:00
Vector < TransferDataEncoder > transfer_data_holders ;
2023-12-07 15:53:38 -07:00
transfer_data_holders . ensure_capacity ( transfer_list . size ( ) ) ;
// 5. For each transferable of transferList:
2025-07-17 09:51:04 -04:00
for ( auto const & transferable : transfer_list ) {
auto * array_buffer = as_if < JS : : ArrayBuffer > ( * transferable ) ;
2025-01-31 09:49:16 +00:00
auto is_detached = array_buffer & & array_buffer - > is_detached ( ) ;
2024-11-24 14:20:57 +01:00
// 1. If transferable has an [[ArrayBufferData]] internal slot and IsDetachedBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
2025-07-17 09:51:04 -04:00
if ( is_detached )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Cannot transfer detached buffer " _utf16 ) ;
2023-12-07 15:53:38 -07:00
// 2. If transferable has a [[Detached]] internal slot and transferable.[[Detached]] is true, then throw a "DataCloneError" DOMException.
2025-01-31 09:49:16 +00:00
if ( auto * transferable_object = as_if < Bindings : : Transferable > ( * transferable ) ) {
2025-07-17 09:51:04 -04:00
if ( transferable_object - > is_detached ( ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( * vm . current_realm ( ) , " Value already transferred " _utf16 ) ;
2023-12-07 15:53:38 -07:00
}
// 3. Let dataHolder be memory[transferable].
// IMPLEMENTATION DEFINED: We just create a data holder here, our memory holds indices into the SerializationRecord
2025-07-17 09:51:04 -04:00
TransferDataEncoder data_holder ;
2023-12-07 15:53:38 -07:00
2024-11-24 14:20:57 +01:00
// 4. If transferable has an [[ArrayBufferData]] internal slot, then:
2025-01-31 09:49:16 +00:00
if ( array_buffer ) {
2024-11-24 14:20:57 +01:00
// 1. If transferable has an [[ArrayBufferMaxByteLength]] internal slot, then:
2025-01-31 09:49:16 +00:00
if ( ! array_buffer - > is_fixed_length ( ) ) {
2024-11-24 14:20:57 +01:00
// 1. Set dataHolder.[[Type]] to "ResizableArrayBuffer".
2025-07-17 09:51:04 -04:00
data_holder . encode ( TransferType : : ResizableArrayBuffer ) ;
2024-11-24 14:20:57 +01:00
// 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]].
// 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]].
2025-07-17 09:51:04 -04:00
data_holder . encode ( array_buffer - > buffer ( ) ) ;
2024-11-24 14:20:57 +01:00
// 4. Set dataHolder.[[ArrayBufferMaxByteLength]] to transferable.[[ArrayBufferMaxByteLength]].
2025-07-17 09:51:04 -04:00
data_holder . encode ( array_buffer - > max_byte_length ( ) ) ;
2024-11-24 14:20:57 +01:00
}
// 2. Otherwise:
else {
// 1. Set dataHolder.[[Type]] to "ArrayBuffer".
2025-07-17 09:51:04 -04:00
data_holder . encode ( TransferType : : ArrayBuffer ) ;
2024-11-24 14:20:57 +01:00
// 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]].
// 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]].
2025-07-17 09:51:04 -04:00
data_holder . encode ( array_buffer - > buffer ( ) ) ;
2024-11-24 14:20:57 +01:00
}
// 3. Perform ? DetachArrayBuffer(transferable).
// NOTE: Specifications can use the [[ArrayBufferDetachKey]] internal slot to prevent ArrayBuffers from being detached. This is used in WebAssembly JavaScript Interface, for example. See: https://html.spec.whatwg.org/multipage/references.html#refsWASMJS
2025-01-31 09:49:16 +00:00
TRY ( JS : : detach_array_buffer ( vm , * array_buffer ) ) ;
2023-12-07 15:53:38 -07:00
}
// 5. Otherwise:
else {
// 1. Assert: transferable is a platform object that is a transferable object.
2025-08-22 11:59:47 +01:00
auto & transferable_object = as < Bindings : : Transferable > ( * transferable ) ;
2023-12-12 13:30:16 -07:00
VERIFY ( is < Bindings : : PlatformObject > ( * transferable ) ) ;
2023-12-07 15:53:38 -07:00
// 2. Let interfaceName be the identifier of the primary interface of transferable.
auto interface_name = transferable_object . primary_interface ( ) ;
// 3. Set dataHolder.[[Type]] to interfaceName.
2025-07-17 09:51:04 -04:00
data_holder . encode ( interface_name ) ;
2023-12-07 15:53:38 -07:00
// 4. Perform the appropriate transfer steps for the interface identified by interfaceName, given transferable and dataHolder.
TRY ( transferable_object . transfer_steps ( data_holder ) ) ;
// 5. Set transferable.[[Detached]] to true.
transferable_object . set_detached ( true ) ;
}
// 6. Append dataHolder to transferDataHolders.
transfer_data_holders . append ( move ( data_holder ) ) ;
}
// 6. Return { [[Serialized]]: serialized, [[TransferDataHolders]]: transferDataHolders }.
return SerializedTransferRecord { . serialized = move ( serialized ) , . transfer_data_holders = move ( transfer_data_holders ) } ;
}
2025-07-14 17:15:09 +01:00
static bool is_transferable_interface_exposed_on_target_realm ( TransferType name , JS : : Realm & realm )
2023-12-07 15:53:38 -07:00
{
auto const & intrinsics = Bindings : : host_defined_intrinsics ( realm ) ;
2024-11-24 14:20:57 +01:00
switch ( name ) {
2023-12-07 15:53:38 -07:00
case TransferType : : MessagePort :
2025-07-14 17:15:09 +01:00
return intrinsics . is_interface_exposed < Bindings : : MessagePortPrototype > ( realm ) ;
2025-05-20 17:20:22 -04:00
case TransferType : : ReadableStream :
2025-07-14 17:15:09 +01:00
return intrinsics . is_interface_exposed < Bindings : : ReadableStreamPrototype > ( realm ) ;
2025-05-20 17:27:51 -04:00
case TransferType : : WritableStream :
2025-07-14 17:15:09 +01:00
return intrinsics . is_interface_exposed < Bindings : : WritableStreamPrototype > ( realm ) ;
2025-05-20 18:05:52 -04:00
case TransferType : : TransformStream :
2025-07-14 17:15:09 +01:00
return intrinsics . is_interface_exposed < Bindings : : TransformStreamPrototype > ( realm ) ;
2025-07-20 13:32:26 +01:00
case TransferType : : ImageBitmap :
return intrinsics . is_interface_exposed < Bindings : : ImageBitmapPrototype > ( realm ) ;
2025-04-13 19:50:49 +02:00
case TransferType : : Unknown :
2024-11-24 14:20:57 +01:00
dbgln ( " Unknown interface type for transfer: {} " , to_underlying ( name ) ) ;
2023-12-07 15:53:38 -07:00
break ;
2025-04-13 19:50:49 +02:00
default :
VERIFY_NOT_REACHED ( ) ;
2023-12-07 15:53:38 -07:00
}
return false ;
}
2025-07-17 09:51:04 -04:00
static WebIDL : : ExceptionOr < GC : : Ref < Bindings : : PlatformObject > > create_transferred_value ( TransferType name , JS : : Realm & target_realm , TransferDataDecoder & decoder )
2023-12-07 15:53:38 -07:00
{
switch ( name ) {
case TransferType : : MessagePort : {
auto message_port = HTML : : MessagePort : : create ( target_realm ) ;
2025-07-17 09:51:04 -04:00
TRY ( message_port - > transfer_receiving_steps ( decoder ) ) ;
2023-12-07 15:53:38 -07:00
return message_port ;
}
2025-05-20 17:20:22 -04:00
case TransferType : : ReadableStream : {
auto readable_stream = target_realm . create < Streams : : ReadableStream > ( target_realm ) ;
2025-07-17 09:51:04 -04:00
TRY ( readable_stream - > transfer_receiving_steps ( decoder ) ) ;
2025-05-20 17:20:22 -04:00
return readable_stream ;
}
2025-05-20 17:27:51 -04:00
case TransferType : : WritableStream : {
auto writable_stream = target_realm . create < Streams : : WritableStream > ( target_realm ) ;
2025-07-17 09:51:04 -04:00
TRY ( writable_stream - > transfer_receiving_steps ( decoder ) ) ;
2025-05-20 17:27:51 -04:00
return writable_stream ;
}
2025-05-20 18:05:52 -04:00
case TransferType : : TransformStream : {
auto transform_stream = target_realm . create < Streams : : TransformStream > ( target_realm ) ;
2025-07-17 09:51:04 -04:00
TRY ( transform_stream - > transfer_receiving_steps ( decoder ) ) ;
2025-05-20 18:05:52 -04:00
return transform_stream ;
}
2025-07-19 08:07:15 +02:00
case TransferType : : ImageBitmap : {
auto image_bitmap = target_realm . create < ImageBitmap > ( target_realm ) ;
TRY ( image_bitmap - > transfer_receiving_steps ( decoder ) ) ;
return image_bitmap ;
}
2024-11-24 14:20:57 +01:00
case TransferType : : ArrayBuffer :
case TransferType : : ResizableArrayBuffer :
2025-04-13 19:50:49 +02:00
case TransferType : : Unknown :
break ;
2023-12-07 15:53:38 -07:00
}
VERIFY_NOT_REACHED ( ) ;
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserializewithtransfer
2025-04-17 08:57:55 -04:00
WebIDL : : ExceptionOr < DeserializedTransferRecord > structured_deserialize_with_transfer ( SerializedTransferRecord & serialize_with_transfer_result , JS : : Realm & target_realm )
2023-12-07 15:53:38 -07:00
{
2025-04-17 08:57:55 -04:00
auto & vm = target_realm . vm ( ) ;
2023-12-07 15:53:38 -07:00
// 1. Let memory be an empty map.
auto memory = DeserializationMemory ( vm . heap ( ) ) ;
// 2. Let transferredValues be a new empty List.
2024-11-15 04:01:23 +13:00
Vector < GC : : Root < JS : : Object > > transferred_values ;
2023-12-07 15:53:38 -07:00
// 3. For each transferDataHolder of serializeWithTransferResult.[[TransferDataHolders]]:
for ( auto & transfer_data_holder : serialize_with_transfer_result . transfer_data_holders ) {
2025-07-17 09:51:04 -04:00
if ( transfer_data_holder . buffer ( ) . data ( ) . is_empty ( ) )
2024-11-24 14:20:57 +01:00
continue ;
2025-07-17 09:51:04 -04:00
TransferDataDecoder decoder { move ( transfer_data_holder ) } ;
2023-12-07 15:53:38 -07:00
2025-07-17 09:51:04 -04:00
// 1. Let value be an uninitialized value.
auto value = TRY ( structured_deserialize_with_transfer_internal ( decoder , target_realm ) ) ;
2023-12-07 15:53:38 -07:00
// 5. Set memory[transferDataHolder] to value.
memory . append ( value ) ;
// 6. Append value to transferredValues.
2024-11-15 04:01:23 +13:00
transferred_values . append ( GC : : make_root ( value . as_object ( ) ) ) ;
2023-12-07 15:53:38 -07:00
}
// 4. Let deserialized be ? StructuredDeserialize(serializeWithTransferResult.[[Serialized]], targetRealm, memory).
auto deserialized = TRY ( structured_deserialize ( vm , serialize_with_transfer_result . serialized , target_realm , memory ) ) ;
// 5. Return { [[Deserialized]]: deserialized, [[TransferredValues]]: transferredValues }.
2025-07-17 09:51:04 -04:00
return DeserializedTransferRecord { . deserialized = deserialized , . transferred_values = move ( transferred_values ) } ;
}
// AD-HOC: This non-standard overload is meant to extract just one transferrable value from a serialized transfer record.
// It's primarily useful for an object's transfer receiving steps to deserialize a nested value.
WebIDL : : ExceptionOr < JS : : Value > structured_deserialize_with_transfer_internal ( TransferDataDecoder & decoder , JS : : Realm & target_realm )
{
auto type = decoder . decode < TransferType > ( ) ;
// 1. Let value be an uninitialized value.
JS : : Value value ;
// 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm
// whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], and
// whose [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]].
// NOTE: In cases where the original memory occupied by [[ArrayBufferData]] is accessible during the deserialization,
// this step is unlikely to throw an exception, as no new memory needs to be allocated: the memory occupied by
// [[ArrayBufferData]] is instead just getting transferred into the new ArrayBuffer. This could be true, for example,
// when both the source and target realms are in the same process.
if ( type = = TransferType : : ArrayBuffer ) {
auto buffer = TRY ( decoder . decode_buffer ( target_realm ) ) ;
value = JS : : ArrayBuffer : : create ( target_realm , move ( buffer ) ) ;
}
// 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object
// in targetRealm whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], whose
// [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]], and whose
// [[ArrayBufferMaxByteLength]] internal slot value is transferDataHolder.[[ArrayBufferMaxByteLength]].
// NOTE: For the same reason as the previous step, this step is also unlikely to throw an exception.
else if ( type = = TransferType : : ResizableArrayBuffer ) {
auto buffer = TRY ( decoder . decode_buffer ( target_realm ) ) ;
auto max_byte_length = decoder . decode < size_t > ( ) ;
auto data = JS : : ArrayBuffer : : create ( target_realm , move ( buffer ) ) ;
data - > set_max_byte_length ( max_byte_length ) ;
value = data ;
}
// 4. Otherwise:
else {
// 1. Let interfaceName be transferDataHolder.[[Type]].
// 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
if ( ! is_transferable_interface_exposed_on_target_realm ( type , target_realm ) )
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( target_realm , " Unknown type transferred " _utf16 ) ;
2025-07-17 09:51:04 -04:00
// 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
// 4. Perform the appropriate transfer-receiving steps for the interface identified by interfaceName given transferDataHolder and value.
value = TRY ( create_transferred_value ( type , target_realm , decoder ) ) ;
}
return value ;
2023-12-07 15:53:38 -07:00
}
2022-04-05 02:24:10 +02:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
WebIDL : : ExceptionOr < SerializationRecord > structured_serialize ( JS : : VM & vm , JS : : Value value )
{
// 1. Return ? StructuredSerializeInternal(value, false).
2023-09-12 16:41:13 -06:00
SerializationMemory memory = { } ;
return structured_serialize_internal ( vm , value , false , memory ) ;
2022-04-05 02:24:10 +02:00
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
WebIDL : : ExceptionOr < SerializationRecord > structured_serialize_for_storage ( JS : : VM & vm , JS : : Value value )
{
// 1. Return ? StructuredSerializeInternal(value, true).
2023-09-12 16:41:13 -06:00
SerializationMemory memory = { } ;
return structured_serialize_internal ( vm , value , true , memory ) ;
2022-04-05 02:24:10 +02:00
}
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
2023-09-12 16:41:13 -06:00
WebIDL : : ExceptionOr < SerializationRecord > structured_serialize_internal ( JS : : VM & vm , JS : : Value value , bool for_storage , SerializationMemory & memory )
2022-04-05 02:24:10 +02:00
{
2023-08-21 15:15:05 -06:00
// 1. If memory was not supplied, let memory be an empty map.
2023-09-12 16:41:13 -06:00
// IMPLEMENTATION DEFINED: We move this requirement up to the callers to make recursion easier
2023-08-21 15:15:05 -06:00
2023-09-12 16:41:13 -06:00
Serializer serializer ( vm , memory , for_storage ) ;
2023-08-21 15:15:05 -06:00
return serializer . serialize ( value ) ;
2022-04-05 02:24:10 +02:00
}
2023-09-12 16:41:13 -06:00
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
2023-11-11 20:14:46 +02:00
WebIDL : : ExceptionOr < JS : : Value > structured_deserialize ( JS : : VM & vm , SerializationRecord const & serialized , JS : : Realm & target_realm , Optional < DeserializationMemory > memory )
2023-09-12 16:41:13 -06:00
{
2025-07-17 11:53:20 -04:00
TemporaryExecutionContext execution_context { target_realm } ;
2023-09-12 16:41:13 -06:00
if ( ! memory . has_value ( ) )
2023-11-11 20:14:46 +02:00
memory = DeserializationMemory { vm . heap ( ) } ;
2023-09-12 16:41:13 -06:00
2025-07-17 09:51:04 -04:00
TransferDataDecoder decoder { serialized } ;
return structured_deserialize_internal ( vm , decoder , target_realm , * memory ) ;
2024-03-10 20:37:41 +01:00
}
2025-07-17 09:51:04 -04:00
WebIDL : : ExceptionOr < JS : : Value > structured_deserialize_internal ( JS : : VM & vm , TransferDataDecoder & serialized , JS : : Realm & target_realm , DeserializationMemory & memory )
2024-03-10 20:37:41 +01:00
{
2025-07-17 09:51:04 -04:00
Deserializer deserializer ( vm , serialized , target_realm , memory ) ;
return deserializer . deserialize ( ) ;
}
TransferDataEncoder : : TransferDataEncoder ( )
: m_encoder ( m_buffer )
{
}
TransferDataEncoder : : TransferDataEncoder ( IPC : : MessageBuffer & & buffer )
: m_buffer ( move ( buffer ) )
, m_encoder ( m_buffer )
{
}
void TransferDataEncoder : : append ( SerializationRecord & & record )
{
MUST ( m_buffer . append_data ( record . data ( ) , record . size ( ) ) ) ;
}
void TransferDataEncoder : : extend ( Vector < TransferDataEncoder > data_holders )
{
for ( auto & data_holder : data_holders )
MUST ( m_buffer . extend ( move ( data_holder . m_buffer ) ) ) ;
}
TransferDataDecoder : : TransferDataDecoder ( SerializationRecord const & record )
: m_stream ( record . span ( ) )
, m_decoder ( m_stream , m_files )
{
}
TransferDataDecoder : : TransferDataDecoder ( TransferDataEncoder & & data_holder )
: m_buffer ( data_holder . take_buffer ( ) )
, m_stream ( m_buffer . data ( ) . span ( ) )
, m_decoder ( m_stream , m_files )
{
// FIXME: The churn between IPC::File and IPC::AutoCloseFileDescriptor is pretty awkward, we should find a way to
// consolidate the way we use these type.
for ( auto & auto_fd : m_buffer . take_fds ( ) )
m_files . enqueue ( IPC : : File : : adopt_fd ( auto_fd - > take_fd ( ) ) ) ;
}
WebIDL : : ExceptionOr < ByteBuffer > TransferDataDecoder : : decode_buffer ( JS : : Realm & realm )
{
auto buffer = m_decoder . decode < ByteBuffer > ( ) ;
if ( buffer . is_error ( ) ) {
VERIFY ( buffer . error ( ) . code ( ) = = ENOMEM ) ;
2025-08-07 19:31:52 -04:00
return WebIDL : : DataCloneError : : create ( realm , " Unable to allocate memory for transferred buffer " _utf16 ) ;
2025-07-17 09:51:04 -04:00
}
return buffer . release_value ( ) ;
2023-09-12 16:41:13 -06:00
}
2022-04-05 02:24:10 +02:00
}
2023-12-19 15:28:56 -07:00
namespace IPC {
template < >
2025-07-17 09:51:04 -04:00
ErrorOr < void > encode ( Encoder & encoder , Web : : HTML : : TransferDataEncoder const & data_holder )
2023-12-19 15:28:56 -07:00
{
2025-07-17 09:51:04 -04:00
// FIXME: The churn between IPC::File and IPC::AutoCloseFileDescriptor is pretty awkward, we should find a way to
// consolidate the way we use these type.
Vector < IPC : : File > files ;
files . ensure_capacity ( data_holder . buffer ( ) . fds ( ) . size ( ) ) ;
for ( auto const & auto_fd : data_holder . buffer ( ) . fds ( ) ) {
auto fd = const_cast < AutoCloseFileDescriptor & > ( * auto_fd ) . take_fd ( ) ;
files . unchecked_append ( IPC : : File : : adopt_fd ( fd ) ) ;
}
TRY ( encoder . encode ( data_holder . buffer ( ) . data ( ) ) ) ;
TRY ( encoder . encode ( files ) ) ;
2023-12-19 15:28:56 -07:00
return { } ;
}
template < >
2025-07-17 09:51:04 -04:00
ErrorOr < Web : : HTML : : TransferDataEncoder > decode ( Decoder & decoder )
2023-12-19 15:28:56 -07:00
{
2025-07-17 09:51:04 -04:00
auto data = TRY ( decoder . decode < Web : : HTML : : SerializationRecord > ( ) ) ;
auto files = TRY ( decoder . decode < Vector < IPC : : File > > ( ) ) ;
// FIXME: The churn between IPC::File and IPC::AutoCloseFileDescriptor is pretty awkward, we should find a way to
// consolidate the way we use these type.
MessageFileType auto_files ;
auto_files . ensure_capacity ( files . size ( ) ) ;
for ( auto & fd : files ) {
auto auto_fd = adopt_ref ( * new AutoCloseFileDescriptor ( fd . take_fd ( ) ) ) ;
auto_files . unchecked_append ( move ( auto_fd ) ) ;
}
IPC : : MessageBuffer buffer { move ( data ) , move ( auto_files ) } ;
return Web : : HTML : : TransferDataEncoder { move ( buffer ) } ;
2023-12-19 15:28:56 -07:00
}
template < >
2025-07-17 09:51:04 -04:00
ErrorOr < void > encode ( Encoder & encoder , Web : : HTML : : SerializedTransferRecord const & record )
2023-12-19 15:28:56 -07:00
{
2025-07-17 09:51:04 -04:00
TRY ( encoder . encode ( record . serialized ) ) ;
TRY ( encoder . encode ( record . transfer_data_holders ) ) ;
return { } ;
2023-12-19 15:28:56 -07:00
}
template < >
2025-07-17 09:51:04 -04:00
ErrorOr < Web : : HTML : : SerializedTransferRecord > decode ( Decoder & decoder )
2023-12-19 15:28:56 -07:00
{
2025-07-17 09:51:04 -04:00
auto serialized = TRY ( decoder . decode < Web : : HTML : : SerializationRecord > ( ) ) ;
auto transfer_data_holders = TRY ( decoder . decode < Vector < Web : : HTML : : TransferDataEncoder > > ( ) ) ;
return Web : : HTML : : SerializedTransferRecord { move ( serialized ) , move ( transfer_data_holders ) } ;
2023-12-19 15:28:56 -07:00
}
}