1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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();
}
}
|