diff options
Diffstat (limited to 'Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp')
-rw-r--r-- | Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp new file mode 100644 index 0000000000..82d0d6cb79 --- /dev/null +++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev> + * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/HashTable.h> +#include <AK/Vector.h> +#include <LibJS/Forward.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, + + // Following two u32s are the double value + NumberPrimitive, + + // TODO: Define many more types + + // This tag or higher are understood to be errors + ValueTagMax, +}; + +// 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) + : m_vm(vm) + { + } + + void serialize(JS::Value value) + { + if (value.is_number()) { + m_serialized.append(ValueTag::NumberPrimitive); + double number = value.as_double(); + m_serialized.append(bit_cast<u32*>(&number), 2); + } else { + // TODO: Define many more types + m_error = "Unsupported type"sv; + } + } + + WebIDL::ExceptionOr<Vector<u32>> result() + { + if (m_error.is_null()) + return m_serialized; + return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error)); + } + +private: + AK::StringView m_error; + SerializationMemory m_memory; // JS value -> index + SerializationRecord m_serialized; + JS::VM& m_vm; +}; + +class Deserializer { +public: + Deserializer(JS::VM& vm, JS::Realm& target_realm, SerializationRecord const& v) + : m_vm(vm) + , m_vector(v) + , m_memory(target_realm.heap()) + { + } + + void deserialize() + { + // First pass: fill up the memory with new values + u32 position = 0; + while (position < m_vector.size()) { + switch (m_vector[position++]) { + case ValueTag::NumberPrimitive: { + u32 bits[2]; + bits[0] = m_vector[position++]; + bits[1] = m_vector[position++]; + double value = *bit_cast<double*>(&bits); + m_memory.append(JS::Value(value)); + break; + } + default: + m_error = "Unsupported type"sv; + return; + } + } + + // Second pass: Update the objects to point to other objects in memory + } + + WebIDL::ExceptionOr<JS::Value> result() + { + if (m_error.is_null()) + return m_memory[0]; + return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error)); + } + +private: + JS::VM& m_vm; + SerializationRecord const& m_vector; + JS::MarkedVector<JS::Value> m_memory; // Index -> JS value + StringView m_error; +}; + +// 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). + return structured_serialize_internal(vm, value, false, {}); +} + +// 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). + return structured_serialize_internal(vm, value, true, {}); +} + +// 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, Optional<SerializationMemory> memory) +{ + // FIXME: Do the spec steps + (void)for_storage; + (void)memory; + + Serializer serializer(vm); + serializer.serialize(value); + return serializer.result(); // TODO: Avoid several copies of vector +} + +// 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<SerializationMemory> memory) +{ + // FIXME: Do the spec steps + (void)memory; + + Deserializer deserializer(vm, target_realm, serialized); + deserializer.deserialize(); + return deserializer.result(); +} + +} |