diff --git a/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt b/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt
index 3955abadde..01f1f25aa5 100644
--- a/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt
+++ b/Tests/LibWeb/Text/expected/HTML/StructuredClone-object-primitives.txt
@@ -6,4 +6,5 @@ This is a String object
9007199254740991
1692748800000
/abc/gimsuy
+[object ArrayBuffer]
ERROR: DataCloneError: Cannot serialize Symbol
\ No newline at end of file
diff --git a/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html b/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html
index 0ba2a843e5..ae3dd770d8 100644
--- a/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html
+++ b/Tests/LibWeb/Text/input/HTML/StructuredClone-object-primitives.html
@@ -10,6 +10,21 @@
println(structuredClone(Date.UTC(2023, 7, 23)));
println(structuredClone(/abc/gimsuy));
+ {
+ let arrayBuffer = new ArrayBuffer(6);
+ for (let i = 0; i < arrayBuffer.byteLength; ++i) {
+ arrayBuffer[i] = i;
+ }
+ let arrayClone = structuredClone(arrayBuffer);
+ for (let i = 0; i < arrayBuffer.byteLength; ++i) {
+ if (arrayBuffer[i] !== arrayBuffer[i]) {
+ println("FAILED");
+ }
+ }
+ // FIXME: This should print something like ArrayBuffer { byteLength: 6 }
+ println(arrayClone);
+ }
+
try {
structuredClone(Symbol("foo"));
println("FAILED")
diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
index 0f8525492b..c390d3e76e 100644
--- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
+++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -70,6 +71,14 @@ enum ValueTag {
RegExpObject,
+ GrowableSharedArrayBuffer,
+
+ SharedArrayBuffer,
+
+ ResizeableArrayBuffer,
+
+ ArrayBuffer,
+
// TODO: Define many more types
// This tag or higher are understood to be errors
@@ -82,9 +91,10 @@ enum ValueTag {
class Serializer {
public:
- Serializer(JS::VM& vm, SerializationMemory& memory)
+ Serializer(JS::VM& vm, SerializationMemory& memory, bool for_storage)
: m_vm(vm)
, m_memory(memory)
+ , m_for_storage(for_storage)
{
}
@@ -187,7 +197,12 @@ public:
TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, String::from_deprecated_string(regexp_object.flags()))));
}
- // 13 - 24: FIXME: Serialize other data types
+ // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
+ else if (value.is_object() && is(value.as_object())) {
+ TRY(serialize_array_buffer(m_serialized, static_cast(value.as_object())));
+ }
+
+ // 14 - 24: FIXME: Serialize other data types
else {
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string));
}
@@ -205,6 +220,7 @@ private:
JS::VM& m_vm;
SerializationMemory& m_memory; // JS value -> index
SerializationRecord m_serialized;
+ bool m_for_storage { false };
WebIDL::ExceptionOr serialize_bytes(Vector& vector, ReadonlyBytes bytes)
{
@@ -238,6 +254,57 @@ private:
TRY(serialize_string(vector, string));
return {};
}
+
+ WebIDL::ExceptionOr serialize_array_buffer(Vector& vector, JS::ArrayBuffer const& array_buffer)
+ {
+ // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then:
+
+ // FIXME: 1. If IsSharedArrayBuffer(value) is true, then:
+ if (false) {
+ // 1. If the current settings object's cross-origin isolated capability is false, then throw a "DataCloneError" DOMException.
+ // NOTE: This check is only needed when serializing (and not when deserializing) as the cross-origin isolated capability cannot change
+ // over time and a SharedArrayBuffer cannot leave an agent cluster.
+ if (current_settings_object().cross_origin_isolated_capability() == CanUseCrossOriginIsolatedAPIs::No)
+ return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize SharedArrayBuffer when cross-origin isolated"_fly_string);
+
+ // 2. If forStorage is true, then throw a "DataCloneError" DOMException.
+ if (m_for_storage)
+ return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize SharedArrayBuffer for storage"_fly_string);
+
+ // FIXME: 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer",
+ // [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]],
+ // [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
+ // FIXME: 4. Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]],
+ // [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]], [[AgentCluster]]: the surrounding agent's agent cluster }.
+ }
+ // 2. Otherwise:
+ else {
+ // 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException.
+ if (array_buffer.is_detached())
+ return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize detached ArrayBuffer"_fly_string);
+
+ // 2. Let size be value.[[ArrayBufferByteLength]].
+ auto size = array_buffer.byte_length();
+
+ // 3. Let dataCopy be ? CreateByteDataBlock(size).
+ // NOTE: This can throw a RangeError exception upon allocation failure.
+ auto data_copy = TRY(JS::create_byte_data_block(m_vm, size));
+
+ // 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size).
+ JS::copy_data_block_bytes(data_copy, 0, array_buffer.buffer(), 0, size);
+
+ // FIXME: 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer",
+ // [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }.
+ if (false) {
+ }
+ // 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }.
+ else {
+ vector.append(ValueTag::ArrayBuffer);
+ TRY(serialize_bytes(vector, data_copy.bytes()));
+ }
+ }
+ return {};
+ }
};
class Deserializer {
@@ -327,6 +394,12 @@ public:
m_memory.append(TRY(JS::regexp_create(m_vm, move(pattern), move(flags))));
break;
}
+ case ValueTag::ArrayBuffer: {
+ auto* realm = m_vm.current_realm();
+ auto bytes = TRY(deserialize_bytes(m_vm, m_vector, position));
+ m_memory.append(JS::ArrayBuffer::create(*realm, move(bytes)));
+ break;
+ }
default:
m_error = "Unsupported type"_fly_string;
break;
@@ -404,13 +477,11 @@ 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)
{
- (void)for_storage;
-
// 1. If memory was not supplied, let memory be an empty map.
if (!memory.has_value())
memory = SerializationMemory {};
- Serializer serializer(vm, *memory);
+ Serializer serializer(vm, *memory, for_storage);
return serializer.serialize(value);
}