diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp index 556c009b5c..592328580b 100644 --- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -76,13 +76,27 @@ enum ValueTag { class Serializer { public: - Serializer(JS::VM& vm) + Serializer(JS::VM& vm, SerializationMemory& memory) : m_vm(vm) + , m_memory(memory) { } - WebIDL::ExceptionOr serialize(JS::Value value) + // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal + WebIDL::ExceptionOr serialize(JS::Value value) { + // 2. If memory[value] exists, then return memory[value]. + // FIXME: Do callers actually need a copy? or can they get away with a range? + if (m_memory.contains(value)) { + auto range = m_memory.get(value).value(); + return m_serialized.span().slice(range.start, range.end); + } + + // 3. Let deep be false. + [[maybe_unused]] bool 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()) { @@ -101,46 +115,70 @@ public: } else if (value.is_string()) { m_serialized.append(ValueTag::StringPrimitive); TRY(serialize_string(m_serialized, value.as_string())); - } else if (value.is_object() && is(value.as_object())) { + } else { + return_primitive_type = false; + } + + if (return_primitive_type) + return m_serialized; + + // FIXME 5. If Type(value) is Symbol, then throw a "DataCloneError" DOMException. + + // 6. Let serialized be an uninitialized value. + // NOTE: We use the range of the soon-to-be-serialized value in our serialized data buffer + // to be the `serialized` spec value. + auto serialized_start = m_serialized.size(); + + // 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }. + if (value.is_object() && is(value.as_object())) { m_serialized.append(ValueTag::BooleanObject); auto& boolean_object = static_cast(value.as_object()); m_serialized.append(bit_cast(static_cast(boolean_object.boolean()))); - } else if (value.is_object() && is(value.as_object())) { + } + + // 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }. + else if (value.is_object() && is(value.as_object())) { m_serialized.append(ValueTag::NumberObject); auto& number_object = static_cast(value.as_object()); double const number = number_object.number(); m_serialized.append(bit_cast(&number), 2); - } else if (value.is_object() && is(value.as_object())) { + } + + // FIXME 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }. + + // 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }. + else if (value.is_object() && is(value.as_object())) { m_serialized.append(ValueTag::StringObject); auto& string_object = static_cast(value.as_object()); TRY(serialize_string(m_serialized, string_object.primitive_string())); - } else if (value.is_object() && is(value.as_object())) { + } + + // 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }. + else if (value.is_object() && is(value.as_object())) { m_serialized.append(ValueTag::DateObject); auto& date_object = static_cast(value.as_object()); double const date_value = date_object.date_value(); m_serialized.append(bit_cast(&date_value), 2); - } else { - // TODO: Define many more types - m_error = "Unsupported type"sv; } + // 12 - 24: FIXME: Serialize other data types + else { + return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"sv)); + } + + // 25. Set memory[value] to serialized. + auto serialized_end = m_serialized.size(); + m_memory.set(make_handle(value), { serialized_start, serialized_end }); + // Second pass: Update the objects to point to other objects in memory - return {}; - } - - WebIDL::ExceptionOr> result() - { - if (m_error.is_null()) - return m_serialized; - return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error)); + return m_serialized; } private: - AK::StringView m_error; - SerializationMemory m_memory; // JS value -> index - SerializationRecord m_serialized; JS::VM& m_vm; + SerializationMemory& m_memory; // JS value -> index + SerializationRecord m_serialized; WebIDL::ExceptionOr serialize_string(Vector& vector, String const& string) { @@ -259,7 +297,7 @@ public: { if (m_error.is_null()) return m_memory[0]; - return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error)); + return WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error); } private: @@ -320,13 +358,14 @@ WebIDL::ExceptionOr structured_serialize_for_storage(JS::VM // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal WebIDL::ExceptionOr structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional memory) { - // FIXME: Do the spec steps (void)for_storage; - (void)memory; - Serializer serializer(vm); - TRY(serializer.serialize(value)); - return serializer.result(); // TODO: Avoid several copies of vector + // 1. If memory was not supplied, let memory be an empty map. + if (!memory.has_value()) + memory = SerializationMemory {}; + + Serializer serializer(vm, *memory); + return serializer.serialize(value); } // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h index 2c27800650..fa111f2d50 100644 --- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.h @@ -21,7 +21,11 @@ namespace Web::HTML { using SerializationRecord = Vector; -using SerializationMemory = HashMap, u32>; +struct SerializationRange { + u64 start = 0; + u64 end = 0; +}; +using SerializationMemory = HashMap, SerializationRange>; WebIDL::ExceptionOr structured_serialize(JS::VM& vm, JS::Value); WebIDL::ExceptionOr structured_serialize_for_storage(JS::VM& vm, JS::Value);