12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370 |
- /*
- * Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
- * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
- * Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
- * Copyright (c) 2023, Idan Horowitz <idan.horowitz@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/StdLibExtras.h>
- #include <AK/String.h>
- #include <AK/Vector.h>
- #include <LibIPC/Decoder.h>
- #include <LibIPC/Encoder.h>
- #include <LibIPC/File.h>
- #include <LibJS/Forward.h>
- #include <LibJS/Runtime/Array.h>
- #include <LibJS/Runtime/ArrayBuffer.h>
- #include <LibJS/Runtime/BigInt.h>
- #include <LibJS/Runtime/BigIntObject.h>
- #include <LibJS/Runtime/BooleanObject.h>
- #include <LibJS/Runtime/DataView.h>
- #include <LibJS/Runtime/Date.h>
- #include <LibJS/Runtime/Map.h>
- #include <LibJS/Runtime/NumberObject.h>
- #include <LibJS/Runtime/PrimitiveString.h>
- #include <LibJS/Runtime/RegExpObject.h>
- #include <LibJS/Runtime/Set.h>
- #include <LibJS/Runtime/StringObject.h>
- #include <LibJS/Runtime/TypedArray.h>
- #include <LibJS/Runtime/VM.h>
- #include <LibWeb/Bindings/ExceptionOrUtils.h>
- #include <LibWeb/Bindings/Intrinsics.h>
- #include <LibWeb/Bindings/Serializable.h>
- #include <LibWeb/Bindings/Transferable.h>
- #include <LibWeb/FileAPI/Blob.h>
- #include <LibWeb/FileAPI/File.h>
- #include <LibWeb/HTML/MessagePort.h>
- #include <LibWeb/HTML/StructuredSerialize.h>
- #include <LibWeb/WebIDL/ExceptionOr.h>
- namespace Web::HTML {
- // Binary format:
- // A list of adjacent shallow values, which may contain references to other
- // values (noted by their position in the list, one value following another).
- // This list represents the "memory" in the StructuredSerialize algorithm.
- // The first item in the list is the root, i.e., the value of everything.
- // The format is generally u32-aligned (hence this leaking out into the type)
- // Each value has a length based on its type, as defined below.
- //
- // (Should more redundancy be added, e.g., for lengths/positions of values?)
- enum ValueTag {
- // Unused, for ease of catching bugs.
- Empty,
- // UndefinedPrimitive is serialized indicating that the Type is Undefined, no value is serialized.
- UndefinedPrimitive,
- // NullPrimitive is serialized indicating that the Type is Null, no value is serialized.
- NullPrimitive,
- // Following u32 is the boolean value.
- BooleanPrimitive,
- // Following two u32s are the double value.
- NumberPrimitive,
- // The BigIntPrimitive is serialized as a string in base 10 representation.
- // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
- BigIntPrimitive,
- // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
- StringPrimitive,
- BooleanObject,
- NumberObject,
- BigIntObject,
- StringObject,
- DateObject,
- RegExpObject,
- GrowableSharedArrayBuffer,
- SharedArrayBuffer,
- ResizeableArrayBuffer,
- ArrayBuffer,
- ArrayBufferView,
- MapObject,
- SetObject,
- ErrorObject,
- ArrayObject,
- Object,
- ObjectReference,
- SerializableObject,
- // TODO: Define many more types
- // This tag or higher are understood to be errors
- ValueTagMax,
- };
- enum ErrorType {
- Error,
- #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
- ClassName,
- JS_ENUMERATE_NATIVE_ERRORS
- #undef __JS_ENUMERATE
- };
- static ErrorType error_name_to_type(String const& name)
- {
- #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
- if (name == #ClassName##sv) \
- return ErrorType::ClassName;
- JS_ENUMERATE_NATIVE_ERRORS
- #undef __JS_ENUMERATE
- return Error;
- }
- // Serializing and deserializing are each two passes:
- // 1. Fill up the memory with all the values, but without translating references
- // 2. Translate all the references into the appropriate form
- class Serializer {
- public:
- Serializer(JS::VM& vm, SerializationMemory& memory, bool for_storage)
- : m_vm(vm)
- , m_memory(memory)
- , m_for_storage(for_storage)
- {
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
- WebIDL::ExceptionOr<SerializationRecord> serialize(JS::Value value)
- {
- // 2. If memory[value] exists, then return memory[value].
- if (m_memory.contains(value)) {
- auto index = m_memory.get(value).value();
- return Vector<u32> { ValueTag::ObjectReference, index };
- }
- // 3. Let deep be false.
- auto deep = false;
- bool return_primitive_type = true;
- // 4. If Type(value) is Undefined, Null, Boolean, Number, BigInt, or String, then return { [[Type]]: "primitive", [[Value]]: value }.
- if (value.is_undefined()) {
- m_serialized.append(ValueTag::UndefinedPrimitive);
- } else if (value.is_null()) {
- m_serialized.append(ValueTag::NullPrimitive);
- } else if (value.is_boolean()) {
- m_serialized.append(ValueTag::BooleanPrimitive);
- serialize_boolean_primitive(m_serialized, value);
- } else if (value.is_number()) {
- m_serialized.append(ValueTag::NumberPrimitive);
- serialize_number_primitive(m_serialized, value);
- } else if (value.is_bigint()) {
- m_serialized.append(ValueTag::BigIntPrimitive);
- TRY(serialize_big_int_primitive(m_vm, m_serialized, value));
- } else if (value.is_string()) {
- m_serialized.append(ValueTag::StringPrimitive);
- TRY(serialize_string_primitive(m_vm, m_serialized, value));
- } else {
- return_primitive_type = false;
- }
- if (return_primitive_type)
- return m_serialized;
- // 5. If Type(value) is Symbol, then throw a "DataCloneError" DOMException.
- if (value.is_symbol())
- return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize Symbol"_fly_string);
- // 6. Let serialized be an uninitialized value.
- // 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }.
- if (value.is_object() && is<JS::BooleanObject>(value.as_object())) {
- m_serialized.append(ValueTag::BooleanObject);
- serialize_boolean_object(m_serialized, value);
- }
- // 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }.
- else if (value.is_object() && is<JS::NumberObject>(value.as_object())) {
- m_serialized.append(ValueTag::NumberObject);
- serialize_number_object(m_serialized, value);
- }
- // 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }.
- else if (value.is_object() && is<JS::BigIntObject>(value.as_object())) {
- m_serialized.append(ValueTag::BigIntObject);
- TRY(serialize_big_int_object(m_vm, m_serialized, value));
- }
- // 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }.
- else if (value.is_object() && is<JS::StringObject>(value.as_object())) {
- m_serialized.append(ValueTag::StringObject);
- TRY(serialize_string_object(m_vm, m_serialized, value));
- }
- // 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }.
- else if (value.is_object() && is<JS::Date>(value.as_object())) {
- m_serialized.append(ValueTag::DateObject);
- serialize_date_object(m_serialized, value);
- }
- // 12. Otherwise, if value has a [[RegExpMatcher]] internal slot, then set serialized to
- // { [[Type]]: "RegExp", [[RegExpMatcher]]: value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]],
- // [[OriginalFlags]]: value.[[OriginalFlags]] }.
- else if (value.is_object() && is<JS::RegExpObject>(value.as_object())) {
- m_serialized.append(ValueTag::RegExpObject);
- TRY(serialize_reg_exp_object(m_vm, m_serialized, value));
- }
- // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
- else if (value.is_object() && is<JS::ArrayBuffer>(value.as_object())) {
- TRY(serialize_array_buffer(m_vm, m_serialized, static_cast<JS::ArrayBuffer&>(value.as_object()), m_for_storage));
- }
- // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
- else if (value.is_object() && is<JS::TypedArrayBase>(value.as_object())) {
- TRY(serialize_viewed_array_buffer(m_vm, m_serialized, static_cast<JS::TypedArrayBase&>(value.as_object()), m_for_storage, m_memory));
- } else if (value.is_object() && is<JS::DataView>(value.as_object())) {
- TRY(serialize_viewed_array_buffer(m_vm, m_serialized, static_cast<JS::DataView&>(value.as_object()), m_for_storage, m_memory));
- }
- // 15. Otherwise, if value has [[MapData]] internal slot, then:
- else if (value.is_object() && is<JS::Map>(value.as_object())) {
- // 1. Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }.
- m_serialized.append(ValueTag::MapObject);
- // 2. Set deep to true.
- deep = true;
- }
- // 16. Otherwise, if value has [[SetData]] internal slot, then:
- else if (value.is_object() && is<JS::Set>(value.as_object())) {
- // 1. Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }.
- m_serialized.append(ValueTag::SetObject);
- // 2. Set deep to true.
- deep = true;
- }
- // 17. Otherwise, if value has an [[ErrorData]] internal slot and value is not a platform object, then:
- else if (value.is_object() && is<JS::Error>(value.as_object()) && !is<Bindings::PlatformObject>(value.as_object())) {
- // 1. Let name be ? Get(value, "name").
- auto name_property = TRY(value.as_object().get(m_vm.names.name));
- // FIXME: Spec bug - https://github.com/whatwg/html/issues/9923
- // MISSING STEP: Set name to ? ToString(name).
- auto name = TRY(name_property.to_string(m_vm));
- // 2. If name is not one of "Error", "EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", or "URIError", then set name to "Error".
- auto type = error_name_to_type(name);
- // 3. Let valueMessageDesc be ? value.[[GetOwnProperty]]("message").
- auto value_message_descriptor = TRY(value.as_object().internal_get_own_property(m_vm.names.message));
- // 4. Let message be undefined if IsDataDescriptor(valueMessageDesc) is false, and ? ToString(valueMessageDesc.[[Value]]) otherwise.
- Optional<String> message;
- if (value_message_descriptor.has_value() && value_message_descriptor->is_data_descriptor())
- message = TRY(value_message_descriptor->value->to_string(m_vm));
- // 5. Set serialized to { [[Type]]: "Error", [[Name]]: name, [[Message]]: message }.
- // FIXME: 6. User agents should attach a serialized representation of any interesting accompanying data which are not yet specified, notably the stack property, to serialized.
- m_serialized.append(ValueTag::ErrorObject);
- m_serialized.append(type);
- m_serialized.append(message.has_value());
- if (message.has_value())
- TRY(serialize_string(m_vm, m_serialized, *message));
- }
- // 18. Otherwise, if value is an Array exotic object, then:
- else if (value.is_object() && is<JS::Array>(value.as_object())) {
- // 1. Let valueLenDescriptor be ? OrdinaryGetOwnProperty(value, "length").
- // 2. Let valueLen be valueLenDescriptor.[[Value]].
- // NON-STANDARD: Array objects in LibJS do not have a real length property, so it must be accessed the usual way
- u64 length = MUST(JS::length_of_array_like(m_vm, value.as_object()));
- // 3. Set serialized to { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: a new empty List }.
- m_serialized.append(ValueTag::ArrayObject);
- m_serialized.append(bit_cast<u32*>(&length), 2);
- // 4. Set deep to true.
- deep = true;
- }
- // FIXME: 19. Otherwise, if value is a platform object that is a serializable object:
- else if (value.is_object() && is<Bindings::Serializable>(value.as_object())) {
- auto& serializable = dynamic_cast<Bindings::Serializable&>(value.as_object());
- m_serialized.append(ValueTag::SerializableObject);
- TRY(serialize_string(m_vm, m_serialized, serializable.interface_name()));
- // 1. Perform the serialization steps for value's primary interface, given value, serialized, and forStorage.
- TRY(serializable.serialization_steps(m_serialized, m_for_storage));
- }
- // 20. Otherwise, if value is a platform object, then throw a "DataCloneError" DOMException.
- else if (value.is_object() && is<Bindings::PlatformObject>(value.as_object())) {
- return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize platform objects"_fly_string));
- }
- // 21. Otherwise, if IsCallable(value) is true, then throw a "DataCloneError" DOMException.
- else if (value.is_function()) {
- return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize functions"_fly_string));
- }
- // FIXME: 22. Otherwise, if value has any internal slot other than [[Prototype]] or [[Extensible]], then throw a "DataCloneError" DOMException.
- // 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.
- // 24. Otherwise:
- else {
- // 1. Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }.
- m_serialized.append(ValueTag::Object);
- // 2. Set deep to true.
- deep = true;
- }
- // 25. Set memory[value] to serialized.
- m_memory.set(make_handle(value), m_next_id++);
- // 26. If deep is true, then:
- if (deep) {
- // 1. If value has a [[MapData]] internal slot, then:
- if (value.is_object() && is<JS::Map>(value.as_object())) {
- auto const& map = static_cast<JS::Map const&>(value.as_object());
- // 1. Let copiedList be a new empty List.
- Vector<JS::Value> copied_list;
- copied_list.ensure_capacity(map.map_size() * 2);
- // 2. For each Record { [[Key]], [[Value]] } entry of value.[[MapData]]:
- for (auto const& entry : static_cast<JS::Map const&>(value.as_object())) {
- // 1. Let copiedEntry be a new Record { [[Key]]: entry.[[Key]], [[Value]]: entry.[[Value]] }.
- // 2. If copiedEntry.[[Key]] is not the special value empty, append copiedEntry to copiedList.
- copied_list.append(entry.key);
- copied_list.append(entry.value);
- }
- u64 size = map.map_size();
- m_serialized.append(bit_cast<u32*>(&size), 2);
- // 3. For each Record { [[Key]], [[Value]] } entry of copiedList:
- for (auto copied_value : copied_list) {
- // 1. Let serializedKey be ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory).
- // 2. Let serializedValue be ? StructuredSerializeInternal(entry.[[Value]], forStorage, memory).
- auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory));
- // 3. Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]].
- m_serialized.extend(serialized_value);
- }
- }
- // 2. Otherwise, if value has a [[SetData]] internal slot, then:
- else if (value.is_object() && is<JS::Set>(value.as_object())) {
- auto const& set = static_cast<JS::Set const&>(value.as_object());
- // 1. Let copiedList be a new empty List.
- Vector<JS::Value> copied_list;
- copied_list.ensure_capacity(set.set_size());
- // 2. For each entry of value.[[SetData]]:
- for (auto const& entry : static_cast<JS::Set const&>(value.as_object())) {
- // 1. If entry is not the special value empty, append entry to copiedList.
- copied_list.append(entry.key);
- }
- u64 size = set.set_size();
- m_serialized.append(bit_cast<u32*>(&size), 2);
- // 3. For each entry of copiedList:
- for (auto copied_value : copied_list) {
- // 1. Let serializedEntry be ? StructuredSerializeInternal(entry, forStorage, memory).
- auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory));
- // 2. Append serializedEntry to serialized.[[SetData]].
- m_serialized.extend(serialized_value);
- }
- }
- // FIXME: 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.
- // 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key):
- else {
- u64 property_count = 0;
- auto count_offset = m_serialized.size();
- m_serialized.append(bit_cast<u32*>(&property_count), 2);
- for (auto key : MUST(value.as_object().enumerable_own_property_names(JS::Object::PropertyKind::Key))) {
- auto property_key = MUST(JS::PropertyKey::from_value(m_vm, key));
- // 1. If ! HasOwnProperty(value, key) is true, then:
- if (MUST(value.as_object().has_own_property(property_key))) {
- // 1. Let inputValue be ? value.[[Get]](key, value).
- auto input_value = TRY(value.as_object().internal_get(property_key, value));
- // 2. Let outputValue be ? StructuredSerializeInternal(inputValue, forStorage, memory).
- auto output_value = TRY(structured_serialize_internal(m_vm, input_value, m_for_storage, m_memory));
- // 3. Append { [[Key]]: key, [[Value]]: outputValue } to serialized.[[Properties]].
- TRY(serialize_string(m_vm, m_serialized, key.as_string()));
- m_serialized.extend(output_value);
- property_count++;
- }
- }
- memcpy(m_serialized.data() + count_offset, &property_count, sizeof(property_count));
- }
- }
- // 27. Return serialized.
- return m_serialized;
- }
- private:
- JS::VM& m_vm;
- SerializationMemory& m_memory; // JS value -> index
- u32 m_next_id { 0 };
- SerializationRecord m_serialized;
- bool m_for_storage { false };
- };
- void serialize_boolean_primitive(SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_boolean());
- serialized.append(static_cast<u32>(value.as_bool()));
- }
- void serialize_number_primitive(SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_number());
- double number = value.as_double();
- serialized.append(bit_cast<u32*>(&number), 2);
- }
- WebIDL::ExceptionOr<void> serialize_big_int_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_bigint());
- auto& val = value.as_bigint();
- TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, val.to_string())));
- return {};
- }
- WebIDL::ExceptionOr<void> serialize_string_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_string());
- TRY(serialize_string(vm, serialized, value.as_string()));
- return {};
- }
- void serialize_boolean_object(SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_object() && is<JS::BooleanObject>(value.as_object()));
- auto& boolean_object = static_cast<JS::BooleanObject&>(value.as_object());
- serialized.append(bit_cast<u32>(static_cast<u32>(boolean_object.boolean())));
- }
- void serialize_number_object(SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_object() && is<JS::NumberObject>(value.as_object()));
- auto& number_object = static_cast<JS::NumberObject&>(value.as_object());
- double const number = number_object.number();
- serialized.append(bit_cast<u32*>(&number), 2);
- }
- WebIDL::ExceptionOr<void> serialize_big_int_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_object() && is<JS::BigIntObject>(value.as_object()));
- auto& bigint_object = static_cast<JS::BigIntObject&>(value.as_object());
- TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, bigint_object.bigint().to_string())));
- return {};
- }
- WebIDL::ExceptionOr<void> serialize_string_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_object() && is<JS::StringObject>(value.as_object()));
- auto& string_object = static_cast<JS::StringObject&>(value.as_object());
- TRY(serialize_string(vm, serialized, string_object.primitive_string()));
- return {};
- }
- void serialize_date_object(SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_object() && is<JS::Date>(value.as_object()));
- auto& date_object = static_cast<JS::Date&>(value.as_object());
- double const date_value = date_object.date_value();
- serialized.append(bit_cast<u32*>(&date_value), 2);
- }
- WebIDL::ExceptionOr<void> serialize_reg_exp_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value)
- {
- VERIFY(value.is_object() && is<JS::RegExpObject>(value.as_object()));
- auto& regexp_object = static_cast<JS::RegExpObject&>(value.as_object());
- // Note: A Regex<ECMA262> object is perfectly happy to be reconstructed with just the source+flags
- // In the future, we could optimize the work being done on the deserialize step by serializing
- // more of the internal state (the [[RegExpMatcher]] internal slot)
- TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, String::from_byte_string(regexp_object.pattern()))));
- TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, String::from_byte_string(regexp_object.flags()))));
- return {};
- }
- void serialize_u64(SerializationRecord& serialized, u64 value)
- {
- serialized.append(bit_cast<u32*>(&value), 2);
- }
- void serialize_i64(SerializationRecord& serialized, i64 value)
- {
- serialized.append(bit_cast<u32*>(&value), 2);
- }
- WebIDL::ExceptionOr<void> serialize_bytes(JS::VM& vm, Vector<u32>& vector, ReadonlyBytes bytes)
- {
- // Append size of the buffer to the serialized structure.
- u64 const size = bytes.size();
- serialize_u64(vector, size);
- // Append the bytes of the buffer to the serialized structure.
- u64 byte_position = 0;
- while (byte_position < size) {
- u32 combined_value = 0;
- for (u8 i = 0; i < 4; ++i) {
- u8 const byte = bytes[byte_position];
- combined_value |= byte << (i * 8);
- byte_position++;
- if (byte_position == size)
- break;
- }
- TRY_OR_THROW_OOM(vm, vector.try_append(combined_value));
- }
- return {};
- }
- WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, DeprecatedFlyString const& string)
- {
- return serialize_bytes(vm, vector, string.view().bytes());
- }
- WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, String const& string)
- {
- return serialize_bytes(vm, vector, { string.code_points().bytes(), string.code_points().byte_length() });
- }
- WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
- {
- auto string = primitive_string.utf8_string();
- TRY(serialize_string(vm, vector, string));
- return {};
- }
- WebIDL::ExceptionOr<void> serialize_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::ArrayBuffer const& array_buffer, bool for_storage)
- {
- // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
- // FIXME: 1. If IsSharedArrayBuffer(value) is true, then:
- if (false) {
- // 1. If the current settings object's cross-origin isolated capability is false, then throw a "DataCloneError" DOMException.
- // NOTE: This check is only needed when serializing (and not when deserializing) as the cross-origin isolated capability cannot change
- // over time and a SharedArrayBuffer cannot leave an agent cluster.
- if (current_settings_object().cross_origin_isolated_capability() == CanUseCrossOriginIsolatedAPIs::No)
- return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer when cross-origin isolated"_fly_string);
- // 2. If forStorage is true, then throw a "DataCloneError" DOMException.
- if (for_storage)
- return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer for storage"_fly_string);
- // FIXME: 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer",
- // [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]],
- // [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
- // FIXME: 4. Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]],
- // [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
- }
- // 2. Otherwise:
- else {
- // 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
- if (array_buffer.is_detached())
- return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize detached ArrayBuffer"_fly_string);
- // 2. Let size be value.[[ArrayBufferByteLength]].
- auto size = array_buffer.byte_length();
- // 3. Let dataCopy be ? CreateByteDataBlock(size).
- // NOTE: This can throw a RangeError exception upon allocation failure.
- auto data_copy = TRY(JS::create_byte_data_block(vm, size));
- // 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
- JS::copy_data_block_bytes(data_copy.buffer(), 0, array_buffer.buffer(), 0, size);
- // FIXME: 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
- // [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
- if (false) {
- }
- // 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
- else {
- vector.append(ValueTag::ArrayBuffer);
- TRY(serialize_bytes(vm, vector, data_copy.buffer().bytes()));
- }
- }
- return {};
- }
- template<OneOf<JS::TypedArrayBase, JS::DataView> ViewType>
- WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, ViewType const& view, bool for_storage, SerializationMemory& memory)
- {
- // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
- auto view_record = [&]() {
- if constexpr (IsSame<ViewType, JS::DataView>) {
- return JS::make_data_view_with_buffer_witness_record(view, JS::ArrayBuffer::Order::SeqCst);
- } else {
- return JS::make_typed_array_with_buffer_witness_record(view, JS::ArrayBuffer::Order::SeqCst);
- }
- }();
- // 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
- if constexpr (IsSame<ViewType, JS::DataView>) {
- if (JS::is_view_out_of_bounds(view_record))
- return WebIDL::DataCloneError::create(*vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "DataView"sv)));
- } else {
- if (JS::is_typed_array_out_of_bounds(view_record))
- return WebIDL::DataCloneError::create(*vm.current_realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "TypedArray"sv)));
- }
- // 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
- auto* buffer = view.viewed_array_buffer();
- // 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
- auto buffer_serialized = TRY(structured_serialize_internal(vm, JS::Value(buffer), for_storage, memory));
- // 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
- // NOTE: We currently only implement this for ArrayBuffer
- VERIFY(buffer_serialized[0] == ValueTag::ArrayBuffer);
- // 5. If value has a [[DataView]] internal slot, then set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView",
- // [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
- if constexpr (IsSame<ViewType, JS::DataView>) {
- vector.append(ValueTag::ArrayBufferView);
- vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
- TRY(serialize_string(vm, vector, "DataView"_string)); // [[Constructor]]
- vector.append(JS::get_view_byte_length(view_record));
- vector.append(view.byte_offset());
- }
- // 6. Otherwise:
- else {
- // 1. Assert: value has a [[TypedArrayName]] internal slot.
- // NOTE: Handled by constexpr check and template constraints
- // 2. Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]],
- // [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
- // [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
- vector.append(ValueTag::ArrayBufferView);
- vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
- TRY(serialize_string(vm, vector, view.element_name())); // [[Constructor]]
- vector.append(JS::typed_array_byte_length(view_record));
- vector.append(view.byte_offset());
- vector.append(JS::typed_array_length(view_record));
- }
- return {};
- }
- template WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::TypedArrayBase const& view, bool for_storage, SerializationMemory& memory);
- template WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::DataView const& view, bool for_storage, SerializationMemory& memory);
- class Deserializer {
- public:
- Deserializer(JS::VM& vm, JS::Realm& target_realm, ReadonlySpan<u32> serialized, DeserializationMemory& memory)
- : m_vm(vm)
- , m_serialized(serialized)
- , m_memory(memory)
- {
- VERIFY(vm.current_realm() == &target_realm);
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
- WebIDL::ExceptionOr<JS::Value> deserialize()
- {
- auto tag = m_serialized[m_position++];
- // 2. If memory[serialized] exists, then return memory[serialized].
- if (tag == ValueTag::ObjectReference) {
- auto index = m_serialized[m_position++];
- if (index == NumericLimits<u32>::max()) {
- return JS::Object::create(*m_vm.current_realm(), nullptr);
- }
- return m_memory[index];
- }
- // 3. Let deep be false.
- auto deep = false;
- // 4. Let value be an uninitialized value.
- JS::Value value;
- auto is_primitive = false;
- switch (tag) {
- // 5. If serialized.[[Type]] is "primitive", then set value to serialized.[[Value]].
- case ValueTag::UndefinedPrimitive: {
- value = JS::js_undefined();
- is_primitive = true;
- break;
- }
- case ValueTag::NullPrimitive: {
- value = JS::js_null();
- is_primitive = true;
- break;
- }
- case ValueTag::BooleanPrimitive: {
- value = JS::Value { deserialize_boolean_primitive(m_serialized, m_position) };
- is_primitive = true;
- break;
- }
- case ValueTag::NumberPrimitive: {
- value = JS::Value { deserialize_number_primitive(m_serialized, m_position) };
- is_primitive = true;
- break;
- }
- case ValueTag::BigIntPrimitive: {
- auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_serialized, m_position));
- value = JS::Value { big_int };
- is_primitive = true;
- break;
- }
- case ValueTag::StringPrimitive: {
- auto string = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position));
- value = JS::Value { string };
- is_primitive = true;
- break;
- }
- // 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]].
- case BooleanObject: {
- value = deserialize_boolean_object(*m_vm.current_realm(), m_serialized, m_position);
- break;
- }
- // 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]].
- case ValueTag::NumberObject: {
- value = deserialize_number_object(*m_vm.current_realm(), m_serialized, m_position);
- break;
- }
- // 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]].
- case ValueTag::BigIntObject: {
- value = TRY(deserialize_big_int_object(*m_vm.current_realm(), m_serialized, m_position));
- break;
- }
- // 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]].
- case ValueTag::StringObject: {
- value = TRY(deserialize_string_object(*m_vm.current_realm(), m_serialized, m_position));
- break;
- }
- // 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]].
- case ValueTag::DateObject: {
- value = deserialize_date_object(*m_vm.current_realm(), m_serialized, m_position);
- break;
- }
- // 11. Otherwise, if serialized.[[Type]] is "RegExp", then set value to a new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is serialized.[[RegExpMatcher]],
- // whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]].
- case ValueTag::RegExpObject: {
- value = TRY(deserialize_reg_exp_object(*m_vm.current_realm(), m_serialized, m_position));
- break;
- }
- // FIXME: 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then:
- // FIXME: 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then:
- // 14. Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]].
- case ValueTag::ArrayBuffer: {
- auto* realm = m_vm.current_realm();
- // If this throws an exception, catch it, and then throw a "DataCloneError" DOMException.
- auto bytes_or_error = deserialize_bytes(m_vm, m_serialized, m_position);
- if (bytes_or_error.is_error())
- return WebIDL::DataCloneError::create(*m_vm.current_realm(), "out of memory"_fly_string);
- value = JS::ArrayBuffer::create(*realm, bytes_or_error.release_value());
- break;
- }
- // 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]].
- // 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then:
- case ValueTag::ArrayBufferView: {
- auto* realm = m_vm.current_realm();
- auto array_buffer_value = TRY(deserialize());
- auto& array_buffer = verify_cast<JS::ArrayBuffer>(array_buffer_value.as_object());
- auto constructor_name = TRY(deserialize_string(m_vm, m_serialized, m_position));
- u32 byte_length = m_serialized[m_position++];
- u32 byte_offset = m_serialized[m_position++];
- if (constructor_name == "DataView"sv) {
- value = JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset);
- } else {
- u32 array_length = m_serialized[m_position++];
- JS::GCPtr<JS::TypedArrayBase> typed_array_ptr;
- #define CREATE_TYPED_ARRAY(ClassName) \
- if (constructor_name == #ClassName##sv) \
- typed_array_ptr = JS::ClassName::create(*realm, array_length, array_buffer);
- #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
- CREATE_TYPED_ARRAY(ClassName)
- JS_ENUMERATE_TYPED_ARRAYS
- #undef __JS_ENUMERATE
- #undef CREATE_TYPED_ARRAY
- VERIFY(typed_array_ptr != nullptr); // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
- typed_array_ptr->set_byte_length(byte_length);
- typed_array_ptr->set_byte_offset(byte_offset);
- value = typed_array_ptr;
- }
- break;
- }
- // 17. Otherwise, if serialized.[[Type]] is "Map", then:
- case ValueTag::MapObject: {
- auto& realm = *m_vm.current_realm();
- // 1. Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List.
- value = JS::Map::create(realm);
- // 2. Set deep to true.
- deep = true;
- break;
- }
- // 18. Otherwise, if serialized.[[Type]] is "Set", then:
- case ValueTag::SetObject: {
- auto& realm = *m_vm.current_realm();
- // 1. Set value to a new Set object in targetRealm whose [[SetData]] internal slot value is a new empty List.
- value = JS::Set::create(realm);
- // 2. Set deep to true.
- deep = true;
- break;
- }
- // 19. Otherwise, if serialized.[[Type]] is "Array", then:
- case ValueTag::ArrayObject: {
- auto& realm = *m_vm.current_realm();
- // 1. Let outputProto be targetRealm.[[Intrinsics]].[[%Array.prototype%]].
- // 2. Set value to ! ArrayCreate(serialized.[[Length]], outputProto).
- auto length = read_u64();
- value = MUST(JS::Array::create(realm, length));
- // 3. Set deep to true.
- deep = true;
- break;
- }
- // 20. Otherwise, if serialized.[[Type]] is "Object", then:
- case ValueTag::Object: {
- auto& realm = *m_vm.current_realm();
- // 1. Set value to a new Object in targetRealm.
- value = JS::Object::create(realm, realm.intrinsics().object_prototype());
- // 2. Set deep to true.
- deep = true;
- break;
- }
- // 21. Otherwise, if serialized.[[Type]] is "Error", then:
- case ValueTag::ErrorObject: {
- auto& realm = *m_vm.current_realm();
- auto type = static_cast<ErrorType>(m_serialized[m_position++]);
- auto has_message = static_cast<bool>(m_serialized[m_position++]);
- if (has_message) {
- auto message = TRY(deserialize_string(m_vm, m_serialized, m_position));
- switch (type) {
- case ErrorType::Error:
- value = JS::Error::create(realm, message);
- break;
- #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
- case ErrorType::ClassName: \
- value = JS::ClassName::create(realm, message); \
- break;
- JS_ENUMERATE_NATIVE_ERRORS
- #undef __JS_ENUMERATE
- }
- } else {
- switch (type) {
- case ErrorType::Error:
- value = JS::Error::create(realm);
- break;
- #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
- case ErrorType::ClassName: \
- value = JS::ClassName::create(realm); \
- break;
- JS_ENUMERATE_NATIVE_ERRORS
- #undef __JS_ENUMERATE
- }
- }
- break;
- }
- // 22. Otherwise:
- default:
- VERIFY(tag == ValueTag::SerializableObject);
- auto& realm = *m_vm.current_realm();
- // 1. Let interfaceName be serialized.[[Type]].
- auto interface_name = TRY(deserialize_string(m_vm, m_serialized, m_position));
- // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
- if (!is_interface_exposed_on_target_realm(interface_name, realm))
- return WebIDL::DataCloneError::create(realm, "Unsupported type"_fly_string);
- // 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
- value = TRY(create_serialized_type(interface_name, realm));
- // 4. Set deep to true.
- deep = true;
- }
- // 23. Set memory[serialized] to value.
- // IMPLEMENTATION DEFINED: We don't add primitive values to the memory to match the serialization indices (which also doesn't add them)
- if (!is_primitive)
- m_memory.append(value);
- // 24. If deep is true, then:
- if (deep) {
- // 1. If serialized.[[Type]] is "Map", then:
- if (tag == ValueTag::MapObject) {
- auto& map = static_cast<JS::Map&>(value.as_object());
- auto length = read_u64();
- // 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[MapData]]:
- for (u64 i = 0u; i < length; ++i) {
- // 1. Let deserializedKey be ? StructuredDeserialize(entry.[[Key]], targetRealm, memory).
- auto deserialized_key = TRY(deserialize());
- // 2. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
- auto deserialized_value = TRY(deserialize());
- // 3. Append { [[Key]]: deserializedKey, [[Value]]: deserializedValue } to value.[[MapData]].
- map.map_set(deserialized_key, deserialized_value);
- }
- }
- // 2. Otherwise, if serialized.[[Type]] is "Set", then:
- else if (tag == ValueTag::SetObject) {
- auto& set = static_cast<JS::Set&>(value.as_object());
- auto length = read_u64();
- // 1. For each entry of serialized.[[SetData]]:
- for (u64 i = 0u; i < length; ++i) {
- // 1. Let deserializedEntry be ? StructuredDeserialize(entry, targetRealm, memory).
- auto deserialized_entry = TRY(deserialize());
- // 2. Append deserializedEntry to value.[[SetData]].
- set.set_add(deserialized_entry);
- }
- }
- // 3. Otherwise, if serialized.[[Type]] is "Array" or "Object", then:
- else if (tag == ValueTag::ArrayObject || tag == ValueTag::Object) {
- auto& object = value.as_object();
- auto length = read_u64();
- // 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[Properties]]:
- for (u64 i = 0u; i < length; ++i) {
- auto key = TRY(deserialize_string(m_vm, m_serialized, m_position));
- // 1. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory).
- auto deserialized_value = TRY(deserialize());
- // 2. Let result be ! CreateDataProperty(value, entry.[[Key]], deserializedValue).
- auto result = MUST(object.create_data_property(key.to_byte_string(), deserialized_value));
- // 3. Assert: result is true.
- VERIFY(result);
- }
- }
- // 4. Otherwise:
- else {
- // 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm.
- auto& serializable = dynamic_cast<Bindings::Serializable&>(value.as_object());
- TRY(serializable.deserialization_steps(m_serialized, m_position));
- }
- }
- // 25. Return value.
- return value;
- }
- private:
- JS::VM& m_vm;
- ReadonlySpan<u32> m_serialized;
- size_t m_position { 0 };
- JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
- u64 read_u64()
- {
- u64 value;
- memcpy(&value, m_serialized.offset_pointer(m_position), sizeof(value));
- m_position += 2;
- return value;
- }
- static WebIDL::ExceptionOr<JS::NonnullGCPtr<Bindings::PlatformObject>> create_serialized_type(StringView interface_name, JS::Realm& realm)
- {
- if (interface_name == "Blob"sv)
- return FileAPI::Blob::create(realm);
- if (interface_name == "File"sv)
- return FileAPI::File::create(realm);
- VERIFY_NOT_REACHED();
- }
- // FIXME: Consolidate this function with the similar is_interface_exposed_on_target_realm() used when transferring objects.
- // Also, the name parameter would be better off being the interface name (as a string) so that we don't need a switch statement.
- static bool is_interface_exposed_on_target_realm(StringView interface_name, JS::Realm& realm)
- {
- auto const& intrinsics = Bindings::host_defined_intrinsics(realm);
- return intrinsics.is_exposed(interface_name);
- }
- };
- bool deserialize_boolean_primitive(ReadonlySpan<u32> const& serialized, size_t& position)
- {
- VERIFY(position < serialized.size());
- return static_cast<bool>(serialized[position++]);
- }
- double deserialize_number_primitive(ReadonlySpan<u32> const& serialized, size_t& position)
- {
- VERIFY(position + 2 <= serialized.size());
- u32 const bits[2] {
- serialized[position++],
- serialized[position++],
- };
- return *bit_cast<double*>(&bits);
- }
- JS::NonnullGCPtr<JS::BooleanObject> deserialize_boolean_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
- {
- auto boolean_primitive = deserialize_boolean_primitive(serialized, position);
- return JS::BooleanObject::create(realm, boolean_primitive);
- }
- JS::NonnullGCPtr<JS::NumberObject> deserialize_number_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
- {
- auto number_primitive = deserialize_number_primitive(serialized, position);
- return JS::NumberObject::create(realm, number_primitive);
- }
- WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigIntObject>> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
- {
- auto big_int_primitive = TRY(deserialize_big_int_primitive(realm.vm(), serialized, position));
- return JS::BigIntObject::create(realm, big_int_primitive);
- }
- WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::StringObject>> deserialize_string_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
- {
- auto string_primitive = TRY(deserialize_string_primitive(realm.vm(), serialized, position));
- return JS::StringObject::create(realm, string_primitive, realm.intrinsics().string_prototype());
- }
- JS::NonnullGCPtr<JS::Date> deserialize_date_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
- {
- VERIFY(position + 2 <= serialized.size());
- u32 const bits[2] {
- serialized[position++],
- serialized[position++],
- };
- auto double_value = *bit_cast<double*>(&bits);
- return JS::Date::create(realm, double_value);
- }
- WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::RegExpObject>> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position)
- {
- auto pattern = TRY(deserialize_string_primitive(realm.vm(), serialized, position));
- auto flags = TRY(deserialize_string_primitive(realm.vm(), serialized, position));
- return TRY(JS::regexp_create(realm.vm(), move(pattern), move(flags)));
- }
- u64 deserialize_u64(ReadonlySpan<u32> const& serialized, size_t& position)
- {
- VERIFY(position + 2 <= serialized.size());
- u32 const bits[2] {
- serialized[position++],
- serialized[position++],
- };
- return *bit_cast<u64*>(&bits);
- }
- i64 deserialize_i64(ReadonlySpan<u32> const& serialized, size_t& position)
- {
- VERIFY(position + 2 <= serialized.size());
- u32 const bits[2] {
- serialized[position++],
- serialized[position++],
- };
- return *bit_cast<i64*>(&bits);
- }
- WebIDL::ExceptionOr<ByteBuffer> deserialize_bytes(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
- {
- u64 const size = deserialize_u64(vector, position);
- auto bytes = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(size));
- u64 byte_position = 0;
- while (position < vector.size() && byte_position < size) {
- for (u8 i = 0; i < 4; ++i) {
- bytes[byte_position++] = (vector[position] >> (i * 8) & 0xFF);
- if (byte_position == size)
- break;
- }
- position++;
- }
- return bytes;
- }
- WebIDL::ExceptionOr<String> deserialize_string(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
- {
- auto bytes = TRY(deserialize_bytes(vm, vector, position));
- return TRY_OR_THROW_OOM(vm, String::from_utf8(StringView { bytes }));
- }
- WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
- {
- auto bytes = TRY(deserialize_bytes(vm, vector, position));
- return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() {
- return JS::PrimitiveString::create(vm, StringView { bytes });
- }));
- }
- WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position)
- {
- auto string = TRY(deserialize_string_primitive(vm, vector, position));
- auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
- return string->utf8_string_view();
- }));
- auto bigint = MUST(::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1)));
- return JS::BigInt::create(vm, bigint);
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializewithtransfer
- WebIDL::ExceptionOr<SerializedTransferRecord> structured_serialize_with_transfer(JS::VM& vm, JS::Value value, Vector<JS::Handle<JS::Object>> const& transfer_list)
- {
- // 1. Let memory be an empty map.
- SerializationMemory memory = {};
- // 2. For each transferable of transferList:
- for (auto const& transferable : transfer_list) {
- // 1. If transferable has neither an [[ArrayBufferData]] internal slot nor a [[Detached]] internal slot, then throw a "DataCloneError" DOMException.
- // FIXME: Handle transferring ArrayBufferData objects
- if (!is<Bindings::Transferable>(*transferable)) {
- return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer type"_fly_string);
- }
- // FIXME: 2. If transferable has an [[ArrayBufferData]] internal slot and IsSharedArrayBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
- // 3. If memory[transferable] exists, then throw a "DataCloneError" DOMException.
- auto transferable_value = JS::Value(transferable);
- if (memory.contains(transferable_value)) {
- return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer value twice"_fly_string);
- }
- // 4. Set memory[transferable] to { [[Type]]: an uninitialized value }.
- memory.set(JS::make_handle(transferable_value), NumericLimits<u32>::max());
- }
- // 3. Let serialized be ? StructuredSerializeInternal(value, false, memory).
- auto serialized = TRY(structured_serialize_internal(vm, value, false, memory));
- // 4. Let transferDataHolders be a new empty List.
- Vector<TransferDataHolder> transfer_data_holders;
- transfer_data_holders.ensure_capacity(transfer_list.size());
- // 5. For each transferable of transferList:
- for (auto& transferable : transfer_list) {
- // 1. FIXME: If transferable has an [[ArrayBufferData]] internal slot and IsDetachedBuffer(transferable) is true, then throw a "DataCloneError" DOMException.
- // 2. If transferable has a [[Detached]] internal slot and transferable.[[Detached]] is true, then throw a "DataCloneError" DOMException.
- if (is<Bindings::Transferable>(*transferable)) {
- auto& transferable_object = dynamic_cast<Bindings::Transferable&>(*transferable);
- if (transferable_object.is_detached()) {
- return WebIDL::DataCloneError::create(*vm.current_realm(), "Value already transferred"_fly_string);
- }
- }
- // 3. Let dataHolder be memory[transferable].
- // IMPLEMENTATION DEFINED: We just create a data holder here, our memory holds indices into the SerializationRecord
- TransferDataHolder data_holder;
- // FIXME 4. If transferable has an [[ArrayBufferData]] internal slot, then:
- if (false) {
- }
- // 5. Otherwise:
- else {
- // 1. Assert: transferable is a platform object that is a transferable object.
- auto& transferable_object = dynamic_cast<Bindings::Transferable&>(*transferable);
- VERIFY(is<Bindings::PlatformObject>(*transferable));
- // 2. Let interfaceName be the identifier of the primary interface of transferable.
- auto interface_name = transferable_object.primary_interface();
- // 3. Set dataHolder.[[Type]] to interfaceName.
- data_holder.data.append(to_underlying(interface_name));
- // 4. Perform the appropriate transfer steps for the interface identified by interfaceName, given transferable and dataHolder.
- TRY(transferable_object.transfer_steps(data_holder));
- // 5. Set transferable.[[Detached]] to true.
- transferable_object.set_detached(true);
- }
- // 6. Append dataHolder to transferDataHolders.
- transfer_data_holders.append(move(data_holder));
- }
- // 6. Return { [[Serialized]]: serialized, [[TransferDataHolders]]: transferDataHolders }.
- return SerializedTransferRecord { .serialized = move(serialized), .transfer_data_holders = move(transfer_data_holders) };
- }
- static bool is_interface_exposed_on_target_realm(u8 name, JS::Realm& realm)
- {
- auto const& intrinsics = Bindings::host_defined_intrinsics(realm);
- switch (static_cast<TransferType>(name)) {
- case TransferType::MessagePort:
- return intrinsics.is_exposed("MessagePort"sv);
- break;
- default:
- dbgln("Unknown interface type for transfer: {}", name);
- break;
- }
- return false;
- }
- static WebIDL::ExceptionOr<JS::NonnullGCPtr<Bindings::PlatformObject>> create_transferred_value(TransferType name, JS::Realm& target_realm, TransferDataHolder& transfer_data_holder)
- {
- switch (name) {
- case TransferType::MessagePort: {
- auto message_port = HTML::MessagePort::create(target_realm);
- TRY(message_port->transfer_receiving_steps(transfer_data_holder));
- return message_port;
- }
- }
- VERIFY_NOT_REACHED();
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserializewithtransfer
- WebIDL::ExceptionOr<DeserializedTransferRecord> structured_deserialize_with_transfer(JS::VM& vm, SerializedTransferRecord& serialize_with_transfer_result)
- {
- auto& target_realm = *vm.current_realm();
- // 1. Let memory be an empty map.
- auto memory = DeserializationMemory(vm.heap());
- // 2. Let transferredValues be a new empty List.
- Vector<JS::Handle<JS::Object>> transferred_values;
- // 3. For each transferDataHolder of serializeWithTransferResult.[[TransferDataHolders]]:
- for (auto& transfer_data_holder : serialize_with_transfer_result.transfer_data_holders) {
- // 1. Let value be an uninitialized value.
- JS::Value value;
- // FIXME: 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm
- // whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], and
- // whose [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]].
- // NOTE: In cases where the original memory occupied by [[ArrayBufferData]] is accessible during the deserialization,
- // this step is unlikely to throw an exception, as no new memory needs to be allocated: the memory occupied by
- // [[ArrayBufferData]] is instead just getting transferred into the new ArrayBuffer. This could be true, for example,
- // when both the source and target realms are in the same process.
- if (false) {
- }
- // FIXME: 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object
- // in targetRealm whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], whose
- // [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]], and whose
- // [[ArrayBufferMaxByteLength]] internal slot value is transferDataHolder.[[ArrayBufferMaxByteLength]].
- // NOTE: For the same reason as the previous step, this step is also unlikely to throw an exception.
- else if (false) {
- }
- // 4. Otherwise:
- else {
- // 1. Let interfaceName be transferDataHolder.[[Type]].
- u8 const interface_name = transfer_data_holder.data.take_first();
- // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException.
- if (!is_interface_exposed_on_target_realm(interface_name, target_realm))
- return WebIDL::DataCloneError::create(target_realm, "Unknown type transferred"_fly_string);
- // 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm.
- // 4. Perform the appropriate transfer-receiving steps for the interface identified by interfaceName given transferDataHolder and value.
- value = TRY(create_transferred_value(static_cast<TransferType>(interface_name), target_realm, transfer_data_holder));
- }
- // 5. Set memory[transferDataHolder] to value.
- memory.append(value);
- // 6. Append value to transferredValues.
- transferred_values.append(JS::make_handle(value.as_object()));
- }
- // 4. Let deserialized be ? StructuredDeserialize(serializeWithTransferResult.[[Serialized]], targetRealm, memory).
- auto deserialized = TRY(structured_deserialize(vm, serialize_with_transfer_result.serialized, target_realm, memory));
- // 5. Return { [[Deserialized]]: deserialized, [[TransferredValues]]: transferredValues }.
- return DeserializedTransferRecord { .deserialized = move(deserialized), .transferred_values = move(transferred_values) };
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
- WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value value)
- {
- // 1. Return ? StructuredSerializeInternal(value, false).
- SerializationMemory memory = {};
- return structured_serialize_internal(vm, value, false, memory);
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
- WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value value)
- {
- // 1. Return ? StructuredSerializeInternal(value, true).
- SerializationMemory memory = {};
- return structured_serialize_internal(vm, value, true, memory);
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
- WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, SerializationMemory& memory)
- {
- // 1. If memory was not supplied, let memory be an empty map.
- // IMPLEMENTATION DEFINED: We move this requirement up to the callers to make recursion easier
- Serializer serializer(vm, memory, for_storage);
- return serializer.serialize(value);
- }
- // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
- WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<DeserializationMemory> memory)
- {
- if (!memory.has_value())
- memory = DeserializationMemory { vm.heap() };
- // IMPLEMENTATION DEFINED: We need to make sure there's an execution context for target_realm on the stack before constructing these JS objects
- auto& target_settings = Bindings::host_defined_environment_settings_object(target_realm);
- target_settings.prepare_to_run_script();
- Deserializer deserializer(vm, target_realm, serialized.span(), *memory);
- auto result = deserializer.deserialize();
- target_settings.clean_up_after_running_script();
- return result;
- }
- }
- namespace IPC {
- template<>
- ErrorOr<void> encode(Encoder& encoder, ::Web::HTML::TransferDataHolder const& data_holder)
- {
- TRY(encoder.encode(data_holder.data));
- TRY(encoder.encode(data_holder.fds));
- return {};
- }
- template<>
- ErrorOr<void> encode(Encoder& encoder, ::Web::HTML::SerializedTransferRecord const& record)
- {
- TRY(encoder.encode(record.serialized));
- TRY(encoder.encode(record.transfer_data_holders));
- return {};
- }
- template<>
- ErrorOr<::Web::HTML::TransferDataHolder> decode(Decoder& decoder)
- {
- auto data = TRY(decoder.decode<Vector<u8>>());
- auto fds = TRY(decoder.decode<Vector<IPC::File>>());
- return ::Web::HTML::TransferDataHolder { move(data), move(fds) };
- }
- template<>
- ErrorOr<::Web::HTML::SerializedTransferRecord> decode(Decoder& decoder)
- {
- auto serialized = TRY(decoder.decode<Vector<u32>>());
- auto transfer_data_holders = TRY(decoder.decode<Vector<::Web::HTML::TransferDataHolder>>());
- return ::Web::HTML::SerializedTransferRecord { move(serialized), move(transfer_data_holders) };
- }
- }
|