Serenity Operating System
1/*
2 * Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
3 * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/HashTable.h>
9#include <AK/Vector.h>
10#include <LibJS/Forward.h>
11#include <LibWeb/HTML/StructuredSerialize.h>
12#include <LibWeb/WebIDL/ExceptionOr.h>
13
14namespace Web::HTML {
15
16// Binary format:
17// A list of adjacent shallow values, which may contain references to other
18// values (noted by their position in the list, one value following another).
19// This list represents the "memory" in the StructuredSerialize algorithm.
20// The first item in the list is the root, i.e., the value of everything.
21// The format is generally u32-aligned (hence this leaking out into the type)
22// Each value has a length based on its type, as defined below.
23//
24// (Should more redundancy be added, e.g., for lengths/positions of values?)
25
26enum ValueTag {
27 // Unused, for ease of catching bugs
28 Empty,
29
30 // Following two u32s are the double value
31 NumberPrimitive,
32
33 // TODO: Define many more types
34
35 // This tag or higher are understood to be errors
36 ValueTagMax,
37};
38
39// Serializing and deserializing are each two passes:
40// 1. Fill up the memory with all the values, but without translating references
41// 2. Translate all the references into the appropriate form
42
43class Serializer {
44public:
45 Serializer(JS::VM& vm)
46 : m_vm(vm)
47 {
48 }
49
50 void serialize(JS::Value value)
51 {
52 if (value.is_number()) {
53 m_serialized.append(ValueTag::NumberPrimitive);
54 double number = value.as_double();
55 m_serialized.append(bit_cast<u32*>(&number), 2);
56 } else {
57 // TODO: Define many more types
58 m_error = "Unsupported type"sv;
59 }
60 }
61
62 WebIDL::ExceptionOr<Vector<u32>> result()
63 {
64 if (m_error.is_null())
65 return m_serialized;
66 return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
67 }
68
69private:
70 AK::StringView m_error;
71 SerializationMemory m_memory; // JS value -> index
72 SerializationRecord m_serialized;
73 JS::VM& m_vm;
74};
75
76class Deserializer {
77public:
78 Deserializer(JS::VM& vm, JS::Realm& target_realm, SerializationRecord const& v)
79 : m_vm(vm)
80 , m_vector(v)
81 , m_memory(target_realm.heap())
82 {
83 }
84
85 void deserialize()
86 {
87 // First pass: fill up the memory with new values
88 u32 position = 0;
89 while (position < m_vector.size()) {
90 switch (m_vector[position++]) {
91 case ValueTag::NumberPrimitive: {
92 u32 bits[2];
93 bits[0] = m_vector[position++];
94 bits[1] = m_vector[position++];
95 double value = *bit_cast<double*>(&bits);
96 m_memory.append(JS::Value(value));
97 break;
98 }
99 default:
100 m_error = "Unsupported type"sv;
101 return;
102 }
103 }
104
105 // Second pass: Update the objects to point to other objects in memory
106 }
107
108 WebIDL::ExceptionOr<JS::Value> result()
109 {
110 if (m_error.is_null())
111 return m_memory[0];
112 return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
113 }
114
115private:
116 JS::VM& m_vm;
117 SerializationRecord const& m_vector;
118 JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
119 StringView m_error;
120};
121
122// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
123WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value value)
124{
125 // 1. Return ? StructuredSerializeInternal(value, false).
126 return structured_serialize_internal(vm, value, false, {});
127}
128
129// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
130WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value value)
131{
132 // 1. Return ? StructuredSerializeInternal(value, true).
133 return structured_serialize_internal(vm, value, true, {});
134}
135
136// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
137WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory)
138{
139 // FIXME: Do the spec steps
140 (void)for_storage;
141 (void)memory;
142
143 Serializer serializer(vm);
144 serializer.serialize(value);
145 return serializer.result(); // TODO: Avoid several copies of vector
146}
147
148// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
149WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory> memory)
150{
151 // FIXME: Do the spec steps
152 (void)memory;
153
154 Deserializer deserializer(vm, target_realm, serialized);
155 deserializer.deserialize();
156 return deserializer.result();
157}
158
159}