1use std::ffi::CStr;
8use std::os::raw;
9use std::ptr::{self, NonNull};
10
11use base::id::{
12 BlobId, DomExceptionId, DomMatrixId, DomPointId, DomQuadId, DomRectId, ImageBitmapId,
13 ImageDataId, Index, MessagePortId, NamespaceIndex, OffscreenCanvasId, PipelineNamespaceId,
14 QuotaExceededErrorId,
15};
16use constellation_traits::{
17 BlobImpl, DomException, DomMatrix, DomPoint, DomQuad, DomRect, MessagePortImpl,
18 Serializable as SerializableInterface, SerializableImageBitmap, SerializableImageData,
19 SerializableQuotaExceededError, StructuredSerializedData, TransferableOffscreenCanvas,
20 Transferrable as TransferrableInterface, TransformStreamData,
21};
22use js::gc::RootedVec;
23use js::glue::{
24 CopyJSStructuredCloneData, GetLengthOfJSStructuredCloneData, WriteBytesToJSStructuredCloneData,
25};
26use js::jsapi::{
27 CloneDataPolicy, HandleObject as RawHandleObject, Heap, JS_IsExceptionPending,
28 JS_ReadUint32Pair, JS_STRUCTURED_CLONE_VERSION, JS_WriteUint32Pair, JSContext, JSObject,
29 JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
30 MutableHandleObject as RawMutableHandleObject, StructuredCloneScope, TransferableOwnership,
31};
32use js::jsval::UndefinedValue;
33use js::realm::CurrentRealm;
34use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
35use js::rust::{
36 CustomAutoRooterGuard, HandleValue, JSAutoStructuredCloneBufferWrapper, MutableHandleValue,
37};
38use rustc_hash::FxHashMap;
39use script_bindings::conversions::{IDLInterface, SafeToJSValConvertible};
40use strum::IntoEnumIterator;
41
42use crate::dom::bindings::conversions::root_from_object;
43use crate::dom::bindings::error::{Error, Fallible};
44use crate::dom::bindings::root::DomRoot;
45use crate::dom::bindings::serializable::{Serializable, StorageKey};
46use crate::dom::bindings::transferable::Transferable;
47use crate::dom::blob::Blob;
48use crate::dom::dompoint::DOMPoint;
49use crate::dom::dompointreadonly::DOMPointReadOnly;
50use crate::dom::globalscope::GlobalScope;
51use crate::dom::imagebitmap::ImageBitmap;
52use crate::dom::imagedata::ImageData;
53use crate::dom::messageport::MessagePort;
54use crate::dom::offscreencanvas::OffscreenCanvas;
55use crate::dom::stream::readablestream::ReadableStream;
56use crate::dom::stream::writablestream::WritableStream;
57use crate::dom::types::{
58 DOMException, DOMMatrix, DOMMatrixReadOnly, DOMQuad, DOMRect, DOMRectReadOnly,
59 QuotaExceededError, TransformStream,
60};
61use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
62use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
63
64#[repr(u32)]
69pub(super) enum StructuredCloneTags {
70 Min = 0xFFFF8000,
72 DomBlob = 0xFFFF8001,
73 MessagePort = 0xFFFF8002,
74 Principals = 0xFFFF8003,
75 DomPointReadOnly = 0xFFFF8004,
76 DomPoint = 0xFFFF8005,
77 ReadableStream = 0xFFFF8006,
78 DomException = 0xFFFF8007,
79 WritableStream = 0xFFFF8008,
80 TransformStream = 0xFFFF8009,
81 ImageBitmap = 0xFFFF800A,
82 OffscreenCanvas = 0xFFFF800B,
83 QuotaExceededError = 0xFFFF800C,
84 DomRect = 0xFFFF800D,
85 DomRectReadOnly = 0xFFFF800E,
86 DomQuad = 0xFFFF800F,
87 DomMatrix = 0xFFFF8010,
88 DomMatrixReadOnly = 0xFFFF8011,
89 ImageData = 0xFFFF8012,
90 Max = 0xFFFFFFFF,
91}
92
93impl From<SerializableInterface> for StructuredCloneTags {
94 fn from(v: SerializableInterface) -> Self {
95 match v {
96 SerializableInterface::Blob => StructuredCloneTags::DomBlob,
97 SerializableInterface::DomPoint => StructuredCloneTags::DomPoint,
98 SerializableInterface::DomPointReadOnly => StructuredCloneTags::DomPointReadOnly,
99 SerializableInterface::DomRect => StructuredCloneTags::DomRect,
100 SerializableInterface::DomRectReadOnly => StructuredCloneTags::DomRectReadOnly,
101 SerializableInterface::DomQuad => StructuredCloneTags::DomQuad,
102 SerializableInterface::DomMatrix => StructuredCloneTags::DomMatrix,
103 SerializableInterface::DomMatrixReadOnly => StructuredCloneTags::DomMatrixReadOnly,
104 SerializableInterface::DomException => StructuredCloneTags::DomException,
105 SerializableInterface::ImageBitmap => StructuredCloneTags::ImageBitmap,
106 SerializableInterface::QuotaExceededError => StructuredCloneTags::QuotaExceededError,
107 SerializableInterface::ImageData => StructuredCloneTags::ImageData,
108 }
109 }
110}
111
112impl From<TransferrableInterface> for StructuredCloneTags {
113 fn from(v: TransferrableInterface) -> Self {
114 match v {
115 TransferrableInterface::ImageBitmap => StructuredCloneTags::ImageBitmap,
116 TransferrableInterface::MessagePort => StructuredCloneTags::MessagePort,
117 TransferrableInterface::OffscreenCanvas => StructuredCloneTags::OffscreenCanvas,
118 TransferrableInterface::ReadableStream => StructuredCloneTags::ReadableStream,
119 TransferrableInterface::WritableStream => StructuredCloneTags::WritableStream,
120 TransferrableInterface::TransformStream => StructuredCloneTags::TransformStream,
121 }
122 }
123}
124
125fn reader_for_type(
126 val: SerializableInterface,
127) -> unsafe fn(
128 &GlobalScope,
129 *mut JSStructuredCloneReader,
130 &mut StructuredDataReader<'_>,
131 CanGc,
132) -> *mut JSObject {
133 match val {
134 SerializableInterface::Blob => read_object::<Blob>,
135 SerializableInterface::DomPoint => read_object::<DOMPoint>,
136 SerializableInterface::DomPointReadOnly => read_object::<DOMPointReadOnly>,
137 SerializableInterface::DomRect => read_object::<DOMRect>,
138 SerializableInterface::DomRectReadOnly => read_object::<DOMRectReadOnly>,
139 SerializableInterface::DomQuad => read_object::<DOMQuad>,
140 SerializableInterface::DomMatrix => read_object::<DOMMatrix>,
141 SerializableInterface::DomMatrixReadOnly => read_object::<DOMMatrixReadOnly>,
142 SerializableInterface::DomException => read_object::<DOMException>,
143 SerializableInterface::ImageBitmap => read_object::<ImageBitmap>,
144 SerializableInterface::QuotaExceededError => read_object::<QuotaExceededError>,
145 SerializableInterface::ImageData => read_object::<ImageData>,
146 }
147}
148
149unsafe fn read_object<T: Serializable>(
150 owner: &GlobalScope,
151 r: *mut JSStructuredCloneReader,
152 sc_reader: &mut StructuredDataReader<'_>,
153 can_gc: CanGc,
154) -> *mut JSObject {
155 let mut name_space: u32 = 0;
156 let mut index: u32 = 0;
157 unsafe {
158 assert!(JS_ReadUint32Pair(
159 r,
160 &mut name_space as *mut u32,
161 &mut index as *mut u32
162 ));
163 }
164 let storage_key = StorageKey { index, name_space };
165
166 let id: NamespaceIndex<T::Index> = storage_key.into();
169
170 let objects = T::serialized_storage(StructuredData::Reader(sc_reader));
172 let objects_map = objects
173 .as_mut()
174 .expect("The SC holder does not have any relevant objects");
175 let serialized = objects_map
176 .remove(&id)
177 .expect("No object to be deserialized found.");
178 if objects_map.is_empty() {
179 *objects = None;
180 }
181
182 if let Ok(obj) = T::deserialize(owner, serialized, can_gc) {
183 let reflector = obj.reflector().get_jsobject().get();
184 sc_reader.roots.push(Heap::boxed(reflector));
185 return reflector;
186 }
187 warn!("Reading structured data failed in {:?}.", owner.get_url());
188 ptr::null_mut()
189}
190
191unsafe fn write_object<T: Serializable>(
192 interface: SerializableInterface,
193 owner: &GlobalScope,
194 object: &T,
195 w: *mut JSStructuredCloneWriter,
196 sc_writer: &mut StructuredDataWriter,
197) -> bool {
198 if let Ok((new_id, serialized)) = object.serialize() {
199 let objects = T::serialized_storage(StructuredData::Writer(sc_writer))
200 .get_or_insert(FxHashMap::default());
201 objects.insert(new_id, serialized);
202 let storage_key = StorageKey::new(new_id);
203
204 unsafe {
205 assert!(JS_WriteUint32Pair(
206 w,
207 StructuredCloneTags::from(interface) as u32,
208 0
209 ));
210 assert!(JS_WriteUint32Pair(
211 w,
212 storage_key.name_space,
213 storage_key.index
214 ));
215 }
216 return true;
217 }
218 warn!("Writing structured data failed in {:?}.", owner.get_url());
219 false
220}
221
222unsafe extern "C" fn read_callback(
223 cx: *mut JSContext,
224 r: *mut JSStructuredCloneReader,
225 _policy: *const CloneDataPolicy,
226 tag: u32,
227 _data: u32,
228 closure: *mut raw::c_void,
229) -> *mut JSObject {
230 assert!(
231 tag < StructuredCloneTags::Max as u32,
232 "tag should be lower than StructuredCloneTags::Max"
233 );
234 assert!(
235 tag > StructuredCloneTags::Min as u32,
236 "tag should be higher than StructuredCloneTags::Min"
237 );
238
239 unsafe {
240 let sc_reader = &mut *(closure as *mut StructuredDataReader<'_>);
241 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
242 let global = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
243 for serializable in SerializableInterface::iter() {
244 if tag == StructuredCloneTags::from(serializable) as u32 {
245 let reader = reader_for_type(serializable);
246 return reader(&global, r, sc_reader, CanGc::note());
247 }
248 }
249 }
250
251 ptr::null_mut()
252}
253
254enum OperationError {
255 InterfaceDoesNotMatch,
256 Exception(Error),
257}
258
259unsafe fn try_serialize<T: Serializable + IDLInterface>(
260 val: SerializableInterface,
261 cx: *mut JSContext,
262 object: RawHandleObject,
263 global: &GlobalScope,
264 w: *mut JSStructuredCloneWriter,
265 writer: &mut StructuredDataWriter,
266) -> Result<bool, OperationError> {
267 let object = unsafe { root_from_object::<T>(*object, cx) };
268 if let Ok(obj) = object {
269 return unsafe { Ok(write_object(val, global, &*obj, w, writer)) };
270 }
271 Err(OperationError::InterfaceDoesNotMatch)
272}
273
274type SerializeOperation = unsafe fn(
275 SerializableInterface,
276 *mut JSContext,
277 RawHandleObject,
278 &GlobalScope,
279 *mut JSStructuredCloneWriter,
280 &mut StructuredDataWriter,
281) -> Result<bool, OperationError>;
282
283fn serialize_for_type(val: SerializableInterface) -> SerializeOperation {
284 match val {
285 SerializableInterface::Blob => try_serialize::<Blob>,
286 SerializableInterface::DomPoint => try_serialize::<DOMPoint>,
287 SerializableInterface::DomPointReadOnly => try_serialize::<DOMPointReadOnly>,
288 SerializableInterface::DomRect => try_serialize::<DOMRect>,
289 SerializableInterface::DomRectReadOnly => try_serialize::<DOMRectReadOnly>,
290 SerializableInterface::DomQuad => try_serialize::<DOMQuad>,
291 SerializableInterface::DomMatrix => try_serialize::<DOMMatrix>,
292 SerializableInterface::DomMatrixReadOnly => try_serialize::<DOMMatrixReadOnly>,
293 SerializableInterface::DomException => try_serialize::<DOMException>,
294 SerializableInterface::ImageBitmap => try_serialize::<ImageBitmap>,
295 SerializableInterface::QuotaExceededError => try_serialize::<QuotaExceededError>,
296 SerializableInterface::ImageData => try_serialize::<ImageData>,
297 }
298}
299
300unsafe extern "C" fn write_callback(
301 cx: *mut JSContext,
302 w: *mut JSStructuredCloneWriter,
303 obj: RawHandleObject,
304 _same_process_scope_required: *mut bool,
305 closure: *mut raw::c_void,
306) -> bool {
307 unsafe {
308 let sc_writer = &mut *(closure as *mut StructuredDataWriter);
309 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
310 let global = GlobalScope::from_context(cx, InRealm::Already(&in_realm_proof));
311 for serializable in SerializableInterface::iter() {
312 let serializer = serialize_for_type(serializable);
313 if let Ok(result) = serializer(serializable, cx, obj, &global, w, sc_writer) {
314 return result;
315 }
316 }
317 }
318 false
319}
320
321fn receiver_for_type(
322 val: TransferrableInterface,
323) -> fn(
324 &mut js::context::JSContext,
325 &GlobalScope,
326 &mut StructuredDataReader<'_>,
327 u64,
328 RawMutableHandleObject,
329) -> Result<(), ()> {
330 match val {
331 TransferrableInterface::ImageBitmap => receive_object::<ImageBitmap>,
332 TransferrableInterface::MessagePort => receive_object::<MessagePort>,
333 TransferrableInterface::OffscreenCanvas => receive_object::<OffscreenCanvas>,
334 TransferrableInterface::ReadableStream => receive_object::<ReadableStream>,
335 TransferrableInterface::WritableStream => receive_object::<WritableStream>,
336 TransferrableInterface::TransformStream => receive_object::<TransformStream>,
337 }
338}
339
340fn receive_object<T: Transferable>(
341 cx: &mut js::context::JSContext,
342 owner: &GlobalScope,
343 sc_reader: &mut StructuredDataReader<'_>,
344 extra_data: u64,
345 return_object: RawMutableHandleObject,
346) -> Result<(), ()> {
347 let big: [u8; 8] = extra_data.to_ne_bytes();
350 let (name_space, index) = big.split_at(4);
351
352 let namespace_id = PipelineNamespaceId(u32::from_ne_bytes(
353 name_space
354 .try_into()
355 .expect("name_space to be a slice of four."),
356 ));
357 let id: NamespaceIndex<T::Index> = NamespaceIndex {
358 namespace_id,
359 index: Index::new(u32::from_ne_bytes(
360 index.try_into().expect("index to be a slice of four."),
361 ))
362 .expect("Index to be non-zero"),
363 };
364
365 let storage = T::serialized_storage(StructuredData::Reader(sc_reader));
367 let serialized = if let Some(objects) = storage.as_mut() {
368 let object = objects.remove(&id).expect("Transferred port to be stored");
369 if objects.is_empty() {
370 *storage = None;
371 }
372 object
373 } else {
374 panic!(
375 "An interface was transfer-received, yet the SC holder does not have any serialized objects"
376 );
377 };
378
379 let Ok(received) = T::transfer_receive(cx, owner, id, serialized) else {
380 return Err(());
381 };
382 return_object.set(received.reflector().rootable().get());
383 sc_reader.roots.push(Heap::boxed(return_object.get()));
384 Ok(())
385}
386
387unsafe extern "C" fn read_transfer_callback(
388 cx: *mut JSContext,
389 _r: *mut JSStructuredCloneReader,
390 _policy: *const CloneDataPolicy,
391 tag: u32,
392 _content: *mut raw::c_void,
393 extra_data: u64,
394 closure: *mut raw::c_void,
395 return_object: RawMutableHandleObject,
396) -> bool {
397 let sc_reader = unsafe { &mut *(closure as *mut StructuredDataReader<'_>) };
398 let mut cx = unsafe {
399 js::context::JSContext::from_ptr(
401 NonNull::new(cx).expect("JSContext pointer should not be null in SM hook"),
402 )
403 };
404 let mut cx = CurrentRealm::assert(&mut cx);
405 let owner = GlobalScope::from_current_realm(&cx);
406
407 for transferrable in TransferrableInterface::iter() {
408 if tag == StructuredCloneTags::from(transferrable) as u32 {
409 let transfer_receiver = receiver_for_type(transferrable);
410 if transfer_receiver(&mut cx, &owner, sc_reader, extra_data, return_object).is_ok() {
411 return true;
412 }
413 }
414 }
415 false
416}
417
418unsafe fn try_transfer<T: Transferable + IDLInterface>(
419 interface: TransferrableInterface,
420 obj: RawHandleObject,
421 cx: &mut js::context::JSContext,
422 sc_writer: &mut StructuredDataWriter,
423 tag: *mut u32,
424 ownership: *mut TransferableOwnership,
425 extra_data: *mut u64,
426) -> Result<(), OperationError> {
427 let object = unsafe { root_from_object::<T>(*obj, cx.raw_cx()) };
428 let Ok(object) = object else {
429 return Err(OperationError::InterfaceDoesNotMatch);
430 };
431
432 unsafe { *tag = StructuredCloneTags::from(interface) as u32 };
433 unsafe { *ownership = TransferableOwnership::SCTAG_TMO_CUSTOM };
434
435 let (id, object) = object.transfer(cx).map_err(OperationError::Exception)?;
436
437 let objects = T::serialized_storage(StructuredData::Writer(sc_writer))
439 .get_or_insert(FxHashMap::default());
440 objects.insert(id, object);
441
442 let index = id.index.0.get();
443
444 let mut big: [u8; 8] = [0; 8];
445 let name_space = id.namespace_id.0.to_ne_bytes();
446 let index = index.to_ne_bytes();
447
448 let (left, right) = big.split_at_mut(4);
449 left.copy_from_slice(&name_space);
450 right.copy_from_slice(&index);
451
452 unsafe { *extra_data = u64::from_ne_bytes(big) };
454 Ok(())
455}
456
457type TransferOperation = unsafe fn(
458 TransferrableInterface,
459 RawHandleObject,
460 &mut js::context::JSContext,
461 &mut StructuredDataWriter,
462 *mut u32,
463 *mut TransferableOwnership,
464 *mut u64,
465) -> Result<(), OperationError>;
466
467fn transfer_for_type(val: TransferrableInterface) -> TransferOperation {
468 match val {
469 TransferrableInterface::ImageBitmap => try_transfer::<ImageBitmap>,
470 TransferrableInterface::MessagePort => try_transfer::<MessagePort>,
471 TransferrableInterface::OffscreenCanvas => try_transfer::<OffscreenCanvas>,
472 TransferrableInterface::ReadableStream => try_transfer::<ReadableStream>,
473 TransferrableInterface::WritableStream => try_transfer::<WritableStream>,
474 TransferrableInterface::TransformStream => try_transfer::<TransformStream>,
475 }
476}
477
478unsafe extern "C" fn write_transfer_callback(
480 cx: *mut JSContext,
481 obj: RawHandleObject,
482 closure: *mut raw::c_void,
483 tag: *mut u32,
484 ownership: *mut TransferableOwnership,
485 _content: *mut *mut raw::c_void,
486 extra_data: *mut u64,
487) -> bool {
488 let sc_writer = unsafe { &mut *(closure as *mut StructuredDataWriter) };
489 let mut cx = unsafe {
490 js::context::JSContext::from_ptr(
492 NonNull::new(cx).expect("JSContext pointer should not be null in SM hook"),
493 )
494 };
495 for transferable in TransferrableInterface::iter() {
496 let try_transfer = transfer_for_type(transferable);
497
498 let transfer_result = unsafe {
499 try_transfer(
500 transferable,
501 obj,
502 &mut cx,
503 sc_writer,
504 tag,
505 ownership,
506 extra_data,
507 )
508 };
509 match transfer_result {
510 Err(error) => match error {
511 OperationError::InterfaceDoesNotMatch => {},
512 OperationError::Exception(error) => {
513 sc_writer.error = Some(error);
514 return false;
515 },
516 },
517 Ok(..) => return true,
518 }
519 }
520
521 false
522}
523
524unsafe extern "C" fn free_transfer_callback(
525 _tag: u32,
526 _ownership: TransferableOwnership,
527 _content: *mut raw::c_void,
528 _extra_data: u64,
529 _closure: *mut raw::c_void,
530) {
531}
532
533unsafe fn can_transfer_for_type(
534 transferable: TransferrableInterface,
535 obj: RawHandleObject,
536 cx: *mut JSContext,
537) -> Result<bool, ()> {
538 unsafe fn can_transfer<T: Transferable + IDLInterface>(
539 obj: RawHandleObject,
540 cx: *mut JSContext,
541 ) -> Result<bool, ()> {
542 unsafe { root_from_object::<T>(*obj, cx).map(|o| Transferable::can_transfer(&*o)) }
543 }
544
545 unsafe {
546 match transferable {
547 TransferrableInterface::ImageBitmap => can_transfer::<ImageBitmap>(obj, cx),
548 TransferrableInterface::MessagePort => can_transfer::<MessagePort>(obj, cx),
549 TransferrableInterface::OffscreenCanvas => can_transfer::<OffscreenCanvas>(obj, cx),
550 TransferrableInterface::ReadableStream => can_transfer::<ReadableStream>(obj, cx),
551 TransferrableInterface::WritableStream => can_transfer::<WritableStream>(obj, cx),
552 TransferrableInterface::TransformStream => can_transfer::<TransformStream>(obj, cx),
553 }
554 }
555}
556
557unsafe extern "C" fn can_transfer_callback(
558 cx: *mut JSContext,
559 obj: RawHandleObject,
560 _same_process_scope_required: *mut bool,
561 _closure: *mut raw::c_void,
562) -> bool {
563 for transferable in TransferrableInterface::iter() {
564 let can_transfer = unsafe { can_transfer_for_type(transferable, obj, cx) };
565 if let Ok(can_transfer) = can_transfer {
566 return can_transfer;
567 }
568 }
569 false
570}
571
572unsafe extern "C" fn report_error_callback(
573 _cx: *mut JSContext,
574 _errorid: u32,
575 closure: *mut raw::c_void,
576 error_message: *const ::std::os::raw::c_char,
577) {
578 let msg_result = unsafe { CStr::from_ptr(error_message).to_str().map(str::to_string) };
579
580 if let Ok(msg) = msg_result {
581 let error = unsafe { &mut *(closure as *mut Option<Error>) };
582
583 if error.is_none() {
584 *error = Some(Error::DataClone(Some(msg)));
585 }
586 }
587}
588
589unsafe extern "C" fn sab_cloned_callback(
590 _cx: *mut JSContext,
591 _receiving: bool,
592 _closure: *mut ::std::os::raw::c_void,
593) -> bool {
594 false
595}
596
597static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
598 read: Some(read_callback),
599 write: Some(write_callback),
600 reportError: Some(report_error_callback),
601 readTransfer: Some(read_transfer_callback),
602 writeTransfer: Some(write_transfer_callback),
603 freeTransfer: Some(free_transfer_callback),
604 canTransfer: Some(can_transfer_callback),
605 sabCloned: Some(sab_cloned_callback),
606};
607
608pub(crate) enum StructuredData<'a, 'b> {
609 Reader(&'a mut StructuredDataReader<'b>),
610 Writer(&'a mut StructuredDataWriter),
611}
612
613#[repr(C)]
616pub(crate) struct StructuredDataReader<'a> {
617 error: Option<Error>,
619 roots: RootedVec<'a, Box<Heap<*mut JSObject>>>,
621 pub(crate) port_impls: Option<FxHashMap<MessagePortId, MessagePortImpl>>,
625 pub(crate) transform_streams_port_impls: Option<FxHashMap<MessagePortId, TransformStreamData>>,
627 pub(crate) blob_impls: Option<FxHashMap<BlobId, BlobImpl>>,
631 pub(crate) points: Option<FxHashMap<DomPointId, DomPoint>>,
633 pub(crate) rects: Option<FxHashMap<DomRectId, DomRect>>,
635 pub(crate) quads: Option<FxHashMap<DomQuadId, DomQuad>>,
637 pub(crate) matrices: Option<FxHashMap<DomMatrixId, DomMatrix>>,
639 pub(crate) exceptions: Option<FxHashMap<DomExceptionId, DomException>>,
641 pub(crate) quota_exceeded_errors:
643 Option<FxHashMap<QuotaExceededErrorId, SerializableQuotaExceededError>>,
644 pub(crate) image_bitmaps: Option<FxHashMap<ImageBitmapId, SerializableImageBitmap>>,
646 pub(crate) transferred_image_bitmaps: Option<FxHashMap<ImageBitmapId, SerializableImageBitmap>>,
648 pub(crate) offscreen_canvases:
650 Option<FxHashMap<OffscreenCanvasId, TransferableOffscreenCanvas>>,
651 pub(crate) image_data: Option<FxHashMap<ImageDataId, SerializableImageData>>,
653}
654
655#[derive(Default)]
657#[repr(C)]
658pub(crate) struct StructuredDataWriter {
659 pub(crate) error: Option<Error>,
661 pub(crate) ports: Option<FxHashMap<MessagePortId, MessagePortImpl>>,
663 pub(crate) transform_streams_port: Option<FxHashMap<MessagePortId, TransformStreamData>>,
665 pub(crate) points: Option<FxHashMap<DomPointId, DomPoint>>,
667 pub(crate) rects: Option<FxHashMap<DomRectId, DomRect>>,
669 pub(crate) quads: Option<FxHashMap<DomQuadId, DomQuad>>,
671 pub(crate) matrices: Option<FxHashMap<DomMatrixId, DomMatrix>>,
673 pub(crate) exceptions: Option<FxHashMap<DomExceptionId, DomException>>,
675 pub(crate) quota_exceeded_errors:
677 Option<FxHashMap<QuotaExceededErrorId, SerializableQuotaExceededError>>,
678 pub(crate) blobs: Option<FxHashMap<BlobId, BlobImpl>>,
680 pub(crate) image_bitmaps: Option<FxHashMap<ImageBitmapId, SerializableImageBitmap>>,
682 pub(crate) transferred_image_bitmaps: Option<FxHashMap<ImageBitmapId, SerializableImageBitmap>>,
684 pub(crate) offscreen_canvases:
686 Option<FxHashMap<OffscreenCanvasId, TransferableOffscreenCanvas>>,
687 pub(crate) image_data: Option<FxHashMap<ImageDataId, SerializableImageData>>,
689}
690
691pub(crate) fn write(
693 cx: SafeJSContext,
694 message: HandleValue,
695 transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
696) -> Fallible<StructuredSerializedData> {
697 unsafe {
698 rooted!(in(*cx) let mut val = UndefinedValue());
699 if let Some(transfer) = transfer {
700 transfer.safe_to_jsval(cx, val.handle_mut(), CanGc::note());
701 }
702 let mut sc_writer = StructuredDataWriter::default();
703 let sc_writer_ptr = &mut sc_writer as *mut _;
704
705 let scbuf = JSAutoStructuredCloneBufferWrapper::new(
706 StructuredCloneScope::DifferentProcess,
707 &STRUCTURED_CLONE_CALLBACKS,
708 );
709 let scdata = &mut ((*scbuf.as_raw_ptr()).data_);
710 let policy = CloneDataPolicy {
711 allowIntraClusterClonableSharedObjects_: false,
712 allowSharedMemoryObjects_: false,
713 };
714 let result = JS_WriteStructuredClone(
715 *cx,
716 message,
717 scdata,
718 StructuredCloneScope::DifferentProcess,
719 &policy,
720 &STRUCTURED_CLONE_CALLBACKS,
721 sc_writer_ptr as *mut raw::c_void,
722 val.handle(),
723 );
724 if !result {
725 let error = if JS_IsExceptionPending(*cx) {
726 Error::JSFailed
727 } else {
728 sc_writer.error.unwrap_or(Error::DataClone(None))
729 };
730
731 return Err(error);
732 }
733
734 let nbytes = GetLengthOfJSStructuredCloneData(scdata);
735 let mut data = Vec::with_capacity(nbytes);
736 CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
737 data.set_len(nbytes);
738
739 let data = StructuredSerializedData {
740 serialized: data,
741 ports: sc_writer.ports.take(),
742 transform_streams: sc_writer.transform_streams_port.take(),
743 points: sc_writer.points.take(),
744 rects: sc_writer.rects.take(),
745 quads: sc_writer.quads.take(),
746 matrices: sc_writer.matrices.take(),
747 exceptions: sc_writer.exceptions.take(),
748 quota_exceeded_errors: sc_writer.quota_exceeded_errors.take(),
749 blobs: sc_writer.blobs.take(),
750 image_bitmaps: sc_writer.image_bitmaps.take(),
751 transferred_image_bitmaps: sc_writer.transferred_image_bitmaps.take(),
752 offscreen_canvases: sc_writer.offscreen_canvases.take(),
753 image_data: sc_writer.image_data.take(),
754 };
755
756 Ok(data)
757 }
758}
759
760pub(crate) fn read(
763 global: &GlobalScope,
764 mut data: StructuredSerializedData,
765 rval: MutableHandleValue,
766 _can_gc: CanGc,
767) -> Fallible<Vec<DomRoot<MessagePort>>> {
768 let cx = GlobalScope::get_cx();
769 let _ac = enter_realm(global);
770 rooted_vec!(let mut roots);
771 let mut sc_reader = StructuredDataReader {
772 error: None,
773 roots,
774 port_impls: data.ports.take(),
775 transform_streams_port_impls: data.transform_streams.take(),
776 blob_impls: data.blobs.take(),
777 points: data.points.take(),
778 rects: data.rects.take(),
779 quads: data.quads.take(),
780 matrices: data.matrices.take(),
781 exceptions: data.exceptions.take(),
782 quota_exceeded_errors: data.quota_exceeded_errors.take(),
783 image_bitmaps: data.image_bitmaps.take(),
784 transferred_image_bitmaps: data.transferred_image_bitmaps.take(),
785 offscreen_canvases: data.offscreen_canvases.take(),
786 image_data: data.image_data.take(),
787 };
788 let sc_reader_ptr = &mut sc_reader as *mut _;
789 unsafe {
790 let scbuf = JSAutoStructuredCloneBufferWrapper::new(
791 StructuredCloneScope::DifferentProcess,
792 &STRUCTURED_CLONE_CALLBACKS,
793 );
794 let scdata = &mut ((*scbuf.as_raw_ptr()).data_);
795
796 WriteBytesToJSStructuredCloneData(
797 data.serialized.as_mut_ptr() as *const u8,
798 data.serialized.len(),
799 scdata,
800 );
801
802 let result = JS_ReadStructuredClone(
803 *cx,
804 scdata,
805 JS_STRUCTURED_CLONE_VERSION,
806 StructuredCloneScope::DifferentProcess,
807 rval,
808 &CloneDataPolicy {
809 allowIntraClusterClonableSharedObjects_: false,
810 allowSharedMemoryObjects_: false,
811 },
812 &STRUCTURED_CLONE_CALLBACKS,
813 sc_reader_ptr as *mut raw::c_void,
814 );
815 if !result {
816 let error = if JS_IsExceptionPending(*cx) {
817 Error::JSFailed
818 } else {
819 sc_reader.error.unwrap_or(Error::DataClone(None))
820 };
821
822 return Err(error);
823 }
824
825 let mut message_ports = vec![];
826 for reflector in sc_reader.roots.iter() {
827 let Ok(message_port) = root_from_object::<MessagePort>(reflector.get(), *cx) else {
828 continue;
829 };
830 message_ports.push(message_port);
831 }
832 assert!(sc_reader.port_impls.is_none());
834 Ok(message_ports)
835 }
836}