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