From 64abc6101d7e952e1f1a6dac1830a6288ac4593d Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 17 Jul 2025 09:51:04 -0400 Subject: [PATCH] LibWeb+WebWorker: Use IPC mechanics for structured serialization Our structured serialization implementation had its own bespoke encoder and decoder to serialize JS values. It also used a u32 buffer under the hood, which made using its structures a bit awkward. We had previously worked around its data structures in transferable streams, which nested transfers of MessagePort instances. We basically had to add hooks into the MessagePort to route to the correct transfer receiving steps, and we could not invoke the correct AOs directly as the spec dictates. We now use IPC mechanics to encode and decode data. This works because, although we are encoding JS values, we are only ultimately encoding primitive and basic AK types. The resulting data structures actually enforce that we implement transferable streams exactly as the spec is worded (I had planned to do that in a separate commit, but the fallout of this patch actually required that change). --- Libraries/LibWeb/Bindings/Serializable.h | 5 +- Libraries/LibWeb/Bindings/Transferable.h | 4 +- Libraries/LibWeb/Crypto/CryptoKey.cpp | 28 +- Libraries/LibWeb/Crypto/CryptoKey.h | 4 +- Libraries/LibWeb/FileAPI/Blob.cpp | 16 +- Libraries/LibWeb/FileAPI/Blob.h | 4 +- Libraries/LibWeb/FileAPI/File.cpp | 24 +- Libraries/LibWeb/FileAPI/File.h | 4 +- Libraries/LibWeb/FileAPI/FileList.cpp | 24 +- Libraries/LibWeb/FileAPI/FileList.h | 4 +- Libraries/LibWeb/Forward.h | 3 +- .../LibWeb/Geometry/DOMMatrixReadOnly.cpp | 100 +- Libraries/LibWeb/Geometry/DOMMatrixReadOnly.h | 4 +- .../LibWeb/Geometry/DOMPointReadOnly.cpp | 27 +- Libraries/LibWeb/Geometry/DOMPointReadOnly.h | 4 +- Libraries/LibWeb/Geometry/DOMQuad.cpp | 47 +- Libraries/LibWeb/Geometry/DOMQuad.h | 4 +- Libraries/LibWeb/Geometry/DOMRectReadOnly.cpp | 28 +- Libraries/LibWeb/Geometry/DOMRectReadOnly.h | 4 +- Libraries/LibWeb/HTML/ImageBitmap.cpp | 14 +- Libraries/LibWeb/HTML/ImageBitmap.h | 8 +- Libraries/LibWeb/HTML/ImageData.cpp | 27 +- Libraries/LibWeb/HTML/ImageData.h | 4 +- Libraries/LibWeb/HTML/MessagePort.cpp | 21 +- Libraries/LibWeb/HTML/MessagePort.h | 4 +- Libraries/LibWeb/HTML/OffscreenCanvas.cpp | 8 +- Libraries/LibWeb/HTML/OffscreenCanvas.h | 4 +- Libraries/LibWeb/HTML/StructuredSerialize.cpp | 1034 +++++++---------- Libraries/LibWeb/HTML/StructuredSerialize.h | 153 +-- .../LibWeb/HTML/StructuredSerializeTypes.h | 3 +- Libraries/LibWeb/HTML/WorkerAgentParent.cpp | 2 +- Libraries/LibWeb/Streams/ReadableStream.cpp | 13 +- Libraries/LibWeb/Streams/ReadableStream.h | 4 +- Libraries/LibWeb/Streams/TransformStream.cpp | 43 +- Libraries/LibWeb/Streams/TransformStream.h | 4 +- Libraries/LibWeb/Streams/WritableStream.cpp | 13 +- Libraries/LibWeb/Streams/WritableStream.h | 4 +- Libraries/LibWeb/WebIDL/DOMException.cpp | 16 +- Libraries/LibWeb/WebIDL/DOMException.h | 4 +- Libraries/LibWeb/Worker/WebWorkerServer.ipc | 2 +- Services/WebWorker/ConnectionFromClient.cpp | 2 +- Services/WebWorker/ConnectionFromClient.h | 2 +- Services/WebWorker/WorkerHost.cpp | 6 +- Services/WebWorker/WorkerHost.h | 2 +- 44 files changed, 765 insertions(+), 970 deletions(-) diff --git a/Libraries/LibWeb/Bindings/Serializable.h b/Libraries/LibWeb/Bindings/Serializable.h index 826ac52f98d..7cbd41a9f37 100644 --- a/Libraries/LibWeb/Bindings/Serializable.h +++ b/Libraries/LibWeb/Bindings/Serializable.h @@ -19,9 +19,10 @@ public: virtual HTML::SerializeType serialize_type() const = 0; // https://html.spec.whatwg.org/multipage/structured-data.html#serialization-steps - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord&, bool for_storage, HTML::SerializationMemory&) = 0; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) = 0; + // https://html.spec.whatwg.org/multipage/structured-data.html#deserialization-steps - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const&, size_t& position, HTML::DeserializationMemory&) = 0; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) = 0; }; } diff --git a/Libraries/LibWeb/Bindings/Transferable.h b/Libraries/LibWeb/Bindings/Transferable.h index 8fb2e61362f..7230f08dbfd 100644 --- a/Libraries/LibWeb/Bindings/Transferable.h +++ b/Libraries/LibWeb/Bindings/Transferable.h @@ -18,10 +18,10 @@ public: virtual ~Transferable() = default; // NOTE: It is an error to call Base::transfer_steps in your impl - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) = 0; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) = 0; // NOTE: It is an error to call Base::transfer_receiving_steps in your impl - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) = 0; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) = 0; virtual HTML::TransferType primary_interface() const = 0; diff --git a/Libraries/LibWeb/Crypto/CryptoKey.cpp b/Libraries/LibWeb/Crypto/CryptoKey.cpp index eefd7402c51..5114c38e589 100644 --- a/Libraries/LibWeb/Crypto/CryptoKey.cpp +++ b/Libraries/LibWeb/Crypto/CryptoKey.cpp @@ -134,51 +134,47 @@ JS_DEFINE_NATIVE_FUNCTION(CryptoKeyPair::private_key_getter) return TRY(Bindings::throw_dom_exception_if_needed(vm, [&] { return impl->private_key(); })); } -WebIDL::ExceptionOr CryptoKey::serialization_steps(HTML::SerializationRecord& serialized, bool for_storage, HTML::SerializationMemory& memory) +WebIDL::ExceptionOr CryptoKey::serialization_steps(HTML::TransferDataEncoder& serialized, bool for_storage, HTML::SerializationMemory& memory) { auto& vm = this->vm(); // 1. Set serialized.[[Type]] to the [[type]] internal slot of value. - HTML::serialize_primitive_type(serialized, static_cast(m_type)); + serialized.encode(m_type); // 2. Set serialized.[[Extractable]] to the [[extractable]] internal slot of value. - HTML::serialize_primitive_type(serialized, m_extractable); + serialized.encode(m_extractable); // 3. Set serialized.[[Algorithm]] to the sub-serialization of the [[algorithm]] internal slot of value. auto serialized_algorithm = TRY(HTML::structured_serialize_internal(vm, m_algorithm, for_storage, memory)); - serialized.extend(move(serialized_algorithm)); + serialized.append(move(serialized_algorithm)); // 4. Set serialized.[[Usages]] to the sub-serialization of the [[usages]] internal slot of value. auto serialized_usages = TRY(HTML::structured_serialize_internal(vm, m_usages, for_storage, memory)); - serialized.extend(move(serialized_usages)); + serialized.append(move(serialized_usages)); // FIXME: 5. Set serialized.[[Handle]] to the [[handle]] internal slot of value. return {}; } -WebIDL::ExceptionOr CryptoKey::deserialization_steps(ReadonlySpan const& serialized, size_t& position, HTML::DeserializationMemory& memory) +WebIDL::ExceptionOr CryptoKey::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory& memory) { auto& vm = this->vm(); auto& realm = this->realm(); // 1. Initialize the [[type]] internal slot of value to serialized.[[Type]]. - m_type = static_cast(HTML::deserialize_primitive_type(serialized, position)); + m_type = serialized.decode(); // 2. Initialize the [[extractable]] internal slot of value to serialized.[[Extractable]]. - m_extractable = HTML::deserialize_primitive_type(serialized, position); + m_extractable = serialized.decode(); // 3. Initialize the [[algorithm]] internal slot of value to the sub-deserialization of serialized.[[Algorithm]]. - auto deserialized_record = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory, position)); - if (deserialized_record.value.has_value()) - m_algorithm = deserialized_record.value.release_value().as_object(); - position = deserialized_record.position; + auto deserialized = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory)); + m_algorithm = deserialized.as_object(); // 4. Initialize the [[usages]] internal slot of value to the sub-deserialization of serialized.[[Usages]]. - deserialized_record = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory, position)); - if (deserialized_record.value.has_value()) - m_usages = deserialized_record.value.release_value().as_object(); - position = deserialized_record.position; + deserialized = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory)); + m_usages = deserialized.as_object(); // FIXME: 5. Initialize the [[handle]] internal slot of value to serialized.[[Handle]]. diff --git a/Libraries/LibWeb/Crypto/CryptoKey.h b/Libraries/LibWeb/Crypto/CryptoKey.h index 7f601a254a7..ef6908f239f 100644 --- a/Libraries/LibWeb/Crypto/CryptoKey.h +++ b/Libraries/LibWeb/Crypto/CryptoKey.h @@ -48,8 +48,8 @@ public: String algorithm_name() const; virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::CryptoKey; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord& record, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; private: CryptoKey(JS::Realm&, InternalKeyData); diff --git a/Libraries/LibWeb/FileAPI/Blob.cpp b/Libraries/LibWeb/FileAPI/Blob.cpp index 8a39c066dc0..c4c5293cf6b 100644 --- a/Libraries/LibWeb/FileAPI/Blob.cpp +++ b/Libraries/LibWeb/FileAPI/Blob.cpp @@ -151,34 +151,32 @@ void Blob::initialize(JS::Realm& realm) Base::initialize(realm); } -WebIDL::ExceptionOr Blob::serialization_steps(HTML::SerializationRecord& record, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr Blob::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&) { - auto& vm = this->vm(); - // FIXME: 1. Set serialized.[[SnapshotState]] to value’s snapshot state. // NON-STANDARD: FileAPI spec doesn't specify that type should be serialized, although // to be conformant with other browsers this needs to be serialized. - TRY(HTML::serialize_string(vm, record, m_type)); + serialized.encode(m_type); // 2. Set serialized.[[ByteSequence]] to value’s underlying byte sequence. - TRY(HTML::serialize_bytes(vm, record, m_byte_buffer.bytes())); + serialized.encode(m_byte_buffer); return {}; } -WebIDL::ExceptionOr Blob::deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) +WebIDL::ExceptionOr Blob::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&) { - auto& vm = this->vm(); + auto& realm = this->realm(); // FIXME: 1. Set value’s snapshot state to serialized.[[SnapshotState]]. // NON-STANDARD: FileAPI spec doesn't specify that type should be deserialized, although // to be conformant with other browsers this needs to be deserialized. - m_type = TRY(HTML::deserialize_string(vm, record, position)); + m_type = serialized.decode(); // 2. Set value’s underlying byte sequence to serialized.[[ByteSequence]]. - m_byte_buffer = TRY(HTML::deserialize_bytes(vm, record, position)); + m_byte_buffer = TRY(serialized.decode_buffer(realm)); return {}; } diff --git a/Libraries/LibWeb/FileAPI/Blob.h b/Libraries/LibWeb/FileAPI/Blob.h index 12760476074..12d93f75b71 100644 --- a/Libraries/LibWeb/FileAPI/Blob.h +++ b/Libraries/LibWeb/FileAPI/Blob.h @@ -58,8 +58,8 @@ public: virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::Blob; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord& record, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; protected: Blob(JS::Realm&, ByteBuffer, String type); diff --git a/Libraries/LibWeb/FileAPI/File.cpp b/Libraries/LibWeb/FileAPI/File.cpp index 724f044631a..46d065bb6a3 100644 --- a/Libraries/LibWeb/FileAPI/File.cpp +++ b/Libraries/LibWeb/FileAPI/File.cpp @@ -88,46 +88,44 @@ WebIDL::ExceptionOr> File::construct_impl(JS::Realm& realm, Vector return create(realm, file_bits, file_name, options); } -WebIDL::ExceptionOr File::serialization_steps(HTML::SerializationRecord& record, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr File::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&) { - auto& vm = this->vm(); - // FIXME: 1. Set serialized.[[SnapshotState]] to value’s snapshot state. // NON-STANDARD: FileAPI spec doesn't specify that type should be serialized, although // to be conformant with other browsers this needs to be serialized. - TRY(HTML::serialize_string(vm, record, m_type)); + serialized.encode(m_type); // 2. Set serialized.[[ByteSequence]] to value’s underlying byte sequence. - TRY(HTML::serialize_bytes(vm, record, m_byte_buffer.bytes())); + serialized.encode(m_byte_buffer); // 3. Set serialized.[[Name]] to the value of value’s name attribute. - TRY(HTML::serialize_string(vm, record, m_name)); + serialized.encode(m_name); // 4. Set serialized.[[LastModified]] to the value of value’s lastModified attribute. - HTML::serialize_primitive_type(record, m_last_modified); + serialized.encode(m_last_modified); return {}; } -WebIDL::ExceptionOr File::deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) +WebIDL::ExceptionOr File::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&) { - auto& vm = this->vm(); + auto& realm = this->realm(); // FIXME: 1. Set value’s snapshot state to serialized.[[SnapshotState]]. // NON-STANDARD: FileAPI spec doesn't specify that type should be deserialized, although // to be conformant with other browsers this needs to be deserialized. - m_type = TRY(HTML::deserialize_string(vm, record, position)); + m_type = serialized.decode(); // 2. Set value’s underlying byte sequence to serialized.[[ByteSequence]]. - m_byte_buffer = TRY(HTML::deserialize_bytes(vm, record, position)); + m_byte_buffer = TRY(serialized.decode_buffer(realm)); // 3. Initialize the value of value’s name attribute to serialized.[[Name]]. - m_name = TRY(HTML::deserialize_string(vm, record, position)); + m_name = serialized.decode(); // 4. Initialize the value of value’s lastModified attribute to serialized.[[LastModified]]. - m_last_modified = HTML::deserialize_primitive_type(record, position); + m_last_modified = serialized.decode(); return {}; } diff --git a/Libraries/LibWeb/FileAPI/File.h b/Libraries/LibWeb/FileAPI/File.h index 04824f5b2da..d778dd68488 100644 --- a/Libraries/LibWeb/FileAPI/File.h +++ b/Libraries/LibWeb/FileAPI/File.h @@ -32,8 +32,8 @@ public: virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::File; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord& record, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const&, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; private: File(JS::Realm&, ByteBuffer, String file_name, String type, i64 last_modified); diff --git a/Libraries/LibWeb/FileAPI/FileList.cpp b/Libraries/LibWeb/FileAPI/FileList.cpp index 9017a21221c..486525f1f8e 100644 --- a/Libraries/LibWeb/FileAPI/FileList.cpp +++ b/Libraries/LibWeb/FileAPI/FileList.cpp @@ -48,31 +48,33 @@ void FileList::visit_edges(Cell::Visitor& visitor) visitor.visit(m_files); } -WebIDL::ExceptionOr FileList::serialization_steps(HTML::SerializationRecord& serialized, bool for_storage, HTML::SerializationMemory& memory) +WebIDL::ExceptionOr FileList::serialization_steps(HTML::TransferDataEncoder& serialized, bool for_storage, HTML::SerializationMemory& memory) { auto& vm = this->vm(); // 1. Set serialized.[[Files]] to an empty list. // 2. For each file in value, append the sub-serialization of file to serialized.[[Files]]. - HTML::serialize_primitive_type(serialized, m_files.size()); - for (auto& file : m_files) - serialized.extend(TRY(HTML::structured_serialize_internal(vm, file, for_storage, memory))); + serialized.encode(m_files.size()); + + for (auto file : m_files) + serialized.append(TRY(HTML::structured_serialize_internal(vm, file, for_storage, memory))); return {}; } -WebIDL::ExceptionOr FileList::deserialization_steps(ReadonlySpan const& serialized, size_t& position, HTML::DeserializationMemory& memory) +WebIDL::ExceptionOr FileList::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory& memory) { auto& vm = this->vm(); - auto& realm = *vm.current_realm(); + auto& realm = this->realm(); // 1. For each file of serialized.[[Files]], add the sub-deserialization of file to value. - auto size = HTML::deserialize_primitive_type(serialized, position); + auto size = serialized.decode(); + for (size_t i = 0; i < size; ++i) { - auto deserialized_record = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory, position)); - if (deserialized_record.value.has_value() && is(deserialized_record.value.value().as_object())) - m_files.append(dynamic_cast(deserialized_record.value.release_value().as_object())); - position = deserialized_record.position; + auto deserialized = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory)); + + if (auto* file = as_if(deserialized.as_object())) + m_files.append(*file); } return {}; diff --git a/Libraries/LibWeb/FileAPI/FileList.h b/Libraries/LibWeb/FileAPI/FileList.h index f17263215b4..62df237d7f8 100644 --- a/Libraries/LibWeb/FileAPI/FileList.h +++ b/Libraries/LibWeb/FileAPI/FileList.h @@ -46,8 +46,8 @@ public: virtual Optional item_value(size_t index) const override; virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::FileList; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord& serialized, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const& serialized, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; private: explicit FileList(JS::Realm&); diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index dee807f9d03..d4f1ebf4cf2 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -672,6 +672,8 @@ class Timer; class TimeRanges; class ToggleEvent; class TrackEvent; +class TransferDataDecoder; +class TransferDataEncoder; class TraversableNavigable; class UserActivation; class ValidityState; @@ -711,7 +713,6 @@ struct SerializedTransferRecord; struct StructuredSerializeOptions; struct SyntheticRealmSettings; struct ToggleTaskTracker; -struct TransferDataHolder; } diff --git a/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.cpp b/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.cpp index e8852eee310..f37e0ecddb4 100644 --- a/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.cpp +++ b/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.cpp @@ -702,82 +702,85 @@ WebIDL::ExceptionOr DOMMatrixReadOnly::to_string() const } // https://drafts.fxtf.org/geometry/#structured-serialization -WebIDL::ExceptionOr DOMMatrixReadOnly::serialization_steps(HTML::SerializationRecord& serialized, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr DOMMatrixReadOnly::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&) { - HTML::serialize_primitive_type(serialized, m_is_2d); + serialized.encode(m_is_2d); + // 1. If value’s is 2D is true: if (m_is_2d) { // 1. Set serialized.[[M11]] to value’s m11 element. - HTML::serialize_primitive_type(serialized, this->m11()); + serialized.encode(m11()); // 2. Set serialized.[[M12]] to value’s m12 element. - HTML::serialize_primitive_type(serialized, this->m12()); + serialized.encode(m12()); // 3. Set serialized.[[M21]] to value’s m21 element. - HTML::serialize_primitive_type(serialized, this->m21()); + serialized.encode(m21()); // 4. Set serialized.[[M22]] to value’s m22 element. - HTML::serialize_primitive_type(serialized, this->m22()); + serialized.encode(m22()); // 5. Set serialized.[[M41]] to value’s m41 element. - HTML::serialize_primitive_type(serialized, this->m41()); + serialized.encode(m41()); // 6. Set serialized.[[M42]] to value’s m42 element. - HTML::serialize_primitive_type(serialized, this->m42()); + serialized.encode(m42()); // 7. Set serialized.[[Is2D]] to true. // NOTE: This is set in the beginning of the function. } // 2. Otherwise: else { // 1. Set serialized.[[M11]] to value’s m11 element. - HTML::serialize_primitive_type(serialized, this->m11()); + serialized.encode(m11()); // 2. Set serialized.[[M12]] to value’s m12 element. - HTML::serialize_primitive_type(serialized, this->m12()); + serialized.encode(m12()); // 3. Set serialized.[[M13]] to value’s m13 element. - HTML::serialize_primitive_type(serialized, this->m13()); + serialized.encode(m13()); // 4. Set serialized.[[M14]] to value’s m14 element. - HTML::serialize_primitive_type(serialized, this->m14()); + serialized.encode(m14()); // 5. Set serialized.[[M21]] to value’s m21 element. - HTML::serialize_primitive_type(serialized, this->m21()); + serialized.encode(m21()); // 6. Set serialized.[[M22]] to value’s m22 element. - HTML::serialize_primitive_type(serialized, this->m22()); + serialized.encode(m22()); // 7. Set serialized.[[M23]] to value’s m23 element. - HTML::serialize_primitive_type(serialized, this->m23()); + serialized.encode(m23()); // 8. Set serialized.[[M24]] to value’s m24 element. - HTML::serialize_primitive_type(serialized, this->m24()); + serialized.encode(m24()); // 9. Set serialized.[[M31]] to value’s m31 element. - HTML::serialize_primitive_type(serialized, this->m31()); + serialized.encode(m31()); // 10. Set serialized.[[M32]] to value’s m32 element. - HTML::serialize_primitive_type(serialized, this->m32()); + serialized.encode(m32()); // 11. Set serialized.[[M33]] to value’s m33 element. - HTML::serialize_primitive_type(serialized, this->m33()); + serialized.encode(m33()); // 12. Set serialized.[[M34]] to value’s m34 element. - HTML::serialize_primitive_type(serialized, this->m34()); + serialized.encode(m34()); // 13. Set serialized.[[M41]] to value’s m41 element. - HTML::serialize_primitive_type(serialized, this->m41()); + serialized.encode(m41()); // 14. Set serialized.[[M42]] to value’s m42 element. - HTML::serialize_primitive_type(serialized, this->m42()); + serialized.encode(m42()); // 15. Set serialized.[[M43]] to value’s m43 element. - HTML::serialize_primitive_type(serialized, this->m43()); + serialized.encode(m43()); // 16. Set serialized.[[M44]] to value’s m44 element. - HTML::serialize_primitive_type(serialized, this->m44()); + serialized.encode(m44()); // 17. Set serialized.[[Is2D]] to false. // NOTE: This is set in the beginning of the function. } + return {}; } // https://drafts.fxtf.org/geometry/#structured-serialization -WebIDL::ExceptionOr DOMMatrixReadOnly::deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) +WebIDL::ExceptionOr DOMMatrixReadOnly::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&) { - bool is_2d = HTML::deserialize_primitive_type(record, position); + bool is_2d = serialized.decode(); + // 1. If serialized.[[Is2D]] is true: if (is_2d) { // 1. Set value’s m11 element to serialized.[[M11]]. - double m11 = HTML::deserialize_primitive_type(record, position); + auto m11 = serialized.decode(); // 2. Set value’s m12 element to serialized.[[M12]]. - double m12 = HTML::deserialize_primitive_type(record, position); + auto m12 = serialized.decode(); // 3. Set value’s m13 element to 0. // 4. Set value’s m14 element to 0. // 5. Set value’s m21 element to serialized.[[M21]]. - double m21 = HTML::deserialize_primitive_type(record, position); + auto m21 = serialized.decode(); // 6. Set value’s m22 element to serialized.[[M22]]. - double m22 = HTML::deserialize_primitive_type(record, position); + auto m22 = serialized.decode(); // 7. Set value’s m23 element to 0. // 8. Set value’s m24 element to 0. // 9. Set value’s m31 element to 0. @@ -785,9 +788,9 @@ WebIDL::ExceptionOr DOMMatrixReadOnly::deserialization_steps(ReadonlySpan< // 11. Set value’s m33 element to 1. // 12. Set value’s m34 element to 0. // 13. Set value’s m41 element to serialized.[[M41]]. - double m41 = HTML::deserialize_primitive_type(record, position); + auto m41 = serialized.decode(); // 14 Set value’s m42 element to serialized.[[M42]]. - double m42 = HTML::deserialize_primitive_type(record, position); + auto m42 = serialized.decode(); // 15. Set value’s m43 element to 0. // 16. Set value’s m44 element to 1. // 17. Set value’s is 2D to true. @@ -797,41 +800,42 @@ WebIDL::ExceptionOr DOMMatrixReadOnly::deserialization_steps(ReadonlySpan< // 2. Otherwise: else { // 1. Set value’s m11 element to serialized.[[M11]]. - double m11 = HTML::deserialize_primitive_type(record, position); + auto m11 = serialized.decode(); // 2. Set value’s m12 element to serialized.[[M12]]. - double m12 = HTML::deserialize_primitive_type(record, position); + auto m12 = serialized.decode(); // 3. Set value’s m13 element to serialized.[[M13]]. - double m13 = HTML::deserialize_primitive_type(record, position); + auto m13 = serialized.decode(); // 4. Set value’s m14 element to serialized.[[M14]]. - double m14 = HTML::deserialize_primitive_type(record, position); + auto m14 = serialized.decode(); // 5. Set value’s m21 element to serialized.[[M21]]. - double m21 = HTML::deserialize_primitive_type(record, position); + auto m21 = serialized.decode(); // 6. Set value’s m22 element to serialized.[[M22]]. - double m22 = HTML::deserialize_primitive_type(record, position); + auto m22 = serialized.decode(); // 7. Set value’s m23 element to serialized.[[M23]]. - double m23 = HTML::deserialize_primitive_type(record, position); + auto m23 = serialized.decode(); // 8. Set value’s m24 element to serialized.[[M24]]. - double m24 = HTML::deserialize_primitive_type(record, position); + auto m24 = serialized.decode(); // 9. Set value’s m31 element to serialized.[[M31]]. - double m31 = HTML::deserialize_primitive_type(record, position); + auto m31 = serialized.decode(); // 10. Set value’s m32 element to serialized.[[M32]]. - double m32 = HTML::deserialize_primitive_type(record, position); + auto m32 = serialized.decode(); // 11. Set value’s m33 element to serialized.[[M33]]. - double m33 = HTML::deserialize_primitive_type(record, position); + auto m33 = serialized.decode(); // 12. Set value’s m34 element to serialized.[[M34]]. - double m34 = HTML::deserialize_primitive_type(record, position); + auto m34 = serialized.decode(); // 13. Set value’s m41 element to serialized.[[M41]]. - double m41 = HTML::deserialize_primitive_type(record, position); + auto m41 = serialized.decode(); // 14. Set value’s m42 element to serialized.[[M42]]. - double m42 = HTML::deserialize_primitive_type(record, position); + auto m42 = serialized.decode(); // 15. Set value’s m43 element to serialized.[[M43]]. - double m43 = HTML::deserialize_primitive_type(record, position); + auto m43 = serialized.decode(); // 16. Set value’s m44 element to serialized.[[M44]]. - double m44 = HTML::deserialize_primitive_type(record, position); + auto m44 = serialized.decode(); // 17. Set value’s is 2D to false. initialize_from_create_3d_matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); } + return {}; } diff --git a/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.h b/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.h index 95cf3e21cf2..0a44b64ca44 100644 --- a/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.h +++ b/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.h @@ -116,8 +116,8 @@ public: WebIDL::ExceptionOr to_string() const; virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::DOMMatrixReadOnly; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord&, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; protected: DOMMatrixReadOnly(JS::Realm&, double m11, double m12, double m21, double m22, double m41, double m42); diff --git a/Libraries/LibWeb/Geometry/DOMPointReadOnly.cpp b/Libraries/LibWeb/Geometry/DOMPointReadOnly.cpp index 982610447e5..12e68a5ca6d 100644 --- a/Libraries/LibWeb/Geometry/DOMPointReadOnly.cpp +++ b/Libraries/LibWeb/Geometry/DOMPointReadOnly.cpp @@ -66,30 +66,37 @@ void DOMPointReadOnly::initialize(JS::Realm& realm) Base::initialize(realm); } -WebIDL::ExceptionOr DOMPointReadOnly::serialization_steps(HTML::SerializationRecord& serialized, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr DOMPointReadOnly::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&) { // 1. Set serialized.[[X]] to value’s x coordinate. - HTML::serialize_primitive_type(serialized, m_x); + serialized.encode(m_x); + // 2. Set serialized.[[Y]] to value’s y coordinate. - HTML::serialize_primitive_type(serialized, m_y); + serialized.encode(m_y); + // 3. Set serialized.[[Z]] to value’s z coordinate. - HTML::serialize_primitive_type(serialized, m_z); + serialized.encode(m_z); + // 4. Set serialized.[[W]] to value’s w coordinate. - HTML::serialize_primitive_type(serialized, m_w); + serialized.encode(m_w); return {}; } -WebIDL::ExceptionOr DOMPointReadOnly::deserialization_steps(ReadonlySpan const& serialized, size_t& position, HTML::DeserializationMemory&) +WebIDL::ExceptionOr DOMPointReadOnly::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&) { // 1. Set value’s x coordinate to serialized.[[X]]. - m_x = HTML::deserialize_primitive_type(serialized, position); + m_x = serialized.decode(); + // 2. Set value’s y coordinate to serialized.[[Y]]. - m_y = HTML::deserialize_primitive_type(serialized, position); + m_y = serialized.decode(); + // 3. Set value’s z coordinate to serialized.[[Z]]. - m_z = HTML::deserialize_primitive_type(serialized, position); + m_z = serialized.decode(); + // 4. Set value’s w coordinate to serialized.[[W]]. - m_w = HTML::deserialize_primitive_type(serialized, position); + m_w = serialized.decode(); + return {}; } diff --git a/Libraries/LibWeb/Geometry/DOMPointReadOnly.h b/Libraries/LibWeb/Geometry/DOMPointReadOnly.h index 332ea1dc11b..b099fdf5afc 100644 --- a/Libraries/LibWeb/Geometry/DOMPointReadOnly.h +++ b/Libraries/LibWeb/Geometry/DOMPointReadOnly.h @@ -45,8 +45,8 @@ public: WebIDL::ExceptionOr> matrix_transform(DOMMatrixInit&) const; virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::DOMPointReadOnly; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord&, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const&, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; protected: DOMPointReadOnly(JS::Realm&, double x, double y, double z, double w); diff --git a/Libraries/LibWeb/Geometry/DOMQuad.cpp b/Libraries/LibWeb/Geometry/DOMQuad.cpp index 1cc265425fa..5ded9e38a1f 100644 --- a/Libraries/LibWeb/Geometry/DOMQuad.cpp +++ b/Libraries/LibWeb/Geometry/DOMQuad.cpp @@ -103,48 +103,51 @@ GC::Ref DOMQuad::get_bounds() const } // https://drafts.fxtf.org/geometry/#structured-serialization -WebIDL::ExceptionOr DOMQuad::serialization_steps(HTML::SerializationRecord& serialized, bool for_storage, HTML::SerializationMemory& memory) +WebIDL::ExceptionOr DOMQuad::serialization_steps(HTML::TransferDataEncoder& serialized, bool for_storage, HTML::SerializationMemory& memory) { auto& vm = this->vm(); + // 1. Set serialized.[[P1]] to the sub-serialization of value’s point 1. - serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p1, for_storage, memory))); + serialized.append(TRY(HTML::structured_serialize_internal(vm, m_p1, for_storage, memory))); + // 2. Set serialized.[[P2]] to the sub-serialization of value’s point 2. - serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p2, for_storage, memory))); + serialized.append(TRY(HTML::structured_serialize_internal(vm, m_p2, for_storage, memory))); + // 3. Set serialized.[[P3]] to the sub-serialization of value’s point 3. - serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p3, for_storage, memory))); + serialized.append(TRY(HTML::structured_serialize_internal(vm, m_p3, for_storage, memory))); + // 4. Set serialized.[[P4]] to the sub-serialization of value’s point 4. - serialized.extend(TRY(HTML::structured_serialize_internal(vm, m_p4, for_storage, memory))); + serialized.append(TRY(HTML::structured_serialize_internal(vm, m_p4, for_storage, memory))); return {}; } // https://drafts.fxtf.org/geometry/#structured-serialization -WebIDL::ExceptionOr DOMQuad::deserialization_steps(ReadonlySpan const& serialized, size_t& position, HTML::DeserializationMemory& memory) +WebIDL::ExceptionOr DOMQuad::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory& memory) { + auto& vm = this->vm(); auto& realm = this->realm(); + + auto deserialize_dom_point = [&](GC::Ref& storage) -> WebIDL::ExceptionOr { + auto deserialized = TRY(HTML::structured_deserialize_internal(vm, serialized, realm, memory)); + + if (auto* dom_point = as_if(deserialized.as_object())) + storage = *dom_point; + + return {}; + }; + // 1. Set value’s point 1 to the sub-deserialization of serialized.[[P1]]. - auto deserialized_record = TRY(HTML::structured_deserialize_internal(vm(), serialized, realm, memory, position)); - if (deserialized_record.value.has_value() && is(deserialized_record.value.value().as_object())) - m_p1 = dynamic_cast(deserialized_record.value.release_value().as_object()); - position = deserialized_record.position; + TRY(deserialize_dom_point(m_p1)); // 2. Set value’s point 2 to the sub-deserialization of serialized.[[P2]]. - deserialized_record = TRY(HTML::structured_deserialize_internal(vm(), serialized, realm, memory, position)); - if (deserialized_record.value.has_value() && is(deserialized_record.value.value().as_object())) - m_p2 = dynamic_cast(deserialized_record.value.release_value().as_object()); - position = deserialized_record.position; + TRY(deserialize_dom_point(m_p2)); // 3. Set value’s point 3 to the sub-deserialization of serialized.[[P3]]. - deserialized_record = TRY(HTML::structured_deserialize_internal(vm(), serialized, realm, memory, position)); - if (deserialized_record.value.has_value() && is(deserialized_record.value.value().as_object())) - m_p3 = dynamic_cast(deserialized_record.value.release_value().as_object()); - position = deserialized_record.position; + TRY(deserialize_dom_point(m_p3)); // 4. Set value’s point 4 to the sub-deserialization of serialized.[[P4]]. - deserialized_record = TRY(HTML::structured_deserialize_internal(vm(), serialized, realm, memory, position)); - if (deserialized_record.value.has_value() && is(deserialized_record.value.value().as_object())) - m_p4 = dynamic_cast(deserialized_record.value.release_value().as_object()); - position = deserialized_record.position; + TRY(deserialize_dom_point(m_p4)); return {}; } diff --git a/Libraries/LibWeb/Geometry/DOMQuad.h b/Libraries/LibWeb/Geometry/DOMQuad.h index cc3d24091e4..9339c4ae565 100644 --- a/Libraries/LibWeb/Geometry/DOMQuad.h +++ b/Libraries/LibWeb/Geometry/DOMQuad.h @@ -45,8 +45,8 @@ public: GC::Ref get_bounds() const; virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::DOMQuad; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord&, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const&, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; private: DOMQuad(JS::Realm&, DOMPointInit const& p1, DOMPointInit const& p2, DOMPointInit const& p3, DOMPointInit const& p4); diff --git a/Libraries/LibWeb/Geometry/DOMRectReadOnly.cpp b/Libraries/LibWeb/Geometry/DOMRectReadOnly.cpp index 76670a8bb68..244df52b7d6 100644 --- a/Libraries/LibWeb/Geometry/DOMRectReadOnly.cpp +++ b/Libraries/LibWeb/Geometry/DOMRectReadOnly.cpp @@ -52,33 +52,39 @@ void DOMRectReadOnly::initialize(JS::Realm& realm) } // https://drafts.fxtf.org/geometry/#structured-serialization -WebIDL::ExceptionOr DOMRectReadOnly::serialization_steps(HTML::SerializationRecord& serialized, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr DOMRectReadOnly::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&) { // 1. Set serialized.[[X]] to value’s x coordinate. - HTML::serialize_primitive_type(serialized, this->x()); + serialized.encode(x()); + // 2. Set serialized.[[Y]] to value’s y coordinate. - HTML::serialize_primitive_type(serialized, this->y()); + serialized.encode(y()); + // 3. Set serialized.[[Width]] to value’s width. - HTML::serialize_primitive_type(serialized, this->width()); + serialized.encode(width()); + // 4. Set serialized.[[Height]] to value’s height. - HTML::serialize_primitive_type(serialized, this->height()); + serialized.encode(height()); + return {}; } // https://drafts.fxtf.org/geometry/#structured-serialization -WebIDL::ExceptionOr DOMRectReadOnly::deserialization_steps(ReadonlySpan const& serialized, size_t& position, HTML::DeserializationMemory&) +WebIDL::ExceptionOr DOMRectReadOnly::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&) { // 1. Set value’s x coordinate to serialized.[[X]]. - auto x = HTML::deserialize_primitive_type(serialized, position); + auto x = serialized.decode(); + // 2. Set value’s y coordinate to serialized.[[Y]]. - auto y = HTML::deserialize_primitive_type(serialized, position); + auto y = serialized.decode(); + // 3. Set value’s width to serialized.[[Width]]. - auto width = HTML::deserialize_primitive_type(serialized, position); + auto width = serialized.decode(); + // 4. Set value’s height to serialized.[[Height]]. - auto height = HTML::deserialize_primitive_type(serialized, position); + auto height = serialized.decode(); m_rect = { x, y, width, height }; - return {}; } diff --git a/Libraries/LibWeb/Geometry/DOMRectReadOnly.h b/Libraries/LibWeb/Geometry/DOMRectReadOnly.h index 7086c284924..95371f137d4 100644 --- a/Libraries/LibWeb/Geometry/DOMRectReadOnly.h +++ b/Libraries/LibWeb/Geometry/DOMRectReadOnly.h @@ -70,8 +70,8 @@ public: } virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::DOMRectReadOnly; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord&, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const&, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; protected: DOMRectReadOnly(JS::Realm&, double x, double y, double width, double height); diff --git a/Libraries/LibWeb/HTML/ImageBitmap.cpp b/Libraries/LibWeb/HTML/ImageBitmap.cpp index 6fb313c949e..cc69acb7c3a 100644 --- a/Libraries/LibWeb/HTML/ImageBitmap.cpp +++ b/Libraries/LibWeb/HTML/ImageBitmap.cpp @@ -33,31 +33,31 @@ void ImageBitmap::visit_edges(Cell::Visitor& visitor) Base::visit_edges(visitor); } -WebIDL::ExceptionOr ImageBitmap::serialization_steps(HTML::SerializationRecord&, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr ImageBitmap::serialization_steps(HTML::TransferDataEncoder&, bool, HTML::SerializationMemory&) { // FIXME: Implement this - dbgln("(STUBBED) ImageBitmap::serialization_steps(HTML::SerializationRecord&, bool, HTML::SerializationMemory&)"); + dbgln("(STUBBED) ImageBitmap::serialization_steps(HTML::TransferDataEncoder&, bool, HTML::SerializationMemory&)"); return {}; } -WebIDL::ExceptionOr ImageBitmap::deserialization_steps(ReadonlySpan const&, size_t&, HTML::DeserializationMemory&) +WebIDL::ExceptionOr ImageBitmap::deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) { // FIXME: Implement this dbgln("(STUBBED) ImageBitmap::deserialization_steps(ReadonlySpan const&, size_t&, HTML::DeserializationMemory&)"); return {}; } -WebIDL::ExceptionOr ImageBitmap::transfer_steps(HTML::TransferDataHolder&) +WebIDL::ExceptionOr ImageBitmap::transfer_steps(HTML::TransferDataEncoder&) { // FIXME: Implement this - dbgln("(STUBBED) ImageBitmap::transfer_steps(HTML::TransferDataHolder&)"); + dbgln("(STUBBED) ImageBitmap::transfer_steps(HTML::TransferDataEncoder&)"); return {}; } -WebIDL::ExceptionOr ImageBitmap::transfer_receiving_steps(HTML::TransferDataHolder&) +WebIDL::ExceptionOr ImageBitmap::transfer_receiving_steps(HTML::TransferDataDecoder&) { // FIXME: Implement this - dbgln("(STUBBED) ImageBitmap::transfer_receiving_steps(HTML::TransferDataHolder&)"); + dbgln("(STUBBED) ImageBitmap::transfer_receiving_steps(HTML::TransferDataDecoder&)"); return {}; } diff --git a/Libraries/LibWeb/HTML/ImageBitmap.h b/Libraries/LibWeb/HTML/ImageBitmap.h index 6053a92ccb0..284cfddc24f 100644 --- a/Libraries/LibWeb/HTML/ImageBitmap.h +++ b/Libraries/LibWeb/HTML/ImageBitmap.h @@ -35,12 +35,12 @@ public: // ^Web::Bindings::Serializable virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::ImageBitmap; } - virtual WebIDL::ExceptionOr serialization_steps(HTML::SerializationRecord&, bool for_storage, HTML::SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const&, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; // ^Web::Bindings::Transferable - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) override; - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) override; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) override; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) override; virtual HTML::TransferType primary_interface() const override; WebIDL::UnsignedLong width() const; diff --git a/Libraries/LibWeb/HTML/ImageData.cpp b/Libraries/LibWeb/HTML/ImageData.cpp index 01631102708..43327f9617c 100644 --- a/Libraries/LibWeb/HTML/ImageData.cpp +++ b/Libraries/LibWeb/HTML/ImageData.cpp @@ -180,22 +180,22 @@ const JS::Uint8ClampedArray* ImageData::data() const } // https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation:serialization-steps -WebIDL::ExceptionOr ImageData::serialization_steps(SerializationRecord& serialized, bool for_storage, SerializationMemory& memory) +WebIDL::ExceptionOr ImageData::serialization_steps(HTML::TransferDataEncoder& serialized, bool for_storage, HTML::SerializationMemory& memory) { auto& vm = this->vm(); // 1. Set serialized.[[Data]] to the sub-serialization of the value of value's data attribute. auto serialized_data = TRY(structured_serialize_internal(vm, m_data, for_storage, memory)); - serialized.extend(move(serialized_data)); + serialized.append(move(serialized_data)); // 2. Set serialized.[[Width]] to the value of value's width attribute. - serialize_primitive_type(serialized, m_bitmap->width()); + serialized.encode(m_bitmap->width()); // 3. Set serialized.[[Height]] to the value of value's height attribute. - serialize_primitive_type(serialized, m_bitmap->height()); + serialized.encode(m_bitmap->height()); // 4. Set serialized.[[ColorSpace]] to the value of value's colorSpace attribute. - serialize_enum(serialized, m_color_space); + serialized.encode(m_color_space); // FIXME:: 5. Set serialized.[[PixelFormat]] to the value of value's pixelFormat attribute. @@ -203,26 +203,25 @@ WebIDL::ExceptionOr ImageData::serialization_steps(SerializationRecord& se } // https://html.spec.whatwg.org/multipage/canvas.html#pixel-manipulation:deserialization-steps -WebIDL::ExceptionOr ImageData::deserialization_steps(ReadonlySpan const& serialized, size_t& position, DeserializationMemory& memory) +WebIDL::ExceptionOr ImageData::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory& memory) { auto& vm = this->vm(); auto& realm = this->realm(); // 1. Initialize value's data attribute to the sub-deserialization of serialized.[[Data]]. - auto [value, position_after_deserialization] = TRY(structured_deserialize_internal(vm, serialized, realm, memory, position)); - if (value.has_value() && value.value().is_object() && is(value.value().as_object())) { - m_data = as(value.release_value().as_object()); - } - position = position_after_deserialization; + auto deserialized = TRY(structured_deserialize_internal(vm, serialized, realm, memory)); + + if (auto* data = as_if(deserialized.as_object())) + m_data = *data; // 2. Initialize value's width attribute to serialized.[[Width]]. - auto const width = deserialize_primitive_type(serialized, position); + auto width = serialized.decode(); // 3. Initialize value's height attribute to serialized.[[Height]]. - auto const height = deserialize_primitive_type(serialized, position); + auto height = serialized.decode(); // 4. Initialize value's colorSpace attribute to serialized.[[ColorSpace]]. - m_color_space = deserialize_primitive_type(serialized, position); + m_color_space = serialized.decode(); // FIXME: 5. Initialize value's pixelFormat attribute to serialized.[[PixelFormat]]. diff --git a/Libraries/LibWeb/HTML/ImageData.h b/Libraries/LibWeb/HTML/ImageData.h index 1620b134714..328540c590c 100644 --- a/Libraries/LibWeb/HTML/ImageData.h +++ b/Libraries/LibWeb/HTML/ImageData.h @@ -47,8 +47,8 @@ public: Bindings::PredefinedColorSpace color_space() const { return m_color_space; } virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::ImageData; } - virtual WebIDL::ExceptionOr serialization_steps(SerializationRecord& serialized, bool for_storage, SerializationMemory&) override; - virtual WebIDL::ExceptionOr deserialization_steps(ReadonlySpan const& serialized, size_t& position, DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; private: [[nodiscard]] static WebIDL::ExceptionOr> initialize(JS::Realm&, u32 rows, u32 pixels_per_row, Optional const&, GC::Ptr = {}, Optional = {}); diff --git a/Libraries/LibWeb/HTML/MessagePort.cpp b/Libraries/LibWeb/HTML/MessagePort.cpp index 59a5ea7769d..dca0255ac2f 100644 --- a/Libraries/LibWeb/HTML/MessagePort.cpp +++ b/Libraries/LibWeb/HTML/MessagePort.cpp @@ -89,7 +89,7 @@ void MessagePort::set_worker_event_target(GC::Ref target) } // https://html.spec.whatwg.org/multipage/web-messaging.html#message-ports:transfer-steps -WebIDL::ExceptionOr MessagePort::transfer_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr MessagePort::transfer_steps(HTML::TransferDataEncoder& data_holder) { // 1. Set value's has been shipped flag to true. m_has_been_shipped = true; @@ -102,23 +102,23 @@ WebIDL::ExceptionOr MessagePort::transfer_steps(HTML::TransferDataHolder& // 1. Set remotePort's has been shipped flag to true. m_remote_port->m_has_been_shipped = true; - // 2. Set dataHolder.[[RemotePort]] to remotePort. - // TODO: Mach IPC auto fd = MUST(m_transport->release_underlying_transport_for_transfer()); m_transport.clear(); - data_holder.fds.append(IPC::File::adopt_fd(fd)); - data_holder.data.append(IPC_FILE_TAG); - } + // 2. Set dataHolder.[[RemotePort]] to remotePort. + // TODO: Mach IPC + data_holder.encode(IPC_FILE_TAG); + data_holder.encode(IPC::File::adopt_fd(fd)); + } // 4. Otherwise, set dataHolder.[[RemotePort]] to null. else { - data_holder.data.append(0); + data_holder.encode(0); } return {}; } -WebIDL::ExceptionOr MessagePort::transfer_receiving_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr MessagePort::transfer_receiving_steps(HTML::TransferDataDecoder& data_holder) { // 1. Set value's has been shipped flag to true. m_has_been_shipped = true; @@ -129,10 +129,9 @@ WebIDL::ExceptionOr MessagePort::transfer_receiving_steps(HTML::TransferDa // 3. If dataHolder.[[RemotePort]] is not null, then entangle dataHolder.[[RemotePort]] and value. // (This will disentangle dataHolder.[[RemotePort]] from the original port that was transferred.) - auto fd_tag = data_holder.data.take_first(); - if (fd_tag == IPC_FILE_TAG) { + if (auto fd_tag = data_holder.decode(); fd_tag == IPC_FILE_TAG) { // TODO: Mach IPC - auto fd = data_holder.fds.take_first(); + auto fd = data_holder.decode(); m_transport = make(MUST(Core::LocalSocket::adopt_fd(fd.take_fd()))); m_transport->set_up_read_hook([strong_this = GC::make_root(this)]() { diff --git a/Libraries/LibWeb/HTML/MessagePort.h b/Libraries/LibWeb/HTML/MessagePort.h index 7e903dd3f30..1c32a14eff5 100644 --- a/Libraries/LibWeb/HTML/MessagePort.h +++ b/Libraries/LibWeb/HTML/MessagePort.h @@ -59,8 +59,8 @@ public: GC::Ptr onmessage(); // ^Transferable - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) override; - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) override; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) override; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) override; virtual HTML::TransferType primary_interface() const override { return m_primary_interface; } void set_worker_event_target(GC::Ref); diff --git a/Libraries/LibWeb/HTML/OffscreenCanvas.cpp b/Libraries/LibWeb/HTML/OffscreenCanvas.cpp index 4257ec23051..1d267a97b6d 100644 --- a/Libraries/LibWeb/HTML/OffscreenCanvas.cpp +++ b/Libraries/LibWeb/HTML/OffscreenCanvas.cpp @@ -80,17 +80,17 @@ OffscreenCanvas::OffscreenCanvas(JS::Realm& realm, RefPtr bitmap) OffscreenCanvas::~OffscreenCanvas() = default; -WebIDL::ExceptionOr OffscreenCanvas::transfer_steps(HTML::TransferDataHolder&) +WebIDL::ExceptionOr OffscreenCanvas::transfer_steps(HTML::TransferDataEncoder&) { // FIXME: Implement this - dbgln("(STUBBED) OffscreenCanvas::transfer_steps(HTML::TransferDataHolder&)"); + dbgln("(STUBBED) OffscreenCanvas::transfer_steps(HTML::TransferDataEncoder&)"); return {}; } -WebIDL::ExceptionOr OffscreenCanvas::transfer_receiving_steps(HTML::TransferDataHolder&) +WebIDL::ExceptionOr OffscreenCanvas::transfer_receiving_steps(HTML::TransferDataDecoder&) { // FIXME: Implement this - dbgln("(STUBBED) OffscreenCanvas::transfer_receiving_steps(HTML::TransferDataHolder&)"); + dbgln("(STUBBED) OffscreenCanvas::transfer_receiving_steps(HTML::TransferDataDecoder&)"); return {}; } diff --git a/Libraries/LibWeb/HTML/OffscreenCanvas.h b/Libraries/LibWeb/HTML/OffscreenCanvas.h index 050877faae0..2e31ca109d0 100644 --- a/Libraries/LibWeb/HTML/OffscreenCanvas.h +++ b/Libraries/LibWeb/HTML/OffscreenCanvas.h @@ -42,8 +42,8 @@ public: virtual ~OffscreenCanvas() override; // ^Web::Bindings::Transferable - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) override; - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) override; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) override; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) override; virtual HTML::TransferType primary_interface() const override; WebIDL::UnsignedLong width() const; diff --git a/Libraries/LibWeb/HTML/StructuredSerialize.cpp b/Libraries/LibWeb/HTML/StructuredSerialize.cpp index a817fe71aee..eca0250cabc 100644 --- a/Libraries/LibWeb/HTML/StructuredSerialize.cpp +++ b/Libraries/LibWeb/HTML/StructuredSerialize.cpp @@ -9,14 +9,9 @@ #include #include -#include -#include -#include #include -#include #include #include -#include #include #include #include @@ -27,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -39,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -70,83 +63,41 @@ #include #include #include -#include 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 class ValueTag : u8 { + Empty, // Unused, for ease of catching bugs. -enum ValueTag { - // Unused, for ease of catching bugs. - Empty, - - // UndefinedPrimitive is serialized indicating that the Type is Undefined, no value is serialized. UndefinedPrimitive, - - // NullPrimitive is serialized indicating that the Type is Null, no value is serialized. NullPrimitive, - - // Following u32 is the boolean value. BooleanPrimitive, - - // Following two u32s are the double value. NumberPrimitive, - - // The BigIntPrimitive is serialized as a string in base 10 representation. - // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation. + StringPrimitive, BigIntPrimitive, - // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation. - StringPrimitive, - BooleanObject, - NumberObject, - - BigIntObject, - StringObject, - + BigIntObject, DateObject, - RegExpObject, + MapObject, + SetObject, + ArrayObject, + ErrorObject, + Object, + ObjectReference, GrowableSharedArrayBuffer, - SharedArrayBuffer, - ResizeableArrayBuffer, - ArrayBuffer, - ArrayBufferView, - MapObject, - - SetObject, - - ErrorObject, - - ArrayObject, - - Object, - - ObjectReference, - SerializableObject, // TODO: Define many more types - - // This tag or higher are understood to be errors - ValueTagMax, }; enum ErrorType { @@ -167,10 +118,14 @@ static ErrorType error_name_to_type(String const& name) return Error; } +static WebIDL::ExceptionOr serialize_array_buffer(JS::VM&, TransferDataEncoder&, JS::ArrayBuffer const&, bool for_storage); + +template ViewType> +static WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM&, TransferDataEncoder&, ViewType const&, bool for_storage, SerializationMemory&); + // 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, SerializationMemory& memory, bool for_storage) @@ -185,103 +140,113 @@ public: // https://whatpr.org/html/9893/structured-data.html#structuredserializeinternal WebIDL::ExceptionOr serialize(JS::Value value) { + TransferDataEncoder serialized; + // 2. If memory[value] exists, then return memory[value]. if (m_memory.contains(value)) { - auto index = m_memory.get(value).value(); - return Vector { ValueTag::ObjectReference, index }; + serialized.encode(ValueTag::ObjectReference); + serialized.encode(m_memory.get(value).value()); + return serialized.take_buffer().take_data(); } // 3. Let deep be false. auto deep = false; - bool return_primitive_type = true; // 4. If value is undefined, null, a Boolean, a Number, a BigInt, or a String, then return { [[Type]]: "primitive", [[Value]]: value }. + bool return_primitive_type = true; + if (value.is_undefined()) { - serialize_enum(m_serialized, ValueTag::UndefinedPrimitive); + serialized.encode(ValueTag::UndefinedPrimitive); } else if (value.is_null()) { - serialize_enum(m_serialized, ValueTag::NullPrimitive); + serialized.encode(ValueTag::NullPrimitive); } else if (value.is_boolean()) { - serialize_enum(m_serialized, ValueTag::BooleanPrimitive); - serialize_boolean_primitive(m_serialized, value); + serialized.encode(ValueTag::BooleanPrimitive); + serialized.encode(value.as_bool()); } else if (value.is_number()) { - serialize_enum(m_serialized, ValueTag::NumberPrimitive); - serialize_number_primitive(m_serialized, value); + serialized.encode(ValueTag::NumberPrimitive); + serialized.encode(value.as_double()); } else if (value.is_bigint()) { - serialize_enum(m_serialized, ValueTag::BigIntPrimitive); - TRY(serialize_big_int_primitive(m_vm, m_serialized, value)); + serialized.encode(ValueTag::BigIntPrimitive); + serialized.encode(MUST(value.as_bigint().big_integer().to_base(10))); } else if (value.is_string()) { - serialize_enum(m_serialized, ValueTag::StringPrimitive); - TRY(serialize_string_primitive(m_vm, m_serialized, value)); + serialized.encode(ValueTag::StringPrimitive); + serialized.encode(value.as_string().utf8_string()); } else { return_primitive_type = false; } if (return_primitive_type) - return m_serialized; + return serialized.take_buffer().take_data(); // 5. If value is a Symbol, then throw a "DataCloneError" DOMException. if (value.is_symbol()) return WebIDL::DataCloneError::create(*m_vm.current_realm(), "Cannot serialize Symbol"_string); // 6. Let serialized be an uninitialized value. + // NOTE: We created the serialized value above. if (value.is_object()) { auto const& object = value.as_object(); // 7. If value has a [[BooleanData]] internal slot, then set serialized to { [[Type]]: "Boolean", [[BooleanData]]: value.[[BooleanData]] }. - if (is(object)) { - serialize_enum(m_serialized, ValueTag::BooleanObject); - serialize_boolean_object(m_serialized, value); + if (auto const* boolean_object = as_if(object)) { + serialized.encode(ValueTag::BooleanObject); + serialized.encode(boolean_object->boolean()); } // 8. Otherwise, if value has a [[NumberData]] internal slot, then set serialized to { [[Type]]: "Number", [[NumberData]]: value.[[NumberData]] }. - else if (is(object)) { - serialize_enum(m_serialized, ValueTag::NumberObject); - serialize_number_object(m_serialized, value); + else if (auto const* number_object = as_if(object)) { + serialized.encode(ValueTag::NumberObject); + serialized.encode(number_object->number()); } // 9. Otherwise, if value has a [[BigIntData]] internal slot, then set serialized to { [[Type]]: "BigInt", [[BigIntData]]: value.[[BigIntData]] }. - else if (is(object)) { - serialize_enum(m_serialized, ValueTag::BigIntObject); - TRY(serialize_big_int_object(m_vm, m_serialized, value)); + else if (auto const* big_int_object = as_if(object)) { + serialized.encode(ValueTag::BigIntObject); + serialized.encode(MUST(big_int_object->bigint().big_integer().to_base(10))); } // 10. Otherwise, if value has a [[StringData]] internal slot, then set serialized to { [[Type]]: "String", [[StringData]]: value.[[StringData]] }. - else if (is(object)) { - serialize_enum(m_serialized, ValueTag::StringObject); - TRY(serialize_string_object(m_vm, m_serialized, value)); + else if (auto const* string_object = as_if(object)) { + serialized.encode(ValueTag::StringObject); + serialized.encode(string_object->primitive_string().utf8_string()); } // 11. Otherwise, if value has a [[DateValue]] internal slot, then set serialized to { [[Type]]: "Date", [[DateValue]]: value.[[DateValue]] }. - else if (is(object)) { - serialize_enum(m_serialized, ValueTag::DateObject); - serialize_date_object(m_serialized, value); + else if (auto const* date = as_if(object)) { + serialized.encode(ValueTag::DateObject); + serialized.encode(date->date_value()); } // 12. Otherwise, if value has a [[RegExpMatcher]] internal slot, then set serialized to // { [[Type]]: "RegExp", [[RegExpMatcher]]: value.[[RegExpMatcher]], [[OriginalSource]]: value.[[OriginalSource]], // [[OriginalFlags]]: value.[[OriginalFlags]] }. - else if (is(object)) { - serialize_enum(m_serialized, ValueTag::RegExpObject); - TRY(serialize_reg_exp_object(m_vm, m_serialized, value)); + else if (auto const* reg_exp_object = as_if(object)) { + // NOTE: A Regex object is perfectly happy to be reconstructed with just the source+flags. + // In the future, we could optimize the work being done on the deserialize step by serializing + // more of the internal state (the [[RegExpMatcher]] internal slot). + serialized.encode(ValueTag::RegExpObject); + serialized.encode(reg_exp_object->pattern()); + serialized.encode(reg_exp_object->flags()); } // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then: else if (auto const* array_buffer = as_if(object)) { - TRY(serialize_array_buffer(m_vm, m_serialized, *array_buffer, m_for_storage)); + TRY(serialize_array_buffer(m_vm, serialized, *array_buffer, m_for_storage)); } // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then: else if (auto const* typed_array_base = as_if(object)) { - TRY(serialize_viewed_array_buffer(m_vm, m_serialized, *typed_array_base, m_for_storage, m_memory)); + TRY(serialize_viewed_array_buffer(m_vm, serialized, *typed_array_base, m_for_storage, m_memory)); } else if (auto const* data_view = as_if(object)) { - TRY(serialize_viewed_array_buffer(m_vm, m_serialized, *data_view, m_for_storage, m_memory)); + TRY(serialize_viewed_array_buffer(m_vm, serialized, *data_view, m_for_storage, m_memory)); } // 15. Otherwise, if value has a [[MapData]] internal slot, then: else if (is(object)) { // 1. Set serialized to { [[Type]]: "Map", [[MapData]]: a new empty List }. - serialize_enum(m_serialized, ValueTag::MapObject); + serialized.encode(ValueTag::MapObject); + // 2. Set deep to true. deep = true; } @@ -289,7 +254,8 @@ public: // 16. Otherwise, if value has a [[SetData]] internal slot, then: else if (is(object)) { // 1. Set serialized to { [[Type]]: "Set", [[SetData]]: a new empty List }. - serialize_enum(m_serialized, ValueTag::SetObject); + serialized.encode(ValueTag::SetObject); + // 2. Set deep to true. deep = true; } @@ -316,11 +282,9 @@ public: // 5. Set serialized to { [[Type]]: "Error", [[Name]]: name, [[Message]]: message }. // FIXME: 6. User agents should attach a serialized representation of any interesting accompanying data which are not yet specified, notably the stack property, to serialized. - serialize_enum(m_serialized, ValueTag::ErrorObject); - serialize_enum(m_serialized, type); - serialize_primitive_type(m_serialized, message.has_value()); - if (message.has_value()) - TRY(serialize_string(m_vm, m_serialized, *message)); + serialized.encode(ValueTag::ErrorObject); + serialized.encode(type); + serialized.encode(message); } // 18. Otherwise, if value is an Array exotic object, then: @@ -331,8 +295,8 @@ public: u64 length = MUST(JS::length_of_array_like(m_vm, object)); // 3. Set serialized to { [[Type]]: "Array", [[Length]]: valueLen, [[Properties]]: a new empty List }. - serialize_enum(m_serialized, ValueTag::ArrayObject); - serialize_primitive_type(m_serialized, length); + serialized.encode(ValueTag::ArrayObject); + serialized.encode(length); // 4. Set deep to true. deep = true; @@ -344,8 +308,8 @@ public: // 2. Let typeString be the identifier of the primary interface of value. // 3. Set serialized to { [[Type]]: typeString }. - serialize_enum(m_serialized, ValueTag::SerializableObject); - serialize_enum(m_serialized, serializable->serialize_type()); + serialized.encode(ValueTag::SerializableObject); + serialized.encode(serializable->serialize_type()); // 4. Set deep to true deep = true; @@ -368,7 +332,7 @@ public: // 24. Otherwise: else { // 1. Set serialized to { [[Type]]: "Object", [[Properties]]: a new empty List }. - serialize_enum(m_serialized, ValueTag::Object); + serialized.encode(ValueTag::Object); // 2. Set deep to true. deep = true; @@ -395,8 +359,9 @@ public: copied_list.append(entry.key); copied_list.append(entry.value); } - u64 size = map->map_size(); - m_serialized.append(bit_cast(&size), 2); + + serialized.encode(map->map_size()); + // 3. For each Record { [[Key]], [[Value]] } entry of copiedList: for (auto copied_value : copied_list) { // 1. Let serializedKey be ? StructuredSerializeInternal(entry.[[Key]], forStorage, memory). @@ -404,7 +369,7 @@ public: auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory)); // 3. Append { [[Key]]: serializedKey, [[Value]]: serializedValue } to serialized.[[MapData]]. - m_serialized.extend(serialized_value); + serialized.append(move(serialized_value)); } } @@ -419,27 +384,30 @@ public: // 1. If entry is not the special value empty, append entry to copiedList. copied_list.append(entry.key); } - serialize_primitive_type(m_serialized, set->set_size()); + + serialized.encode(set->set_size()); + // 3. For each entry of copiedList: for (auto copied_value : copied_list) { // 1. Let serializedEntry be ? StructuredSerializeInternal(entry, forStorage, memory). auto serialized_value = TRY(structured_serialize_internal(m_vm, copied_value, m_for_storage, m_memory)); // 2. Append serializedEntry to serialized.[[SetData]]. - m_serialized.extend(serialized_value); + serialized.append(move(serialized_value)); } } // 3. Otherwise, if value is a platform object that is a serializable object, then perform the serialization steps for value's primary interface, given value, serialized, and forStorage. else if (auto* serializable = as_if(object)) { - TRY(serializable->serialization_steps(m_serialized, m_for_storage, m_memory)); + TRY(serializable->serialization_steps(serialized, m_for_storage, m_memory)); } // 4. Otherwise, for each key in ! EnumerableOwnProperties(value, key): else { u64 property_count = 0; - auto count_offset = m_serialized.size(); - serialize_primitive_type(m_serialized, property_count); + auto count_offset = serialized.buffer().data().size(); + serialized.encode(property_count); + for (auto key : MUST(object.enumerable_own_property_names(JS::Object::PropertyKind::Key))) { auto property_key = MUST(JS::PropertyKey::from_value(m_vm, key)); @@ -452,147 +420,36 @@ public: auto output_value = TRY(structured_serialize_internal(m_vm, input_value, m_for_storage, m_memory)); // 3. Append { [[Key]]: key, [[Value]]: outputValue } to serialized.[[Properties]]. - TRY(serialize_string(m_vm, m_serialized, key.as_string())); - m_serialized.extend(output_value); + serialized.encode(key.as_string().utf8_string()); + serialized.append(move(output_value)); - property_count++; + ++property_count; } } - memcpy(m_serialized.data() + count_offset, &property_count, sizeof(property_count)); + + if (property_count) { + auto* data = const_cast(serialized.buffer().data().data()); + memcpy(data + count_offset, &property_count, sizeof(property_count)); + } } } // 27. Return serialized. - return m_serialized; + return serialized.take_buffer().take_data(); } private: JS::VM& m_vm; SerializationMemory& m_memory; // JS value -> index u32 m_next_id { 0 }; - SerializationRecord m_serialized; bool m_for_storage { false }; }; -void serialize_boolean_primitive(SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_boolean()); - serialize_primitive_type(serialized, value.as_bool()); -} - -void serialize_number_primitive(SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_number()); - serialize_primitive_type(serialized, value.as_double()); -} - -WebIDL::ExceptionOr serialize_big_int_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_bigint()); - auto& val = value.as_bigint(); - TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, val.to_string()))); - return {}; -} - -WebIDL::ExceptionOr serialize_string_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_string()); - TRY(serialize_string(vm, serialized, value.as_string())); - return {}; -} - -void serialize_boolean_object(SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_object() && is(value.as_object())); - auto& boolean_object = static_cast(value.as_object()); - serialize_primitive_type(serialized, boolean_object.boolean()); -} - -void serialize_number_object(SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_object() && is(value.as_object())); - auto& number_object = static_cast(value.as_object()); - serialize_primitive_type(serialized, number_object.number()); -} - -WebIDL::ExceptionOr serialize_big_int_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_object() && is(value.as_object())); - auto& bigint_object = static_cast(value.as_object()); - TRY(serialize_string(vm, serialized, TRY_OR_THROW_OOM(vm, bigint_object.bigint().to_string()))); - return {}; -} - -WebIDL::ExceptionOr serialize_string_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_object() && is(value.as_object())); - auto& string_object = static_cast(value.as_object()); - TRY(serialize_string(vm, serialized, string_object.primitive_string())); - return {}; -} - -void serialize_date_object(SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_object() && is(value.as_object())); - auto& date_object = static_cast(value.as_object()); - serialize_primitive_type(serialized, date_object.date_value()); -} - -WebIDL::ExceptionOr serialize_reg_exp_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value) -{ - VERIFY(value.is_object() && is(value.as_object())); - auto& regexp_object = static_cast(value.as_object()); - // Note: A Regex object is perfectly happy to be reconstructed with just the source+flags - // In the future, we could optimize the work being done on the deserialize step by serializing - // more of the internal state (the [[RegExpMatcher]] internal slot) - TRY(serialize_string(vm, serialized, regexp_object.pattern())); - TRY(serialize_string(vm, serialized, regexp_object.flags())); - return {}; -} - -WebIDL::ExceptionOr serialize_bytes(JS::VM& vm, Vector& vector, ReadonlyBytes bytes) -{ - // Append size of the buffer to the serialized structure. - u64 const size = bytes.size(); - serialize_primitive_type(vector, size); - // Append the bytes of the buffer to the serialized structure. - u64 byte_position = 0; - while (byte_position < size) { - u32 combined_value = 0; - for (u8 i = 0; i < 4; ++i) { - u8 const byte = bytes[byte_position]; - combined_value |= byte << (i * 8); - byte_position++; - if (byte_position == size) - break; - } - TRY_OR_THROW_OOM(vm, vector.try_append(combined_value)); - } - return {}; -} - -WebIDL::ExceptionOr serialize_string(JS::VM& vm, Vector& vector, StringView string) -{ - return serialize_bytes(vm, vector, string.bytes()); -} - -WebIDL::ExceptionOr serialize_string(JS::VM& vm, Vector& vector, String const& string) -{ - return serialize_bytes(vm, vector, { string.code_points().bytes(), string.code_points().byte_length() }); -} - -WebIDL::ExceptionOr serialize_string(JS::VM& vm, Vector& vector, JS::PrimitiveString const& primitive_string) -{ - auto string = primitive_string.utf8_string(); - TRY(serialize_string(vm, vector, string)); - return {}; -} - -WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector, JS::ArrayBuffer const& array_buffer, bool for_storage) +WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, TransferDataEncoder& data_holder, JS::ArrayBuffer const& array_buffer, bool for_storage) { // 13. Otherwise, if value has an [[ArrayBufferData]] internal slot, then: - // 1. If IsSharedArrayBuffer(value) is true, then: + // 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 @@ -609,18 +466,17 @@ WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector // [[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()); + data_holder.encode(ValueTag::GrowableSharedArrayBuffer); + data_holder.encode(array_buffer.buffer()); + data_holder.encode(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())); + data_holder.encode(ValueTag::SharedArrayBuffer); + data_holder.encode(array_buffer.buffer()); } } - // 2. Otherwise: else { // 1. If IsDetachedBuffer(value) is true, then throw a "DataCloneError" DOMException. @@ -640,30 +496,29 @@ WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector // 5. If value has an [[ArrayBufferMaxByteLength]] internal slot, then set serialized to { [[Type]]: "ResizableArrayBuffer", // [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size, [[ArrayBufferMaxByteLength]]: value.[[ArrayBufferMaxByteLength]] }. 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()); + data_holder.encode(ValueTag::ResizeableArrayBuffer); + data_holder.encode(data_copy.buffer()); + data_holder.encode(array_buffer.max_byte_length()); } // 6. Otherwise, set serialized to { [[Type]]: "ArrayBuffer", [[ArrayBufferData]]: dataCopy, [[ArrayBufferByteLength]]: size }. else { - serialize_enum(vector, ValueTag::ArrayBuffer); - TRY(serialize_bytes(vm, vector, data_copy.buffer().bytes())); + data_holder.encode(ValueTag::ArrayBuffer); + data_holder.encode(data_copy.buffer()); } } return {}; } template ViewType> -WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& vector, ViewType const& view, bool for_storage, SerializationMemory& memory) +WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, TransferDataEncoder& data_holder, ViewType const& view, bool for_storage, SerializationMemory& memory) { // 14. Otherwise, if value has a [[ViewedArrayBuffer]] internal slot, then: auto view_record = [&]() { - if constexpr (IsSame) { + if constexpr (IsSame) return JS::make_data_view_with_buffer_witness_record(view, JS::ArrayBuffer::Order::SeqCst); - } else { + else return JS::make_typed_array_with_buffer_witness_record(view, JS::ArrayBuffer::Order::SeqCst); - } }(); // 1. If IsArrayBufferViewOutOfBounds(value) is true, then throw a "DataCloneError" DOMException. @@ -683,23 +538,20 @@ WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& // 4. Assert: bufferSerialized.[[Type]] is "ArrayBuffer", "ResizableArrayBuffer", "SharedArrayBuffer", or "GrowableSharedArrayBuffer". // NOTE: Object reference + memory check is required when ArrayBuffer is transferred. - auto tag = buffer_serialized[0]; - VERIFY(tag == ValueTag::ArrayBuffer - || tag == ValueTag::ResizeableArrayBuffer - || tag == ValueTag::SharedArrayBuffer - || tag == ValueTag::GrowableSharedArrayBuffer + auto tag = TransferDataDecoder { buffer_serialized }.decode(); + + VERIFY(first_is_one_of(tag, ValueTag::ArrayBuffer, ValueTag::ResizeableArrayBuffer, ValueTag::SharedArrayBuffer, 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]] }. if constexpr (IsSame) { - serialize_enum(vector, ValueTag::ArrayBufferView); - vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]] - TRY(serialize_string(vm, vector, "DataView"_string)); // [[Constructor]] - serialize_primitive_type(vector, JS::get_view_byte_length(view_record)); - serialize_primitive_type(vector, view.byte_offset()); + data_holder.encode(ValueTag::ArrayBufferView); + data_holder.append(move(buffer_serialized)); // [[ArrayBufferSerialized]] + data_holder.encode("DataView"_string); // [[Constructor]] + data_holder.encode(JS::get_view_byte_length(view_record)); + data_holder.encode(view.byte_offset()); } - // 6. Otherwise: else { // 1. Assert: value has a [[TypedArrayName]] internal slot. @@ -707,42 +559,42 @@ WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& // 2. Set serialized to { [[Type]]: "ArrayBufferView", [[Constructor]]: value.[[TypedArrayName]], // [[ArrayBufferSerialized]]: bufferSerialized, [[ByteLength]]: value.[[ByteLength]], // [[ByteOffset]]: value.[[ByteOffset]], [[ArrayLength]]: value.[[ArrayLength]] }. - serialize_enum(vector, ValueTag::ArrayBufferView); - vector.extend(move(buffer_serialized)); // [[ArrayBufferSerialized]] - TRY(serialize_string(vm, vector, view.element_name().to_string())); // [[Constructor]] - serialize_primitive_type(vector, JS::typed_array_byte_length(view_record)); - serialize_primitive_type(vector, view.byte_offset()); - serialize_primitive_type(vector, JS::typed_array_length(view_record)); + data_holder.encode(ValueTag::ArrayBufferView); + data_holder.append(move(buffer_serialized)); // [[ArrayBufferSerialized]] + data_holder.encode(view.element_name().to_string()); // [[Constructor]] + data_holder.encode(JS::typed_array_byte_length(view_record)); + data_holder.encode(view.byte_offset()); + data_holder.encode(JS::typed_array_length(view_record)); } + return {}; } -template WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& vector, JS::TypedArrayBase const& view, bool for_storage, SerializationMemory& memory); -template WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& vector, JS::DataView const& view, bool for_storage, SerializationMemory& memory); + +template WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM&, TransferDataEncoder&, JS::TypedArrayBase const&, bool, SerializationMemory&); +template WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM&, TransferDataEncoder&, JS::DataView const&, bool, SerializationMemory&); class Deserializer { public: - Deserializer(JS::VM& vm, JS::Realm& target_realm, ReadonlySpan serialized, DeserializationMemory& memory, Optional position = {}) + Deserializer(JS::VM& vm, TransferDataDecoder& serialized, JS::Realm& target_realm, DeserializationMemory& memory) : m_vm(vm) , m_serialized(serialized) , m_memory(memory) - , m_position(position.value_or(0)) { VERIFY(vm.current_realm() == &target_realm); } - size_t position() const { return m_position; } - // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize WebIDL::ExceptionOr deserialize() { - auto tag = deserialize_primitive_type(m_serialized, m_position); + auto& realm = *m_vm.current_realm(); + + auto tag = m_serialized.decode(); // 2. If memory[serialized] exists, then return memory[serialized]. if (tag == ValueTag::ObjectReference) { - auto index = m_serialized[m_position++]; - if (index == NumericLimits::max()) { + auto index = m_serialized.decode(); + if (index == NumericLimits::max()) return JS::Object::create(*m_vm.current_realm(), nullptr); - } return m_memory[index]; } @@ -753,234 +605,233 @@ public: JS::Value value; auto is_primitive = false; + + auto decode_string = [&]() { + auto string = m_serialized.decode(); + return JS::PrimitiveString::create(m_vm, string); + }; + + auto decode_big_int = [&]() { + auto string = m_serialized.decode(); + return JS::BigInt::create(m_vm, MUST(::Crypto::SignedBigInteger::from_base(10, string))); + }; + switch (tag) { // 5. If serialized.[[Type]] is "primitive", then set value to serialized.[[Value]]. - case ValueTag::UndefinedPrimitive: { + case ValueTag::UndefinedPrimitive: value = JS::js_undefined(); is_primitive = true; break; - } - case ValueTag::NullPrimitive: { + case ValueTag::NullPrimitive: value = JS::js_null(); is_primitive = true; break; - } - case ValueTag::BooleanPrimitive: { - value = JS::Value { deserialize_boolean_primitive(m_serialized, m_position) }; + case ValueTag::BooleanPrimitive: + value = JS::Value { m_serialized.decode() }; is_primitive = true; break; - } - case ValueTag::NumberPrimitive: { - value = JS::Value { deserialize_number_primitive(m_serialized, m_position) }; + case ValueTag::NumberPrimitive: + value = JS::Value { m_serialized.decode() }; is_primitive = true; break; - } - case ValueTag::BigIntPrimitive: { - auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_serialized, m_position)); - value = JS::Value { big_int }; + case ValueTag::BigIntPrimitive: + value = decode_big_int(); is_primitive = true; break; - } - case ValueTag::StringPrimitive: { - auto string = TRY(deserialize_string_primitive(m_vm, m_serialized, m_position)); - value = JS::Value { string }; + case ValueTag::StringPrimitive: + value = decode_string(); is_primitive = true; break; - } + // 6. Otherwise, if serialized.[[Type]] is "Boolean", then set value to a new Boolean object in targetRealm whose [[BooleanData]] internal slot value is serialized.[[BooleanData]]. - case BooleanObject: { - value = deserialize_boolean_object(*m_vm.current_realm(), m_serialized, m_position); + case ValueTag::BooleanObject: + value = JS::BooleanObject::create(realm, m_serialized.decode()); break; - } + // 7. Otherwise, if serialized.[[Type]] is "Number", then set value to a new Number object in targetRealm whose [[NumberData]] internal slot value is serialized.[[NumberData]]. - case ValueTag::NumberObject: { - value = deserialize_number_object(*m_vm.current_realm(), m_serialized, m_position); + case ValueTag::NumberObject: + value = JS::NumberObject::create(realm, m_serialized.decode()); break; - } + // 8. Otherwise, if serialized.[[Type]] is "BigInt", then set value to a new BigInt object in targetRealm whose [[BigIntData]] internal slot value is serialized.[[BigIntData]]. - case ValueTag::BigIntObject: { - value = TRY(deserialize_big_int_object(*m_vm.current_realm(), m_serialized, m_position)); + case ValueTag::BigIntObject: + value = JS::BigIntObject::create(realm, decode_big_int()); break; - } + // 9. Otherwise, if serialized.[[Type]] is "String", then set value to a new String object in targetRealm whose [[StringData]] internal slot value is serialized.[[StringData]]. - case ValueTag::StringObject: { - value = TRY(deserialize_string_object(*m_vm.current_realm(), m_serialized, m_position)); + case ValueTag::StringObject: + value = JS::StringObject::create(realm, decode_string(), realm.intrinsics().string_prototype()); break; - } + // 10. Otherwise, if serialized.[[Type]] is "Date", then set value to a new Date object in targetRealm whose [[DateValue]] internal slot value is serialized.[[DateValue]]. - case ValueTag::DateObject: { - value = deserialize_date_object(*m_vm.current_realm(), m_serialized, m_position); + case ValueTag::DateObject: + value = JS::Date::create(realm, m_serialized.decode()); break; - } + // 11. Otherwise, if serialized.[[Type]] is "RegExp", then set value to a new RegExp object in targetRealm whose [[RegExpMatcher]] internal slot value is serialized.[[RegExpMatcher]], // whose [[OriginalSource]] internal slot value is serialized.[[OriginalSource]], and whose [[OriginalFlags]] internal slot value is serialized.[[OriginalFlags]]. case ValueTag::RegExpObject: { - value = TRY(deserialize_reg_exp_object(*m_vm.current_realm(), m_serialized, m_position)); + auto pattern = decode_string(); + auto flags = decode_string(); + + value = MUST(JS::regexp_create(m_vm, pattern, flags)); break; } + // 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().shared_array_buffer_constructor(), bytes.size())); - bytes.span().copy_to(buffer->buffer().span()); - value = buffer; + auto buffer = TRY(m_serialized.decode_buffer(realm)); + value = JS::ArrayBuffer::create(realm, move(buffer), JS::DataBlock::Shared::Yes); 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().shared_array_buffer_constructor(), bytes.size())); - bytes.span().copy_to(buffer->buffer().span()); - buffer->set_max_byte_length(max_byte_length); - value = buffer; + auto buffer = TRY(m_serialized.decode_buffer(realm)); + auto max_byte_length = m_serialized.decode(); + + auto data = JS::ArrayBuffer::create(realm, move(buffer), JS::DataBlock::Shared::Yes); + data->set_max_byte_length(max_byte_length); + + value = data; 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(); - // 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); - value = JS::ArrayBuffer::create(*realm, bytes_or_error.release_value()); + auto buffer = TRY(m_serialized.decode_buffer(realm)); + value = JS::ArrayBuffer::create(realm, move(buffer)); break; } + // 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; + auto buffer = TRY(m_serialized.decode_buffer(realm)); + auto max_byte_length = m_serialized.decode(); + + auto data = JS::ArrayBuffer::create(realm, move(buffer)); + data->set_max_byte_length(max_byte_length); + + value = data; break; } + // 16. Otherwise, if serialized.[[Type]] is "ArrayBufferView", then: case ValueTag::ArrayBufferView: { - auto* realm = m_vm.current_realm(); auto array_buffer_value = TRY(deserialize()); auto& array_buffer = as(array_buffer_value.as_object()); - auto constructor_name = TRY(deserialize_string(m_vm, m_serialized, m_position)); - u32 byte_length = deserialize_primitive_type(m_serialized, m_position); - u32 byte_offset = deserialize_primitive_type(m_serialized, m_position); + + auto constructor_name = m_serialized.decode(); + auto byte_length = m_serialized.decode(); + auto byte_offset = m_serialized.decode(); if (constructor_name == "DataView"sv) { - value = JS::DataView::create(*realm, &array_buffer, byte_length, byte_offset); + value = JS::DataView::create(realm, &array_buffer, byte_length, byte_offset); } else { - u32 array_length = deserialize_primitive_type(m_serialized, m_position); - GC::Ptr typed_array_ptr; -#define CREATE_TYPED_ARRAY(ClassName) \ - if (constructor_name == #ClassName##sv) \ - typed_array_ptr = JS::ClassName::create(*realm, array_length, array_buffer); + auto array_length = m_serialized.decode(); + + GC::Ptr typed_array; #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \ - CREATE_TYPED_ARRAY(ClassName) + if (constructor_name == #ClassName##sv) \ + typed_array = JS::ClassName::create(realm, array_length, array_buffer); 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); - value = typed_array_ptr; + + VERIFY(typed_array); // FIXME: Handle errors better here? Can a fuzzer put weird stuff in the buffer? + + typed_array->set_byte_length(byte_length); + typed_array->set_byte_offset(byte_offset); + value = typed_array; } break; } + // 17. Otherwise, if serialized.[[Type]] is "Map", then: case ValueTag::MapObject: { - auto& realm = *m_vm.current_realm(); // 1. Set value to a new Map object in targetRealm whose [[MapData]] internal slot value is a new empty List. value = JS::Map::create(realm); + // 2. Set deep to true. deep = true; break; } + // 18. Otherwise, if serialized.[[Type]] is "Set", then: case ValueTag::SetObject: { - auto& realm = *m_vm.current_realm(); // 1. Set value to a new Set object in targetRealm whose [[SetData]] internal slot value is a new empty List. value = JS::Set::create(realm); + // 2. Set deep to true. deep = true; break; } + // 19. Otherwise, if serialized.[[Type]] is "Array", then: case ValueTag::ArrayObject: { - auto& realm = *m_vm.current_realm(); // 1. Let outputProto be targetRealm.[[Intrinsics]].[[%Array.prototype%]]. // 2. Set value to ! ArrayCreate(serialized.[[Length]], outputProto). - auto length = deserialize_primitive_type(m_serialized, m_position); - value = MUST(JS::Array::create(realm, length)); + value = MUST(JS::Array::create(realm, m_serialized.decode(), realm.intrinsics().array_prototype())); + // 3. Set deep to true. deep = true; break; } + // 20. Otherwise, if serialized.[[Type]] is "Object", then: case ValueTag::Object: { - auto& realm = *m_vm.current_realm(); // 1. Set value to a new Object in targetRealm. value = JS::Object::create(realm, realm.intrinsics().object_prototype()); + // 2. Set deep to true. deep = true; break; } + // 21. Otherwise, if serialized.[[Type]] is "Error", then: case ValueTag::ErrorObject: { - auto& realm = *m_vm.current_realm(); - auto type = deserialize_primitive_type(m_serialized, m_position); - auto has_message = deserialize_primitive_type(m_serialized, m_position); - if (has_message) { - auto message = TRY(deserialize_string(m_vm, m_serialized, m_position)); - switch (type) { - case ErrorType::Error: - value = JS::Error::create(realm, message); - break; + auto type = m_serialized.decode(); + auto message = m_serialized.decode>(); + + GC::Ptr error; + + switch (type) { + case ErrorType::Error: + error = JS::Error::create(realm); + break; #define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ case ErrorType::ClassName: \ - value = JS::ClassName::create(realm, message); \ + error = JS::ClassName::create(realm); \ break; - JS_ENUMERATE_NATIVE_ERRORS + JS_ENUMERATE_NATIVE_ERRORS #undef __JS_ENUMERATE - } - } else { - switch (type) { - case ErrorType::Error: - value = JS::Error::create(realm); - break; -#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \ - case ErrorType::ClassName: \ - value = JS::ClassName::create(realm); \ - break; - JS_ENUMERATE_NATIVE_ERRORS -#undef __JS_ENUMERATE - } } + + VERIFY(error); + + if (message.has_value()) + error->set_message(message.release_value()); + + value = error; break; } + // 22. Otherwise: default: VERIFY(tag == ValueTag::SerializableObject); - auto& realm = *m_vm.current_realm(); // 1. Let interfaceName be serialized.[[Type]]. - auto interface_name = deserialize_primitive_type(m_serialized, m_position); + auto interface_name = m_serialized.decode(); + // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException. if (!is_serializable_interface_exposed_on_target_realm(interface_name, realm)) return WebIDL::DataCloneError::create(realm, "Unsupported type"_string); @@ -1001,8 +852,9 @@ public: if (deep) { // 1. If serialized.[[Type]] is "Map", then: if (tag == ValueTag::MapObject) { - auto& map = static_cast(value.as_object()); - auto length = deserialize_primitive_type(m_serialized, m_position); + auto& map = as(value.as_object()); + auto length = m_serialized.decode(); + // 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[MapData]]: for (u64 i = 0u; i < length; ++i) { // 1. Let deserializedKey be ? StructuredDeserialize(entry.[[Key]], targetRealm, memory). @@ -1018,8 +870,9 @@ public: // 2. Otherwise, if serialized.[[Type]] is "Set", then: else if (tag == ValueTag::SetObject) { - auto& set = static_cast(value.as_object()); - auto length = deserialize_primitive_type(m_serialized, m_position); + auto& set = as(value.as_object()); + auto length = m_serialized.decode(); + // 1. For each entry of serialized.[[SetData]]: for (u64 i = 0u; i < length; ++i) { // 1. Let deserializedEntry be ? StructuredDeserialize(entry, targetRealm, memory). @@ -1033,10 +886,11 @@ public: // 3. Otherwise, if serialized.[[Type]] is "Array" or "Object", then: else if (tag == ValueTag::ArrayObject || tag == ValueTag::Object) { auto& object = value.as_object(); - auto length = deserialize_primitive_type(m_serialized, m_position); + auto length = m_serialized.decode(); + // 1. For each Record { [[Key]], [[Value]] } entry of serialized.[[Properties]]: for (u64 i = 0u; i < length; ++i) { - auto key = TRY(deserialize_string(m_vm, m_serialized, m_position)); + auto key = m_serialized.decode(); // 1. Let deserializedValue be ? StructuredDeserialize(entry.[[Value]], targetRealm, memory). auto deserialized_value = TRY(deserialize()); @@ -1053,7 +907,7 @@ public: else { // 1. Perform the appropriate deserialization steps for the interface identified by serialized.[[Type]], given serialized, value, and targetRealm. auto& serializable = dynamic_cast(value.as_object()); - TRY(serializable.deserialization_steps(m_serialized, m_position, m_memory)); + TRY(serializable.deserialization_steps(m_serialized, m_memory)); } } @@ -1062,11 +916,6 @@ public: } private: - JS::VM& m_vm; - ReadonlySpan m_serialized; - GC::RootVector m_memory; // Index -> JS value - size_t m_position { 0 }; - static bool is_serializable_interface_exposed_on_target_realm(SerializeType name, JS::Realm& realm) { auto const& intrinsics = Bindings::host_defined_intrinsics(realm); @@ -1144,97 +993,12 @@ private: VERIFY_NOT_REACHED(); } } + + JS::VM& m_vm; + TransferDataDecoder& m_serialized; + GC::RootVector m_memory; }; -bool deserialize_boolean_primitive(ReadonlySpan const& serialized, size_t& position) -{ - return deserialize_primitive_type(serialized, position); -} - -double deserialize_number_primitive(ReadonlySpan const& serialized, size_t& position) -{ - return deserialize_primitive_type(serialized, position); -} - -GC::Ref deserialize_boolean_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) -{ - auto boolean_primitive = deserialize_boolean_primitive(serialized, position); - return JS::BooleanObject::create(realm, boolean_primitive); -} - -GC::Ref deserialize_number_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) -{ - auto number_primitive = deserialize_number_primitive(serialized, position); - return JS::NumberObject::create(realm, number_primitive); -} - -WebIDL::ExceptionOr> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) -{ - auto big_int_primitive = TRY(deserialize_big_int_primitive(realm.vm(), serialized, position)); - return JS::BigIntObject::create(realm, big_int_primitive); -} - -WebIDL::ExceptionOr> deserialize_string_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) -{ - auto string_primitive = TRY(deserialize_string_primitive(realm.vm(), serialized, position)); - return JS::StringObject::create(realm, string_primitive, realm.intrinsics().string_prototype()); -} - -GC::Ref deserialize_date_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) -{ - auto double_value = deserialize_primitive_type(serialized, position); - return JS::Date::create(realm, double_value); -} - -WebIDL::ExceptionOr> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position) -{ - auto pattern = TRY(deserialize_string_primitive(realm.vm(), serialized, position)); - auto flags = TRY(deserialize_string_primitive(realm.vm(), serialized, position)); - return TRY(JS::regexp_create(realm.vm(), move(pattern), move(flags))); -} - -WebIDL::ExceptionOr deserialize_bytes(JS::VM& vm, ReadonlySpan vector, size_t& position) -{ - u64 const size = deserialize_primitive_type(vector, position); - - auto bytes = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(size)); - u64 byte_position = 0; - while (position < vector.size() && byte_position < size) { - for (u8 i = 0; i < 4; ++i) { - bytes[byte_position++] = (vector[position] >> (i * 8) & 0xFF); - if (byte_position == size) - break; - } - position++; - } - return bytes; -} - -WebIDL::ExceptionOr deserialize_string(JS::VM& vm, ReadonlySpan vector, size_t& position) -{ - auto bytes = TRY(deserialize_bytes(vm, vector, position)); - return TRY_OR_THROW_OOM(vm, String::from_utf8(StringView { bytes })); -} - -WebIDL::ExceptionOr> deserialize_string_primitive(JS::VM& vm, ReadonlySpan vector, size_t& position) -{ - auto bytes = TRY(deserialize_bytes(vm, vector, position)); - - return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() { - return JS::PrimitiveString::create(vm, StringView { bytes }); - })); -} - -WebIDL::ExceptionOr> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan vector, size_t& position) -{ - auto string = TRY(deserialize_string_primitive(vm, vector, position)); - auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() { - return string->utf8_string_view(); - })); - auto bigint = MUST(::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1))); - return JS::BigInt::create(vm, bigint); -} - // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializewithtransfer WebIDL::ExceptionOr structured_serialize_with_transfer(JS::VM& vm, JS::Value value, Vector> const& transfer_list) { @@ -1243,24 +1007,22 @@ WebIDL::ExceptionOr structured_serialize_with_transfer // 2. For each transferable of transferList: for (auto const& transferable : transfer_list) { - auto is_array_buffer = is(*transferable); + auto const* as_array_buffer = as_if(*transferable); // 1. If transferable has neither an [[ArrayBufferData]] internal slot nor a [[Detached]] internal slot, then throw a "DataCloneError" DOMException. // FIXME: Handle transferring objects with [[Detached]] internal slot. - if (!is_array_buffer && !is(*transferable)) { + if (!as_array_buffer && !is(*transferable)) return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer type"_string); - } // 2. If transferable has an [[ArrayBufferData]] internal slot and IsSharedArrayBuffer(transferable) is true, then throw a "DataCloneError" DOMException. - if (is_array_buffer && dynamic_cast(*transferable).is_shared_array_buffer()) { + if (as_array_buffer && as_array_buffer->is_shared_array_buffer()) return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer shared array buffer"_string); - } + + JS::Value transferable_value { transferable }; // 3. If memory[transferable] exists, then throw a "DataCloneError" DOMException. - auto transferable_value = JS::Value(transferable); - if (memory.contains(transferable_value)) { + if (memory.contains(transferable_value)) return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer value twice"_string); - } // 4. Set memory[transferable] to { [[Type]]: an uninitialized value }. memory.set(GC::make_root(transferable_value), memory.size()); @@ -1270,57 +1032,56 @@ WebIDL::ExceptionOr structured_serialize_with_transfer auto serialized = TRY(structured_serialize_internal(vm, value, false, memory)); // 4. Let transferDataHolders be a new empty List. - Vector transfer_data_holders; + Vector transfer_data_holders; transfer_data_holders.ensure_capacity(transfer_list.size()); // 5. For each transferable of transferList: - for (auto& transferable : transfer_list) { - auto array_buffer = as_if(*transferable); + for (auto const& transferable : transfer_list) { + auto* array_buffer = as_if(*transferable); auto is_detached = array_buffer && array_buffer->is_detached(); // 1. If transferable has an [[ArrayBufferData]] internal slot and IsDetachedBuffer(transferable) is true, then throw a "DataCloneError" DOMException. - if (is_detached) { + if (is_detached) return WebIDL::DataCloneError::create(*vm.current_realm(), "Cannot transfer detached buffer"_string); - } // 2. If transferable has a [[Detached]] internal slot and transferable.[[Detached]] is true, then throw a "DataCloneError" DOMException. if (auto* transferable_object = as_if(*transferable)) { - if (transferable_object->is_detached()) { + if (transferable_object->is_detached()) return WebIDL::DataCloneError::create(*vm.current_realm(), "Value already transferred"_string); - } } // 3. Let dataHolder be memory[transferable]. // IMPLEMENTATION DEFINED: We just create a data holder here, our memory holds indices into the SerializationRecord - TransferDataHolder data_holder; + TransferDataEncoder data_holder; // 4. If transferable has an [[ArrayBufferData]] internal slot, then: if (array_buffer) { // 1. If transferable has an [[ArrayBufferMaxByteLength]] internal slot, then: if (!array_buffer->is_fixed_length()) { // 1. Set dataHolder.[[Type]] to "ResizableArrayBuffer". + data_holder.encode(TransferType::ResizableArrayBuffer); + // 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]]. // 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]]. - // 4. Set dataHolder.[[ArrayBufferMaxByteLength]] to transferable.[[ArrayBufferMaxByteLength]]. - serialize_enum(data_holder.data, TransferType::ResizableArrayBuffer); - MUST(serialize_bytes(vm, data_holder.data, array_buffer->buffer().bytes())); // serializes both byte length and bytes - serialize_primitive_type(data_holder.data, array_buffer->max_byte_length()); - } + data_holder.encode(array_buffer->buffer()); + // 4. Set dataHolder.[[ArrayBufferMaxByteLength]] to transferable.[[ArrayBufferMaxByteLength]]. + data_holder.encode(array_buffer->max_byte_length()); + } // 2. Otherwise: else { // 1. Set dataHolder.[[Type]] to "ArrayBuffer". + data_holder.encode(TransferType::ArrayBuffer); + // 2. Set dataHolder.[[ArrayBufferData]] to transferable.[[ArrayBufferData]]. // 3. Set dataHolder.[[ArrayBufferByteLength]] to transferable.[[ArrayBufferByteLength]]. - serialize_enum(data_holder.data, TransferType::ArrayBuffer); - MUST(serialize_bytes(vm, data_holder.data, array_buffer->buffer().bytes())); // serializes both byte length and bytes + data_holder.encode(array_buffer->buffer()); } // 3. Perform ? DetachArrayBuffer(transferable). // NOTE: Specifications can use the [[ArrayBufferDetachKey]] internal slot to prevent ArrayBuffers from being detached. This is used in WebAssembly JavaScript Interface, for example. See: https://html.spec.whatwg.org/multipage/references.html#refsWASMJS TRY(JS::detach_array_buffer(vm, *array_buffer)); } - // 5. Otherwise: else { // 1. Assert: transferable is a platform object that is a transferable object. @@ -1331,7 +1092,7 @@ WebIDL::ExceptionOr structured_serialize_with_transfer auto interface_name = transferable_object.primary_interface(); // 3. Set dataHolder.[[Type]] to interfaceName. - serialize_enum(data_holder.data, interface_name); + data_holder.encode(interface_name); // 4. Perform the appropriate transfer steps for the interface identified by interfaceName, given transferable and dataHolder. TRY(transferable_object.transfer_steps(data_holder)); @@ -1369,33 +1130,31 @@ static bool is_transferable_interface_exposed_on_target_realm(TransferType name, return false; } -static WebIDL::ExceptionOr> create_transferred_value(TransferType name, JS::Realm& target_realm, TransferDataHolder& transfer_data_holder) +static WebIDL::ExceptionOr> create_transferred_value(TransferType name, JS::Realm& target_realm, TransferDataDecoder& decoder) { switch (name) { case TransferType::MessagePort: { auto message_port = HTML::MessagePort::create(target_realm); - TRY(message_port->transfer_receiving_steps(transfer_data_holder)); + TRY(message_port->transfer_receiving_steps(decoder)); return message_port; } case TransferType::ReadableStream: { auto readable_stream = target_realm.create(target_realm); - TRY(readable_stream->transfer_receiving_steps(transfer_data_holder)); + TRY(readable_stream->transfer_receiving_steps(decoder)); return readable_stream; } case TransferType::WritableStream: { auto writable_stream = target_realm.create(target_realm); - TRY(writable_stream->transfer_receiving_steps(transfer_data_holder)); + TRY(writable_stream->transfer_receiving_steps(decoder)); return writable_stream; } case TransferType::TransformStream: { auto transform_stream = target_realm.create(target_realm); - TRY(transform_stream->transfer_receiving_steps(transfer_data_holder)); + TRY(transform_stream->transfer_receiving_steps(decoder)); return transform_stream; } case TransferType::ArrayBuffer: case TransferType::ResizableArrayBuffer: - dbgln("ArrayBuffer ({}) is not a platform object.", to_underlying(name)); - break; case TransferType::Unknown: break; } @@ -1415,55 +1174,13 @@ WebIDL::ExceptionOr structured_deserialize_with_tran // 3. For each transferDataHolder of serializeWithTransferResult.[[TransferDataHolders]]: for (auto& transfer_data_holder : serialize_with_transfer_result.transfer_data_holders) { - if (transfer_data_holder.data.is_empty()) + if (transfer_data_holder.buffer().data().is_empty()) continue; + TransferDataDecoder decoder { move(transfer_data_holder) }; + // 1. Let value be an uninitialized value. - JS::Value value; - - size_t data_holder_position = 0; - auto type = deserialize_primitive_type(transfer_data_holder.data.span(), data_holder_position); - - // 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm - // whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], and - // whose [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]]. - // NOTE: In cases where the original memory occupied by [[ArrayBufferData]] is accessible during the deserialization, - // this step is unlikely to throw an exception, as no new memory needs to be allocated: the memory occupied by - // [[ArrayBufferData]] is instead just getting transferred into the new ArrayBuffer. This could be true, for example, - // when both the source and target realms are in the same process. - if (type == TransferType::ArrayBuffer) { - auto bytes = TRY(deserialize_bytes(vm, transfer_data_holder.data, data_holder_position)); - JS::ArrayBuffer* data = TRY(JS::allocate_array_buffer(vm, target_realm.intrinsics().array_buffer_constructor(), bytes.size())); - bytes.span().copy_to(data->buffer().span()); - value = JS::Value(data); - } - - // 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object - // in targetRealm whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], whose - // [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]], and whose - // [[ArrayBufferMaxByteLength]] internal slot value is transferDataHolder.[[ArrayBufferMaxByteLength]]. - // NOTE: For the same reason as the previous step, this step is also unlikely to throw an exception. - else if (type == TransferType::ResizableArrayBuffer) { - auto bytes = TRY(deserialize_bytes(vm, transfer_data_holder.data, data_holder_position)); - auto max_byte_length = deserialize_primitive_type(transfer_data_holder.data, data_holder_position); - JS::ArrayBuffer* data = TRY(JS::allocate_array_buffer(vm, target_realm.intrinsics().array_buffer_constructor(), bytes.size())); - data->set_max_byte_length(max_byte_length); - bytes.span().copy_to(data->buffer().span()); - value = JS::Value(data); - } - - // 4. Otherwise: - else { - // 1. Let interfaceName be transferDataHolder.[[Type]]. - // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException. - if (!is_transferable_interface_exposed_on_target_realm(type, target_realm)) - return WebIDL::DataCloneError::create(target_realm, "Unknown type transferred"_string); - - // 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm. - // 4. Perform the appropriate transfer-receiving steps for the interface identified by interfaceName given transferDataHolder and value. - transfer_data_holder.data.remove(0, data_holder_position); - value = TRY(create_transferred_value(type, target_realm, transfer_data_holder)); - } + auto value = TRY(structured_deserialize_with_transfer_internal(decoder, target_realm)); // 5. Set memory[transferDataHolder] to value. memory.append(value); @@ -1476,7 +1193,58 @@ WebIDL::ExceptionOr structured_deserialize_with_tran auto deserialized = TRY(structured_deserialize(vm, serialize_with_transfer_result.serialized, target_realm, memory)); // 5. Return { [[Deserialized]]: deserialized, [[TransferredValues]]: transferredValues }. - return DeserializedTransferRecord { .deserialized = move(deserialized), .transferred_values = move(transferred_values) }; + return DeserializedTransferRecord { .deserialized = deserialized, .transferred_values = move(transferred_values) }; +} + +// AD-HOC: This non-standard overload is meant to extract just one transferrable value from a serialized transfer record. +// It's primarily useful for an object's transfer receiving steps to deserialize a nested value. +WebIDL::ExceptionOr structured_deserialize_with_transfer_internal(TransferDataDecoder& decoder, JS::Realm& target_realm) +{ + auto type = decoder.decode(); + + // 1. Let value be an uninitialized value. + JS::Value value; + + // 2. If transferDataHolder.[[Type]] is "ArrayBuffer", then set value to a new ArrayBuffer object in targetRealm + // whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], and + // whose [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]]. + // NOTE: In cases where the original memory occupied by [[ArrayBufferData]] is accessible during the deserialization, + // this step is unlikely to throw an exception, as no new memory needs to be allocated: the memory occupied by + // [[ArrayBufferData]] is instead just getting transferred into the new ArrayBuffer. This could be true, for example, + // when both the source and target realms are in the same process. + if (type == TransferType::ArrayBuffer) { + auto buffer = TRY(decoder.decode_buffer(target_realm)); + value = JS::ArrayBuffer::create(target_realm, move(buffer)); + } + + // 3. Otherwise, if transferDataHolder.[[Type]] is "ResizableArrayBuffer", then set value to a new ArrayBuffer object + // in targetRealm whose [[ArrayBufferData]] internal slot value is transferDataHolder.[[ArrayBufferData]], whose + // [[ArrayBufferByteLength]] internal slot value is transferDataHolder.[[ArrayBufferByteLength]], and whose + // [[ArrayBufferMaxByteLength]] internal slot value is transferDataHolder.[[ArrayBufferMaxByteLength]]. + // NOTE: For the same reason as the previous step, this step is also unlikely to throw an exception. + else if (type == TransferType::ResizableArrayBuffer) { + auto buffer = TRY(decoder.decode_buffer(target_realm)); + auto max_byte_length = decoder.decode(); + + auto data = JS::ArrayBuffer::create(target_realm, move(buffer)); + data->set_max_byte_length(max_byte_length); + + value = data; + } + + // 4. Otherwise: + else { + // 1. Let interfaceName be transferDataHolder.[[Type]]. + // 2. If the interface identified by interfaceName is not exposed in targetRealm, then throw a "DataCloneError" DOMException. + if (!is_transferable_interface_exposed_on_target_realm(type, target_realm)) + return WebIDL::DataCloneError::create(target_realm, "Unknown type transferred"_string); + + // 3. Set value to a new instance of the interface identified by interfaceName, created in targetRealm. + // 4. Perform the appropriate transfer-receiving steps for the interface identified by interfaceName given transferDataHolder and value. + value = TRY(create_transferred_value(type, target_realm, decoder)); + } + + return value; } // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize @@ -1513,20 +1281,65 @@ WebIDL::ExceptionOr structured_deserialize(JS::VM& vm, SerializationR if (!memory.has_value()) memory = DeserializationMemory { vm.heap() }; - auto result = TRY(structured_deserialize_internal(vm, serialized.span(), target_realm, *memory)); - VERIFY(result.value.has_value()); - return *result.value; + TransferDataDecoder decoder { serialized }; + return structured_deserialize_internal(vm, decoder, target_realm, *memory); } -WebIDL::ExceptionOr structured_deserialize_internal(JS::VM& vm, ReadonlySpan const& serialized, JS::Realm& target_realm, DeserializationMemory& memory, Optional position) +WebIDL::ExceptionOr structured_deserialize_internal(JS::VM& vm, TransferDataDecoder& serialized, JS::Realm& target_realm, DeserializationMemory& memory) { - Deserializer deserializer(vm, target_realm, serialized, memory, move(position)); - auto value = TRY(deserializer.deserialize()); - auto deserialized_record = DeserializedRecord { - .value = value, - .position = deserializer.position(), - }; - return deserialized_record; + Deserializer deserializer(vm, serialized, target_realm, memory); + return deserializer.deserialize(); +} + +TransferDataEncoder::TransferDataEncoder() + : m_encoder(m_buffer) +{ +} + +TransferDataEncoder::TransferDataEncoder(IPC::MessageBuffer&& buffer) + : m_buffer(move(buffer)) + , m_encoder(m_buffer) +{ +} + +void TransferDataEncoder::append(SerializationRecord&& record) +{ + MUST(m_buffer.append_data(record.data(), record.size())); +} + +void TransferDataEncoder::extend(Vector data_holders) +{ + for (auto& data_holder : data_holders) + MUST(m_buffer.extend(move(data_holder.m_buffer))); +} + +TransferDataDecoder::TransferDataDecoder(SerializationRecord const& record) + : m_stream(record.span()) + , m_decoder(m_stream, m_files) +{ +} + +TransferDataDecoder::TransferDataDecoder(TransferDataEncoder&& data_holder) + : m_buffer(data_holder.take_buffer()) + , m_stream(m_buffer.data().span()) + , m_decoder(m_stream, m_files) +{ + // FIXME: The churn between IPC::File and IPC::AutoCloseFileDescriptor is pretty awkward, we should find a way to + // consolidate the way we use these type. + for (auto& auto_fd : m_buffer.take_fds()) + m_files.enqueue(IPC::File::adopt_fd(auto_fd->take_fd())); +} + +WebIDL::ExceptionOr TransferDataDecoder::decode_buffer(JS::Realm& realm) +{ + auto buffer = m_decoder.decode(); + + if (buffer.is_error()) { + VERIFY(buffer.error().code() == ENOMEM); + return WebIDL::DataCloneError::create(realm, "Unable to allocate memory for transferred buffer"_string); + } + + return buffer.release_value(); } } @@ -1534,15 +1347,45 @@ WebIDL::ExceptionOr structured_deserialize_internal(JS::VM& namespace IPC { template<> -ErrorOr encode(Encoder& encoder, ::Web::HTML::TransferDataHolder const& data_holder) +ErrorOr encode(Encoder& encoder, Web::HTML::TransferDataEncoder const& data_holder) { - TRY(encoder.encode(data_holder.data)); - TRY(encoder.encode(data_holder.fds)); + // FIXME: The churn between IPC::File and IPC::AutoCloseFileDescriptor is pretty awkward, we should find a way to + // consolidate the way we use these type. + Vector files; + files.ensure_capacity(data_holder.buffer().fds().size()); + + for (auto const& auto_fd : data_holder.buffer().fds()) { + auto fd = const_cast(*auto_fd).take_fd(); + files.unchecked_append(IPC::File::adopt_fd(fd)); + } + + TRY(encoder.encode(data_holder.buffer().data())); + TRY(encoder.encode(files)); return {}; } template<> -ErrorOr encode(Encoder& encoder, ::Web::HTML::SerializedTransferRecord const& record) +ErrorOr decode(Decoder& decoder) +{ + auto data = TRY(decoder.decode()); + auto files = TRY(decoder.decode>()); + + // FIXME: The churn between IPC::File and IPC::AutoCloseFileDescriptor is pretty awkward, we should find a way to + // consolidate the way we use these type. + MessageFileType auto_files; + auto_files.ensure_capacity(files.size()); + + for (auto& fd : files) { + auto auto_fd = adopt_ref(*new AutoCloseFileDescriptor(fd.take_fd())); + auto_files.unchecked_append(move(auto_fd)); + } + + IPC::MessageBuffer buffer { move(data), move(auto_files) }; + return Web::HTML::TransferDataEncoder { move(buffer) }; +} + +template<> +ErrorOr encode(Encoder& encoder, Web::HTML::SerializedTransferRecord const& record) { TRY(encoder.encode(record.serialized)); TRY(encoder.encode(record.transfer_data_holders)); @@ -1550,19 +1393,12 @@ ErrorOr encode(Encoder& encoder, ::Web::HTML::SerializedTransferRecord con } template<> -ErrorOr<::Web::HTML::TransferDataHolder> decode(Decoder& decoder) +ErrorOr decode(Decoder& decoder) { - auto data = TRY(decoder.decode>()); - auto fds = TRY(decoder.decode>()); - return ::Web::HTML::TransferDataHolder { move(data), move(fds) }; -} + auto serialized = TRY(decoder.decode()); + auto transfer_data_holders = TRY(decoder.decode>()); -template<> -ErrorOr<::Web::HTML::SerializedTransferRecord> decode(Decoder& decoder) -{ - auto serialized = TRY(decoder.decode>()); - auto transfer_data_holders = TRY(decoder.decode>()); - return ::Web::HTML::SerializedTransferRecord { move(serialized), move(transfer_data_holders) }; + return Web::HTML::SerializedTransferRecord { move(serialized), move(transfer_data_holders) }; } } diff --git a/Libraries/LibWeb/HTML/StructuredSerialize.h b/Libraries/LibWeb/HTML/StructuredSerialize.h index 02e504e8c22..d2298a112c1 100644 --- a/Libraries/LibWeb/HTML/StructuredSerialize.h +++ b/Libraries/LibWeb/HTML/StructuredSerialize.h @@ -8,30 +8,66 @@ #pragma once -#include -#include +#include #include -#include +#include +#include +#include #include #include #include #include -// Structured serialize is an entirely different format from IPC because: -// - It contains representation of type information -// - It may contain circularities -// - It is restricted to JS values - namespace Web::HTML { -struct TransferDataHolder { - Vector data; - Vector fds; +class TransferDataEncoder { +public: + explicit TransferDataEncoder(); + explicit TransferDataEncoder(IPC::MessageBuffer&&); + + template + void encode(T const& value) + { + MUST(m_encoder.encode(value)); + } + + void append(SerializationRecord&&); + void extend(Vector); + + IPC::MessageBuffer const& buffer() const { return m_buffer; } + IPC::MessageBuffer take_buffer() { return move(m_buffer); } + +private: + IPC::MessageBuffer m_buffer; + IPC::Encoder m_encoder; +}; + +class TransferDataDecoder { +public: + explicit TransferDataDecoder(SerializationRecord const&); + explicit TransferDataDecoder(TransferDataEncoder&&); + + template + T decode() + { + static_assert(!IsSame, "Use decode_buffer to handle OOM"); + return MUST(m_decoder.decode()); + } + + WebIDL::ExceptionOr decode_buffer(JS::Realm&); + +private: + IPC::MessageBuffer m_buffer; + + FixedMemoryStream m_stream; + Queue m_files; + + IPC::Decoder m_decoder; }; struct SerializedTransferRecord { SerializationRecord serialized; - Vector transfer_data_holders; + Vector transfer_data_holders; }; struct DeserializedTransferRecord { @@ -39,100 +75,31 @@ struct DeserializedTransferRecord { Vector> transferred_values; }; -struct DeserializedRecord { - Optional value; - size_t position; -}; +WebIDL::ExceptionOr structured_serialize(JS::VM&, JS::Value); +WebIDL::ExceptionOr structured_serialize_for_storage(JS::VM&, JS::Value); +WebIDL::ExceptionOr structured_serialize_internal(JS::VM&, JS::Value, bool for_storage, SerializationMemory&); -WebIDL::ExceptionOr structured_serialize(JS::VM& vm, JS::Value); -WebIDL::ExceptionOr structured_serialize_for_storage(JS::VM& vm, JS::Value); -WebIDL::ExceptionOr structured_serialize_internal(JS::VM& vm, JS::Value, bool for_storage, SerializationMemory&); +WebIDL::ExceptionOr structured_deserialize(JS::VM&, SerializationRecord const&, JS::Realm&, Optional = {}); +WebIDL::ExceptionOr structured_deserialize_internal(JS::VM&, TransferDataDecoder&, JS::Realm&, DeserializationMemory&); -WebIDL::ExceptionOr structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional = {}); -WebIDL::ExceptionOr structured_deserialize_internal(JS::VM& vm, ReadonlySpan const& serialized, JS::Realm& target_realm, DeserializationMemory& memory, Optional position = {}); - -void serialize_boolean_primitive(SerializationRecord& serialized, JS::Value& value); -void serialize_number_primitive(SerializationRecord& serialized, JS::Value& value); -WebIDL::ExceptionOr serialize_big_int_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value); -WebIDL::ExceptionOr serialize_string_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value); -void serialize_boolean_object(SerializationRecord& serialized, JS::Value& value); -void serialize_number_object(SerializationRecord& serialized, JS::Value& value); -WebIDL::ExceptionOr serialize_big_int_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value); -WebIDL::ExceptionOr serialize_string_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value); -void serialize_date_object(SerializationRecord& serialized, JS::Value& value); -WebIDL::ExceptionOr serialize_reg_exp_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value); - -template -requires(IsIntegral || IsFloatingPoint) -void serialize_primitive_type(SerializationRecord& serialized, T value) -{ - if constexpr (sizeof(T) < sizeof(u32)) { - // NOTE: If the value is smaller than a u32, we can just store it directly. - serialized.append(static_cast(value)); - return; - } - serialized.append(bit_cast(&value), sizeof(T) / 4); -} - -template -requires(IsEnum) -void serialize_enum(SerializationRecord& serialized, T value) -{ - serialize_primitive_type>(serialized, to_underlying(value)); -} - -WebIDL::ExceptionOr serialize_bytes(JS::VM& vm, Vector& vector, ReadonlyBytes bytes); -WebIDL::ExceptionOr serialize_string(JS::VM& vm, Vector& vector, StringView); -WebIDL::ExceptionOr serialize_string(JS::VM& vm, Vector& vector, String const& string); -WebIDL::ExceptionOr serialize_string(JS::VM& vm, Vector& vector, JS::PrimitiveString const& primitive_string); -WebIDL::ExceptionOr serialize_array_buffer(JS::VM& vm, Vector& vector, JS::ArrayBuffer const& array_buffer, bool for_storage); -template ViewType> -WebIDL::ExceptionOr serialize_viewed_array_buffer(JS::VM& vm, Vector& vector, ViewType const& view, bool for_storage, SerializationMemory& memory); - -bool deserialize_boolean_primitive(ReadonlySpan const& serialized, size_t& position); -double deserialize_number_primitive(ReadonlySpan const& serialized, size_t& position); -GC::Ref deserialize_boolean_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); -GC::Ref deserialize_number_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); -WebIDL::ExceptionOr> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); -WebIDL::ExceptionOr> deserialize_string_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); -GC::Ref deserialize_date_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); -WebIDL::ExceptionOr> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan const& serialized, size_t& position); - -template -requires(IsIntegral || IsFloatingPoint || IsEnum) -T deserialize_primitive_type(ReadonlySpan const& serialized, size_t& position) -{ - T value; - // NOTE: Make sure we always round up, otherwise Ts that are less than 32 bit will end up with a size of 0. - auto size = 1 + ((sizeof(value) - 1) / 4); - VERIFY(position + size <= serialized.size()); - memcpy(&value, serialized.offset_pointer(position), sizeof(value)); - position += size; - return value; -} - -WebIDL::ExceptionOr deserialize_bytes(JS::VM& vm, ReadonlySpan vector, size_t& position); -WebIDL::ExceptionOr deserialize_string(JS::VM& vm, ReadonlySpan vector, size_t& position); -WebIDL::ExceptionOr> deserialize_string_primitive(JS::VM& vm, ReadonlySpan vector, size_t& position); -WebIDL::ExceptionOr> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan vector, size_t& position); - -WebIDL::ExceptionOr structured_serialize_with_transfer(JS::VM& vm, JS::Value value, Vector> const& transfer_list); -WebIDL::ExceptionOr structured_deserialize_with_transfer(SerializedTransferRecord&, JS::Realm& target_realm); +WebIDL::ExceptionOr structured_serialize_with_transfer(JS::VM&, JS::Value, Vector> const& transfer_list); +WebIDL::ExceptionOr structured_deserialize_with_transfer(SerializedTransferRecord&, JS::Realm&); +WebIDL::ExceptionOr structured_deserialize_with_transfer_internal(TransferDataDecoder&, JS::Realm&); } namespace IPC { template<> -ErrorOr encode(Encoder&, ::Web::HTML::SerializedTransferRecord const&); +ErrorOr encode(Encoder&, Web::HTML::TransferDataEncoder const&); template<> -ErrorOr encode(Encoder&, ::Web::HTML::TransferDataHolder const&); +ErrorOr decode(Decoder&); template<> -ErrorOr<::Web::HTML::SerializedTransferRecord> decode(Decoder&); +ErrorOr encode(Encoder&, Web::HTML::SerializedTransferRecord const&); template<> -ErrorOr<::Web::HTML::TransferDataHolder> decode(Decoder&); +ErrorOr decode(Decoder&); } diff --git a/Libraries/LibWeb/HTML/StructuredSerializeTypes.h b/Libraries/LibWeb/HTML/StructuredSerializeTypes.h index 696c169e12e..6f1a52cad41 100644 --- a/Libraries/LibWeb/HTML/StructuredSerializeTypes.h +++ b/Libraries/LibWeb/HTML/StructuredSerializeTypes.h @@ -9,13 +9,14 @@ #include #include #include +#include #include namespace Web::HTML { using DeserializationMemory = GC::RootVector; -using SerializationRecord = Vector; using SerializationMemory = HashMap, u32>; +using SerializationRecord = IPC::MessageDataType; enum class SerializeType : u8 { Unknown = 0, diff --git a/Libraries/LibWeb/HTML/WorkerAgentParent.cpp b/Libraries/LibWeb/HTML/WorkerAgentParent.cpp index 2161454dfe2..60a2a88e7bf 100644 --- a/Libraries/LibWeb/HTML/WorkerAgentParent.cpp +++ b/Libraries/LibWeb/HTML/WorkerAgentParent.cpp @@ -30,7 +30,7 @@ void WorkerAgentParent::initialize(JS::Realm& realm) m_message_port = MessagePort::create(realm); m_message_port->entangle_with(*m_outside_port); - TransferDataHolder data_holder; + TransferDataEncoder data_holder; MUST(m_message_port->transfer_steps(data_holder)); // FIXME: Specification says this supposed to happen in step 11 of onComplete handler defined in https://html.spec.whatwg.org/multipage/workers.html#run-a-worker diff --git a/Libraries/LibWeb/Streams/ReadableStream.cpp b/Libraries/LibWeb/Streams/ReadableStream.cpp index ed857db5d0b..49a7ab456f6 100644 --- a/Libraries/LibWeb/Streams/ReadableStream.cpp +++ b/Libraries/LibWeb/Streams/ReadableStream.cpp @@ -457,7 +457,7 @@ GC::Ref ReadableStream::piped_through(GC::Ref t } // https://streams.spec.whatwg.org/#ref-for-transfer-steps -WebIDL::ExceptionOr ReadableStream::transfer_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr ReadableStream::transfer_steps(HTML::TransferDataEncoder& data_holder) { auto& realm = this->realm(); auto& vm = realm.vm(); @@ -472,7 +472,7 @@ WebIDL::ExceptionOr ReadableStream::transfer_steps(HTML::TransferDataHolde auto port1 = HTML::MessagePort::create(realm); // 3. Let port2 be a new MessagePort in the current Realm. - auto port2 = HTML::MessagePort::create(realm, HTML::TransferType::ReadableStream); + auto port2 = HTML::MessagePort::create(realm); // 4. Entangle port1 and port2. port1->entangle_with(port2); @@ -491,22 +491,23 @@ WebIDL::ExceptionOr ReadableStream::transfer_steps(HTML::TransferDataHolde // 9. Set dataHolder.[[port]] to ! StructuredSerializeWithTransfer(port2, « port2 »). auto result = MUST(HTML::structured_serialize_with_transfer(vm, port2, { { GC::Root { port2 } } })); - data_holder = move(result.transfer_data_holders.first()); + data_holder.extend(move(result.transfer_data_holders)); return {}; } // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps -WebIDL::ExceptionOr ReadableStream::transfer_receiving_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr ReadableStream::transfer_receiving_steps(HTML::TransferDataDecoder& data_holder) { auto& realm = this->realm(); HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes }; // 1. Let deserializedRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[port]], the current Realm). + auto deserialized_record = MUST(HTML::structured_deserialize_with_transfer_internal(data_holder, realm)); + // 2. Let port be deserializedRecord.[[Deserialized]]. - auto port = HTML::MessagePort::create(realm); - TRY(port->transfer_receiving_steps(data_holder)); + auto& port = as(deserialized_record.as_object()); // 3. Perform ! SetUpCrossRealmTransformReadable(value, port). set_up_cross_realm_transform_readable(realm, *this, port); diff --git a/Libraries/LibWeb/Streams/ReadableStream.h b/Libraries/LibWeb/Streams/ReadableStream.h index 427e80f1765..4fc9bb1ea3a 100644 --- a/Libraries/LibWeb/Streams/ReadableStream.h +++ b/Libraries/LibWeb/Streams/ReadableStream.h @@ -117,8 +117,8 @@ public: GC::Ptr current_byob_request_view(); // ^Transferable - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) override; - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) override; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) override; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) override; virtual HTML::TransferType primary_interface() const override { return HTML::TransferType::ReadableStream; } private: diff --git a/Libraries/LibWeb/Streams/TransformStream.cpp b/Libraries/LibWeb/Streams/TransformStream.cpp index 2e2db0a1188..e24ff3e5403 100644 --- a/Libraries/LibWeb/Streams/TransformStream.cpp +++ b/Libraries/LibWeb/Streams/TransformStream.cpp @@ -188,18 +188,11 @@ void TransformStream::set_up(GC::Ref transform_algorithm, GC } // https://streams.spec.whatwg.org/#ref-for-transfer-steps② -WebIDL::ExceptionOr TransformStream::transfer_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr TransformStream::transfer_steps(HTML::TransferDataEncoder& data_holder) { auto& realm = this->realm(); auto& vm = realm.vm(); - auto serialize_stream = [&](auto stream) { - auto result = MUST(HTML::structured_serialize_with_transfer(vm, stream, { { GC::Root { stream } } })); - - data_holder.data.extend(move(result.transfer_data_holders.first().data)); - data_holder.fds.extend(move(result.transfer_data_holders.first().fds)); - }; - // 1. Let readable be value.[[readable]]. auto readable = this->readable(); @@ -215,48 +208,32 @@ WebIDL::ExceptionOr TransformStream::transfer_steps(HTML::TransferDataHold return WebIDL::DataCloneError::create(realm, "Cannot transfer locked WritableStream"_string); // 5. Set dataHolder.[[readable]] to ! StructuredSerializeWithTransfer(readable, « readable »). - serialize_stream(readable); + auto readable_result = MUST(HTML::structured_serialize_with_transfer(vm, readable, { { GC::Root { readable } } })); + data_holder.extend(move(readable_result.transfer_data_holders)); // 6. Set dataHolder.[[writable]] to ! StructuredSerializeWithTransfer(writable, « writable »). - serialize_stream(writable); + auto writable_result = MUST(HTML::structured_serialize_with_transfer(vm, writable, { { GC::Root { writable } } })); + data_holder.extend(move(writable_result.transfer_data_holders)); return {}; } -template -static WebIDL::ExceptionOr> deserialize_stream(JS::Realm& realm, HTML::TransferDataHolder& data_holder) -{ - auto transfer_type = data_holder.data.take_first(); - - if constexpr (IsSame) - VERIFY(transfer_type == to_underlying(HTML::TransferType::ReadableStream)); - else if constexpr (IsSame) - VERIFY(transfer_type == to_underlying(HTML::TransferType::WritableStream)); - else - static_assert(DependentFalse); - - auto stream = realm.create(realm); - TRY(stream->transfer_receiving_steps(data_holder)); - - return stream; -} - // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps② -WebIDL::ExceptionOr TransformStream::transfer_receiving_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr TransformStream::transfer_receiving_steps(HTML::TransferDataDecoder& data_holder) { auto& realm = this->realm(); // 1. Let readableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[readable]], the current Realm). - auto readable = TRY(deserialize_stream(realm, data_holder)); + auto readable_record = MUST(HTML::structured_deserialize_with_transfer_internal(data_holder, realm)); // 2. Let writableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[writable]], the current Realm). - auto writable = TRY(deserialize_stream(realm, data_holder)); + auto writeable_record = MUST(HTML::structured_deserialize_with_transfer_internal(data_holder, realm)); // 3. Set value.[[readable]] to readableRecord.[[Deserialized]]. - set_readable(readable); + set_readable(as(readable_record.as_object())); // 4. Set value.[[writable]] to writableRecord.[[Deserialized]]. - set_writable(writable); + set_writable(as(writeable_record.as_object())); // 5. Set value.[[backpressure]], value.[[backpressureChangePromise]], and value.[[controller]] to undefined. set_backpressure({}); diff --git a/Libraries/LibWeb/Streams/TransformStream.h b/Libraries/LibWeb/Streams/TransformStream.h index a9f5be1de5f..01c1bcf4196 100644 --- a/Libraries/LibWeb/Streams/TransformStream.h +++ b/Libraries/LibWeb/Streams/TransformStream.h @@ -48,8 +48,8 @@ public: void enqueue(JS::Value chunk); // ^Transferable - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) override; - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) override; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) override; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) override; virtual HTML::TransferType primary_interface() const override { return HTML::TransferType::TransformStream; } private: diff --git a/Libraries/LibWeb/Streams/WritableStream.cpp b/Libraries/LibWeb/Streams/WritableStream.cpp index 407576cdce6..b40d5d45280 100644 --- a/Libraries/LibWeb/Streams/WritableStream.cpp +++ b/Libraries/LibWeb/Streams/WritableStream.cpp @@ -135,7 +135,7 @@ WebIDL::ExceptionOr> WritableStream::get_wr } // https://streams.spec.whatwg.org/#ref-for-transfer-steps① -WebIDL::ExceptionOr WritableStream::transfer_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr WritableStream::transfer_steps(HTML::TransferDataEncoder& data_holder) { auto& realm = this->realm(); auto& vm = realm.vm(); @@ -150,7 +150,7 @@ WebIDL::ExceptionOr WritableStream::transfer_steps(HTML::TransferDataHolde auto port1 = HTML::MessagePort::create(realm); // 3. Let port2 be a new MessagePort in the current Realm. - auto port2 = HTML::MessagePort::create(realm, HTML::TransferType::WritableStream); + auto port2 = HTML::MessagePort::create(realm); // 4. Entangle port1 and port2. port1->entangle_with(port2); @@ -169,22 +169,23 @@ WebIDL::ExceptionOr WritableStream::transfer_steps(HTML::TransferDataHolde // 9. Set dataHolder.[[port]] to ! StructuredSerializeWithTransfer(port2, « port2 »). auto result = MUST(HTML::structured_serialize_with_transfer(vm, port2, { { GC::Root { port2 } } })); - data_holder = move(result.transfer_data_holders.first()); + data_holder.extend(move(result.transfer_data_holders)); return {}; } // https://streams.spec.whatwg.org/#ref-for-transfer-receiving-steps① -WebIDL::ExceptionOr WritableStream::transfer_receiving_steps(HTML::TransferDataHolder& data_holder) +WebIDL::ExceptionOr WritableStream::transfer_receiving_steps(HTML::TransferDataDecoder& data_holder) { auto& realm = this->realm(); HTML::TemporaryExecutionContext execution_context { realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes }; // 1. Let deserializedRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[port]], the current Realm). + auto deserialized_record = MUST(HTML::structured_deserialize_with_transfer_internal(data_holder, realm)); + // 2. Let port be deserializedRecord.[[Deserialized]]. - auto port = HTML::MessagePort::create(realm); - TRY(port->transfer_receiving_steps(data_holder)); + auto& port = as(deserialized_record.as_object()); // 3. Perform ! SetUpCrossRealmTransformWritable(value, port). set_up_cross_realm_transform_writable(realm, *this, port); diff --git a/Libraries/LibWeb/Streams/WritableStream.h b/Libraries/LibWeb/Streams/WritableStream.h index b7355e2a18e..2befb16aa70 100644 --- a/Libraries/LibWeb/Streams/WritableStream.h +++ b/Libraries/LibWeb/Streams/WritableStream.h @@ -89,8 +89,8 @@ public: SinglyLinkedList>& write_requests() { return m_write_requests; } // ^Transferable - virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataHolder&) override; - virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataHolder&) override; + virtual WebIDL::ExceptionOr transfer_steps(HTML::TransferDataEncoder&) override; + virtual WebIDL::ExceptionOr transfer_receiving_steps(HTML::TransferDataDecoder&) override; virtual HTML::TransferType primary_interface() const override { return HTML::TransferType::WritableStream; } private: diff --git a/Libraries/LibWeb/WebIDL/DOMException.cpp b/Libraries/LibWeb/WebIDL/DOMException.cpp index 62d7a3fdec6..a59dc4e1f24 100644 --- a/Libraries/LibWeb/WebIDL/DOMException.cpp +++ b/Libraries/LibWeb/WebIDL/DOMException.cpp @@ -48,30 +48,26 @@ void DOMException::initialize(JS::Realm& realm) Base::initialize(realm); } -ExceptionOr DOMException::serialization_steps(HTML::SerializationRecord& record, bool, HTML::SerializationMemory&) +WebIDL::ExceptionOr DOMException::serialization_steps(HTML::TransferDataEncoder& serialized, bool, HTML::SerializationMemory&) { - auto& vm = this->vm(); - // 1. Set serialized.[[Name]] to value’s name. - TRY(HTML::serialize_string(vm, record, m_name.to_string())); + serialized.encode(m_name.to_string()); // 2. Set serialized.[[Message]] to value’s message. - TRY(HTML::serialize_string(vm, record, m_message.to_string())); + serialized.encode(m_message.to_string()); // FIXME: 3. User agents should attach a serialized representation of any interesting accompanying data which are not yet specified, notably the stack property, to serialized. return {}; } -ExceptionOr DOMException::deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) +WebIDL::ExceptionOr DOMException::deserialization_steps(HTML::TransferDataDecoder& serialized, HTML::DeserializationMemory&) { - auto& vm = this->vm(); - // 1. Set value’s name to serialized.[[Name]]. - m_name = TRY(HTML::deserialize_string(vm, record, position)); + m_name = serialized.decode(); // 2. Set value’s message to serialized.[[Message]]. - m_message = TRY(HTML::deserialize_string(vm, record, position)); + m_message = serialized.decode(); // FIXME: 3. If any other data is attached to serialized, then deserialize and attach it to value. diff --git a/Libraries/LibWeb/WebIDL/DOMException.h b/Libraries/LibWeb/WebIDL/DOMException.h index a08ae233b80..e2aa598dff2 100644 --- a/Libraries/LibWeb/WebIDL/DOMException.h +++ b/Libraries/LibWeb/WebIDL/DOMException.h @@ -113,8 +113,8 @@ public: virtual HTML::SerializeType serialize_type() const override { return HTML::SerializeType::DOMException; } - virtual ExceptionOr serialization_steps(HTML::SerializationRecord& record, bool for_storage, HTML::SerializationMemory&) override; - virtual ExceptionOr deserialization_steps(ReadonlySpan const& record, size_t& position, HTML::DeserializationMemory&) override; + virtual WebIDL::ExceptionOr serialization_steps(HTML::TransferDataEncoder&, bool for_storage, HTML::SerializationMemory&) override; + virtual WebIDL::ExceptionOr deserialization_steps(HTML::TransferDataDecoder&, HTML::DeserializationMemory&) override; protected: DOMException(JS::Realm&, FlyString name, String message); diff --git a/Libraries/LibWeb/Worker/WebWorkerServer.ipc b/Libraries/LibWeb/Worker/WebWorkerServer.ipc index 6a0ebe98e88..4a732d94a96 100644 --- a/Libraries/LibWeb/Worker/WebWorkerServer.ipc +++ b/Libraries/LibWeb/Worker/WebWorkerServer.ipc @@ -11,7 +11,7 @@ endpoint WebWorkerServer { Web::Bindings::WorkerType type, Web::Bindings::RequestCredentials credentials, String name, - Web::HTML::TransferDataHolder message_port, + Web::HTML::TransferDataEncoder message_port, Web::HTML::SerializedEnvironmentSettingsObject outside_settings, Web::Bindings::AgentType agent_type) =| diff --git a/Services/WebWorker/ConnectionFromClient.cpp b/Services/WebWorker/ConnectionFromClient.cpp index c560db81b53..cd3a15afd4b 100644 --- a/Services/WebWorker/ConnectionFromClient.cpp +++ b/Services/WebWorker/ConnectionFromClient.cpp @@ -63,7 +63,7 @@ Web::Page const& ConnectionFromClient::page() const return m_page_host->page(); } -void ConnectionFromClient::start_worker(URL::URL url, Web::Bindings::WorkerType type, Web::Bindings::RequestCredentials credentials, String name, Web::HTML::TransferDataHolder implicit_port, Web::HTML::SerializedEnvironmentSettingsObject outside_settings, Web::Bindings::AgentType agent_type) +void ConnectionFromClient::start_worker(URL::URL url, Web::Bindings::WorkerType type, Web::Bindings::RequestCredentials credentials, String name, Web::HTML::TransferDataEncoder implicit_port, Web::HTML::SerializedEnvironmentSettingsObject outside_settings, Web::Bindings::AgentType agent_type) { m_worker_host = make_ref_counted(move(url), type, move(name)); diff --git a/Services/WebWorker/ConnectionFromClient.h b/Services/WebWorker/ConnectionFromClient.h index e86dd89b2c5..857e68bb809 100644 --- a/Services/WebWorker/ConnectionFromClient.h +++ b/Services/WebWorker/ConnectionFromClient.h @@ -41,7 +41,7 @@ private: Web::Page& page(); Web::Page const& page() const; - virtual void start_worker(URL::URL url, Web::Bindings::WorkerType type, Web::Bindings::RequestCredentials credentials, String name, Web::HTML::TransferDataHolder, Web::HTML::SerializedEnvironmentSettingsObject, Web::Bindings::AgentType) override; + virtual void start_worker(URL::URL url, Web::Bindings::WorkerType type, Web::Bindings::RequestCredentials credentials, String name, Web::HTML::TransferDataEncoder, Web::HTML::SerializedEnvironmentSettingsObject, Web::Bindings::AgentType) override; virtual void handle_file_return(i32 error, Optional file, i32 request_id) override; GC::Root m_page_host; diff --git a/Services/WebWorker/WorkerHost.cpp b/Services/WebWorker/WorkerHost.cpp index bdc528f5350..1e2d7003e8c 100644 --- a/Services/WebWorker/WorkerHost.cpp +++ b/Services/WebWorker/WorkerHost.cpp @@ -36,7 +36,7 @@ WorkerHost::WorkerHost(URL::URL url, Web::Bindings::WorkerType type, String name WorkerHost::~WorkerHost() = default; // https://html.spec.whatwg.org/multipage/workers.html#run-a-worker -void WorkerHost::run(GC::Ref page, Web::HTML::TransferDataHolder message_port_data, Web::HTML::SerializedEnvironmentSettingsObject const& outside_settings_snapshot, Web::Bindings::RequestCredentials credentials, bool is_shared) +void WorkerHost::run(GC::Ref page, Web::HTML::TransferDataEncoder message_port_data, Web::HTML::SerializedEnvironmentSettingsObject const& outside_settings_snapshot, Web::Bindings::RequestCredentials credentials, bool is_shared) { // 3. Let unsafeWorkerCreationTime be the unsafe shared current time. auto unsafe_worker_creation_time = Web::HighResolutionTime::unsafe_shared_current_time(); @@ -157,6 +157,7 @@ void WorkerHost::run(GC::Ref page, Web::HTML::TransferDataHolder mess auto on_complete_function = [inside_settings, worker_global_scope, message_port_data = move(message_port_data), url = m_url, is_shared](GC::Ptr script) mutable { auto& realm = inside_settings->realm(); + // 1. If script is null or if script's error to rethrow is non-null, then: if (!script || !script->error_to_rethrow().is_null()) { // FIXME: 1. Queue a global task on the DOM manipulation task source given worker's relevant global object to fire an event named error at worker. @@ -180,7 +181,8 @@ void WorkerHost::run(GC::Ref page, Web::HTML::TransferDataHolder mess worker_global_scope->set_internal_port(inside_port); // 5. Entangle outside port and inside port. - MUST(inside_port->transfer_receiving_steps(message_port_data)); + Web::HTML::TransferDataDecoder decoder { move(message_port_data) }; + MUST(inside_port->transfer_receiving_steps(decoder)); // 6. Create a new WorkerLocation object and associate it with worker global scope. worker_global_scope->set_location(realm.create(*worker_global_scope)); diff --git a/Services/WebWorker/WorkerHost.h b/Services/WebWorker/WorkerHost.h index ee256727c1d..e373e438ac7 100644 --- a/Services/WebWorker/WorkerHost.h +++ b/Services/WebWorker/WorkerHost.h @@ -21,7 +21,7 @@ public: explicit WorkerHost(URL::URL url, Web::Bindings::WorkerType type, String name); ~WorkerHost(); - void run(GC::Ref, Web::HTML::TransferDataHolder message_port_data, Web::HTML::SerializedEnvironmentSettingsObject const&, Web::Bindings::RequestCredentials, bool is_shared); + void run(GC::Ref, Web::HTML::TransferDataEncoder message_port_data, Web::HTML::SerializedEnvironmentSettingsObject const&, Web::Bindings::RequestCredentials, bool is_shared); private: GC::Root m_console;