mirror of
https://github.com/lexmount/moli.git
synced 2026-10-19 08:01:35 +00:00
feat(geometry): implement DOMPoint matrixTransform
This commit is contained in:
@@ -2475,7 +2475,6 @@ css/geometry/DOMMatrix-attributes.html
|
||||
css/geometry/DOMMatrix-stringifier.html
|
||||
css/geometry/DOMMatrix2DInit-validate-fixup.html
|
||||
css/geometry/DOMMatrixInit-validate-fixup.html
|
||||
css/geometry/DOMPoint-002.html
|
||||
css/geometry/DOMQuad-001.html
|
||||
css/geometry/DOMQuad-002.html
|
||||
css/geometry/DOMQuad-nan.html
|
||||
|
||||
@@ -3347,6 +3347,7 @@ css/geometry/DOMMatrix-invert-preserves-2d.html
|
||||
css/geometry/DOMMatrix-invertSelf.html
|
||||
css/geometry/DOMMatrix-newobject.html
|
||||
css/geometry/DOMPoint-001.html
|
||||
css/geometry/DOMPoint-002.html
|
||||
css/geometry/DOMRect-001.html
|
||||
css/geometry/DOMRect-002.html
|
||||
css/geometry/DOMRect-nan.html
|
||||
|
||||
@@ -213,6 +213,13 @@ struct DomPointPrototypeAccessorsDeclaration {
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(interface = web_api_interfaces::DOMPointReadOnly)]
|
||||
struct DomPointReadOnlyPrototypeMethodsDeclaration {
|
||||
#[webapi(
|
||||
method = "matrixTransform",
|
||||
length = 0,
|
||||
callback = dom_point_matrix_transform_callback
|
||||
)]
|
||||
matrix_transform: (),
|
||||
|
||||
#[webapi(method = "toJSON", enumerable, callback = dom_point_to_json_callback)]
|
||||
to_json: (),
|
||||
}
|
||||
@@ -528,6 +535,35 @@ impl Default for DomPointInit {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, webidl::WebIdlDictionary)]
|
||||
#[webidl(prefix = "DOMMatrixInit")]
|
||||
struct DomMatrixInit {
|
||||
a: Option<f64>,
|
||||
b: Option<f64>,
|
||||
c: Option<f64>,
|
||||
d: Option<f64>,
|
||||
e: Option<f64>,
|
||||
f: Option<f64>,
|
||||
#[webidl(name = "is2D")]
|
||||
is_2d: Option<bool>,
|
||||
m11: Option<f64>,
|
||||
m12: Option<f64>,
|
||||
m13: Option<f64>,
|
||||
m14: Option<f64>,
|
||||
m21: Option<f64>,
|
||||
m22: Option<f64>,
|
||||
m23: Option<f64>,
|
||||
m24: Option<f64>,
|
||||
m31: Option<f64>,
|
||||
m32: Option<f64>,
|
||||
m33: Option<f64>,
|
||||
m34: Option<f64>,
|
||||
m41: Option<f64>,
|
||||
m42: Option<f64>,
|
||||
m43: Option<f64>,
|
||||
m44: Option<f64>,
|
||||
}
|
||||
|
||||
pub(super) fn dom_point_constructor_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -835,6 +871,133 @@ fn dom_point_readonly_from_point_callback<'s>(
|
||||
rv.set(build_dom_point_readonly_object(scope, init.x, init.y, init.z, init.w).into());
|
||||
}
|
||||
|
||||
fn dom_point_matrix_transform_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'_, v8::Value>,
|
||||
) {
|
||||
if !dom_point_receiver_branded(scope, args.this()) {
|
||||
throw_type_error(scope, "Illegal invocation");
|
||||
return;
|
||||
}
|
||||
let Some(matrix) = dom_matrix_init_arg(scope, &args, 0) else {
|
||||
return;
|
||||
};
|
||||
let point = DomPointInit {
|
||||
x: dom_point_slot(scope, args.this(), DOM_POINT_X_SLOT, 0.0),
|
||||
y: dom_point_slot(scope, args.this(), DOM_POINT_Y_SLOT, 0.0),
|
||||
z: dom_point_slot(scope, args.this(), DOM_POINT_Z_SLOT, 0.0),
|
||||
w: dom_point_slot(scope, args.this(), DOM_POINT_W_SLOT, 1.0),
|
||||
};
|
||||
rv.set(
|
||||
build_dom_point_object(
|
||||
scope,
|
||||
matrix.m11 * point.x
|
||||
+ matrix.m21 * point.y
|
||||
+ matrix.m31 * point.z
|
||||
+ matrix.m41 * point.w,
|
||||
matrix.m12 * point.x
|
||||
+ matrix.m22 * point.y
|
||||
+ matrix.m32 * point.z
|
||||
+ matrix.m42 * point.w,
|
||||
matrix.m13 * point.x
|
||||
+ matrix.m23 * point.y
|
||||
+ matrix.m33 * point.z
|
||||
+ matrix.m43 * point.w,
|
||||
matrix.m14 * point.x
|
||||
+ matrix.m24 * point.y
|
||||
+ matrix.m34 * point.z
|
||||
+ matrix.m44 * point.w,
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
fn dom_matrix_init_arg<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: &v8::FunctionCallbackArguments<'s>,
|
||||
index: i32,
|
||||
) -> Option<DomMatrixComponents> {
|
||||
if index >= args.length() || args.get(index).is_undefined() {
|
||||
return Some(DomMatrixComponents::identity());
|
||||
}
|
||||
let init = match webidl::parse_dictionary::<DomMatrixInit>(
|
||||
scope,
|
||||
args.get(index),
|
||||
webidl::Context::argument("DOMPointReadOnly.matrixTransform", (index + 1) as usize),
|
||||
) {
|
||||
Ok(Some(init)) => init,
|
||||
Ok(None) => DomMatrixInit::default(),
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
validated_dom_matrix_init(scope, init)
|
||||
}
|
||||
|
||||
fn validated_dom_matrix_init(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
init: DomMatrixInit,
|
||||
) -> Option<DomMatrixComponents> {
|
||||
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 components = DomMatrixComponents {
|
||||
m11,
|
||||
m12,
|
||||
m13: init.m13.unwrap_or(0.0),
|
||||
m14: init.m14.unwrap_or(0.0),
|
||||
m21,
|
||||
m22,
|
||||
m23: init.m23.unwrap_or(0.0),
|
||||
m24: init.m24.unwrap_or(0.0),
|
||||
m31: init.m31.unwrap_or(0.0),
|
||||
m32: init.m32.unwrap_or(0.0),
|
||||
m33: init.m33.unwrap_or(1.0),
|
||||
m34: init.m34.unwrap_or(0.0),
|
||||
m41,
|
||||
m42,
|
||||
m43: init.m43.unwrap_or(0.0),
|
||||
m44: init.m44.unwrap_or(1.0),
|
||||
};
|
||||
if init.is_2d == Some(true) && !components.is_2d() {
|
||||
throw_type_error(
|
||||
scope,
|
||||
"DOMMatrixInit is2D is true, but the matrix contains 3D values.",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Some(components)
|
||||
}
|
||||
|
||||
fn validated_dom_matrix_alias(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
alias_name: &'static str,
|
||||
alias: Option<f64>,
|
||||
matrix_name: &'static str,
|
||||
matrix: Option<f64>,
|
||||
default: f64,
|
||||
) -> Option<f64> {
|
||||
if let (Some(alias), Some(matrix)) = (alias, matrix)
|
||||
&& !same_dom_matrix_number(alias, matrix)
|
||||
{
|
||||
throw_type_error(
|
||||
scope,
|
||||
&format!("DOMMatrixInit {alias_name} and {matrix_name} values differ."),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Some(matrix.or(alias).unwrap_or(default))
|
||||
}
|
||||
|
||||
fn same_dom_matrix_number(lhs: f64, rhs: f64) -> bool {
|
||||
lhs == rhs || (lhs.is_nan() && rhs.is_nan())
|
||||
}
|
||||
|
||||
fn dom_matrix_from_matrix_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
|
||||
@@ -5670,6 +5670,66 @@ fn dom_point_readonly_from_point_uses_dictionary_conversion_and_function_realm()
|
||||
assert_eq!(result, "0|true|false|false|1,0,3,4|0,0,0,1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dom_point_matrix_transform_validates_matrix_init_and_returns_in_the_function_realm() {
|
||||
let mut vm = new_storage_test_vm("https://dompoint-matrix-transform.test/");
|
||||
|
||||
vm.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const root = document.documentElement ||
|
||||
document.appendChild(document.createElement("html"));
|
||||
const body = document.body || root.appendChild(document.createElement("body"));
|
||||
const frame = document.createElement("iframe");
|
||||
frame.id = "dompoint-matrix-transform-realm";
|
||||
body.appendChild(frame);
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("DOMPoint matrixTransform child frame should be created");
|
||||
materialize_single_child_default_realm_for_test(
|
||||
&mut vm,
|
||||
"DOMPoint matrixTransform child Realm",
|
||||
);
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const other = document.getElementById("dompoint-matrix-transform-realm").contentWindow;
|
||||
const source = new DOMPointReadOnly(5, 4, 3, 2);
|
||||
const transformed = other.DOMPointReadOnly.prototype.matrixTransform.call(source, {
|
||||
a: 2, m11: 2, d: 3, e: 10, f: 20, m33: 4, m44: 5
|
||||
});
|
||||
const outcome = init => {
|
||||
try {
|
||||
source.matrixTransform(init);
|
||||
return "no throw";
|
||||
} catch (error) {
|
||||
return error.name;
|
||||
}
|
||||
};
|
||||
return [
|
||||
other.DOMPointReadOnly.prototype.matrixTransform.length,
|
||||
transformed instanceof other.DOMPoint,
|
||||
transformed instanceof DOMPoint,
|
||||
[transformed.x, transformed.y, transformed.z, transformed.w].join(","),
|
||||
outcome({a: 1, m11: 2}),
|
||||
outcome({is2D: true, m33: 1.0000001}),
|
||||
outcome({a: NaN, m11: NaN}),
|
||||
outcome({a: 0, m11: -0})
|
||||
].join("|");
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("DOMPoint matrixTransform should evaluate");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
"0|true|false|30,52,12,10|TypeError|TypeError|no throw|no throw"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dom_matrix_objects_keep_declared_brand_and_own_slots() {
|
||||
let mut vm = new_storage_test_vm("https://dommatrix-declared-slots.test/");
|
||||
|
||||
Reference in New Issue
Block a user