diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index 338edeb035..74bae634f9 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -2467,7 +2467,6 @@ css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html css/cssom/idlharness.html css/cssom/style-sheet-interfaces-001.html css/geometry/DOMMatrix2DInit-validate-fixup.html -css/geometry/structured-serialization.html css/mediaqueries/at-custom-media-cssom.html css/mediaqueries/at-custom-media-parsing.html css/mediaqueries/display-mode.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index 1c85e042c7..48c42988fc 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -3367,6 +3367,7 @@ css/geometry/DOMRectList.html css/geometry/WebKitCSSMatrix.html css/geometry/historical.html css/geometry/spec-examples.html +css/geometry/structured-serialization.html css/mediaqueries/aspect-ratio-serialization.html css/mediaqueries/duplicate-media-stylesheet-crash.html css/mediaqueries/dynamic-range.html diff --git a/moli-renderer-v8/src/context_bootstrap.rs b/moli-renderer-v8/src/context_bootstrap.rs index 8a9900d28b..2b072b6dfb 100644 --- a/moli-renderer-v8/src/context_bootstrap.rs +++ b/moli-renderer-v8/src/context_bootstrap.rs @@ -24,6 +24,7 @@ mod events; pub(crate) mod exposed_interfaces; mod file_api; mod form_data_runtime; +mod geometry_clone; mod geometry_runtime; mod history_mutation; mod history_runtime; @@ -273,6 +274,10 @@ pub(crate) use self::file_api::{ flush_one_pending_file_reader, is_file_list_object, selected_file_from_object, }; pub(crate) use self::form_data_runtime::form_data_request_body; +pub(crate) use self::geometry_clone::{ + GeometryClonePayload, build_geometry_object_from_clone_payload, + geometry_clone_payload_from_object, +}; use self::geometry_runtime::{build_dom_point_object, optional_dom_point_init_arg}; pub(crate) use self::image_data::{ ImageDataClonePayload, build_image_data_object_from_clone_payload, diff --git a/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs b/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs index 935b1205b6..7d74ce0206 100644 --- a/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs +++ b/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs @@ -24,8 +24,8 @@ use super::super::{ }, form_data_runtime::build_form_data_constructor_template, geometry_runtime::{ - dom_matrix_constructor_callback, dom_point_constructor_callback, - dom_point_readonly_constructor_callback, + dom_matrix_constructor_callback, dom_matrix_readonly_constructor_callback, + dom_point_constructor_callback, dom_point_readonly_constructor_callback, }, idle_detection::idle_detector_constructor_callback, image_data::image_data_constructor_callback, @@ -536,6 +536,14 @@ pub(in crate::context_bootstrap) fn build_constructor_template<'s>( .length(0) .build(scope) } + ConstructorKind::DomMatrixReadOnly => { + v8::FunctionTemplate::builder(moli_webapi_declare::web_api_constructor!( + web_api_interfaces::DOMMatrixReadOnly, + dom_matrix_readonly_constructor_callback + )) + .length(0) + .build(scope) + } ConstructorKind::DomMatrix => { v8::FunctionTemplate::builder(dom_matrix_constructor_callback) .length(0) diff --git a/moli-renderer-v8/src/context_bootstrap/dom_quad.rs b/moli-renderer-v8/src/context_bootstrap/dom_quad.rs index 31b7ccd7ae..6ecee1470e 100644 --- a/moli-renderer-v8/src/context_bootstrap/dom_quad.rs +++ b/moli-renderer-v8/src/context_bootstrap/dom_quad.rs @@ -322,6 +322,27 @@ fn dom_quad_points<'s>( ]) } +pub(super) fn dom_quad_clone_data<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> Option<[[f64; 4]; 4]> { + let points = dom_quad_points(scope, object)?; + Some(points.map(|point| { + let point = geometry_runtime::dom_point_init_from_object(scope, point); + [point.x, point.y, point.z, point.w] + })) +} + +pub(super) fn build_dom_quad_clone_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + points: [[f64; 4]; 4], +) -> v8::Local<'s, v8::Object> { + build_dom_quad( + scope, + points.map(|[x, y, z, w]| geometry_runtime::DomPointInit { x, y, z, w }), + ) +} + fn dom_quad_receiver_branded<'s>( scope: &mut v8::PinScope<'s, '_>, receiver: v8::Local<'s, v8::Object>, diff --git a/moli-renderer-v8/src/context_bootstrap/dom_rect.rs b/moli-renderer-v8/src/context_bootstrap/dom_rect.rs index 161c3d71a7..05c907d3df 100644 --- a/moli-renderer-v8/src/context_bootstrap/dom_rect.rs +++ b/moli-renderer-v8/src/context_bootstrap/dom_rect.rs @@ -561,6 +561,37 @@ fn dom_rect_receiver_branded<'s>( web_api_interfaces::DOMRectReadOnly::is_instance(scope, receiver) } +pub(super) fn dom_rect_clone_data<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> Option<(bool, [f64; 4])> { + if !dom_rect_receiver_branded(scope, object) { + return None; + } + let mutable = web_api_interfaces::DOMRect::is_instance(scope, object); + Some(( + mutable, + [ + dom_rect_slot(object, scope, DOM_RECT_X_SLOT), + dom_rect_slot(object, scope, DOM_RECT_Y_SLOT), + dom_rect_slot(object, scope, DOM_RECT_WIDTH_SLOT), + dom_rect_slot(object, scope, DOM_RECT_HEIGHT_SLOT), + ], + )) +} + +pub(super) fn build_dom_rect_clone_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + mutable: bool, + [x, y, width, height]: [f64; 4], +) -> v8::Local<'s, v8::Object> { + if mutable { + build_dom_rect_object(scope, x, y, width, height) + } else { + build_dom_rect_readonly_object(scope, x, y, width, height) + } +} + const DOM_RECT_WRITABLE_ATTRIBUTE_SLOTS: &[&str] = &[ DOM_RECT_X_SLOT, DOM_RECT_Y_SLOT, diff --git a/moli-renderer-v8/src/context_bootstrap/geometry_clone.rs b/moli-renderer-v8/src/context_bootstrap/geometry_clone.rs new file mode 100644 index 0000000000..63ba819240 --- /dev/null +++ b/moli-renderer-v8/src/context_bootstrap/geometry_clone.rs @@ -0,0 +1,65 @@ +use super::{dom_quad, dom_rect, geometry_runtime}; + +#[derive(Clone, Copy, Debug)] +pub(crate) enum GeometryClonePayload { + Point { + mutable: bool, + values: [f64; 4], + }, + Rect { + mutable: bool, + values: [f64; 4], + }, + Quad { + points: [[f64; 4]; 4], + }, + Matrix { + mutable: bool, + is_2d: bool, + values: [f64; 16], + }, +} + +pub(crate) fn geometry_clone_payload_from_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> Option { + if let Some((mutable, values)) = geometry_runtime::dom_point_clone_data(scope, object) { + return Some(GeometryClonePayload::Point { mutable, values }); + } + if let Some((mutable, values)) = dom_rect::dom_rect_clone_data(scope, object) { + return Some(GeometryClonePayload::Rect { mutable, values }); + } + if let Some(points) = dom_quad::dom_quad_clone_data(scope, object) { + return Some(GeometryClonePayload::Quad { points }); + } + geometry_runtime::dom_matrix_clone_data(scope, object).map(|(mutable, is_2d, values)| { + GeometryClonePayload::Matrix { + mutable, + is_2d, + values, + } + }) +} + +pub(crate) fn build_geometry_object_from_clone_payload<'s>( + scope: &mut v8::PinScope<'s, '_>, + payload: GeometryClonePayload, +) -> v8::Local<'s, v8::Object> { + match payload { + GeometryClonePayload::Point { mutable, values } => { + geometry_runtime::build_dom_point_clone_object(scope, mutable, values) + } + GeometryClonePayload::Rect { mutable, values } => { + dom_rect::build_dom_rect_clone_object(scope, mutable, values) + } + GeometryClonePayload::Quad { points } => { + dom_quad::build_dom_quad_clone_object(scope, points) + } + GeometryClonePayload::Matrix { + mutable, + is_2d, + values, + } => geometry_runtime::build_dom_matrix_clone_object(scope, mutable, is_2d, values), + } +} diff --git a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs index d8023bfad5..df90c78dee 100644 --- a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs +++ b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs @@ -657,6 +657,28 @@ pub(super) fn dom_matrix_constructor_callback<'s>( rv.set(args.this().into()); } +pub(super) fn dom_matrix_readonly_constructor_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + if !args.is_construct_call() { + throw_type_error( + scope, + "Failed to construct 'DOMMatrixReadOnly': Please use the 'new' operator.", + ); + return; + } + initialize_dom_matrix_readonly_identity_object(scope, args.this()); + if args.length() > 0 + && !args.get(0).is_undefined() + && !apply_dom_matrix_init(scope, args.this(), args.get(0)) + { + return; + } + rv.set(args.this().into()); +} + pub(in crate::context_bootstrap) fn initialize_dom_point_object<'s>( scope: &mut v8::PinScope<'s, '_>, object: v8::Local<'s, v8::Object>, @@ -1175,6 +1197,37 @@ fn dom_point_receiver_branded<'s>( web_api_interfaces::DOMPointReadOnly::is_instance(scope, receiver) } +pub(super) fn dom_point_clone_data<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> Option<(bool, [f64; 4])> { + if !dom_point_receiver_branded(scope, object) { + return None; + } + let mutable = web_api_interfaces::DOMPoint::is_instance(scope, object); + Some(( + mutable, + [ + dom_point_slot(scope, object, DOM_POINT_X_SLOT, 0.0), + dom_point_slot(scope, object, DOM_POINT_Y_SLOT, 0.0), + dom_point_slot(scope, object, DOM_POINT_Z_SLOT, 0.0), + dom_point_slot(scope, object, DOM_POINT_W_SLOT, 1.0), + ], + )) +} + +pub(super) fn build_dom_point_clone_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + mutable: bool, + [x, y, z, w]: [f64; 4], +) -> v8::Local<'s, v8::Object> { + if mutable { + build_dom_point_object(scope, x, y, z, w) + } else { + build_dom_point_readonly_object(scope, x, y, z, w) + } +} + fn dom_matrix_require_readonly_receiver<'s>( scope: &mut v8::PinScope<'s, '_>, receiver: v8::Local<'s, v8::Object>, @@ -1197,6 +1250,107 @@ fn dom_matrix_require_mutable_receiver<'s>( false } +pub(super) fn dom_matrix_clone_data<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> Option<(bool, bool, [f64; DOM_MATRIX_COMPONENT_COUNT])> { + if !web_api_interfaces::DOMMatrixReadOnly::is_instance(scope, object) { + return None; + } + let mutable = web_api_interfaces::DOMMatrix::is_instance(scope, object); + let is_2d = dom_matrix_is_2d(scope, object); + let mut components = dom_matrix_components(scope, object); + if is_2d { + components = DomMatrixComponents { + m11: components.m11, + m12: components.m12, + m21: components.m21, + m22: components.m22, + m41: components.m41, + m42: components.m42, + ..DomMatrixComponents::identity() + }; + } + Some(( + mutable, + is_2d, + [ + components.m11, + components.m12, + components.m13, + components.m14, + components.m21, + components.m22, + components.m23, + components.m24, + components.m31, + components.m32, + components.m33, + components.m34, + components.m41, + components.m42, + components.m43, + components.m44, + ], + )) +} + +pub(super) fn build_dom_matrix_clone_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + mutable: bool, + is_2d: bool, + values: [f64; DOM_MATRIX_COMPONENT_COUNT], +) -> v8::Local<'s, v8::Object> { + let [ + m11, + m12, + m13, + m14, + m21, + m22, + m23, + m24, + m31, + m32, + m33, + m34, + m41, + m42, + m43, + m44, + ] = values; + let value = DomMatrixValue { + components: DomMatrixComponents { + m11, + m12, + m13, + m14, + m21, + m22, + m23, + m24, + m31, + m32, + m33, + m34, + m41, + m42, + m43, + m44, + }, + is_2d, + }; + if mutable { + DomMatrixObjectDeclaration::from_value(value) + .bind(scope) + .expect("DOMMatrix clone declaration should bind") + } else { + DomMatrixReadOnlyObjectDeclaration::from_value(value) + .bind(scope) + .expect("DOMMatrixReadOnly clone declaration should bind") + } +} + fn dom_matrix_to_json_callback<'s>( scope: &mut v8::PinScope<'s, '_>, args: v8::FunctionCallbackArguments<'s>, @@ -2331,6 +2485,15 @@ fn initialize_dom_matrix_identity_object<'s>( .expect("DOMMatrix declaration should initialize object"); } +fn initialize_dom_matrix_readonly_identity_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) { + DomMatrixReadOnlyObjectDeclaration::identity() + .initialize(scope, object) + .expect("DOMMatrixReadOnly declaration should initialize object"); +} + fn dom_matrix_is_2d<'s>( scope: &mut v8::PinScope<'s, '_>, object: v8::Local<'s, v8::Object>, diff --git a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs index 0e45e1af37..eb5c876c3f 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs @@ -1105,7 +1105,7 @@ const CONSTRUCTOR_SPECS_AFTER_STREAMS: &[ConstructorSpec] = &[ }, ConstructorSpec { interface: web_api_interfaces::DOMMatrixReadOnly::DESCRIPTOR, - kind: ConstructorKind::DomMatrix, + kind: ConstructorKind::DomMatrixReadOnly, }, ConstructorSpec { interface: web_api_interfaces::DOMMatrix::DESCRIPTOR, diff --git a/moli-renderer-v8/src/context_bootstrap/specs/types.rs b/moli-renderer-v8/src/context_bootstrap/specs/types.rs index c62fdfc1a8..3b1c245820 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/types.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/types.rs @@ -116,6 +116,7 @@ pub(in crate::context_bootstrap) enum ConstructorKind { DomPointReadOnly, DomPoint, DomQuad, + DomMatrixReadOnly, DomMatrix, ResizeObserver, PerformanceObserver, diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs index 8d0f8e656b..acd003a37a 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/structured_clone.rs @@ -323,6 +323,90 @@ fn structured_clone_preserves_dom_exception_fields_and_brand() { ); } +#[test] +fn structured_clone_preserves_geometry_interfaces_and_internal_values() { + let mut vm = new_storage_test_vm("https://geometry-structured-clone.test/"); + + let result = vm + .eval( + r#" + (() => { + const same = (actual, expected) => Object.is(actual, expected); + const check = (name, source, attrs, expected, nested = false) => { + const originals = nested ? attrs.map(attr => source[attr]) : []; + source.expando = "not serialized"; + for (const attr of attrs) { + Object.defineProperty(source, attr, { + get() { throw new Error(`unexpected ${name}.${attr} getter`); } + }); + } + const clone = structuredClone(source); + const valuesMatch = attrs.every((attr, index) => { + if (!nested) return same(clone[attr], expected[index]); + return clone[attr] !== originals[index] + && expected[index].every((value, component) => + same(clone[attr][["x", "y", "z", "w"][component]], value)); + }); + return Object.prototype.toString.call(clone) === `[object ${name}]` + && Object.getPrototypeOf(clone) === self[name].prototype + && !("expando" in clone) + && valuesMatch; + }; + + const pointAttrs = ["x", "y", "z", "w"]; + const rectAttrs = ["x", "y", "width", "height"]; + const quadAttrs = ["p1", "p2", "p3", "p4"]; + const pointValues = [1, -0, Infinity, NaN]; + const quadValues = [ + [1, 2, 3, 4], + [-0, -0, -0, -0], + [Infinity, Infinity, Infinity, Infinity], + [NaN, NaN, NaN, NaN] + ]; + const matrixAttrs = [ + "a", "b", "c", "d", "e", "f", + "m11", "m12", "m13", "m14", + "m21", "m22", "m23", "m24", + "m31", "m32", "m33", "m34", + "m41", "m42", "m43", "m44", "is2D" + ]; + const normalized2DAttrs = new Set([ + "m13", "m14", "m23", "m24", + "m31", "m32", "m34", "m43" + ]); + const matrixCheck = (name, values) => { + const source = new self[name](values); + if (values.length === 6) { + for (const attr of normalized2DAttrs) source[attr] = -0; + } + const expected = matrixAttrs.map(attr => + normalized2DAttrs.has(attr) && values.length === 6 ? 0 : source[attr]); + return check(name, source, matrixAttrs, expected); + }; + + return [ + check("DOMPointReadOnly", new DOMPointReadOnly(...pointValues), + pointAttrs, pointValues), + check("DOMPoint", new DOMPoint(...pointValues), pointAttrs, pointValues), + check("DOMRectReadOnly", new DOMRectReadOnly(...pointValues), + rectAttrs, pointValues), + check("DOMRect", new DOMRect(...pointValues), rectAttrs, pointValues), + check("DOMQuad", new DOMQuad(...quadValues.map( + ([x, y, z, w]) => ({x, y, z, w}))), quadAttrs, quadValues, true), + matrixCheck("DOMMatrixReadOnly", [1, -0, Infinity, NaN, 5, 6]), + matrixCheck("DOMMatrix", [ + 11, -0, Infinity, NaN, 21, 22, 23, 24, + 31, 32, 33, 34, 41, 42, 43, 44 + ]) + ].join("|"); + })() + "#, + ) + .expect("Geometry structuredClone probe should evaluate"); + + assert_eq!(result, "true|true|true|true|true|true|true"); +} + #[test] fn structured_clone_transfers_array_buffer_and_preserves_view_aliases() { let mut vm = new_storage_test_vm("https://array-buffer-transfer.test/"); diff --git a/moli-renderer-v8/src/structured_clone.rs b/moli-renderer-v8/src/structured_clone.rs index 66909b86a5..e63a6c42d7 100644 --- a/moli-renderer-v8/src/structured_clone.rs +++ b/moli-renderer-v8/src/structured_clone.rs @@ -9,8 +9,8 @@ use std::{ use crate::{ context_bootstrap::{ CryptoKeyAlgorithmClonePayload, CryptoKeyClonePayload, FileSystemFileSnapshotClonePayload, - FileSystemHandleClonePayload, ImageDataClonePayload, ReadableStreamClonePayload, - TransformStreamClonePayload, WritableStreamClonePayload, + FileSystemHandleClonePayload, GeometryClonePayload, ImageDataClonePayload, + ReadableStreamClonePayload, TransformStreamClonePayload, WritableStreamClonePayload, attach_file_system_file_snapshot_clone_payload, build_file_object, build_file_system_handle_from_clone_payload, build_image_data_object_from_clone_payload, build_readable_stream_clone_shell, build_transform_stream_clone_shell, @@ -25,6 +25,7 @@ use crate::{ prepare_transform_stream_transfer, prepare_writable_stream_transfer, quota_exceeded_error_clone_fields, require_internal_stream_value, selected_file_from_object, + build_geometry_object_from_clone_payload, geometry_clone_payload_from_object, }, dom::native::SelectedFile, types::MessagePortId, @@ -44,6 +45,15 @@ pub(crate) const HOST_OBJECT_TAG_FILE_SYSTEM_HANDLE: u32 = 7; const HOST_OBJECT_TAG_QUOTA_EXCEEDED_ERROR: u32 = 8; const HOST_OBJECT_TAG_WRITABLE_STREAM: u32 = 9; const HOST_OBJECT_TAG_TRANSFORM_STREAM: u32 = 10; +const HOST_OBJECT_TAG_GEOMETRY: u32 = 11; + +const GEOMETRY_KIND_DOM_POINT_READONLY: u32 = 0; +const GEOMETRY_KIND_DOM_POINT: u32 = 1; +const GEOMETRY_KIND_DOM_RECT_READONLY: u32 = 2; +const GEOMETRY_KIND_DOM_RECT: u32 = 3; +const GEOMETRY_KIND_DOM_QUAD: u32 = 4; +const GEOMETRY_KIND_DOM_MATRIX_READONLY: u32 = 5; +const GEOMETRY_KIND_DOM_MATRIX: u32 = 6; #[derive(Clone, Debug, Default)] pub(crate) struct V8StructuredClonePayload { @@ -368,6 +378,12 @@ impl v8::ValueSerializerImpl for WireSerializer { return Some(true); } } + Some("DOMPoint" | "DOMPointReadOnly" | "DOMRect" | "DOMRectReadOnly" | "DOMQuad" | "DOMMatrix" | "DOMMatrixReadOnly") => { + if let Some(payload) = geometry_clone_payload_from_object(scope, object) { + write_geometry_clone_payload(serializer, payload); + return Some(true); + } + } Some("ImageData") => { if let Some(payload) = image_data_clone_payload_from_object(scope, object) { write_image_data_payload(serializer, payload); @@ -593,6 +609,15 @@ impl v8::ValueDeserializerImpl for WireDeserializer { None }) } + HOST_OBJECT_TAG_GEOMETRY => read_geometry_clone_payload(deserializer) + .map(|payload| build_geometry_object_from_clone_payload(scope, payload)) + .or_else(|| { + throw_data_clone_exception( + scope, + "Failed to deserialize structured clone Geometry object.", + ); + None + }), HOST_OBJECT_TAG_READABLE_STREAM => { let clone_id = read_u32(deserializer)?; let Some(payload) = self.readable_streams.get(&clone_id).cloned() else { @@ -824,6 +849,125 @@ pub(crate) fn read_image_data_payload<'s>( ) } +fn write_geometry_clone_payload( + serializer: &dyn v8::ValueSerializerHelper, + payload: GeometryClonePayload, +) { + serializer.write_uint32(HOST_OBJECT_TAG_GEOMETRY); + match payload { + GeometryClonePayload::Point { mutable, values } => { + serializer.write_uint32(if mutable { + GEOMETRY_KIND_DOM_POINT + } else { + GEOMETRY_KIND_DOM_POINT_READONLY + }); + write_doubles(serializer, &values); + } + GeometryClonePayload::Rect { mutable, values } => { + serializer.write_uint32(if mutable { + GEOMETRY_KIND_DOM_RECT + } else { + GEOMETRY_KIND_DOM_RECT_READONLY + }); + write_doubles(serializer, &values); + } + GeometryClonePayload::Quad { points } => { + serializer.write_uint32(GEOMETRY_KIND_DOM_QUAD); + for point in points { + write_doubles(serializer, &point); + } + } + GeometryClonePayload::Matrix { + mutable, + is_2d, + values, + } => { + serializer.write_uint32(if mutable { + GEOMETRY_KIND_DOM_MATRIX + } else { + GEOMETRY_KIND_DOM_MATRIX_READONLY + }); + serializer.write_uint32(u32::from(is_2d)); + if is_2d { + write_doubles( + serializer, + &[ + values[0], values[1], values[4], values[5], values[12], values[13], + ], + ); + } else { + write_doubles(serializer, &values); + } + } + } +} + +fn read_geometry_clone_payload( + deserializer: &dyn v8::ValueDeserializerHelper, +) -> Option { + let kind = read_u32(deserializer)?; + match kind { + GEOMETRY_KIND_DOM_POINT_READONLY | GEOMETRY_KIND_DOM_POINT => { + Some(GeometryClonePayload::Point { + mutable: kind == GEOMETRY_KIND_DOM_POINT, + values: read_doubles(deserializer)?, + }) + } + GEOMETRY_KIND_DOM_RECT_READONLY | GEOMETRY_KIND_DOM_RECT => { + Some(GeometryClonePayload::Rect { + mutable: kind == GEOMETRY_KIND_DOM_RECT, + values: read_doubles(deserializer)?, + }) + } + GEOMETRY_KIND_DOM_QUAD => { + let mut points = [[0.0; 4]; 4]; + for point in &mut points { + *point = read_doubles(deserializer)?; + } + Some(GeometryClonePayload::Quad { points }) + } + GEOMETRY_KIND_DOM_MATRIX_READONLY | GEOMETRY_KIND_DOM_MATRIX => { + let is_2d = match read_u32(deserializer)? { + 0 => false, + 1 => true, + _ => return None, + }; + let values = if is_2d { + let [m11, m12, m21, m22, m41, m42] = read_doubles(deserializer)?; + [ + m11, m12, 0.0, 0.0, m21, m22, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, m41, m42, 0.0, 1.0, + ] + } else { + read_doubles(deserializer)? + }; + Some(GeometryClonePayload::Matrix { + mutable: kind == GEOMETRY_KIND_DOM_MATRIX, + is_2d, + values, + }) + } + _ => None, + } +} + +fn write_doubles(serializer: &dyn v8::ValueSerializerHelper, values: &[f64]) { + for value in values { + serializer.write_double(*value); + } +} + +fn read_doubles( + deserializer: &dyn v8::ValueDeserializerHelper, +) -> Option<[f64; N]> { + let mut values = [0.0; N]; + for value in &mut values { + if !deserializer.read_double(value) { + return None; + } + } + Some(values) +} + pub(crate) fn write_crypto_key_payload<'s>( scope: &mut v8::PinScope<'s, '_>, object: v8::Local<'s, v8::Object>,