script: Allow to throw a custom exception on structured cloning (#37948)

The structured cloning with transfer list
https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer
throws a "DataCloneError" DOM expection by default if
serialization/transferral
is not possible, but a platform object can throw a custom excepton on
its serialization/transfer steps.

One example is OffscreenCanvas, which can throw
an "InvalidStateError" exception if the context mode is not none on
transfer steps.

https://html.spec.whatwg.org/multipage/#the-offscreencanvas-interface:transfer-steps

Testing: Improvements in the following tests
-
html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transferrable*

Fixes: #37919

Signed-off-by: Andrei Volykhin <andrei.volykhin@gmail.com>
This commit is contained in:
Andrei Volykhin 2025-07-09 14:43:09 +03:00 committed by GitHub
parent cae73341b2
commit a5b02047f9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 165 additions and 130 deletions

View file

@ -11,7 +11,7 @@ use constellation_traits::SerializableImageBitmap;
use dom_struct::dom_struct;
use euclid::default::{Point2D, Rect, Size2D};
use pixels::{CorsStatus, PixelFormat, Snapshot, SnapshotAlphaMode, SnapshotPixelFormat};
use script_bindings::error::Error;
use script_bindings::error::{Error, Fallible};
use script_bindings::realms::{AlreadyInRealm, InRealm};
use crate::dom::bindings::cell::DomRefCell;
@ -630,14 +630,16 @@ impl Serializable for ImageBitmap {
/// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:serialization-steps>
fn serialize(&self) -> Result<(ImageBitmapId, Self::Data), ()> {
// Step 1. If value's origin-clean flag is not set, then throw a "DataCloneError" DOMException.
if !self.origin_is_clean() {
// <https://html.spec.whatwg.org/multipage/#structuredserializeinternal>
// Step 19.1. If value has a [[Detached]] internal slot whose value is
// true, then throw a "DataCloneError" DOMException.
if self.is_detached() {
return Err(());
}
// If value has a [[Detached]] internal slot whose value is true,
// then throw a "DataCloneError" DOMException.
if self.is_detached() {
// Step 1. If value's origin-clean flag is not set, then throw a
// "DataCloneError" DOMException.
if !self.origin_is_clean() {
return Err(());
}
@ -673,45 +675,41 @@ impl Transferable for ImageBitmap {
type Index = ImageBitmapIndex;
type Data = SerializableImageBitmap;
fn can_transfer(&self) -> bool {
if !self.origin_is_clean() || self.is_detached() {
return false;
}
true
}
/// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:transfer-steps>
fn transfer(&self) -> Result<(ImageBitmapId, SerializableImageBitmap), ()> {
// Step 1. If value's origin-clean flag is not set, then throw a "DataCloneError" DOMException.
if !self.origin_is_clean() {
return Err(());
fn transfer(&self) -> Fallible<(ImageBitmapId, SerializableImageBitmap)> {
// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
// Step 5.2. If transferable has a [[Detached]] internal slot and
// transferable.[[Detached]] is true, then throw a "DataCloneError"
// DOMException.
if self.is_detached() {
return Err(Error::DataClone(None));
}
// If value has a [[Detached]] internal slot whose value is true,
// then throw a "DataCloneError" DOMException.
if self.is_detached() {
return Err(());
// Step 1. If value's origin-clean flag is not set, then throw a
// "DataCloneError" DOMException.
if !self.origin_is_clean() {
return Err(Error::DataClone(None));
}
// Step 2. Set dataHolder.[[BitmapData]] to value's bitmap data.
// Step 3. Unset value's bitmap data.
let serialized = SerializableImageBitmap {
let transferred = SerializableImageBitmap {
bitmap_data: self.bitmap_data.borrow_mut().take().unwrap(),
};
Ok((ImageBitmapId::new(), serialized))
Ok((ImageBitmapId::new(), transferred))
}
/// <https://html.spec.whatwg.org/multipage/#the-imagebitmap-interface:transfer-receiving-steps>
fn transfer_receive(
owner: &GlobalScope,
_: ImageBitmapId,
serialized: SerializableImageBitmap,
transferred: SerializableImageBitmap,
) -> Result<DomRoot<Self>, ()> {
// Step 1. Set value's bitmap data to serialized.[[BitmapData]].
Ok(ImageBitmap::new(
owner,
serialized.bitmap_data,
transferred.bitmap_data,
CanGc::note(),
))
}