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