1use std::collections::HashMap;
8use std::ffi::CStr;
9use std::os::raw;
10use std::ptr;
11
12use base::id::{
13 BlobId, DomExceptionId, DomMatrixId, DomPointId, DomQuadId, DomRectId, ImageBitmapId, Index,
14 MessagePortId, NamespaceIndex, OffscreenCanvasId, PipelineNamespaceId, QuotaExceededErrorId,
15};
16use constellation_traits::{
17 BlobImpl, DomException, DomMatrix, DomPoint, DomQuad, DomRect, MessagePortImpl,
18 Serializable as SerializableInterface, SerializableImageBitmap, SerializableQuotaExceededError,
19 StructuredSerializedData, TransferableOffscreenCanvas, Transferrable as TransferrableInterface,
20 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::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
34use js::rust::{
35 CustomAutoRooterGuard, HandleValue, JSAutoStructuredCloneBufferWrapper, MutableHandleValue,
36};
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_with(HashMap::new);
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 =
420 T::serialized_storage(StructuredData::Writer(sc_writer)).get_or_insert_with(HashMap::new);
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<HashMap<MessagePortId, MessagePortImpl>>,
591 pub(crate) transform_streams_port_impls: Option<HashMap<MessagePortId, TransformStreamData>>,
593 pub(crate) blob_impls: Option<HashMap<BlobId, BlobImpl>>,
597 pub(crate) points: Option<HashMap<DomPointId, DomPoint>>,
599 pub(crate) rects: Option<HashMap<DomRectId, DomRect>>,
601 pub(crate) quads: Option<HashMap<DomQuadId, DomQuad>>,
603 pub(crate) matrices: Option<HashMap<DomMatrixId, DomMatrix>>,
605 pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>,
607 pub(crate) quota_exceeded_errors:
609 Option<HashMap<QuotaExceededErrorId, SerializableQuotaExceededError>>,
610 pub(crate) image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>,
612 pub(crate) transferred_image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>,
614 pub(crate) offscreen_canvases: Option<HashMap<OffscreenCanvasId, TransferableOffscreenCanvas>>,
616}
617
618#[derive(Default)]
620#[repr(C)]
621pub(crate) struct StructuredDataWriter {
622 pub(crate) error: Option<Error>,
624 pub(crate) ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
626 pub(crate) transform_streams_port: Option<HashMap<MessagePortId, TransformStreamData>>,
628 pub(crate) points: Option<HashMap<DomPointId, DomPoint>>,
630 pub(crate) rects: Option<HashMap<DomRectId, DomRect>>,
632 pub(crate) quads: Option<HashMap<DomQuadId, DomQuad>>,
634 pub(crate) matrices: Option<HashMap<DomMatrixId, DomMatrix>>,
636 pub(crate) exceptions: Option<HashMap<DomExceptionId, DomException>>,
638 pub(crate) quota_exceeded_errors:
640 Option<HashMap<QuotaExceededErrorId, SerializableQuotaExceededError>>,
641 pub(crate) blobs: Option<HashMap<BlobId, BlobImpl>>,
643 pub(crate) image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>,
645 pub(crate) transferred_image_bitmaps: Option<HashMap<ImageBitmapId, SerializableImageBitmap>>,
647 pub(crate) offscreen_canvases: Option<HashMap<OffscreenCanvasId, TransferableOffscreenCanvas>>,
649}
650
651pub(crate) fn write(
653 cx: SafeJSContext,
654 message: HandleValue,
655 transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
656) -> Fallible<StructuredSerializedData> {
657 unsafe {
658 rooted!(in(*cx) let mut val = UndefinedValue());
659 if let Some(transfer) = transfer {
660 transfer.safe_to_jsval(cx, val.handle_mut());
661 }
662 let mut sc_writer = StructuredDataWriter::default();
663 let sc_writer_ptr = &mut sc_writer as *mut _;
664
665 let scbuf = JSAutoStructuredCloneBufferWrapper::new(
666 StructuredCloneScope::DifferentProcess,
667 &STRUCTURED_CLONE_CALLBACKS,
668 );
669 let scdata = &mut ((*scbuf.as_raw_ptr()).data_);
670 let policy = CloneDataPolicy {
671 allowIntraClusterClonableSharedObjects_: false,
672 allowSharedMemoryObjects_: false,
673 };
674 let result = JS_WriteStructuredClone(
675 *cx,
676 message,
677 scdata,
678 StructuredCloneScope::DifferentProcess,
679 &policy,
680 &STRUCTURED_CLONE_CALLBACKS,
681 sc_writer_ptr as *mut raw::c_void,
682 val.handle(),
683 );
684 if !result {
685 let error = if JS_IsExceptionPending(*cx) {
686 Error::JSFailed
687 } else {
688 sc_writer.error.unwrap_or(Error::DataClone(None))
689 };
690
691 return Err(error);
692 }
693
694 let nbytes = GetLengthOfJSStructuredCloneData(scdata);
695 let mut data = Vec::with_capacity(nbytes);
696 CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
697 data.set_len(nbytes);
698
699 let data = StructuredSerializedData {
700 serialized: data,
701 ports: sc_writer.ports.take(),
702 transform_streams: sc_writer.transform_streams_port.take(),
703 points: sc_writer.points.take(),
704 rects: sc_writer.rects.take(),
705 quads: sc_writer.quads.take(),
706 matrices: sc_writer.matrices.take(),
707 exceptions: sc_writer.exceptions.take(),
708 quota_exceeded_errors: sc_writer.quota_exceeded_errors.take(),
709 blobs: sc_writer.blobs.take(),
710 image_bitmaps: sc_writer.image_bitmaps.take(),
711 transferred_image_bitmaps: sc_writer.transferred_image_bitmaps.take(),
712 offscreen_canvases: sc_writer.offscreen_canvases.take(),
713 };
714
715 Ok(data)
716 }
717}
718
719pub(crate) fn read(
722 global: &GlobalScope,
723 mut data: StructuredSerializedData,
724 rval: MutableHandleValue,
725) -> Fallible<Vec<DomRoot<MessagePort>>> {
726 let cx = GlobalScope::get_cx();
727 let _ac = enter_realm(global);
728 rooted_vec!(let mut roots);
729 let mut sc_reader = StructuredDataReader {
730 error: None,
731 roots,
732 port_impls: data.ports.take(),
733 transform_streams_port_impls: data.transform_streams.take(),
734 blob_impls: data.blobs.take(),
735 points: data.points.take(),
736 rects: data.rects.take(),
737 quads: data.quads.take(),
738 matrices: data.matrices.take(),
739 exceptions: data.exceptions.take(),
740 quota_exceeded_errors: data.quota_exceeded_errors.take(),
741 image_bitmaps: data.image_bitmaps.take(),
742 transferred_image_bitmaps: data.transferred_image_bitmaps.take(),
743 offscreen_canvases: data.offscreen_canvases.take(),
744 };
745 let sc_reader_ptr = &mut sc_reader as *mut _;
746 unsafe {
747 let scbuf = JSAutoStructuredCloneBufferWrapper::new(
748 StructuredCloneScope::DifferentProcess,
749 &STRUCTURED_CLONE_CALLBACKS,
750 );
751 let scdata = &mut ((*scbuf.as_raw_ptr()).data_);
752
753 WriteBytesToJSStructuredCloneData(
754 data.serialized.as_mut_ptr() as *const u8,
755 data.serialized.len(),
756 scdata,
757 );
758
759 let result = JS_ReadStructuredClone(
760 *cx,
761 scdata,
762 JS_STRUCTURED_CLONE_VERSION,
763 StructuredCloneScope::DifferentProcess,
764 rval,
765 &CloneDataPolicy {
766 allowIntraClusterClonableSharedObjects_: false,
767 allowSharedMemoryObjects_: false,
768 },
769 &STRUCTURED_CLONE_CALLBACKS,
770 sc_reader_ptr as *mut raw::c_void,
771 );
772 if !result {
773 let error = if JS_IsExceptionPending(*cx) {
774 Error::JSFailed
775 } else {
776 sc_reader.error.unwrap_or(Error::DataClone(None))
777 };
778
779 return Err(error);
780 }
781
782 let mut message_ports = vec![];
783 for reflector in sc_reader.roots.iter() {
784 let Ok(message_port) = root_from_object::<MessagePort>(reflector.get(), *cx) else {
785 continue;
786 };
787 message_ports.push(message_port);
788 }
789 assert!(sc_reader.port_impls.is_none());
791 Ok(message_ports)
792 }
793}