diff --git a/Tests/LibWeb/Text/expected/HTML/StructuredClone-array-buffer-views.txt b/Tests/LibWeb/Text/expected/HTML/StructuredClone-array-buffer-views.txt
new file mode 100644
index 0000000000..ecec036968
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/HTML/StructuredClone-array-buffer-views.txt
@@ -0,0 +1,12 @@
+Uint8Array 30,40,50,60
+Uint8ClampedArray 30,40,50,60
+Uint16Array 30,40,50,60
+Uint32Array 30,40,50,60
+Int8Array 30,40,50,60
+Int16Array 30,40,50,60
+Int32Array 30,40,50,60
+Float32Array 30,40,50,60
+Float64Array 30,40,50,60
+BigUint64Array 30,40,50,60
+BigInt64Array 30,40,50,60
+DataView [object DataView]
diff --git a/Tests/LibWeb/Text/input/HTML/StructuredClone-array-buffer-views.html b/Tests/LibWeb/Text/input/HTML/StructuredClone-array-buffer-views.html
new file mode 100644
index 0000000000..7615a1789b
--- /dev/null
+++ b/Tests/LibWeb/Text/input/HTML/StructuredClone-array-buffer-views.html
@@ -0,0 +1,51 @@
+
+
diff --git a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
index 4cf45babc1..610a96ce5a 100644
--- a/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
+++ b/Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
@@ -14,11 +14,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -81,6 +83,8 @@ enum ValueTag {
ArrayBuffer,
+ ArrayBufferView,
+
// TODO: Define many more types
// This tag or higher are understood to be errors
@@ -204,7 +208,14 @@ public:
TRY(serialize_array_buffer(m_serialized, static_cast(value.as_object())));
}
- // 14 - 24: FIXME: Serialize other data types
+ // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
+ else if (value.is_object() && is(value.as_object())) {
+ TRY(serialize_viewed_array_buffer(m_serialized, static_cast(value.as_object())));
+ } else if (value.is_object() && is(value.as_object())) {
+ TRY(serialize_viewed_array_buffer(m_serialized, static_cast(value.as_object())));
+ }
+
+ // 15 - 24: FIXME: Serialize other data types
else {
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), "Unsupported type"_fly_string));
}
@@ -245,6 +256,11 @@ private:
return {};
}
+ WebIDL::ExceptionOr serialize_string(Vector& vector, DeprecatedFlyString const& string)
+ {
+ return serialize_bytes(vector, string.view().bytes());
+ }
+
WebIDL::ExceptionOr serialize_string(Vector& vector, String const& string)
{
return serialize_bytes(vector, { string.code_points().bytes(), string.code_points().byte_length() });
@@ -307,6 +323,54 @@ private:
}
return {};
}
+
+ template ViewType>
+ WebIDL::ExceptionOr serialize_viewed_array_buffer(Vector& vector, ViewType const& view)
+ {
+ // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then:
+
+ // FIXME: 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException.
+
+ // 2. Let buffer be the value of value's [[ViewedArrayBuffer]] internal slot.
+ auto* buffer = view.viewed_array_buffer();
+
+ // 3. Let bufferSerialized be ? StructuredSerializeInternal(buffer, forStorage, memory).
+ auto buffer_serialized = TRY(structured_serialize_internal(m_vm, JS::Value(buffer), m_for_storage, m_memory));
+
+ // 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer".
+ // NOTE: We currently only implement this for ArrayBuffer
+ VERIFY(buffer_serialized[0] == ValueTag::ArrayBuffer);
+
+ // 5. If value has a [[DataView]] internal slot, then set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView",
+ // [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }.
+ if constexpr (IsSame) {
+ vector.append(ValueTag::ArrayBufferView);
+ u64 const serialized_buffer_size = buffer_serialized.size();
+ vector.append(bit_cast(&serialized_buffer_size), 2);
+ vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
+ TRY(serialize_string(vector, "DataView"_string)); // [[Constructor]]
+ vector.append(view.byte_length());
+ vector.append(view.byte_offset());
+ }
+
+ // 6. Otherwise:
+ else {
+ // 1. Assert: value has a [[TypedArrayName]] internal slot.
+ // NOTE: Handled by constexpr check and template constraints
+ // 2. Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]],
+ // [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]],
+ // [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }.
+ vector.append(ValueTag::ArrayBufferView);
+ u64 const serialized_buffer_size = buffer_serialized.size();
+ vector.append(bit_cast(&serialized_buffer_size), 2);
+ vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]]
+ TRY(serialize_string(vector, view.element_name())); // [[Constructor]]
+ vector.append(view.byte_length());
+ vector.append(view.byte_offset());
+ vector.append(view.array_length());
+ }
+ return {};
+ }
};
class Deserializer {
@@ -404,6 +468,39 @@ public:
m_memory.append(JS::ArrayBuffer::create(*realm, move(bytes)));
break;
}
+ case ValueTag::ArrayBufferView: {
+ auto* realm = m_vm.current_realm();
+ u32 size_bits[2];
+ size_bits[0] = m_vector[position++];
+ size_bits[1] = m_vector[position++];
+ u64 const array_buffer_size = *bit_cast(&size_bits);
+ auto array_buffer_value = TRY(structured_deserialize_impl(m_vm, m_vector.slice(position, array_buffer_size), *realm, m_serialization_memory));
+ auto& array_buffer = verify_cast(array_buffer_value.as_object());
+ position += array_buffer_size;
+ auto constructor_name = TRY(deserialize_string(m_vm, m_vector, position));
+ u32 byte_length = m_vector[position++];
+ u32 byte_offset = m_vector[position++];
+
+ if (constructor_name == "DataView"sv) {
+ m_memory.append(JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset));
+ } else {
+ u32 array_length = m_vector[position++];
+ JS::GCPtr typed_array_ptr;
+#define CREATE_TYPED_ARRAY(ClassName) \
+ if (constructor_name == #ClassName##sv) \
+ typed_array_ptr = JS::ClassName::create(*realm, array_length, array_buffer);
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
+ CREATE_TYPED_ARRAY(ClassName)
+ JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
+#undef CREATE_TYPED_ARRAY
+ VERIFY(typed_array_ptr != nullptr); // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer?
+ typed_array_ptr->set_byte_length(byte_length);
+ typed_array_ptr->set_byte_offset(byte_offset);
+ m_memory.append(typed_array_ptr);
+ }
+ break;
+ }
default:
m_error = "Unsupported type"_fly_string;
break;