diff options
author | Kingsley Yung <kingsley@kkoyung.dev> | 2025-04-15 21:25:31 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-15 13:25:31 +0000 |
commit | dacd951c9fcdb819cd9946a3d5d2cf10558713eb (patch) | |
tree | 25c592aeab0951fe84d2a7f99820465755c9b0fb | |
parent | cc04caa8cee75e4d96f8f42fd23c0ffa98d15954 (diff) | |
download | servo-dacd951c9fcdb819cd9946a3d5d2cf10558713eb.tar.gz servo-dacd951c9fcdb819cd9946a3d5d2cf10558713eb.zip |
Make DOMException serializable (#36535)
Follow the implementation of making DOMPoint and DOMPointReadOnly
serializable in PR #35989
Testing: Passed a test previously expected to fail.
Fixes: #36463
---------
Signed-off-by: Kingsley Yung <kingsley@kkoyung.dev>
6 files changed, 138 insertions, 12 deletions
diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index e53318516a9..fc2f9ecfea0 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -10,9 +10,9 @@ use std::num::NonZeroU32; use std::os::raw; use std::ptr; -use base::id::{BlobId, DomPointId, MessagePortId, PipelineNamespaceId}; +use base::id::{BlobId, DomExceptionId, DomPointId, MessagePortId, PipelineNamespaceId}; use constellation_traits::{ - BlobImpl, DomPoint, MessagePortImpl, Serializable as SerializableInterface, + BlobImpl, DomException, DomPoint, MessagePortImpl, Serializable as SerializableInterface, StructuredSerializedData, Transferrable as TransferrableInterface, }; use js::glue::{ @@ -42,6 +42,7 @@ use crate::dom::dompointreadonly::DOMPointReadOnly; use crate::dom::globalscope::GlobalScope; use crate::dom::messageport::MessagePort; use crate::dom::readablestream::ReadableStream; +use crate::dom::types::DOMException; use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; @@ -59,6 +60,7 @@ pub(super) enum StructuredCloneTags { DomPointReadOnly = 0xFFFF8004, DomPoint = 0xFFFF8005, ReadableStream = 0xFFFF8006, + DomException = 0xFFFF8007, Max = 0xFFFFFFFF, } @@ -68,6 +70,7 @@ impl From<SerializableInterface> for StructuredCloneTags { SerializableInterface::Blob => StructuredCloneTags::DomBlob, SerializableInterface::DomPointReadOnly => StructuredCloneTags::DomPointReadOnly, SerializableInterface::DomPoint => StructuredCloneTags::DomPoint, + SerializableInterface::DomException => StructuredCloneTags::DomException, } } } @@ -93,6 +96,7 @@ fn reader_for_type( SerializableInterface::Blob => read_object::<Blob>, SerializableInterface::DomPointReadOnly => read_object::<DOMPointReadOnly>, SerializableInterface::DomPoint => read_object::<DOMPoint>, + SerializableInterface::DomException => read_object::<DOMException>, } } @@ -226,6 +230,7 @@ fn serialize_for_type(val: SerializableInterface) -> SerializeOperation { SerializableInterface::Blob => try_serialize::<Blob>, SerializableInterface::DomPointReadOnly => try_serialize::<DOMPointReadOnly>, SerializableInterface::DomPoint => try_serialize::<DOMPoint>, + SerializableInterface::DomException => try_serialize::<DOMException>, } } @@ -500,6 +505,8 @@ pub(crate) struct StructuredDataReader { /// A map of deserialized points, stored temporarily here to keep them rooted. pub(crate) points_read_only: Option<HashMap<StorageKey, DomRoot<DOMPointReadOnly>>>, pub(crate) dom_points: Option<HashMap<StorageKey, DomRoot<DOMPoint>>>, + /// A map of deserialized exceptions, stored temporarily here to keep them rooted. + pub(crate) dom_exceptions: Option<HashMap<StorageKey, DomRoot<DOMException>>>, /// A vec of transfer-received DOM ports, /// to be made available to script through a message event. pub(crate) message_ports: Option<Vec<DomRoot<MessagePort>>>, @@ -513,6 +520,8 @@ pub(crate) struct StructuredDataReader { pub(crate) blob_impls: Option<HashMap<BlobId, BlobImpl>>, /// A map of serialized points. pub(crate) points: Option<HashMap<DomPointId, DomPoint>>, + /// A map of serialized exceptions. + pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>, pub(crate) readable_streams: Option<Vec<DomRoot<ReadableStream>>>, } @@ -526,6 +535,8 @@ pub(crate) struct StructuredDataWriter { pub(crate) ports: Option<HashMap<MessagePortId, MessagePortImpl>>, /// Serialized points. pub(crate) points: Option<HashMap<DomPointId, DomPoint>>, + /// Serialized exceptions. + pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>, /// Serialized blobs. pub(crate) blobs: Option<HashMap<BlobId, BlobImpl>>, } @@ -579,6 +590,7 @@ pub(crate) fn write( serialized: data, ports: sc_writer.ports.take(), points: sc_writer.points.take(), + exceptions: sc_writer.exceptions.take(), blobs: sc_writer.blobs.take(), }; @@ -600,9 +612,11 @@ pub(crate) fn read( message_ports: None, points_read_only: None, dom_points: None, + dom_exceptions: None, port_impls: data.ports.take(), blob_impls: data.blobs.take(), points: data.points.take(), + exceptions: data.exceptions.take(), errors: DOMErrorRecord { message: None }, readable_streams: None, }; diff --git a/components/script/dom/domexception.rs b/components/script/dom/domexception.rs index a5ad0bfb043..35a21556a2d 100644 --- a/components/script/dom/domexception.rs +++ b/components/script/dom/domexception.rs @@ -2,6 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::collections::HashMap; +use std::num::NonZeroU32; + +use base::id::{DomExceptionId, DomExceptionIndex, PipelineNamespaceId}; +use constellation_traits::DomException; use dom_struct::dom_struct; use js::rust::HandleObject; @@ -13,7 +18,9 @@ use crate::dom::bindings::reflector::{ Reflector, reflect_dom_object, reflect_dom_object_with_proto, }; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::serializable::{IntoStorageKey, Serializable, StorageKey}; use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::structuredclone::{StructuredData, StructuredDataReader}; use crate::dom::globalscope::GlobalScope; use crate::script_runtime::CanGc; @@ -215,3 +222,68 @@ impl DOMExceptionMethods<crate::DomTypeHolder> for DOMException { self.message.clone() } } + +impl Serializable for DOMException { + type Id = DomExceptionId; + type Data = DomException; + + // https://webidl.spec.whatwg.org/#idl-DOMException + fn serialize(&self) -> Result<(Self::Id, Self::Data), ()> { + let serialized = DomException { + message: self.message.to_string(), + name: self.name.to_string(), + }; + Ok((DomExceptionId::new(), serialized)) + } + + // https://webidl.spec.whatwg.org/#idl-DOMException + fn deserialize( + owner: &GlobalScope, + serialized: Self::Data, + can_gc: CanGc, + ) -> Result<DomRoot<Self>, ()> + where + Self: Sized, + { + Ok(Self::new_with_custom_message( + owner, + DOMErrorName::from(&DOMString::from_string(serialized.name)).ok_or(())?, + serialized.message, + can_gc, + )) + } + + fn serialized_storage(data: StructuredData<'_>) -> &mut Option<HashMap<Self::Id, Self::Data>> { + match data { + StructuredData::Reader(reader) => &mut reader.exceptions, + StructuredData::Writer(writer) => &mut writer.exceptions, + } + } + + fn deserialized_storage( + reader: &mut StructuredDataReader, + ) -> &mut Option<HashMap<StorageKey, DomRoot<Self>>> { + &mut reader.dom_exceptions + } +} + +impl From<StorageKey> for DomExceptionId { + fn from(storage_key: StorageKey) -> DomExceptionId { + let namespace_id = PipelineNamespaceId(storage_key.name_space); + let index = DomExceptionIndex( + NonZeroU32::new(storage_key.index).expect("Deserialized exception index is zero"), + ); + + DomExceptionId { + namespace_id, + index, + } + } +} + +impl IntoStorageKey for DomExceptionId { + fn into_storage_key(self) -> StorageKey { + let DomExceptionIndex(index) = self.index; + StorageKey::new(self.namespace_id, index) + } +} diff --git a/components/shared/base/id.rs b/components/shared/base/id.rs index 6a9729c45c5..c59a541b214 100644 --- a/components/shared/base/id.rs +++ b/components/shared/base/id.rs @@ -196,6 +196,7 @@ impl PipelineNamespace { self, ServiceWorkerRegistrationIndex} namespace_id_method! {next_blob_id, BlobId, self, BlobIndex} namespace_id_method! {next_dom_point_id, DomPointId, self, DomPointIndex} + namespace_id_method! {next_dom_exception_id, DomExceptionId, self, DomExceptionIndex} } thread_local!(pub static PIPELINE_NAMESPACE: Cell<Option<PipelineNamespace>> = const { Cell::new(None) }); @@ -425,6 +426,19 @@ impl DomPointId { } } +namespace_id! {DomExceptionId, DomExceptionIndex, "DomException"} + +impl DomExceptionId { + pub fn new() -> DomExceptionId { + PIPELINE_NAMESPACE.with(|tls| { + let mut namespace = tls.get().expect("No namespace set for this thread!"); + let next_exception_id = namespace.next_dom_exception_id(); + tls.set(Some(namespace)); + next_exception_id + }) + } +} + namespace_id! {HistoryStateId, HistoryStateIndex, "HistoryState"} impl HistoryStateId { diff --git a/components/shared/constellation/message_port.rs b/components/shared/constellation/message_port.rs index 79c34cd4022..8fe40418004 100644 --- a/components/shared/constellation/message_port.rs +++ b/components/shared/constellation/message_port.rs @@ -13,7 +13,7 @@ use std::cell::RefCell; use std::collections::{HashMap, VecDeque}; use std::path::PathBuf; -use base::id::{BlobId, DomPointId, MessagePortId}; +use base::id::{BlobId, DomExceptionId, DomPointId, MessagePortId}; use log::warn; use malloc_size_of_derive::MallocSizeOf; use net_traits::filemanager_thread::RelativePos; @@ -177,6 +177,8 @@ pub struct StructuredSerializedData { pub blobs: Option<HashMap<BlobId, BlobImpl>>, /// Serialized point objects. pub points: Option<HashMap<DomPointId, DomPoint>>, + /// Serialized exception objects. + pub exceptions: Option<HashMap<DomExceptionId, DomException>>, /// Transferred objects. pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>, } @@ -205,6 +207,8 @@ pub enum Serializable { DomPoint, /// The `DOMPointReadOnly` interface. DomPointReadOnly, + /// The `DOMException` interface. + DomException, } impl Serializable { @@ -215,6 +219,9 @@ impl Serializable { StructuredSerializedData::clone_all_of_type::<DomPoint> }, Serializable::DomPoint => StructuredSerializedData::clone_all_of_type::<DomPoint>, + Serializable::DomException => { + StructuredSerializedData::clone_all_of_type::<DomException> + }, } } } @@ -294,6 +301,7 @@ pub struct PortMessageTask { /// Messages for communication between the constellation and a global managing ports. #[derive(Debug, Deserialize, Serialize)] +#[allow(clippy::large_enum_variant)] pub enum MessagePortMsg { /// Complete the transfer for a batch of ports. CompleteTransfer(HashMap<MessagePortId, VecDeque<PortMessageTask>>), @@ -526,3 +534,30 @@ impl BroadcastClone for DomPoint { Some(self.clone()) } } + +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] +/// A serializable version of the DOMException interface. +pub struct DomException { + pub message: String, + pub name: String, +} + +impl BroadcastClone for DomException { + type Id = DomExceptionId; + + fn source( + data: &StructuredSerializedData, + ) -> &Option<std::collections::HashMap<Self::Id, Self>> { + &data.exceptions + } + + fn destination( + data: &mut StructuredSerializedData, + ) -> &mut Option<std::collections::HashMap<Self::Id, Self>> { + &mut data.exceptions + } + + fn clone_for_broadcast(&self) -> Option<Self> { + Some(self.clone()) + } +} diff --git a/tests/wpt/meta/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html.ini b/tests/wpt/meta/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html.ini index 5321153d366..37a5e29351d 100644 --- a/tests/wpt/meta/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html.ini +++ b/tests/wpt/meta/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html.ini @@ -4,9 +4,3 @@ [ImageData expandos are not cloned] expected: FAIL - - [DOMException objects can be cloned] - expected: FAIL - - [DOMException objects created by the UA can be cloned] - expected: FAIL diff --git a/tests/wpt/meta/streams/transferable/reason.html.ini b/tests/wpt/meta/streams/transferable/reason.html.ini deleted file mode 100644 index b10a529b02f..00000000000 --- a/tests/wpt/meta/streams/transferable/reason.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[reason.html] - [DOMException errors should be preserved] - expected: FAIL |