diff --git a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs index 0bad901980..683bb4b028 100644 --- a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs +++ b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs @@ -617,6 +617,23 @@ struct DomMatrixInit { m44: Option, } +#[derive(Default, webidl::WebIdlDictionary)] +#[webidl(prefix = "DOMMatrix2DInit")] +struct DomMatrix2DInit { + a: Option, + b: Option, + c: Option, + d: Option, + e: Option, + f: Option, + m11: Option, + m12: Option, + m21: Option, + m22: Option, + m41: Option, + m42: Option, +} + #[derive(Clone, Copy)] struct DomMatrixValue { components: DomMatrixComponents, @@ -1070,6 +1087,37 @@ fn dom_matrix_init_arg<'s>( dom_matrix_init_value(scope, args.get(index), prefix, (index + 1) as usize) } +pub(in crate::context_bootstrap) fn dom_matrix_2d_init_arg<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: &v8::FunctionCallbackArguments<'s>, + index: i32, + prefix: &'static str, +) -> Option<[f64; 6]> { + if index >= args.length() || args.get(index).is_undefined() { + return Some([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]); + } + let init = match webidl::parse_dictionary::( + scope, + args.get(index), + webidl::Context::argument(prefix, (index + 1) as usize), + ) { + Ok(Some(init)) => init, + Ok(None) => DomMatrix2DInit::default(), + Err(error) => { + webidl::throw_error(scope, &error); + return None; + } + }; + Some([ + validated_dom_matrix_alias(scope, "DOMMatrix2DInit", "a", init.a, "m11", init.m11, 1.0)?, + validated_dom_matrix_alias(scope, "DOMMatrix2DInit", "b", init.b, "m12", init.m12, 0.0)?, + validated_dom_matrix_alias(scope, "DOMMatrix2DInit", "c", init.c, "m21", init.m21, 0.0)?, + validated_dom_matrix_alias(scope, "DOMMatrix2DInit", "d", init.d, "m22", init.m22, 1.0)?, + validated_dom_matrix_alias(scope, "DOMMatrix2DInit", "e", init.e, "m41", init.m41, 0.0)?, + validated_dom_matrix_alias(scope, "DOMMatrix2DInit", "f", init.f, "m42", init.m42, 0.0)?, + ]) +} + fn dom_matrix_init_value<'s>( scope: &mut v8::PinScope<'s, '_>, value: v8::Local<'s, v8::Value>, @@ -1095,12 +1143,18 @@ fn validated_dom_matrix_init( scope: &mut v8::PinScope<'_, '_>, init: DomMatrixInit, ) -> Option { - let m11 = validated_dom_matrix_alias(scope, "a", init.a, "m11", init.m11, 1.0)?; - let m12 = validated_dom_matrix_alias(scope, "b", init.b, "m12", init.m12, 0.0)?; - let m21 = validated_dom_matrix_alias(scope, "c", init.c, "m21", init.m21, 0.0)?; - let m22 = validated_dom_matrix_alias(scope, "d", init.d, "m22", init.m22, 1.0)?; - let m41 = validated_dom_matrix_alias(scope, "e", init.e, "m41", init.m41, 0.0)?; - let m42 = validated_dom_matrix_alias(scope, "f", init.f, "m42", init.m42, 0.0)?; + let m11 = + validated_dom_matrix_alias(scope, "DOMMatrixInit", "a", init.a, "m11", init.m11, 1.0)?; + let m12 = + validated_dom_matrix_alias(scope, "DOMMatrixInit", "b", init.b, "m12", init.m12, 0.0)?; + let m21 = + validated_dom_matrix_alias(scope, "DOMMatrixInit", "c", init.c, "m21", init.m21, 0.0)?; + let m22 = + validated_dom_matrix_alias(scope, "DOMMatrixInit", "d", init.d, "m22", init.m22, 1.0)?; + let m41 = + validated_dom_matrix_alias(scope, "DOMMatrixInit", "e", init.e, "m41", init.m41, 0.0)?; + let m42 = + validated_dom_matrix_alias(scope, "DOMMatrixInit", "f", init.f, "m42", init.m42, 0.0)?; let components = DomMatrixComponents { m11, m12, @@ -1135,6 +1189,7 @@ fn validated_dom_matrix_init( fn validated_dom_matrix_alias( scope: &mut v8::PinScope<'_, '_>, + dictionary_name: &'static str, alias_name: &'static str, alias: Option, matrix_name: &'static str, @@ -1146,7 +1201,7 @@ fn validated_dom_matrix_alias( { throw_type_error( scope, - &format!("DOMMatrixInit {alias_name} and {matrix_name} values differ."), + &format!("{dictionary_name} {alias_name} and {matrix_name} values differ."), ); return None; } diff --git a/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs b/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs index 4e7c39e6dc..f9ae05e12d 100644 --- a/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs +++ b/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs @@ -193,7 +193,7 @@ struct SvgTransformListTemplateMethodsDeclaration { #[webapi( method = "createSVGTransformFromMatrix", - length = 1, + length = 0, callback = svg_transform_list_create_transform_from_matrix_callback )] create_svg_transform_from_matrix: (), @@ -236,7 +236,7 @@ struct SvgTransformTemplateMethodsDeclaration { #[webapi(constant = "SVG_TRANSFORM_SKEWY", value = SVG_TRANSFORM_TYPE_SKEWY)] transform_skew_y: (), - #[webapi(method = "setMatrix", length = 1, callback = svg_transform_set_matrix_callback)] + #[webapi(method = "setMatrix", length = 0, callback = svg_transform_set_matrix_callback)] set_matrix: (), #[webapi( @@ -451,7 +451,7 @@ struct SvgSvgElementTemplateMethodsDeclaration { #[webapi( method = "createSVGTransformFromMatrix", - length = 1, + length = 0, callback = svg_svg_element_create_transform_from_matrix_callback )] create_svg_transform_from_matrix: (), diff --git a/moli-renderer-v8/src/context_bootstrap/svg_runtime/builders.rs b/moli-renderer-v8/src/context_bootstrap/svg_runtime/builders.rs index 388d27d80d..98ab885c81 100644 --- a/moli-renderer-v8/src/context_bootstrap/svg_runtime/builders.rs +++ b/moli-renderer-v8/src/context_bootstrap/svg_runtime/builders.rs @@ -232,7 +232,7 @@ struct SvgTransformListObjectDeclaration<'scope> { #[webapi( method = "createSVGTransformFromMatrix", callback = svg_transform_list_create_transform_from_matrix_callback, - length = 1 + length = 0 )] create_svg_transform_from_matrix: (), #[webapi(method, callback = svg_transform_list_consolidate_callback, length = 0)] @@ -248,7 +248,7 @@ struct SvgTransformObjectDeclaration<'scope> { angle: f64, #[webapi(slot = SVG_TRANSFORM_MATRIX_SLOT)] matrix: v8::Local<'scope, v8::Object>, - #[webapi(method, callback = svg_transform_set_matrix_callback, length = 1)] + #[webapi(method, callback = svg_transform_set_matrix_callback, length = 0)] set_matrix: (), #[webapi(method, callback = svg_transform_set_translate_callback, length = 2)] set_translate: (), @@ -1038,15 +1038,6 @@ pub(super) fn svg_matrix_value_or_throw<'s>( None } -pub(super) fn cloned_svg_matrix_value_or_throw<'s>( - scope: &mut v8::PinScope<'s, '_>, - value: v8::Local<'s, v8::Value>, -) -> Option> { - let matrix = svg_matrix_value_or_throw(scope, value)?; - let components = svg_matrix_components(scope, matrix); - Some(build_svg_matrix(scope, components)) -} - pub(super) fn svg_value_list_item_or_default<'s>( scope: &mut v8::PinScope<'s, '_>, value: v8::Local<'s, v8::Value>, diff --git a/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs b/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs index 9f8f435537..dd51031e4a 100644 --- a/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs +++ b/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs @@ -17,6 +17,16 @@ const SVG_ANIMATED_ACCESSOR_NAMES: &[&str] = &["baseVal", "animVal"]; const SVG_TRANSFORM_ACCESSOR_NAMES: &[&str] = &["type", "matrix", "angle"]; const SVG_MATRIX_ACCESSOR_NAMES: &[&str] = &["a", "b", "c", "d", "e", "f"]; +fn svg_dom_matrix_2d_init_arg<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: &v8::FunctionCallbackArguments<'s>, + prefix: &'static str, +) -> Option { + let [a, b, c, d, e, f] = + super::super::geometry_runtime::dom_matrix_2d_init_arg(scope, args, 0, prefix)?; + Some(SvgMatrixComponents { a, b, c, d, e, f }) +} + const SVG_TEXT_POSITIONING_LIST_ATTRIBUTES: &[(&str, &str, SvgListKind)] = &[ ("x", SVG_TEXT_POSITIONING_X_SLOT, SvgListKind::Length), ("y", SVG_TEXT_POSITIONING_Y_SLOT, SvgListKind::Length), @@ -1363,13 +1373,13 @@ pub(super) fn svg_transform_list_create_transform_from_matrix_callback<'s>( args: v8::FunctionCallbackArguments<'s>, mut rv: v8::ReturnValue<'_, v8::Value>, ) { - let Some(parsed) = webidl::parse_args::(scope, &args) else { + let Some(components) = svg_dom_matrix_2d_init_arg( + scope, + &args, + "SVGTransformList.createSVGTransformFromMatrix", + ) else { return; }; - let Some(matrix) = cloned_svg_matrix_value_or_throw(scope, parsed.item) else { - return; - }; - let components = svg_matrix_components(scope, matrix); rv.set(build_svg_transform(scope, SvgTransform::matrix(components)).into()); } @@ -1442,13 +1452,10 @@ pub(super) fn svg_transform_set_matrix_callback<'s>( args: v8::FunctionCallbackArguments<'s>, mut rv: v8::ReturnValue<'_, v8::Value>, ) { - let Some(parsed) = webidl::parse_args::(scope, &args) else { + let Some(components) = svg_dom_matrix_2d_init_arg(scope, &args, "SVGTransform.setMatrix") + else { return; }; - let Some(matrix) = cloned_svg_matrix_value_or_throw(scope, parsed.matrix) else { - return; - }; - let components = svg_matrix_components(scope, matrix); set_svg_transform_state(scope, args.this(), SvgTransform::matrix(components)); reflect_svg_transform_item_to_owner_list(scope, args.this()); rv.set_undefined(); @@ -1459,7 +1466,7 @@ pub(super) fn svg_svg_element_create_matrix_callback<'s>( _args: v8::FunctionCallbackArguments<'s>, mut rv: v8::ReturnValue<'_, v8::Value>, ) { - rv.set(build_svg_matrix(scope, SvgMatrixComponents::identity()).into()); + rv.set(super::super::geometry_runtime::build_dom_matrix_identity_object(scope).into()); } pub(super) fn svg_svg_element_create_transform_callback<'s>( @@ -1477,13 +1484,11 @@ pub(super) fn svg_svg_element_create_transform_from_matrix_callback<'s>( args: v8::FunctionCallbackArguments<'s>, mut rv: v8::ReturnValue<'_, v8::Value>, ) { - let Some(parsed) = webidl::parse_args::(scope, &args) else { + let Some(components) = + svg_dom_matrix_2d_init_arg(scope, &args, "SVGSVGElement.createSVGTransformFromMatrix") + else { return; }; - let Some(matrix) = cloned_svg_matrix_value_or_throw(scope, parsed.matrix) else { - return; - }; - let components = svg_matrix_components(scope, matrix); rv.set(build_svg_transform(scope, SvgTransform::matrix(components)).into()); } diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/misc.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/misc.rs index 1ada123c79..5b0b43c391 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/misc.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/misc.rs @@ -5539,6 +5539,7 @@ fn geometry_exposes_legacy_window_aliases() { const rect = new SVGRect(3, 4, 5, 6); const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); const matrix = svg.createSVGMatrix(); + const multiplied = matrix.multiply({a: 3, d: 2}); matrix.a = 2; return JSON.stringify({ identities: [ @@ -5552,8 +5553,11 @@ fn geometry_exposes_legacy_window_aliases() { matrix: [ matrix instanceof DOMMatrix, matrix instanceof SVGMatrix, - DOMMatrix.prototype.isPrototypeOf(matrix), + Object.getPrototypeOf(matrix) === DOMMatrix.prototype, + !Object.hasOwn(matrix, "multiply"), matrix.a, + multiplied.a, + multiplied.d, Object.prototype.toString.call(matrix) ].join(","), descriptors: aliases.map(descriptorShape).join("|") @@ -5566,13 +5570,61 @@ fn geometry_exposes_legacy_window_aliases() { assert_eq!( result, concat!( - r#"{"identities":"true,true,true,true","point":"true,1,2,[object DOMPoint]","rect":"true,3,6,[object DOMRect]","matrix":"true,true,true,2,[object SVGMatrix]","descriptors":""#, + r#"{"identities":"true,true,true,true","point":"true,1,2,[object DOMPoint]","rect":"true,3,6,[object DOMRect]","matrix":"true,true,true,true,2,3,2,[object DOMMatrix]","descriptors":""#, "true,true,false,true|true,true,false,true|", "true,true,false,true|true,true,false,true\"}" ) ); } +#[test] +fn svg_transform_factories_convert_optional_dom_matrix_2d_init() { + let mut vm = new_storage_test_vm("https://svg-transform-dom-matrix-init.test/"); + + let result = vm + .eval( + r#" +(() => { + const SVG_NS = "http://www.w3.org/2000/svg"; + const svg = document.createElementNS(SVG_NS, "svg"); + const group = document.createElementNS(SVG_NS, "g"); + const fromElement = svg.createSVGTransformFromMatrix({a: 2, e: 5}); + const defaultElement = svg.createSVGTransformFromMatrix(); + const fromFactory = svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(9, 10)); + const transform = svg.createSVGTransform(); + transform.setMatrix({b: 4, f: 6}); + const fromList = group.transform.baseVal.createSVGTransformFromMatrix({c: 7, d: 8}); + let mismatch; + try { + transform.setMatrix({a: 1, m11: 2}); + mismatch = "none"; + } catch (error) { + mismatch = error.name; + } + return JSON.stringify({ + fromElement: [fromElement.matrix.a, fromElement.matrix.e].join(","), + defaultElement: [defaultElement.matrix.a, defaultElement.matrix.d].join(","), + fromFactory: [fromFactory.matrix.e, fromFactory.matrix.f].join(","), + setMatrix: [transform.matrix.b, transform.matrix.f].join(","), + fromList: [fromList.matrix.c, fromList.matrix.d].join(","), + mismatch, + lengths: [ + SVGSVGElement.prototype.createSVGTransformFromMatrix.length, + SVGTransform.prototype.setMatrix.length, + SVGTransformList.prototype.createSVGTransformFromMatrix.length + ].join(",") + }); +})() +"#, + ) + .expect("SVG transform DOMMatrix2DInit conversion should evaluate"); + + assert_eq!( + result, + r#"{"fromElement":"2,5","defaultElement":"1,1","fromFactory":"9,10","setMatrix":"4,6","fromList":"7,8","mismatch":"TypeError","lengths":"0,0,0"}"# + ); +} + #[test] fn dom_matrix_window_operations_use_webidl_descriptors() { let mut vm = new_storage_test_vm("https://dommatrix-operation-descriptors.test/"); diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs index a3d1c538e5..bc73f9bd9a 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface.rs @@ -8174,6 +8174,7 @@ fn svg_list_matrix_and_transform_declared_methods_keep_descriptors() { numberInsertItemBefore: methodDescriptor(numberList, "insertItemBefore"), transformListCreate: methodDescriptor(transformList, "createSVGTransformFromMatrix"), transformListConsolidate: methodDescriptor(transformList, "consolidate"), + transformSetMatrix: methodDescriptor(transform, "setMatrix"), transformSetRotate: methodDescriptor(transform, "setRotate"), transformSetSkewX: methodDescriptor(transform, "setSkewX"), matrixScaleNonUniform: methodDescriptor(matrix, "scaleNonUniform"), @@ -8193,7 +8194,7 @@ fn svg_list_matrix_and_transform_declared_methods_keep_descriptors() { assert_eq!( result, - r#"{"lengthGetItem":"false,true,true,getItem,1","lengthAppendItem":"false,true,true,appendItem,1","numberInsertItemBefore":"false,true,true,insertItemBefore,2","transformListCreate":"false,true,true,createSVGTransformFromMatrix,1","transformListConsolidate":"false,true,true,consolidate,0","transformSetRotate":"false,true,true,setRotate,3","transformSetSkewX":"false,true,true,setSkewX,1","matrixScaleNonUniform":"false,true,true,scaleNonUniform,2","matrixRotateFromVector":"false,true,true,rotateFromVector,2","matrixFlipX":"false,true,true,flipX,0","transformOwnMethods":["setMatrix","setRotate","setScale","setSkewX","setSkewY","setTranslate"],"matrixOwnMethods":["flipX","flipY","inverse","multiply","rotate","rotateFromVector","scale","scaleNonUniform","skewX","skewY","translate"]}"# + r#"{"lengthGetItem":"false,true,true,getItem,1","lengthAppendItem":"false,true,true,appendItem,1","numberInsertItemBefore":"false,true,true,insertItemBefore,2","transformListCreate":"false,true,true,createSVGTransformFromMatrix,0","transformListConsolidate":"false,true,true,consolidate,0","transformSetMatrix":"false,true,true,setMatrix,0","transformSetRotate":"false,true,true,setRotate,3","transformSetSkewX":"false,true,true,setSkewX,1","matrixScaleNonUniform":"false,true,true,scaleNonUniform,2","matrixRotateFromVector":"false,true,true,rotateFromVector,2","matrixFlipX":"false,true,true,flipX,0","transformOwnMethods":["setMatrix","setRotate","setScale","setSkewX","setSkewY","setTranslate"],"matrixOwnMethods":["flipX","flipY","inverse","multiply","rotate","rotateFromVector","scale","scaleNonUniform","skewX","skewY","translate"]}"# ); }