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;