fix(geometry): convert matrix dictionary ancestors first

This commit is contained in:
ldm0
2026-10-11 18:06:48 +08:00
parent 6e6dfd4349
commit 3379329c4e
4 changed files with 168 additions and 24 deletions
@@ -555,28 +555,18 @@ 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(inherit)]
base: DomMatrix2DInit,
#[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>,
}
@@ -1222,18 +1212,60 @@ fn validated_dom_matrix_init(
scope: &mut v8::PinScope<'_, '_>,
init: DomMatrixInit,
) -> Option<DomMatrixValue> {
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 m11 = validated_dom_matrix_alias(
scope,
"DOMMatrixInit",
"a",
init.base.a,
"m11",
init.base.m11,
1.0,
)?;
let m12 = validated_dom_matrix_alias(
scope,
"DOMMatrixInit",
"b",
init.base.b,
"m12",
init.base.m12,
0.0,
)?;
let m21 = validated_dom_matrix_alias(
scope,
"DOMMatrixInit",
"c",
init.base.c,
"m21",
init.base.m21,
0.0,
)?;
let m22 = validated_dom_matrix_alias(
scope,
"DOMMatrixInit",
"d",
init.base.d,
"m22",
init.base.m22,
1.0,
)?;
let m41 = validated_dom_matrix_alias(
scope,
"DOMMatrixInit",
"e",
init.base.e,
"m41",
init.base.m41,
0.0,
)?;
let m42 = validated_dom_matrix_alias(
scope,
"DOMMatrixInit",
"f",
init.base.f,
"m42",
init.base.m42,
0.0,
)?;
let components = DomMatrixComponents {
m11,
m12,
@@ -0,0 +1,88 @@
(() => {
const checks = [];
const check = (name, fn) => {
try { checks.push({name, passed: fn() === true}); }
catch (error) { checks.push({name, passed: false, error: String(error)}); }
};
const realms = [window, document.querySelector('iframe').contentWindow];
const base = ['a', 'b', 'c', 'd', 'e', 'f', 'm11', 'm12', 'm21', 'm22', 'm41', 'm42'];
const own = ['is2D', 'm13', 'm14', 'm23', 'm24', 'm31', 'm32', 'm33', 'm34', 'm43', 'm44'];
const names = [...base, ...own];
for (const [ownerIndex, owner] of realms.entries()) {
for (const [calleeIndex, callee] of realms.entries()) {
const calls = [
['DOMMatrix.fromMatrix', value => callee.DOMMatrix.fromMatrix(value)],
['DOMMatrixReadOnly.fromMatrix', value => callee.DOMMatrixReadOnly.fromMatrix(value)],
['DOMPoint.matrixTransform', value => new callee.DOMPoint(1, 2, 3, 1).matrixTransform(value)],
['DOMMatrix.multiply', value => new callee.DOMMatrix().multiply(value)],
['DOMMatrix.multiplySelf', value => new callee.DOMMatrix().multiplySelf(value)],
['DOMMatrix.preMultiplySelf', value => new callee.DOMMatrix().preMultiplySelf(value)],
];
for (const [name, call] of calls) {
const prefix = `matrix-order/${ownerIndex}/${calleeIndex}/${name}`;
check(prefix + '/getter-conversion-order', () => {
const order = [], value = {};
for (const key of names) Object.defineProperty(value, key, {get() {
order.push(key);
return key === 'is2D' ? false : {valueOf() {order.push(key + ':number'); return 1;}};
}});
call(value);
const expected = names.flatMap(key => key === 'is2D' ? [key] : [key, key + ':number']);
return order.join() === expected.join();
});
for (const proxy of [false, true]) check(prefix + '/inherited-or-proxy/' + proxy, () => {
const order = [], target = {};
for (const key of names) Object.defineProperty(target, key, {get() {order.push(key); return key === 'is2D' ? false : 1;}});
call(proxy ? new owner.Proxy(target, {}) : owner.Object.create(target));
return order.join() === names.join();
});
for (const key of names) for (const conversion of [false, true]) {
if (conversion && key === 'is2D') continue;
check(prefix + '/exception/' + key + '/' + conversion, () => {
const sentinel = {}, order = [], value = {};
for (const member of names) Object.defineProperty(value, member, {get() {
order.push(member);
if (member === key) {
if (!conversion) throw sentinel;
return {valueOf() {order.push(member + ':number'); throw sentinel;}};
}
return member === 'is2D' ? false : 1;
}});
try { call(value); } catch (error) {
const expected = names.slice(0, names.indexOf(key) + 1);
if (conversion) expected.push(key + ':number');
return error === sentinel && order.join() === expected.join();
}
return false;
});
}
for (const [index, value] of [undefined, null, {}, [], () => {}].entries()) {
check(prefix + '/empty/' + index, () => {call(value); return true;});
}
for (const [index, value] of [0, true, 'matrix', Symbol(), 1n].entries()) {
check(prefix + '/invalid/' + index, () => {
try {call(value);} catch (error) {return Object.getPrototypeOf(error) === callee.TypeError.prototype;}
return false;
});
}
check(prefix + '/validation-after-all-members', () => {
const order = [], sentinel = {};
const value = new owner.Proxy({a: 1, m11: 2}, {get(target, key) {
order.push(key);
if (key === 'm44') throw sentinel;
return target[key];
}});
try {call(value);} catch (error) {return error === sentinel && order.join() === names.join();}
return false;
});
check(prefix + '/boolean-does-not-coerce', () => {
let conversions = 0;
call({is2D: {valueOf() {conversions++; throw Error('boolean coercion');}}});
return conversions === 0;
});
}
}
}
globalThis.__matrixDictionaryOrderResults = {complete: true, total: checks.length, passed: checks.filter(row => row.passed).length, checks};
return true;
})()
@@ -0,0 +1,23 @@
use super::*;
#[test]
fn matrix_dictionaries_convert_ancestors_before_own_members() {
let mut vm = new_storage_page_task_executor_test_vm("https://matrix-dictionary-order.test/");
vm.eval("document.body.innerHTML = '<iframe></iframe>'")
.unwrap();
vm.eval(include_str!("geometry_matrix_dictionary_order.js"))
.unwrap();
assert_eq!(
vm.eval("__matrixDictionaryOrderResults.complete").unwrap(),
"true"
);
assert_eq!(
vm.eval("__matrixDictionaryOrderResults.total").unwrap(),
"1440"
);
assert_eq!(
vm.eval("JSON.stringify(__matrixDictionaryOrderResults.checks.filter(row => !row.passed))")
.unwrap(),
"[]"
);
}
@@ -2263,6 +2263,7 @@ mod fetch_request_guard;
mod fetch_resource_timing;
mod frame_element_security;
mod geometry_matrix_dictionary_order;
mod geometry_point_conversion_order;
mod headers_list;
mod history_document_identity;