diff --git a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs index eb258512ba..c3d1fb04e7 100644 --- a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs +++ b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs @@ -555,28 +555,18 @@ impl Default for DomPointInit { #[derive(Default, webidl::WebIdlDictionary)] #[webidl(prefix = "DOMMatrixInit")] struct DomMatrixInit { - a: Option, - b: Option, - c: Option, - d: Option, - e: Option, - f: Option, + #[webidl(inherit)] + base: DomMatrix2DInit, #[webidl(name = "is2D")] is_2d: Option, - m11: Option, - m12: Option, m13: Option, m14: Option, - m21: Option, - m22: Option, m23: Option, m24: Option, m31: Option, m32: Option, m33: Option, m34: Option, - m41: Option, - m42: Option, m43: Option, m44: Option, } @@ -1222,18 +1212,60 @@ fn validated_dom_matrix_init( scope: &mut v8::PinScope<'_, '_>, init: DomMatrixInit, ) -> Option { - 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, diff --git a/moli-renderer-v8/src/script_vm/tests/geometry_matrix_dictionary_order.js b/moli-renderer-v8/src/script_vm/tests/geometry_matrix_dictionary_order.js new file mode 100644 index 0000000000..ede5241173 --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/geometry_matrix_dictionary_order.js @@ -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; +})() diff --git a/moli-renderer-v8/src/script_vm/tests/geometry_matrix_dictionary_order.rs b/moli-renderer-v8/src/script_vm/tests/geometry_matrix_dictionary_order.rs new file mode 100644 index 0000000000..72a633704f --- /dev/null +++ b/moli-renderer-v8/src/script_vm/tests/geometry_matrix_dictionary_order.rs @@ -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 = ''") + .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(), + "[]" + ); +} diff --git a/moli-renderer-v8/src/script_vm/tests/mod.rs b/moli-renderer-v8/src/script_vm/tests/mod.rs index e0629fb127..9909f2a577 100644 --- a/moli-renderer-v8/src/script_vm/tests/mod.rs +++ b/moli-renderer-v8/src/script_vm/tests/mod.rs @@ -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;