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