/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 | | } |