diff --git a/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Libraries/LibWeb/HTML/StructuredSerialize.cpp index 8ee29134f9f..765babe642d 100644 --- a/Libraries/LibWeb/HTML/StructuredSerialize.cpp +++ b/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -567,8 +567,8 @@ WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector { // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then: - // FIXME: 1. If IsSharedArrayBuffer(value) is true, then: - if (false) { + // 1. If IsSharedArrayBuffer(value) is true, then: + if (array_buffer.is_shared_array_buffer()) { // 1. If the current principal 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. @@ -579,12 +579,23 @@ WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector if (for_storage) return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot serialize SharedArrayBuffer for storage"_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 }. + if (!array_buffer.is_fixed_length()) { + // 3. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "GrowableSharedArrayBuffer", + // [[ArrayBufferData]]: value.[[ArrayBufferData]], [[ArrayBufferByteLengthData]]: value.[[ArrayBufferByteLengthData]], + // [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]], + // FIXME: [[AgentCluster]]: the surrounding agent's agent cluster }. + serialize_enum(vector, ValueTag::GrowableSharedArrayBuffer); + TRY(serialize_bytes(vm, vector, array_buffer.buffer().bytes())); + serialize_primitive_type(vector, array_buffer.max_byte_length()); + } else { + // 4. Otherwise, set serialized to { [[Type]]: "SharedArrayBuffer", [[ArrayBufferData]]: value.[[ArrayBufferData]], + // [[ArrayBufferByteLength]]: value.[[ArrayBufferByteLength]], + // FIXME: [[AgentCluster]]: the surrounding agent's agent cluster }. + serialize_enum(vector, ValueTag::SharedArrayBuffer); + TRY(serialize_bytes(vm, vector, array_buffer.buffer().bytes())); + } } + // 2. Otherwise: else { // 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException. @@ -601,9 +612,12 @@ WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector // 4. Perform CopyDataBlockBytes(dataCopy, 0, value.[[ArrayBufferData]], 0, size). JS::copy_data_block_bytes(data_copy.buffer(), 0, array_buffer.buffer(), 0, size); - // FIXME: 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer", + // 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer", // [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }. - if (false) { + if (!array_buffer.is_fixed_length()) { + serialize_enum(vector, ValueTag::ResizeableArrayBuffer); + TRY(serialize_bytes(vm, vector, data_copy.buffer().bytes())); + serialize_primitive_type(vector, array_buffer.max_byte_length()); } // 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }. else { @@ -643,10 +657,13 @@ WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& auto buffer_serialized = TRY(structured_serialize_internal(vm, buffer, for_storage, memory)); // 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer". - // NOTE: We currently only implement this for ArrayBuffer // NOTE: Object reference + memory check is required when ArrayBuffer is transfered. auto tag = buffer_serialized[0]; - VERIFY(tag == ValueTag::ArrayBuffer || (tag == ValueTag::ObjectReference && memory.contains(buffer))); + VERIFY(tag == ValueTag::ArrayBuffer + || tag == ValueTag::ResizeableArrayBuffer + || tag == ValueTag::SharedArrayBuffer + || tag == ValueTag::GrowableSharedArrayBuffer + || (tag == ValueTag::ObjectReference && memory.contains(buffer))); // 5. If value has a [[DataView]] internal slot, then set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: "DataView", // [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], [[ByteOffset]]: value.[[ByteOffset]] }. @@ -776,8 +793,39 @@ public: value = TRY(deserialize_reg_exp_object(*m_vm.current_realm(), m_serialized, m_position)); break; } - // FIXME: 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then: - // FIXME: 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then: + // 12. Otherwise, if serialized.[[Type]] is "SharedArrayBuffer", then: + case ValueTag::SharedArrayBuffer: { + // FIXME: 1. If targetRealm's corresponding agent cluster is not serialized.[[AgentCluster]], then throw a "DataCloneError" DOMException. + // 2. Otherwise, set value to a new SharedArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]] + // and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]]. + auto* realm = m_vm.current_realm(); + auto bytes_or_error = deserialize_bytes(m_vm, m_serialized, m_position); + if (bytes_or_error.is_error()) + return WebIDL::DataCloneError::create(*realm, "out of memory"_string); + auto bytes = bytes_or_error.release_value(); + JS::ArrayBuffer* buffer = TRY(JS::allocate_shared_array_buffer(m_vm, realm->intrinsics().array_buffer_constructor(), bytes.size())); + bytes.span().copy_to(buffer->buffer().span()); + value = buffer; + break; + } + // 13. Otherwise, if serialized.[[Type]] is "GrowableSharedArrayBuffer", then: + case ValueTag::GrowableSharedArrayBuffer: { + // FIXME: 1. If targetRealm's corresponding agent cluster is not serialized.[[AgentCluster]], then throw a "DataCloneError" DOMException. + // 2. Otherwise, set value to a new SharedArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], + // whose [[ArrayBufferByteLengthData]] internal slot value is serialized.[[ArrayBufferByteLengthData]], + // and whose [[ArrayBufferMaxByteLength]] internal slot value is serialized.[[ArrayBufferMaxByteLength]]. + auto* realm = m_vm.current_realm(); + auto bytes_or_error = deserialize_bytes(m_vm, m_serialized, m_position); + if (bytes_or_error.is_error()) + return WebIDL::DataCloneError::create(*realm, "out of memory"_string); + size_t max_byte_length = deserialize_primitive_type(m_serialized, m_position); + auto bytes = bytes_or_error.release_value(); + JS::ArrayBuffer* buffer = TRY(JS::allocate_shared_array_buffer(m_vm, realm->intrinsics().array_buffer_constructor(), bytes.size())); + bytes.span().copy_to(buffer->buffer().span()); + buffer->set_max_byte_length(max_byte_length); + value = buffer; + break; + } // 14. Otherwise, if serialized.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], and whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]]. case ValueTag::ArrayBuffer: { auto* realm = m_vm.current_realm(); @@ -788,7 +836,19 @@ public: value = JS::ArrayBuffer::create(*realm, bytes_or_error.release_value()); break; } - // FIXME: 15. Otherwise, if serialized.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]], and whose [[ArrayBufferMaxByteLength]] internal slot value is a serialized.[[ArrayBufferMaxByteLength]]. + // 15. Otherwise, if serialized.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object in targetRealm whose [[ArrayBufferData]] internal slot value is serialized.[[ArrayBufferData]], whose [[ArrayBufferByteLength]] internal slot value is serialized.[[ArrayBufferByteLength]], and whose [[ArrayBufferMaxByteLength]] internal slot value is a serialized.[[ArrayBufferMaxByteLength]]. + case ValueTag::ResizeableArrayBuffer: { + auto* realm = m_vm.current_realm(); + // If this throws an exception, catch it, and then throw a "DataCloneError" DOMException. + auto bytes_or_error = deserialize_bytes(m_vm, m_serialized, m_position); + if (bytes_or_error.is_error()) + return WebIDL::DataCloneError::create(*m_vm.current_realm(), "out of memory"_string); + size_t max_byte_length = deserialize_primitive_type(m_serialized, m_position); + auto buffer = JS::ArrayBuffer::create(*realm, bytes_or_error.release_value()); + buffer->set_max_byte_length(max_byte_length); + value = buffer; + break; + } // 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then: case ValueTag::ArrayBufferView: { auto* realm = m_vm.current_realm(); diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt b/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt index 996071f6794..cf819fd63e0 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/html/infrastructure/safe-passing-of-structured-data/window-postmessage.window.txt @@ -2,8 +2,8 @@ Harness status: OK Found 150 tests -133 Pass -16 Fail +134 Pass +15 Fail 1 Optional Feature Unsupported Pass primitive undefined Pass primitive null @@ -137,7 +137,7 @@ Pass ObjectPrototype must lose its exotic-ness when cloned Pass Serializing a non-serializable platform object fails Pass An object whose interface is deleted from the global must still deserialize Pass A subclass instance will deserialize as its closest serializable superclass -Fail Resizable ArrayBuffer +Pass Resizable ArrayBuffer Fail Growable SharedArrayBuffer Pass Length-tracking TypedArray Pass Length-tracking DataView