Coverage Report

Created: 2025-12-18 07:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
3
 * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
4
 * Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
5
 * Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 */
9
10
#include <AK/StdLibExtras.h>
11
#include <AK/String.h>
12
#include <AK/Vector.h>
13
#include <LibIPC/Decoder.h>
14
#include <LibIPC/Encoder.h>
15
#include <LibIPC/File.h>
16
#include <LibJS/Forward.h>
17
#include <LibJS/Runtime/Array.h>
18
#include <LibJS/Runtime/ArrayBuffer.h>
19
#include <LibJS/Runtime/BigInt.h>
20
#include <LibJS/Runtime/BigIntObject.h>
21
#include <LibJS/Runtime/BooleanObject.h>
22
#include <LibJS/Runtime/DataView.h>
23
#include <LibJS/Runtime/Date.h>
24
#include <LibJS/Runtime/Map.h>
25
#include <LibJS/Runtime/NumberObject.h>
26
#include <LibJS/Runtime/PrimitiveString.h>
27
#include <LibJS/Runtime/RegExpObject.h>
28
#include <LibJS/Runtime/Set.h>
29
#include <LibJS/Runtime/StringObject.h>
30
#include <LibJS/Runtime/TypedArray.h>
31
#include <LibJS/Runtime/VM.h>
32
#include <LibWeb/Bindings/ExceptionOrUtils.h>
33
#include <LibWeb/Bindings/Intrinsics.h>
34
#include <LibWeb/Bindings/Serializable.h>
35
#include <LibWeb/Bindings/Transferable.h>
36
#include <LibWeb/Crypto/CryptoKey.h>
37
#include <LibWeb/FileAPI/Blob.h>
38
#include <LibWeb/FileAPI/File.h>
39
#include <LibWeb/FileAPI/FileList.h>
40
#include <LibWeb/Geometry/DOMMatrix.h>
41
#include <LibWeb/Geometry/DOMMatrixReadOnly.h>
42
#include <LibWeb/Geometry/DOMPoint.h>
43
#include <LibWeb/Geometry/DOMPointReadOnly.h>
44
#include <LibWeb/Geometry/DOMQuad.h>
45
#include <LibWeb/Geometry/DOMRect.h>
46
#include <LibWeb/Geometry/DOMRectReadOnly.h>
47
#include <LibWeb/HTML/MessagePort.h>
48
#include <LibWeb/HTML/StructuredSerialize.h>
49
#include <LibWeb/WebIDL/ExceptionOr.h>
50
51
namespace Web::HTML {
52
53
// Binary format:
54
// A list of adjacent shallow values, which may contain references to other
55
// values (noted by their position in the list, one value following another).
56
// This list represents the "memory" in the StructuredSerialize algorithm.
57
// The first item in the list is the root, i.e., the value of everything.
58
// The format is generally u32-aligned (hence this leaking out into the type)
59
// Each value has a length based on its type, as defined below.
60
//
61
// (Should more redundancy be added, e.g., for lengths/positions of values?)
62
63
enum ValueTag {
64
    // Unused, for ease of catching bugs.
65
    Empty,
66
67
    // UndefinedPrimitive is serialized indicating that the Type is Undefined, no value is serialized.
68
    UndefinedPrimitive,
69
70
    // NullPrimitive is serialized indicating that the Type is Null, no value is serialized.
71
    NullPrimitive,
72
73
    // Following u32 is the boolean value.
74
    BooleanPrimitive,
75
76
    // Following two u32s are the double value.
77
    NumberPrimitive,
78
79
    // The BigIntPrimitive is serialized as a string in base 10 representation.
80
    // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
81
    BigIntPrimitive,
82
83
    // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
84
    StringPrimitive,
85
86
    BooleanObject,
87
88
    NumberObject,
89
90
    BigIntObject,
91
92
    StringObject,
93
94
    DateObject,
95
96
    RegExpObject,
97
98
    GrowableSharedArrayBuffer,
99
100
    SharedArrayBuffer,
101
102
    ResizeableArrayBuffer,
103
104
    ArrayBuffer,
105
106
    ArrayBufferView,
107
108
    MapObject,
109
110
    SetObject,
111
112
    ErrorObject,
113
114
    ArrayObject,
115
116
    Object,
117
118
    ObjectReference,
119
120
    SerializableObject,
121
122
    // TODO: Define many more types
123
124
    // This tag or higher are understood to be errors
125
    ValueTagMax,
126
};
127
128
enum ErrorType {
129
    Error,
130
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
131
    ClassName,
132
    JS_ENUMERATE_NATIVE_ERRORS
133
#undef __JS_ENUMERATE
134
};
135
136
static ErrorType error_name_to_type(String const& name)
137
0
{
138
0
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
139
0
    if (name == #ClassName##sv)                                                          \
140
0
        return ErrorType::ClassName;
141
0
    JS_ENUMERATE_NATIVE_ERRORS
142
0
#undef __JS_ENUMERATE
143
0
    return Error;
144
0
}
145
146
// Serializing and deserializing are each two passes:
147
// 1. Fill up the memory with all the values, but without translating references
148
// 2. Translate all the references into the appropriate form
149
150
class Serializer {
151
public:
152
    Serializer(JS::VM& vm, SerializationMemory& memory, bool for_storage)
153
0
        : m_vm(vm)
154
0
        , m_memory(memory)
155
0
        , m_for_storage(for_storage)
156
0
    {
157
0
    }
158
159
    // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
160
    WebIDL::ExceptionOr<SerializationRecord> serialize(JS::Value value)
161
0
    {
162
        // 2. If memory[value] exists, then return memory[value].
163
0
        if (m_memory.contains(value)) {
164
0
            auto index = m_memory.get(value).value();
165
0
            return Vector<u32> { ValueTag::ObjectReference, index };
166
0
        }
167
168
        // 3. Let deep be false.
169
0
        auto deep = false;
170
171
0
        bool return_primitive_type = true;
172
        // 4. If value is undefined, null, a Boolean, a Number, a BigInt, or a String, then return { [[Type]]: "primitive", [[Value]]: value }.
173
0
        if (value.is_undefined()) {
174
0
            serialize_enum(m_serialized, ValueTag::UndefinedPrimitive);
175
0
        } else if (value.is_null()) {
176
0
            serialize_enum(m_serialized, ValueTag::NullPrimitive);
177
0
        } else if (value.is_boolean()) {
178
0
            serialize_enum(m_serialized, ValueTag::BooleanPrimitive);
179
0
            serialize_boolean_primitive(m_serialized, value);
180
0
        } else if (value.is_number()) {
181
0
            serialize_enum(m_serialized, ValueTag::NumberPrimitive);
182
0
            serialize_number_primitive(m_serialized, value);
183
0
        } else if (value.is_bigint()) {
184
0
            serialize_enum(m_serialized, ValueTag::BigIntPrimitive);
185
0
            TRY(serialize_big_int_primitive(m_vm, m_serialized, value));
186
0
        } else if (value.is_string()) {
187
0
            serialize_enum(m_serialized, ValueTag::StringPrimitive);
188
0
            TRY(serialize_string_primitive(m_vm, m_serialized, value));
189
0
        } else {
190
0
            return_primitive_type = false;
191
0
        }
192
193
0
        if (return_primitive_type)
194
0
            return m_serialized;
195
196
        // 5. If value is a Symbol, then throw a "DataCloneError" DOMException.
197
0
        if (value.is_symbol())
198
0
            return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize Symbol"_string);
199
200
        // 6. Let serialized be an uninitialized value.
201
202
        // 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
203
0
        if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
204
0
            serialize_enum(m_serialized, ValueTag::BooleanObject);
205
0
            serialize_boolean_object(m_serialized, value);
206
0
        }
207
208
        // 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }.
209
0
        else if (value.is_object() && is<JS::NumberObject>(value.as_object())) {
210
0
            serialize_enum(m_serialized, ValueTag::NumberObject);
211
0
            serialize_number_object(m_serialized, value);
212
0
        }
213
214
        // 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }.
215
0
        else if (value.is_object() && is<JS::BigIntObject>(value.as_object())) {
216
0
            serialize_enum(m_serialized, ValueTag::BigIntObject);
217
0
            TRY(serialize_big_int_object(m_vm, m_serialized, value));
218
0
        }
219
220
        // 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }.
221
0
        else if (value.is_object() && is<JS::StringObject>(value.as_object())) {
222
0
            serialize_enum(m_serialized, ValueTag::StringObject);
223
0
            TRY(serialize_string_object(m_vm, m_serialized, value));
224
0
        }
225
226
        // 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
227
0
        else if (value.is_object() && is<JS::Date>(value.as_object())) {
228
0
            serialize_enum(m_serialized, ValueTag::DateObject);
229
0
            serialize_date_object(m_serialized, value);
230
0
        }
231
232
        // 12. Otherwise, if value has a [[RegExpMatcher]] internal slot, then set serialized to
233
        //     { [[Type]]: "RegExp", [[RegExpMatcher]]: value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]],
234
        //       [[OriginalFlags]]: value.[[OriginalFlags]] }.
235
0
        else if (value.is_object() && is<JS::RegExpObject>(value.as_object())) {
236
0
            serialize_enum(m_serialized, ValueTag::RegExpObject);
237
0
            TRY(serialize_reg_exp_object(m_vm, m_serialized, value));
238
0
        }
239
240
        // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
241
0
        else if (value.is_object() && is<JS::ArrayBuffer>(value.as_object())) {
242
0
            TRY(serialize_array_buffer(m_vm, m_serialized, static_cast<JS::ArrayBuffer&>(value.as_object()), m_for_storage));
243
0
        }
244
245
        // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
246
0
        else if (value.is_object() && is<JS::TypedArrayBase>(value.as_object())) {
247
0
            TRY(serialize_viewed_array_buffer(m_vm, m_serialized, static_cast<JS::TypedArrayBase&>(value.as_object()), m_for_storage, m_memory));
248
0
        } else if (value.is_object() && is<JS::DataView>(value.as_object())) {
249
0
            TRY(serialize_viewed_array_buffer(m_vm, m_serialized, static_cast<JS::DataView&>(value.as_object()), m_for_storage, m_memory));
250
0
        }
251
252
        // 15. Otherwise, if value has [[MapData]] internal slot, then:
253
0
        else if (value.is_object() && is<JS::Map>(value.as_object())) {
254
            // 1. Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }.
255
0
            serialize_enum(m_serialized, ValueTag::MapObject);
256
            // 2. Set deep to true.
257
0
            deep = true;
258
0
        }
259
260
        // 16. Otherwise, if value has [[SetData]] internal slot, then:
261
0
        else if (value.is_object() && is<JS::Set>(value.as_object())) {
262
            // 1. Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }.
263
0
            serialize_enum(m_serialized, ValueTag::SetObject);
264
            // 2. Set deep to true.
265
0
            deep = true;
266
0
        }
267
268
        // 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then:
269
0
        else if (value.is_object() && is<JS::Error>(value.as_object()) && !is<Bindings::PlatformObject>(value.as_object())) {
270
            // 1. Let name be ? Get(value, "name").
271
0
            auto name_property = TRY(value.as_object().get(m_vm.names.name));
272
273
            // FIXME: Spec bug - https://github.com/whatwg/html/issues/9923
274
            // MISSING STEP: Set name to ? ToString(name).
275
0
            auto name = TRY(name_property.to_string(m_vm));
276
277
            // 2. If name is not one of "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", or "URIError", then set name to "Error".
278
0
            auto type = error_name_to_type(name);
279
280
            // 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
281
0
            auto value_message_descriptor = TRY(value.as_object().internal_get_own_property(m_vm.names.message));
282
283
            // 4. Let message be undefined if IsDataDescriptor(valueMessageDesc) is false, and ? ToString(valueMessageDesc.[[Value]]) otherwise.
284
0
            Optional<String> message;
285
0
            if (value_message_descriptor.has_value() && value_message_descriptor->is_data_descriptor())
286
0
                message = TRY(value_message_descriptor->value->to_string(m_vm));
287
288
            // 5. Set serialized to { [[Type]]: "Error", [[Name]]: name, [[Message]]: message }.
289
            // 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.
290
0
            serialize_enum(m_serialized, ValueTag::ErrorObject);
291
0
            serialize_enum(m_serialized, type);
292
0
            serialize_primitive_type(m_serialized, message.has_value());
293
0
            if (message.has_value())
294
0
                TRY(serialize_string(m_vm, m_serialized, *message));
295
0
        }
296
297
        // 18. Otherwise, if value is an Array exotic object, then:
298
0
        else if (value.is_object() && is<JS::Array>(value.as_object())) {
299
            // 1. Let valueLenDescriptor be ? OrdinaryGetOwnProperty(value, "length").
300
            // 2. Let valueLen be valueLenDescriptor.[[Value]].
301
            // NON-STANDARD: Array objects in LibJS do not have a real length property, so it must be accessed the usual way
302
0
            u64 length = MUST(JS::length_of_array_like(m_vm, value.as_object()));
303
304
            // 3. Set serialized to { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: a new empty List }.
305
0
            serialize_enum(m_serialized, ValueTag::ArrayObject);
306
0
            serialize_primitive_type(m_serialized, length);
307
308
            // 4. Set deep to true.
309
0
            deep = true;
310
0
        }
311
312
        // 19. Otherwise, if value is a platform object that is a serializable object:
313
0
        else if (value.is_object() && is<Bindings::Serializable>(value.as_object())) {
314
0
            auto& serializable = dynamic_cast<Bindings::Serializable&>(value.as_object());
315
316
            // FIXME: 1. If value has a [[Detached]] internal slot whose value is true, then throw a "DataCloneError" DOMException.
317
318
            // 2. Let typeString be the identifier of the primary interface of value.
319
            // 3. Set serialized to { [[Type]]: typeString }.
320
0
            serialize_enum(m_serialized, ValueTag::SerializableObject);
321
0
            TRY(serialize_string(m_vm, m_serialized, serializable.interface_name()));
322
323
            // 4. Set deep to true
324
0
            deep = true;
325
0
        }
326
327
        // 20. Otherwise, if value is a platform object, then throw a "DataCloneError" DOMException.
328
0
        else if (value.is_object() && is<Bindings::PlatformObject>(value.as_object())) {
329
0
            return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize platform objects"_string));
330
0
        }
331
332
        // 21. Otherwise, if IsCallable(value) is true, then throw a "DataCloneError" DOMException.
333
0
        else if (value.is_function()) {
334
0
            return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize functions"_string));
335
0
        }
336
337
        // FIXME: 22. Otherwise, if value has any internal slot other than [[Prototype]] or [[Extensible]], then throw a "DataCloneError" DOMException.
338
339
        // 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.
340
341
        // 24. Otherwise:
342
0
        else {
343
            // 1. Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }.
344
0
            serialize_enum(m_serialized, ValueTag::Object);
345
346
            // 2. Set deep to true.
347
0
            deep = true;
348
0
        }
349
350
        // 25. Set memory[value] to serialized.
351
0
        m_memory.set(make_handle(value), m_next_id++);
352
353
        // 26. If deep is true, then:
354
0
        if (deep) {
355
            // 1. If value has a [[MapData]] internal slot, then:
356
0
            if (value.is_object() && is<JS::Map>(value.as_object())) {
357
0
                auto const& map = static_cast<JS::Map const&>(value.as_object());
358
                // 1. Let copiedList be a new empty List.
359
0
                Vector<JS::Value> copied_list;
360
0
                copied_list.ensure_capacity(map.map_size() * 2);
361
                // 2. For each Record { [[Key]], [[Value]] } entry of value.[[MapData]]:
362
0
                for (auto const& entry : static_cast<JS::Map const&>(value.as_object())) {
363
                    // 1. Let copiedEntry be a new Record { [[Key]]: entry.[[Key]], [[Value]]: entry.[[Value]] }.
364
                    // 2. If copiedEntry.[[Key]] is not the special value empty, append copiedEntry to copiedList.
365
0
                    copied_list.append(entry.key);
366
0
                    copied_list.append(entry.value);
367
0
                }
368
0
                u64 size = map.map_size();
369
0
                m_serialized.append(bit_cast<u32*>(&size), 2);
370
                // 3. For each Record { [[Key]], [[Value]] } entry of copiedList:
371
0
                for (auto copied_value : copied_list) {
372
                    // 1. Let serializedKey be ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory).
373
                    // 2. Let serializedValue be ? StructuredSerializeInternal(entry.[[Value]], forStorage, memory).
374
0
                    auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory));
375
376
                    // 3. Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]].
377
0
                    m_serialized.extend(serialized_value);
378
0
                }
379
0
            }
380
381
            // 2. Otherwise, if value has a [[SetData]] internal slot, then:
382
0
            else if (value.is_object() && is<JS::Set>(value.as_object())) {
383
0
                auto const& set = static_cast<JS::Set const&>(value.as_object());
384
                // 1. Let copiedList be a new empty List.
385
0
                Vector<JS::Value> copied_list;
386
0
                copied_list.ensure_capacity(set.set_size());
387
                // 2. For each entry of value.[[SetData]]:
388
0
                for (auto const& entry : static_cast<JS::Set const&>(value.as_object())) {
389
                    // 1. If entry is not the special value empty, append entry to copiedList.
390
0
                    copied_list.append(entry.key);
391
0
                }
392
0
                serialize_primitive_type(m_serialized, set.set_size());
393
                // 3. For each entry of copiedList:
394
0
                for (auto copied_value : copied_list) {
395
                    // 1. Let serializedEntry be ? StructuredSerializeInternal(entry, forStorage, memory).
396
0
                    auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory));
397
398
                    // 2. Append serializedEntry to serialized.[[SetData]].
399
0
                    m_serialized.extend(serialized_value);
400
0
                }
401
0
            }
402
403
            // 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.
404
0
            else if (value.is_object() && is<Bindings::Serializable>(value.as_object())) {
405
0
                auto& serializable = dynamic_cast<Bindings::Serializable&>(value.as_object());
406
0
                TRY(serializable.serialization_steps(m_serialized, m_for_storage, m_memory));
407
0
            }
408
409
            // 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key):
410
0
            else {
411
0
                u64 property_count = 0;
412
0
                auto count_offset = m_serialized.size();
413
0
                serialize_primitive_type(m_serialized, property_count);
414
0
                for (auto key : MUST(value.as_object().enumerable_own_property_names(JS::Object::PropertyKind::Key))) {
415
0
                    auto property_key = MUST(JS::PropertyKey::from_value(m_vm, key));
416
417
                    // 1. If ! HasOwnProperty(value, key) is true, then:
418
0
                    if (MUST(value.as_object().has_own_property(property_key))) {
419
                        // 1. Let inputValue be ? value.[[Get]](key, value).
420
0
                        auto input_value = TRY(value.as_object().internal_get(property_key, value));
421
422
                        // 2. Let outputValue be ? StructuredSerializeInternal(inputValue, forStorage, memory).
423
0
                        auto output_value = TRY(structured_serialize_internal(m_vm, input_value, m_for_storage, m_memory));
424
425
                        // 3. Append { [[Key]]: key, [[Value]]: outputValue } to serialized.[[Properties]].
426
0
                        TRY(serialize_string(m_vm, m_serialized, key.as_string()));
427
0
                        m_serialized.extend(output_value);
428
429
0
                        property_count++;
430
0
                    }
431
0
                }
432
0
                memcpy(m_serialized.data() + count_offset, &property_count, sizeof(property_count));
433
0
            }
434
0
        }
435
436
        // 27. Return serialized.
437
0
        return m_serialized;
438
0
    }
439
440
private:
441
    JS::VM& m_vm;
442
    SerializationMemory& m_memory; // JS value -> index
443
    u32 m_next_id { 0 };
444
    SerializationRecord m_serialized;
445
    bool m_for_storage { false };
446
};
447
448
void serialize_boolean_primitive(SerializationRecord& serialized, JS::Value& value)
449
0
{
450
0
    VERIFY(value.is_boolean());
451
0
    serialize_primitive_type(serialized, value.as_bool());
452
0
}
453
454
void serialize_number_primitive(SerializationRecord& serialized, JS::Value& value)
455
0
{
456
0
    VERIFY(value.is_number());
457
0
    serialize_primitive_type(serialized, value.as_double());
458
0
}
459
460
WebIDL::ExceptionOr<void> serialize_big_int_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
461
0
{
462
0
    VERIFY(value.is_bigint());
463
0
    auto& val = value.as_bigint();
464
0
    TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, val.to_string())));
465
0
    return {};
466
0
}
467
468
WebIDL::ExceptionOr<void> serialize_string_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
469
0
{
470
0
    VERIFY(value.is_string());
471
0
    TRY(serialize_string(vm, serialized, value.as_string()));
472
0
    return {};
473
0
}
474
475
void serialize_boolean_object(SerializationRecord& serialized, JS::Value& value)
476
0
{
477
0
    VERIFY(value.is_object() && is<JS::BooleanObject>(value.as_object()));
478
0
    auto& boolean_object = static_cast<JS::BooleanObject&>(value.as_object());
479
0
    serialize_primitive_type(serialized, boolean_object.boolean());
480
0
}
481
482
void serialize_number_object(SerializationRecord& serialized, JS::Value& value)
483
0
{
484
0
    VERIFY(value.is_object() && is<JS::NumberObject>(value.as_object()));
485
0
    auto& number_object = static_cast<JS::NumberObject&>(value.as_object());
486
0
    serialize_primitive_type(serialized, number_object.number());
487
0
}
488
489
WebIDL::ExceptionOr<void> serialize_big_int_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
490
0
{
491
0
    VERIFY(value.is_object() && is<JS::BigIntObject>(value.as_object()));
492
0
    auto& bigint_object = static_cast<JS::BigIntObject&>(value.as_object());
493
0
    TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, bigint_object.bigint().to_string())));
494
0
    return {};
495
0
}
496
497
WebIDL::ExceptionOr<void> serialize_string_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
498
0
{
499
0
    VERIFY(value.is_object() && is<JS::StringObject>(value.as_object()));
500
0
    auto& string_object = static_cast<JS::StringObject&>(value.as_object());
501
0
    TRY(serialize_string(vm, serialized, string_object.primitive_string()));
502
0
    return {};
503
0
}
504
505
void serialize_date_object(SerializationRecord& serialized, JS::Value& value)
506
0
{
507
0
    VERIFY(value.is_object() && is<JS::Date>(value.as_object()));
508
0
    auto& date_object = static_cast<JS::Date&>(value.as_object());
509
0
    serialize_primitive_type(serialized, date_object.date_value());
510
0
}
511
512
WebIDL::ExceptionOr<void> serialize_reg_exp_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
513
0
{
514
0
    VERIFY(value.is_object() && is<JS::RegExpObject>(value.as_object()));
515
0
    auto& regexp_object = static_cast<JS::RegExpObject&>(value.as_object());
516
    // Note: A Regex<ECMA262> object is perfectly happy to be reconstructed with just the source+flags
517
    //       In the future, we could optimize the work being done on the deserialize step by serializing
518
    //       more of the internal state (the [[RegExpMatcher]] internal slot)
519
0
    TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, String::from_byte_string(regexp_object.pattern()))));
520
0
    TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, String::from_byte_string(regexp_object.flags()))));
521
0
    return {};
522
0
}
523
524
WebIDL::ExceptionOr<void> serialize_bytes(JS::VM& vm, Vector<u32>& vector, ReadonlyBytes bytes)
525
0
{
526
    // Append size of the buffer to the serialized structure.
527
0
    u64 const size = bytes.size();
528
0
    serialize_primitive_type(vector, size);
529
    // Append the bytes of the buffer to the serialized structure.
530
0
    u64 byte_position = 0;
531
0
    while (byte_position < size) {
532
0
        u32 combined_value = 0;
533
0
        for (u8 i = 0; i < 4; ++i) {
534
0
            u8 const byte = bytes[byte_position];
535
0
            combined_value |= byte << (i * 8);
536
0
            byte_position++;
537
0
            if (byte_position == size)
538
0
                break;
539
0
        }
540
0
        TRY_OR_THROW_OOM(vm, vector.try_append(combined_value));
541
0
    }
542
0
    return {};
543
0
}
544
545
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, DeprecatedFlyString const& string)
546
0
{
547
0
    return serialize_bytes(vm, vector, string.view().bytes());
548
0
}
549
550
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, String const& string)
551
0
{
552
0
    return serialize_bytes(vm, vector, { string.code_points().bytes(), string.code_points().byte_length() });
553
0
}
554
555
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
556
0
{
557
0
    auto string = primitive_string.utf8_string();
558
0
    TRY(serialize_string(vm, vector, string));
559
0
    return {};
560
0
}
561
562
WebIDL::ExceptionOr<void> serialize_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::ArrayBuffer const& array_buffer, bool for_storage)
563
0
{
564
    // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
565
566
    // FIXME: 1.  If IsSharedArrayBuffer(value) is true, then:
567
0
    if (false) {
568
        // 1. If the current settings object's cross-origin isolated capability is false, then throw a "DataCloneError" DOMException.
569
        // NOTE: This check is only needed when serializing (and not when deserializing) as the cross-origin isolated capability cannot change
570
        //       over time and a SharedArrayBuffer cannot leave an agent cluster.
571
0
        if (current_settings_object().cross_origin_isolated_capability() == CanUseCrossOriginIsolatedAPIs::No)
572
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer when cross-origin isolated"_string);
573
574
        // 2. If forStorage is true, then throw a "DataCloneError" DOMException.
575
0
        if (for_storage)
576
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer for storage"_string);
577
578
        // FIXME: 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer",
579
        //           [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]],
580
        //           [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
581
        // FIXME: 4. Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]],
582
        //           [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
583
0
    }
584
    // 2. Otherwise:
585
0
    else {
586
        // 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
587
0
        if (array_buffer.is_detached())
588
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize detached ArrayBuffer"_string);
589
590
        // 2. Let size be value.[[ArrayBufferByteLength]].
591
0
        auto size = array_buffer.byte_length();
592
593
        // 3. Let dataCopy be ? CreateByteDataBlock(size).
594
        //    NOTE: This can throw a RangeError exception upon allocation failure.
595
0
        auto data_copy = TRY(JS::create_byte_data_block(vm, size));
596
597
        // 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
598
0
        JS::copy_data_block_bytes(data_copy.buffer(), 0, array_buffer.buffer(), 0, size);
599
600
        // FIXME: 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
601
        //    [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
602
0
        if (false) {
603
0
        }
604
        // 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
605
0
        else {
606
0
            serialize_enum(vector, ValueTag::ArrayBuffer);
607
0
            TRY(serialize_bytes(vm, vector, data_copy.buffer().bytes()));
608
0
        }
609
0
    }
610
0
    return {};
611
0
}
612
613
template<OneOf<JS::TypedArrayBase, JS::DataView> ViewType>
614
WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, ViewType const& view, bool for_storage, SerializationMemory& memory)
615
0
{
616
    // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
617
618
0
    auto view_record = [&]() {
619
0
        if constexpr (IsSame<ViewType, JS::DataView>) {
620
0
            return JS::make_data_view_with_buffer_witness_record(view, JS::ArrayBuffer::Order::SeqCst);
621
0
        } else {
622
0
            return JS::make_typed_array_with_buffer_witness_record(view, JS::ArrayBuffer::Order::SeqCst);
623
0
        }
624
0
    }();
Unexecuted instantiation: _ZZN3Web4HTML29serialize_viewed_array_bufferITkN2AK8Concepts5OneOfIN2JS14TypedArrayBaseENS5_8DataViewEEES6_EENS_6WebIDL11ExceptionOrIvEERNS5_2VMERNS2_6VectorIjLm0EEERKT_bRNS2_7HashMapINS5_6HandleINS5_5ValueEEEjNS2_6TraitsISM_EENSN_IjEELb0EEEENKUlvE_clEv
Unexecuted instantiation: _ZZN3Web4HTML29serialize_viewed_array_bufferITkN2AK8Concepts5OneOfIN2JS14TypedArrayBaseENS5_8DataViewEEES7_EENS_6WebIDL11ExceptionOrIvEERNS5_2VMERNS2_6VectorIjLm0EEERKT_bRNS2_7HashMapINS5_6HandleINS5_5ValueEEEjNS2_6TraitsISM_EENSN_IjEELb0EEEENKUlvE_clEv
625
626
    // 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
627
0
    if constexpr (IsSame<ViewType, JS::DataView>) {
628
0
        if (JS::is_view_out_of_bounds(view_record))
629
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "DataView"sv)));
630
0
    } else {
631
0
        if (JS::is_typed_array_out_of_bounds(view_record))
632
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "TypedArray"sv)));
633
0
    }
634
635
    // 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
636
0
    auto* buffer = view.viewed_array_buffer();
637
638
    // 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
639
0
    auto buffer_serialized = TRY(structured_serialize_internal(vm, JS::Value(buffer), for_storage, memory));
640
641
    // 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
642
    // NOTE: We currently only implement this for ArrayBuffer
643
0
    VERIFY(buffer_serialized[0] == ValueTag::ArrayBuffer);
644
645
    // 5. If value has a [[DataView]] internal slot, then set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView",
646
    //    [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
647
0
    if constexpr (IsSame<ViewType, JS::DataView>) {
648
0
        serialize_enum(vector, ValueTag::ArrayBufferView);
649
0
        vector.extend(move(buffer_serialized));               // [[ArrayBufferSerialized]]
650
0
        TRY(serialize_string(vm, vector, "DataView"_string)); // [[Constructor]]
651
0
        serialize_primitive_type(vector, JS::get_view_byte_length(view_record));
652
0
        serialize_primitive_type(vector, view.byte_offset());
653
    }
654
655
    // 6. Otherwise:
656
0
    else {
657
        // 1. Assert: value has a [[TypedArrayName]] internal slot.
658
        //    NOTE: Handled by constexpr check and template constraints
659
        // 2. Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]],
660
        //    [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
661
        //    [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
662
0
        serialize_enum(vector, ValueTag::ArrayBufferView);
663
0
        vector.extend(move(buffer_serialized));                 // [[ArrayBufferSerialized]]
664
0
        TRY(serialize_string(vm, vector, view.element_name())); // [[Constructor]]
665
0
        serialize_primitive_type(vector, JS::typed_array_byte_length(view_record));
666
0
        serialize_primitive_type(vector, view.byte_offset());
667
0
        serialize_primitive_type(vector, JS::typed_array_length(view_record));
668
0
    }
669
0
    return {};
670
0
}
Unexecuted instantiation: _ZN3Web4HTML29serialize_viewed_array_bufferITkN2AK8Concepts5OneOfIN2JS14TypedArrayBaseENS5_8DataViewEEES6_EENS_6WebIDL11ExceptionOrIvEERNS5_2VMERNS2_6VectorIjLm0EEERKT_bRNS2_7HashMapINS5_6HandleINS5_5ValueEEEjNS2_6TraitsISM_EENSN_IjEELb0EEE
Unexecuted instantiation: _ZN3Web4HTML29serialize_viewed_array_bufferITkN2AK8Concepts5OneOfIN2JS14TypedArrayBaseENS5_8DataViewEEES7_EENS_6WebIDL11ExceptionOrIvEERNS5_2VMERNS2_6VectorIjLm0EEERKT_bRNS2_7HashMapINS5_6HandleINS5_5ValueEEEjNS2_6TraitsISM_EENSN_IjEELb0EEE
671
template WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::TypedArrayBase const& view, bool for_storage, SerializationMemory& memory);
672
template WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::DataView const& view, bool for_storage, SerializationMemory& memory);
673
674
class Deserializer {
675
public:
676
    Deserializer(JS::VM& vm, JS::Realm& target_realm, ReadonlySpan<u32> serialized, DeserializationMemory& memory, Optional<size_t> position = {})
677
0
        : m_vm(vm)
678
0
        , m_serialized(serialized)
679
0
        , m_memory(memory)
680
0
        , m_position(position.value_or(0))
681
0
    {
682
0
        VERIFY(vm.current_realm() == &target_realm);
683
0
    }
684
685
0
    size_t position() const { return m_position; }
686
687
    // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
688
    WebIDL::ExceptionOr<JS::Value> deserialize()
689
0
    {
690
0
        auto tag = deserialize_primitive_type<ValueTag>(m_serialized, m_position);
691
692
        // 2. If memory[serialized] exists, then return memory[serialized].
693
0
        if (tag == ValueTag::ObjectReference) {
694
0
            auto index = m_serialized[m_position++];
695
0
            if (index == NumericLimits<u32>::max()) {
696
0
                return JS::Object::create(*m_vm.current_realm(), nullptr);
697
0
            }
698
0
            return m_memory[index];
699
0
        }
700
701
        // 3. Let deep be false.
702
0
        auto deep = false;
703
704
        // 4. Let value be an uninitialized value.
705
0
        JS::Value value;
706
707
0
        auto is_primitive = false;
708
0
        switch (tag) {
709
        // 5. If serialized.[[Type]] is "primitive", then set value to serialized.[[Value]].
710
0
        case ValueTag::UndefinedPrimitive: {
711
0
            value = JS::js_undefined();
712
0
            is_primitive = true;
713
0
            break;
714
0
        }
715
0
        case ValueTag::NullPrimitive: {
716
0
            value = JS::js_null();
717
0
            is_primitive = true;
718
0
            break;
719
0
        }
720
0
        case ValueTag::BooleanPrimitive: {
721
0
            value = JS::Value { deserialize_boolean_primitive(m_serialized, m_position) };
722
0
            is_primitive = true;
723
0
            break;
724
0
        }
725
0
        case ValueTag::NumberPrimitive: {
726
0
            value = JS::Value { deserialize_number_primitive(m_serialized, m_position) };
727
0
            is_primitive = true;
728
0
            break;
729
0
        }
730
0
        case ValueTag::BigIntPrimitive: {
731
0
            auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_serialized, m_position));
732
0
            value = JS::Value { big_int };
733
0
            is_primitive = true;
734
0
            break;
735
0
        }
736
0
        case ValueTag::StringPrimitive: {
737
0
            auto string = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position));
738
0
            value = JS::Value { string };
739
0
            is_primitive = true;
740
0
            break;
741
0
        }
742
        // 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]].
743
0
        case BooleanObject: {
744
0
            value = deserialize_boolean_object(*m_vm.current_realm(), m_serialized, m_position);
745
0
            break;
746
0
        }
747
        // 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]].
748
0
        case ValueTag::NumberObject: {
749
0
            value = deserialize_number_object(*m_vm.current_realm(), m_serialized, m_position);
750
0
            break;
751
0
        }
752
        // 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]].
753
0
        case ValueTag::BigIntObject: {
754
0
            value = TRY(deserialize_big_int_object(*m_vm.current_realm(), m_serialized, m_position));
755
0
            break;
756
0
        }
757
        // 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]].
758
0
        case ValueTag::StringObject: {
759
0
            value = TRY(deserialize_string_object(*m_vm.current_realm(), m_serialized, m_position));
760
0
            break;
761
0
        }
762
        // 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]].
763
0
        case ValueTag::DateObject: {
764
0
            value = deserialize_date_object(*m_vm.current_realm(), m_serialized, m_position);
765
0
            break;
766
0
        }
767
        // 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]],
768
        //     whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]].
769
0
        case ValueTag::RegExpObject: {
770
0
            value = TRY(deserialize_reg_exp_object(*m_vm.current_realm(), m_serialized, m_position));
771
0
            break;
772
0
        }
773
        // FIXME: 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:
774
        // FIXME: 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then:
775
        // 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]].
776
0
        case ValueTag::ArrayBuffer: {
777
0
            auto* realm = m_vm.current_realm();
778
            // If this throws an exception, catch it, and then throw a "DataCloneError" DOMException.
779
0
            auto bytes_or_error = deserialize_bytes(m_vm, m_serialized, m_position);
780
0
            if (bytes_or_error.is_error())
781
0
                return WebIDL::DataCloneError::create(*m_vm.current_realm(), "out of memory"_string);
782
0
            value = JS::ArrayBuffer::create(*realm, bytes_or_error.release_value());
783
0
            break;
784
0
        }
785
        // FIXME: 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]].
786
        // 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then:
787
0
        case ValueTag::ArrayBufferView: {
788
0
            auto* realm = m_vm.current_realm();
789
0
            auto array_buffer_value = TRY(deserialize());
790
0
            auto& array_buffer = verify_cast<JS::ArrayBuffer>(array_buffer_value.as_object());
791
0
            auto constructor_name = TRY(deserialize_string(m_vm, m_serialized, m_position));
792
0
            u32 byte_length = deserialize_primitive_type<u32>(m_serialized, m_position);
793
0
            u32 byte_offset = deserialize_primitive_type<u32>(m_serialized, m_position);
794
795
0
            if (constructor_name == "DataView"sv) {
796
0
                value = JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset);
797
0
            } else {
798
0
                u32 array_length = deserialize_primitive_type<u32>(m_serialized, m_position);
799
0
                JS::GCPtr<JS::TypedArrayBase> typed_array_ptr;
800
0
#define CREATE_TYPED_ARRAY(ClassName)       \
801
0
    if (constructor_name == #ClassName##sv) \
802
0
        typed_array_ptr = JS::ClassName::create(*realm, array_length, array_buffer);
803
0
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
804
0
    CREATE_TYPED_ARRAY(ClassName)
805
0
                JS_ENUMERATE_TYPED_ARRAYS
806
0
#undef __JS_ENUMERATE
807
0
#undef CREATE_TYPED_ARRAY
808
0
                VERIFY(typed_array_ptr != nullptr); // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
809
0
                typed_array_ptr->set_byte_length(byte_length);
810
0
                typed_array_ptr->set_byte_offset(byte_offset);
811
0
                value = typed_array_ptr;
812
0
            }
813
0
            break;
814
0
        }
815
        // 17. Otherwise, if serialized.[[Type]] is "Map", then:
816
0
        case ValueTag::MapObject: {
817
0
            auto& realm = *m_vm.current_realm();
818
            // 1. Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.
819
0
            value = JS::Map::create(realm);
820
            // 2. Set deep to true.
821
0
            deep = true;
822
0
            break;
823
0
        }
824
        // 18. Otherwise, if serialized.[[Type]] is "Set", then:
825
0
        case ValueTag::SetObject: {
826
0
            auto& realm = *m_vm.current_realm();
827
            // 1. Set value to a new Set object in targetRealm whose [[SetData]] internal slot value is a new empty List.
828
0
            value = JS::Set::create(realm);
829
            // 2. Set deep to true.
830
0
            deep = true;
831
0
            break;
832
0
        }
833
        // 19. Otherwise, if serialized.[[Type]] is "Array", then:
834
0
        case ValueTag::ArrayObject: {
835
0
            auto& realm = *m_vm.current_realm();
836
            // 1. Let outputProto be targetRealm.[[Intrinsics]].[[%Array.prototype%]].
837
            // 2. Set value to ! ArrayCreate(serialized.[[Length]], outputProto).
838
0
            auto length = deserialize_primitive_type<u64>(m_serialized, m_position);
839
0
            value = MUST(JS::Array::create(realm, length));
840
            // 3. Set deep to true.
841
0
            deep = true;
842
0
            break;
843
0
        }
844
        // 20. Otherwise, if serialized.[[Type]] is "Object", then:
845
0
        case ValueTag::Object: {
846
0
            auto& realm = *m_vm.current_realm();
847
            // 1. Set value to a new Object in targetRealm.
848
0
            value = JS::Object::create(realm, realm.intrinsics().object_prototype());
849
            // 2. Set deep to true.
850
0
            deep = true;
851
0
            break;
852
0
        }
853
        // 21. Otherwise, if serialized.[[Type]] is "Error", then:
854
0
        case ValueTag::ErrorObject: {
855
0
            auto& realm = *m_vm.current_realm();
856
0
            auto type = deserialize_primitive_type<ErrorType>(m_serialized, m_position);
857
0
            auto has_message = deserialize_primitive_type<bool>(m_serialized, m_position);
858
0
            if (has_message) {
859
0
                auto message = TRY(deserialize_string(m_vm, m_serialized, m_position));
860
0
                switch (type) {
861
0
                case ErrorType::Error:
862
0
                    value = JS::Error::create(realm, message);
863
0
                    break;
864
0
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
865
0
    case ErrorType::ClassName:                                                           \
866
0
        value = JS::ClassName::create(realm, message);                                   \
867
0
        break;
868
0
                    JS_ENUMERATE_NATIVE_ERRORS
869
0
#undef __JS_ENUMERATE
870
0
                }
871
0
            } else {
872
0
                switch (type) {
873
0
                case ErrorType::Error:
874
0
                    value = JS::Error::create(realm);
875
0
                    break;
876
0
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
877
0
    case ErrorType::ClassName:                                                           \
878
0
        value = JS::ClassName::create(realm);                                            \
879
0
        break;
880
0
                    JS_ENUMERATE_NATIVE_ERRORS
881
0
#undef __JS_ENUMERATE
882
0
                }
883
0
            }
884
0
            break;
885
0
        }
886
        // 22. Otherwise:
887
0
        default:
888
0
            VERIFY(tag == ValueTag::SerializableObject);
889
890
0
            auto& realm = *m_vm.current_realm();
891
            // 1. Let interfaceName be serialized.[[Type]].
892
0
            auto interface_name = TRY(deserialize_string(m_vm, m_serialized, m_position));
893
            // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
894
0
            if (!is_interface_exposed_on_target_realm(interface_name, realm))
895
0
                return WebIDL::DataCloneError::create(realm, "Unsupported type"_string);
896
897
            // 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
898
0
            value = TRY(create_serialized_type(interface_name, realm));
899
900
            // 4. Set deep to true.
901
0
            deep = true;
902
0
        }
903
904
        // 23. Set memory[serialized] to value.
905
        // IMPLEMENTATION DEFINED: We don't add primitive values to the memory to match the serialization indices (which also doesn't add them)
906
0
        if (!is_primitive)
907
0
            m_memory.append(value);
908
909
        // 24. If deep is true, then:
910
0
        if (deep) {
911
            // 1. If serialized.[[Type]] is "Map", then:
912
0
            if (tag == ValueTag::MapObject) {
913
0
                auto& map = static_cast<JS::Map&>(value.as_object());
914
0
                auto length = deserialize_primitive_type<u64>(m_serialized, m_position);
915
                // 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[MapData]]:
916
0
                for (u64 i = 0u; i < length; ++i) {
917
                    // 1. Let deserializedKey be ? StructuredDeserialize(entry.[[Key]], targetRealm, memory).
918
0
                    auto deserialized_key = TRY(deserialize());
919
920
                    // 2. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
921
0
                    auto deserialized_value = TRY(deserialize());
922
923
                    // 3. Append { [[Key]]: deserializedKey, [[Value]]: deserializedValue } to value.[[MapData]].
924
0
                    map.map_set(deserialized_key, deserialized_value);
925
0
                }
926
0
            }
927
928
            // 2. Otherwise, if serialized.[[Type]] is "Set", then:
929
0
            else if (tag == ValueTag::SetObject) {
930
0
                auto& set = static_cast<JS::Set&>(value.as_object());
931
0
                auto length = deserialize_primitive_type<u64>(m_serialized, m_position);
932
                // 1. For each entry of serialized.[[SetData]]:
933
0
                for (u64 i = 0u; i < length; ++i) {
934
                    // 1. Let deserializedEntry be ? StructuredDeserialize(entry, targetRealm, memory).
935
0
                    auto deserialized_entry = TRY(deserialize());
936
937
                    // 2. Append deserializedEntry to value.[[SetData]].
938
0
                    set.set_add(deserialized_entry);
939
0
                }
940
0
            }
941
942
            // 3. Otherwise, if serialized.[[Type]] is "Array" or "Object", then:
943
0
            else if (tag == ValueTag::ArrayObject || tag == ValueTag::Object) {
944
0
                auto& object = value.as_object();
945
0
                auto length = deserialize_primitive_type<u64>(m_serialized, m_position);
946
                // 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[Properties]]:
947
0
                for (u64 i = 0u; i < length; ++i) {
948
0
                    auto key = TRY(deserialize_string(m_vm, m_serialized, m_position));
949
950
                    // 1. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
951
0
                    auto deserialized_value = TRY(deserialize());
952
953
                    // 2. Let result be ! CreateDataProperty(value, entry.[[Key]], deserializedValue).
954
0
                    auto result = MUST(object.create_data_property(key.to_byte_string(), deserialized_value));
955
956
                    // 3. Assert: result is true.
957
0
                    VERIFY(result);
958
0
                }
959
0
            }
960
961
            // 4. Otherwise:
962
0
            else {
963
                // 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm.
964
0
                auto& serializable = dynamic_cast<Bindings::Serializable&>(value.as_object());
965
0
                TRY(serializable.deserialization_steps(m_serialized, m_position, m_memory));
966
0
            }
967
0
        }
968
969
        // 25. Return value.
970
0
        return value;
971
0
    }
972
973
private:
974
    JS::VM& m_vm;
975
    ReadonlySpan<u32> m_serialized;
976
    JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
977
    size_t m_position { 0 };
978
979
    static WebIDL::ExceptionOr<JS::NonnullGCPtr<Bindings::PlatformObject>> create_serialized_type(StringView interface_name, JS::Realm& realm)
980
0
    {
981
0
        if (interface_name == "Blob"sv)
982
0
            return FileAPI::Blob::create(realm);
983
0
        if (interface_name == "File"sv)
984
0
            return FileAPI::File::create(realm);
985
0
        if (interface_name == "FileList"sv)
986
0
            return FileAPI::FileList::create(realm);
987
0
        if (interface_name == "DOMMatrixReadOnly"sv)
988
0
            return Geometry::DOMMatrixReadOnly::create(realm);
989
0
        if (interface_name == "DOMMatrix"sv)
990
0
            return Geometry::DOMMatrix::create(realm);
991
0
        if (interface_name == "DOMPointReadOnly"sv)
992
0
            return Geometry::DOMPointReadOnly::create(realm);
993
0
        if (interface_name == "DOMPoint"sv)
994
0
            return Geometry::DOMPoint::create(realm);
995
0
        if (interface_name == "DOMRectReadOnly"sv)
996
0
            return Geometry::DOMRectReadOnly::create(realm);
997
0
        if (interface_name == "DOMRect"sv)
998
0
            return Geometry::DOMRect::create(realm);
999
0
        if (interface_name == "CryptoKey"sv)
1000
0
            return Crypto::CryptoKey::create(realm);
1001
0
        if (interface_name == "DOMQuad"sv)
1002
0
            return Geometry::DOMQuad::create(realm);
1003
1004
0
        VERIFY_NOT_REACHED();
1005
0
    }
1006
1007
    // FIXME: Consolidate this function with the similar is_interface_exposed_on_target_realm() used when transferring objects.
1008
    //        Also, the name parameter would be better off being the interface name (as a string) so that we don't need a switch statement.
1009
    static bool is_interface_exposed_on_target_realm(StringView interface_name, JS::Realm& realm)
1010
0
    {
1011
0
        auto const& intrinsics = Bindings::host_defined_intrinsics(realm);
1012
0
        return intrinsics.is_exposed(interface_name);
1013
0
    }
1014
};
1015
1016
bool deserialize_boolean_primitive(ReadonlySpan<u32> const& serialized, size_t& position)
1017
0
{
1018
0
    return deserialize_primitive_type<bool>(serialized, position);
1019
0
}
1020
1021
double deserialize_number_primitive(ReadonlySpan<u32> const& serialized, size_t& position)
1022
0
{
1023
0
    return deserialize_primitive_type<double>(serialized, position);
1024
0
}
1025
1026
JS::NonnullGCPtr<JS::BooleanObject> deserialize_boolean_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
1027
0
{
1028
0
    auto boolean_primitive = deserialize_boolean_primitive(serialized, position);
1029
0
    return JS::BooleanObject::create(realm, boolean_primitive);
1030
0
}
1031
1032
JS::NonnullGCPtr<JS::NumberObject> deserialize_number_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
1033
0
{
1034
0
    auto number_primitive = deserialize_number_primitive(serialized, position);
1035
0
    return JS::NumberObject::create(realm, number_primitive);
1036
0
}
1037
1038
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigIntObject>> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
1039
0
{
1040
0
    auto big_int_primitive = TRY(deserialize_big_int_primitive(realm.vm(), serialized, position));
1041
0
    return JS::BigIntObject::create(realm, big_int_primitive);
1042
0
}
1043
1044
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::StringObject>> deserialize_string_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
1045
0
{
1046
0
    auto string_primitive = TRY(deserialize_string_primitive(realm.vm(), serialized, position));
1047
0
    return JS::StringObject::create(realm, string_primitive, realm.intrinsics().string_prototype());
1048
0
}
1049
1050
JS::NonnullGCPtr<JS::Date> deserialize_date_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
1051
0
{
1052
0
    auto double_value = deserialize_primitive_type<double>(serialized, position);
1053
0
    return JS::Date::create(realm, double_value);
1054
0
}
1055
1056
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::RegExpObject>> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
1057
0
{
1058
0
    auto pattern = TRY(deserialize_string_primitive(realm.vm(), serialized, position));
1059
0
    auto flags = TRY(deserialize_string_primitive(realm.vm(), serialized, position));
1060
0
    return TRY(JS::regexp_create(realm.vm(), move(pattern), move(flags)));
1061
0
}
1062
1063
WebIDL::ExceptionOr<ByteBuffer> deserialize_bytes(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
1064
0
{
1065
0
    u64 const size = deserialize_primitive_type<u64>(vector, position);
1066
1067
0
    auto bytes = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(size));
1068
0
    u64 byte_position = 0;
1069
0
    while (position < vector.size() && byte_position < size) {
1070
0
        for (u8 i = 0; i < 4; ++i) {
1071
0
            bytes[byte_position++] = (vector[position] >> (i * 8) & 0xFF);
1072
0
            if (byte_position == size)
1073
0
                break;
1074
0
        }
1075
0
        position++;
1076
0
    }
1077
0
    return bytes;
1078
0
}
1079
1080
WebIDL::ExceptionOr<String> deserialize_string(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
1081
0
{
1082
0
    auto bytes = TRY(deserialize_bytes(vm, vector, position));
1083
0
    return TRY_OR_THROW_OOM(vm, String::from_utf8(StringView { bytes }));
1084
0
}
1085
1086
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
1087
0
{
1088
0
    auto bytes = TRY(deserialize_bytes(vm, vector, position));
1089
1090
0
    return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() {
1091
0
        return JS::PrimitiveString::create(vm, StringView { bytes });
1092
0
    }));
1093
0
}
1094
1095
WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
1096
0
{
1097
0
    auto string = TRY(deserialize_string_primitive(vm, vector, position));
1098
0
    auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
1099
0
        return string->utf8_string_view();
1100
0
    }));
1101
0
    auto bigint = MUST(::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1)));
1102
0
    return JS::BigInt::create(vm, bigint);
1103
0
}
1104
1105
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializewithtransfer
1106
WebIDL::ExceptionOr<SerializedTransferRecord> structured_serialize_with_transfer(JS::VM& vm, JS::Value value, Vector<JS::Handle<JS::Object>> const& transfer_list)
1107
0
{
1108
    // 1. Let memory be an empty map.
1109
0
    SerializationMemory memory = {};
1110
1111
    // 2. For each transferable of transferList:
1112
0
    for (auto const& transferable : transfer_list) {
1113
1114
        // 1. If transferable has neither an [[ArrayBufferData]] internal slot nor a [[Detached]] internal slot, then throw a "DataCloneError" DOMException.
1115
        // FIXME: Handle transferring ArrayBufferData objects
1116
0
        if (!is<Bindings::Transferable>(*transferable)) {
1117
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer type"_string);
1118
0
        }
1119
1120
        // FIXME: 2. If transferable has an [[ArrayBufferData]] internal slot and IsSharedArrayBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
1121
1122
        // 3. If memory[transferable] exists, then throw a "DataCloneError" DOMException.
1123
0
        auto transferable_value = JS::Value(transferable);
1124
0
        if (memory.contains(transferable_value)) {
1125
0
            return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer value twice"_string);
1126
0
        }
1127
1128
        // 4. Set memory[transferable] to { [[Type]]: an uninitialized value }.
1129
0
        memory.set(JS::make_handle(transferable_value), NumericLimits<u32>::max());
1130
0
    }
1131
1132
    // 3. Let serialized be ? StructuredSerializeInternal(value, false, memory).
1133
0
    auto serialized = TRY(structured_serialize_internal(vm, value, false, memory));
1134
1135
    // 4. Let transferDataHolders be a new empty List.
1136
0
    Vector<TransferDataHolder> transfer_data_holders;
1137
0
    transfer_data_holders.ensure_capacity(transfer_list.size());
1138
1139
    // 5. For each transferable of transferList:
1140
0
    for (auto& transferable : transfer_list) {
1141
        // 1. FIXME: If transferable has an [[ArrayBufferData]] internal slot and IsDetachedBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
1142
1143
        // 2. If transferable has a [[Detached]] internal slot and transferable.[[Detached]] is true, then throw a "DataCloneError" DOMException.
1144
0
        if (is<Bindings::Transferable>(*transferable)) {
1145
0
            auto& transferable_object = dynamic_cast<Bindings::Transferable&>(*transferable);
1146
0
            if (transferable_object.is_detached()) {
1147
0
                return WebIDL::DataCloneError::create(*vm.current_realm(), "Value already transferred"_string);
1148
0
            }
1149
0
        }
1150
1151
        // 3. Let dataHolder be memory[transferable].
1152
        // IMPLEMENTATION DEFINED: We just create a data holder here, our memory holds indices into the SerializationRecord
1153
0
        TransferDataHolder data_holder;
1154
1155
        // FIXME 4. If transferable has an [[ArrayBufferData]] internal slot, then:
1156
0
        if (false) {
1157
0
        }
1158
1159
        // 5. Otherwise:
1160
0
        else {
1161
            // 1. Assert: transferable is a platform object that is a transferable object.
1162
0
            auto& transferable_object = dynamic_cast<Bindings::Transferable&>(*transferable);
1163
0
            VERIFY(is<Bindings::PlatformObject>(*transferable));
1164
1165
            // 2. Let interfaceName be the identifier of the primary interface of transferable.
1166
0
            auto interface_name = transferable_object.primary_interface();
1167
1168
            // 3. Set dataHolder.[[Type]] to interfaceName.
1169
0
            data_holder.data.append(to_underlying(interface_name));
1170
1171
            // 4. Perform the appropriate transfer steps for the interface identified by interfaceName, given transferable and dataHolder.
1172
0
            TRY(transferable_object.transfer_steps(data_holder));
1173
1174
            // 5. Set transferable.[[Detached]] to true.
1175
0
            transferable_object.set_detached(true);
1176
0
        }
1177
1178
        // 6. Append dataHolder to transferDataHolders.
1179
0
        transfer_data_holders.append(move(data_holder));
1180
0
    }
1181
1182
    // 6. Return { [[Serialized]]: serialized, [[TransferDataHolders]]: transferDataHolders }.
1183
0
    return SerializedTransferRecord { .serialized = move(serialized), .transfer_data_holders = move(transfer_data_holders) };
1184
0
}
1185
1186
static bool is_interface_exposed_on_target_realm(u8 name, JS::Realm& realm)
1187
0
{
1188
0
    auto const& intrinsics = Bindings::host_defined_intrinsics(realm);
1189
0
    switch (static_cast<TransferType>(name)) {
1190
0
    case TransferType::MessagePort:
1191
0
        return intrinsics.is_exposed("MessagePort"sv);
1192
0
        break;
1193
0
    default:
1194
0
        dbgln("Unknown interface type for transfer: {}", name);
1195
0
        break;
1196
0
    }
1197
0
    return false;
1198
0
}
1199
1200
static WebIDL::ExceptionOr<JS::NonnullGCPtr<Bindings::PlatformObject>> create_transferred_value(TransferType name, JS::Realm& target_realm, TransferDataHolder& transfer_data_holder)
1201
0
{
1202
0
    switch (name) {
1203
0
    case TransferType::MessagePort: {
1204
0
        auto message_port = HTML::MessagePort::create(target_realm);
1205
0
        TRY(message_port->transfer_receiving_steps(transfer_data_holder));
1206
0
        return message_port;
1207
0
    }
1208
0
    }
1209
0
    VERIFY_NOT_REACHED();
1210
0
}
1211
1212
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserializewithtransfer
1213
WebIDL::ExceptionOr<DeserializedTransferRecord> structured_deserialize_with_transfer(JS::VM& vm, SerializedTransferRecord& serialize_with_transfer_result)
1214
0
{
1215
0
    auto& target_realm = *vm.current_realm();
1216
1217
    // 1. Let memory be an empty map.
1218
0
    auto memory = DeserializationMemory(vm.heap());
1219
1220
    // 2. Let transferredValues be a new empty List.
1221
0
    Vector<JS::Handle<JS::Object>> transferred_values;
1222
1223
    // 3. For each transferDataHolder of serializeWithTransferResult.[[TransferDataHolders]]:
1224
0
    for (auto& transfer_data_holder : serialize_with_transfer_result.transfer_data_holders) {
1225
        // 1. Let value be an uninitialized value.
1226
0
        JS::Value value;
1227
1228
        // FIXME: 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm
1229
        //    whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], and
1230
        //    whose [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]].
1231
        // NOTE: In cases where the original memory occupied by [[ArrayBufferData]] is accessible during the deserialization,
1232
        //       this step is unlikely to throw an exception, as no new memory needs to be allocated: the memory occupied by
1233
        //       [[ArrayBufferData]] is instead just getting transferred into the new ArrayBuffer. This could be true, for example,
1234
        //       when both the source and target realms are in the same process.
1235
0
        if (false) {
1236
0
        }
1237
1238
        // FIXME: 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object
1239
        //     in targetRealm whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], whose
1240
        //     [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]], and whose
1241
        //     [[ArrayBufferMaxByteLength]] internal slot value is transferDataHolder.[[ArrayBufferMaxByteLength]].
1242
        // NOTE: For the same reason as the previous step, this step is also unlikely to throw an exception.
1243
0
        else if (false) {
1244
0
        }
1245
1246
        // 4. Otherwise:
1247
0
        else {
1248
            // 1. Let interfaceName be transferDataHolder.[[Type]].
1249
0
            u8 const interface_name = transfer_data_holder.data.take_first();
1250
1251
            // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
1252
0
            if (!is_interface_exposed_on_target_realm(interface_name, target_realm))
1253
0
                return WebIDL::DataCloneError::create(target_realm, "Unknown type transferred"_string);
1254
1255
            // 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
1256
            // 4. Perform the appropriate transfer-receiving steps for the interface identified by interfaceName given transferDataHolder and value.
1257
0
            value = TRY(create_transferred_value(static_cast<TransferType>(interface_name), target_realm, transfer_data_holder));
1258
0
        }
1259
1260
        // 5. Set memory[transferDataHolder] to value.
1261
0
        memory.append(value);
1262
1263
        // 6. Append value to transferredValues.
1264
0
        transferred_values.append(JS::make_handle(value.as_object()));
1265
0
    }
1266
1267
    // 4. Let deserialized be ? StructuredDeserialize(serializeWithTransferResult.[[Serialized]], targetRealm, memory).
1268
0
    auto deserialized = TRY(structured_deserialize(vm, serialize_with_transfer_result.serialized, target_realm, memory));
1269
1270
    // 5. Return { [[Deserialized]]: deserialized, [[TransferredValues]]: transferredValues }.
1271
0
    return DeserializedTransferRecord { .deserialized = move(deserialized), .transferred_values = move(transferred_values) };
1272
0
}
1273
1274
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
1275
WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value value)
1276
0
{
1277
    // 1. Return ? StructuredSerializeInternal(value, false).
1278
0
    SerializationMemory memory = {};
1279
0
    return structured_serialize_internal(vm, value, false, memory);
1280
0
}
1281
1282
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
1283
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value value)
1284
0
{
1285
    // 1. Return ? StructuredSerializeInternal(value, true).
1286
0
    SerializationMemory memory = {};
1287
0
    return structured_serialize_internal(vm, value, true, memory);
1288
0
}
1289
1290
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
1291
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, SerializationMemory& memory)
1292
0
{
1293
    // 1. If memory was not supplied, let memory be an empty map.
1294
    // IMPLEMENTATION DEFINED: We move this requirement up to the callers to make recursion easier
1295
1296
0
    Serializer serializer(vm, memory, for_storage);
1297
0
    return serializer.serialize(value);
1298
0
}
1299
1300
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
1301
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<DeserializationMemory> memory)
1302
0
{
1303
0
    if (!memory.has_value())
1304
0
        memory = DeserializationMemory { vm.heap() };
1305
1306
    // IMPLEMENTATION DEFINED: We need to make sure there's an execution context for target_realm on the stack before constructing these JS objects
1307
0
    auto& target_settings = Bindings::host_defined_environment_settings_object(target_realm);
1308
0
    target_settings.prepare_to_run_script();
1309
1310
0
    auto result = TRY(structured_deserialize_internal(vm, serialized.span(), target_realm, *memory));
1311
1312
0
    target_settings.clean_up_after_running_script();
1313
0
    VERIFY(result.value.has_value());
1314
0
    return *result.value;
1315
0
}
1316
1317
WebIDL::ExceptionOr<DeserializedRecord> structured_deserialize_internal(JS::VM& vm, ReadonlySpan<u32> const& serialized, JS::Realm& target_realm, DeserializationMemory& memory, Optional<size_t> position)
1318
0
{
1319
0
    Deserializer deserializer(vm, target_realm, serialized, memory, move(position));
1320
0
    auto value = TRY(deserializer.deserialize());
1321
0
    auto deserialized_record = DeserializedRecord {
1322
0
        .value = value,
1323
0
        .position = deserializer.position(),
1324
0
    };
1325
0
    return deserialized_record;
1326
0
}
1327
1328
}
1329
1330
namespace IPC {
1331
1332
template<>
1333
ErrorOr<void> encode(Encoder& encoder, ::Web::HTML::TransferDataHolder const& data_holder)
1334
0
{
1335
0
    TRY(encoder.encode(data_holder.data));
1336
0
    TRY(encoder.encode(data_holder.fds));
1337
0
    return {};
1338
0
}
1339
1340
template<>
1341
ErrorOr<void> encode(Encoder& encoder, ::Web::HTML::SerializedTransferRecord const& record)
1342
0
{
1343
0
    TRY(encoder.encode(record.serialized));
1344
0
    TRY(encoder.encode(record.transfer_data_holders));
1345
0
    return {};
1346
0
}
1347
1348
template<>
1349
ErrorOr<::Web::HTML::TransferDataHolder> decode(Decoder& decoder)
1350
0
{
1351
0
    auto data = TRY(decoder.decode<Vector<u8>>());
1352
0
    auto fds = TRY(decoder.decode<Vector<IPC::File>>());
1353
0
    return ::Web::HTML::TransferDataHolder { move(data), move(fds) };
1354
0
}
1355
1356
template<>
1357
ErrorOr<::Web::HTML::SerializedTransferRecord> decode(Decoder& decoder)
1358
0
{
1359
0
    auto serialized = TRY(decoder.decode<Vector<u32>>());
1360
0
    auto transfer_data_holders = TRY(decoder.decode<Vector<::Web::HTML::TransferDataHolder>>());
1361
0
    return ::Web::HTML::SerializedTransferRecord { move(serialized), move(transfer_data_holders) };
1362
0
}
1363
1364
}