diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
index 3fbb980600..afa32edc54 100644
--- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
+++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
@@ -1,14 +1,19 @@
/*
* Copyright (c) 2022, Daniel Ehrenberg
* Copyright (c) 2022, Andrew Kaster
+ * Copyright (c) 2023, Kenneth Myhra
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include
+#include
#include
#include
+#include
+#include
#include
+#include
#include
#include
@@ -25,12 +30,28 @@ namespace Web::HTML {
// (Should more redundancy be added, e.g., for lengths/positions of values?)
enum ValueTag {
- // Unused, for ease of catching bugs
+ // Unused, for ease of catching bugs.
Empty,
- // Following two u32s are the double value
+ // 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,
+
// TODO: Define many more types
// This tag or higher are understood to be errors
@@ -48,16 +69,34 @@ public:
{
}
- void serialize(JS::Value value)
+ WebIDL::ExceptionOr serialize(JS::Value value)
{
- if (value.is_number()) {
+ 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);
+ m_serialized.append(static_cast(value.as_bool()));
+ } else if (value.is_number()) {
m_serialized.append(ValueTag::NumberPrimitive);
double number = value.as_double();
m_serialized.append(bit_cast(&number), 2);
+ } else if (value.is_bigint()) {
+ m_serialized.append(ValueTag::BigIntPrimitive);
+ auto& val = value.as_bigint();
+ TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, val.to_string())));
+ } else if (value.is_string()) {
+ m_serialized.append(ValueTag::StringPrimitive);
+ TRY(serialize_string(m_serialized, value.as_string()));
} else {
// TODO: Define many more types
m_error = "Unsupported type"sv;
}
+
+ // Second pass: Update the objects to point to other objects in memory
+
+ return {};
}
WebIDL::ExceptionOr> result()
@@ -72,6 +111,27 @@ private:
SerializationMemory m_memory; // JS value -> index
SerializationRecord m_serialized;
JS::VM& m_vm;
+
+ WebIDL::ExceptionOr serialize_string(Vector& vector, String const& string)
+ {
+ u64 const size = string.code_points().length();
+ // Append size of the string to the serialized structure.
+ TRY_OR_THROW_OOM(m_vm, vector.try_append(bit_cast(&size), 2));
+ for (auto code_point : string.code_points()) {
+ // Append each code point to the serialized structure.
+ TRY_OR_THROW_OOM(m_vm, vector.try_append(code_point));
+ }
+ return {};
+ }
+
+ WebIDL::ExceptionOr serialize_string(Vector& vector, JS::PrimitiveString const& primitive_string)
+ {
+ auto string = TRY(Bindings::throw_dom_exception_if_needed(m_vm, [&primitive_string]() {
+ return primitive_string.utf8_string();
+ }));
+ TRY(serialize_string(vector, string));
+ return {};
+ }
};
class Deserializer {
@@ -83,12 +143,24 @@ public:
{
}
- void deserialize()
+ WebIDL::ExceptionOr deserialize()
{
// First pass: fill up the memory with new values
u32 position = 0;
while (position < m_vector.size()) {
switch (m_vector[position++]) {
+ case ValueTag::UndefinedPrimitive: {
+ m_memory.append(JS::js_undefined());
+ break;
+ }
+ case ValueTag::NullPrimitive: {
+ m_memory.append(JS::js_null());
+ break;
+ }
+ case ValueTag::BooleanPrimitive: {
+ m_memory.append(JS::Value(static_cast(m_vector[position++])));
+ break;
+ }
case ValueTag::NumberPrimitive: {
u32 bits[2];
bits[0] = m_vector[position++];
@@ -97,13 +169,22 @@ public:
m_memory.append(JS::Value(value));
break;
}
+ case ValueTag::BigIntPrimitive: {
+ auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
+ m_memory.append(JS::Value { big_int });
+ break;
+ }
+ case ValueTag::StringPrimitive: {
+ auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
+ m_memory.append(JS::Value { string });
+ break;
+ }
default:
m_error = "Unsupported type"sv;
- return;
+ break;
}
}
-
- // Second pass: Update the objects to point to other objects in memory
+ return {};
}
WebIDL::ExceptionOr result()
@@ -118,6 +199,33 @@ private:
SerializationRecord const& m_vector;
JS::MarkedVector m_memory; // Index -> JS value
StringView m_error;
+
+ static WebIDL::ExceptionOr> deserialize_string_primitive(JS::VM& vm, Vector const& vector, u32& position)
+ {
+ u32 size_bits[2];
+ size_bits[0] = vector[position++];
+ size_bits[1] = vector[position++];
+ u64 const size = *bit_cast(&size_bits);
+
+ u8 bits[size];
+ for (u32 i = 0; i < size; ++i)
+ bits[i] = vector[position++];
+
+ ReadonlyBytes const bytes = { bits, size };
+
+ return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() {
+ return JS::PrimitiveString::create(vm, StringView { bytes });
+ }));
+ }
+
+ static WebIDL::ExceptionOr> deserialize_big_int_primitive(JS::VM& vm, Vector const& vector, u32& 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();
+ }));
+ return JS::BigInt::create(vm, ::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1)));
+ }
};
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
@@ -142,7 +250,7 @@ WebIDL::ExceptionOr structured_serialize_internal(JS::VM& v
(void)memory;
Serializer serializer(vm);
- serializer.serialize(value);
+ TRY(serializer.serialize(value));
return serializer.result(); // TODO: Avoid several copies of vector
}
@@ -153,7 +261,7 @@ WebIDL::ExceptionOr structured_deserialize(JS::VM& vm, SerializationR
(void)memory;
Deserializer deserializer(vm, target_realm, serialized);
- deserializer.deserialize();
+ TRY(deserializer.deserialize());
return deserializer.result();
}