From 9aef440dbe1a882010eaecb2f7232fc3748aac25 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Sun, 30 Aug 2026 08:27:12 +0800 Subject: [PATCH] fix(geometry): construct DOMPointReadOnly instances --- .../wpt-cross-current/failed-cases.txt | 1 - .../wpt-cross-current/passed-cases.txt | 1 + .../assets/constructor_templates.rs | 13 ++- .../src/context_bootstrap/geometry_runtime.rs | 104 +++++++++++++++--- .../src/context_bootstrap/specs/registry.rs | 2 +- .../src/context_bootstrap/specs/types.rs | 1 + .../src/script_vm/tests/browser_api/misc.rs | 32 ++++++ 7 files changed, 135 insertions(+), 19 deletions(-) diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index ff90244a10..31363f1768 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -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-001.html css/geometry/DOMPoint-002.html css/geometry/DOMQuad-001.html css/geometry/DOMQuad-002.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index 19d2f31b81..8284224ea0 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -3346,6 +3346,7 @@ css/geometry/DOMMatrix-invert-non-invertible.html css/geometry/DOMMatrix-invert-preserves-2d.html css/geometry/DOMMatrix-invertSelf.html css/geometry/DOMMatrix-newobject.html +css/geometry/DOMPoint-001.html css/geometry/DOMRect-001.html css/geometry/DOMRect-002.html css/geometry/DOMRect-nan.html 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 b8db5765b0..ff2fbb36fd 100644 --- a/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs +++ b/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs @@ -19,7 +19,10 @@ use super::super::{ file_reader_sync_constructor_callback, }, form_data_runtime::build_form_data_constructor_template, - geometry_runtime::{dom_matrix_constructor_callback, dom_point_constructor_callback}, + geometry_runtime::{ + dom_matrix_constructor_callback, dom_point_constructor_callback, + dom_point_readonly_constructor_callback, + }, idle_detection::idle_detector_constructor_callback, image_data::image_data_constructor_callback, location_runtime::build_location_constructor_template, @@ -491,6 +494,14 @@ pub(in crate::context_bootstrap) fn build_constructor_template<'s>( .length(0) .build(scope) } + ConstructorKind::DomPointReadOnly => { + v8::FunctionTemplate::builder(moli_webapi_declare::web_api_constructor!( + web_api_interfaces::DOMPointReadOnly, + dom_point_readonly_constructor_callback + )) + .length(0) + .build(scope) + } ConstructorKind::DomPoint => { v8::FunctionTemplate::builder(moli_webapi_declare::web_api_constructor!( web_api_interfaces::DOMPoint, diff --git a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs index b20eca9323..acc26f8a94 100644 --- a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs +++ b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs @@ -45,6 +45,22 @@ struct DomPointObjectDeclaration { w: f64, } +#[derive(WebApiObject)] +#[webapi( + interface = web_api_interfaces::DOMPointReadOnly, + fallback_to_string_tag = "DOMPointReadOnly" +)] +struct DomPointReadOnlyObjectDeclaration { + #[webapi(slot = DOM_POINT_X_SLOT)] + x: f64, + #[webapi(slot = DOM_POINT_Y_SLOT)] + y: f64, + #[webapi(slot = DOM_POINT_Z_SLOT)] + z: f64, + #[webapi(slot = DOM_POINT_W_SLOT)] + w: f64, +} + macro_rules! dom_matrix_object_declaration { ($name:ident, $interface:ident) => { #[derive(WebApiObject)] @@ -195,12 +211,25 @@ struct DomPointPrototypeAccessorsDeclaration { } #[derive(WebApiFunctionTemplate)] -#[webapi(interface = web_api_interfaces::DOMPoint)] -struct DomPointPrototypeMethodsDeclaration { +#[webapi(interface = web_api_interfaces::DOMPointReadOnly)] +struct DomPointReadOnlyPrototypeMethodsDeclaration { #[webapi(method = "toJSON", enumerable, callback = dom_point_to_json_callback)] to_json: (), } +#[derive(WebApiFunctionTemplate)] +#[webapi(interface = web_api_interfaces::DOMPointReadOnly)] +struct DomPointReadOnlyPrototypeAccessorsDeclaration { + #[webapi(accessor_property, getter = dom_point_getter_callback, data = callback_data_index_value(scope, 0), enumerable)] + x: (), + #[webapi(accessor_property, getter = dom_point_getter_callback, data = callback_data_index_value(scope, 1), enumerable)] + y: (), + #[webapi(accessor_property, getter = dom_point_getter_callback, data = callback_data_index_value(scope, 2), enumerable)] + z: (), + #[webapi(accessor_property, getter = dom_point_getter_callback, data = callback_data_index_value(scope, 3), enumerable)] + w: (), +} + #[derive(WebApiFunctionTemplate)] #[webapi(interface = web_api_interfaces::DOMPoint)] struct DomPointConstructorDeclaration { @@ -500,22 +529,45 @@ pub(super) fn dom_point_constructor_callback<'s>( ); return; } - let Some(x) = geometry_number_arg(scope, &args, 0, 0.0, "DOMPoint") else { + let Some(init) = dom_point_constructor_init(scope, &args, "DOMPoint") else { return; }; - let Some(y) = geometry_number_arg(scope, &args, 1, 0.0, "DOMPoint") else { - return; - }; - let Some(z) = geometry_number_arg(scope, &args, 2, 0.0, "DOMPoint") else { - return; - }; - let Some(w) = geometry_number_arg(scope, &args, 3, 1.0, "DOMPoint") else { - return; - }; - initialize_dom_point_object(scope, args.this(), x, y, z, w); + initialize_dom_point_object(scope, args.this(), init.x, init.y, init.z, init.w); rv.set(args.this().into()); } +pub(super) fn dom_point_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 'DOMPointReadOnly': Please use the 'new' operator.", + ); + return; + } + let Some(init) = dom_point_constructor_init(scope, &args, "DOMPointReadOnly") else { + return; + }; + initialize_dom_point_readonly_object(scope, args.this(), init.x, init.y, init.z, init.w); + rv.set(args.this().into()); +} + +fn dom_point_constructor_init<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: &v8::FunctionCallbackArguments<'s>, + prefix: &'static str, +) -> Option { + Some(DomPointInit { + x: geometry_number_arg(scope, args, 0, 0.0, prefix)?, + y: geometry_number_arg(scope, args, 1, 0.0, prefix)?, + z: geometry_number_arg(scope, args, 2, 0.0, prefix)?, + w: geometry_number_arg(scope, args, 3, 1.0, prefix)?, + }) +} + pub(super) fn dom_matrix_constructor_callback<'s>( scope: &mut v8::PinScope<'s, '_>, args: v8::FunctionCallbackArguments<'s>, @@ -551,6 +603,19 @@ pub(in crate::context_bootstrap) fn initialize_dom_point_object<'s>( .expect("DOMPoint declaration should initialize object"); } +fn initialize_dom_point_readonly_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, + x: f64, + y: f64, + z: f64, + w: f64, +) { + DomPointReadOnlyObjectDeclaration::new(x, y, z, w) + .initialize(scope, object) + .expect("DOMPointReadOnly declaration should initialize object"); +} + pub(in crate::context_bootstrap) fn build_dom_point_object<'s>( scope: &mut v8::PinScope<'s, '_>, x: f64, @@ -586,10 +651,17 @@ pub(in crate::context_bootstrap) fn install_geometry_template_bindings<'s>( ) { let prototype = template.prototype_template(scope); match interface_name { + "DOMPointReadOnly" => { + DomPointReadOnlyPrototypeAccessorsDeclaration::initialize_prototype_template( + scope, prototype, + ); + DomPointReadOnlyPrototypeMethodsDeclaration::initialize_prototype_template( + scope, prototype, + ); + } "DOMPoint" => { DomPointConstructorDeclaration::initialize_template(scope, template); DomPointPrototypeAccessorsDeclaration::initialize_prototype_template(scope, prototype); - DomPointPrototypeMethodsDeclaration::initialize_prototype_template(scope, prototype); } "DOMMatrixReadOnly" => { DomMatrixReadOnlyConstructorDeclaration::initialize_template(scope, template); @@ -638,7 +710,7 @@ fn dom_point_setter_callback<'s>( rv.set_undefined(); return; }; - if !dom_point_receiver_branded(scope, args.this()) { + if !web_api_interfaces::DOMPoint::is_instance(scope, args.this()) { throw_type_error(scope, "Illegal invocation"); return; } @@ -862,7 +934,7 @@ fn dom_point_receiver_branded<'s>( scope: &mut v8::PinScope<'s, '_>, receiver: v8::Local<'s, v8::Object>, ) -> bool { - web_api_interfaces::DOMPoint::is_instance(scope, receiver) + web_api_interfaces::DOMPointReadOnly::is_instance(scope, receiver) } fn dom_matrix_require_readonly_receiver<'s>( diff --git a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs index d9139482b6..e44658404b 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs @@ -977,7 +977,7 @@ const CONSTRUCTOR_SPECS_AFTER_STREAMS: &[ConstructorSpec] = &[ }, ConstructorSpec { interface: web_api_interfaces::DOMPointReadOnly::DESCRIPTOR, - kind: ConstructorKind::Illegal, + kind: ConstructorKind::DomPointReadOnly, }, ConstructorSpec { interface: web_api_interfaces::DOMPoint::DESCRIPTOR, diff --git a/moli-renderer-v8/src/context_bootstrap/specs/types.rs b/moli-renderer-v8/src/context_bootstrap/specs/types.rs index faa534fbde..de28450860 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/types.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/types.rs @@ -104,6 +104,7 @@ pub(in crate::context_bootstrap) enum ConstructorKind { WebSocketStream, DomRectReadOnly, DomRect, + DomPointReadOnly, DomPoint, DomMatrix, ResizeObserver, 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 d78d9c6162..0b12a30a5e 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 @@ -5593,6 +5593,38 @@ fn dom_point_accessors_use_private_slots_and_reject_forged_receivers() { ); } +#[test] +fn dom_point_readonly_constructor_uses_readonly_instances_and_shared_methods() { + let mut vm = new_storage_test_vm("https://dompoint-readonly-constructor.test/"); + + let result = vm + .eval( + r#" +(() => { + const point = new DOMPointReadOnly(1, 2, 3, 4); + point.x = 9; + return [ + point instanceof DOMPointReadOnly, + point instanceof DOMPoint, + [point.x, point.y, point.z, point.w].join(","), + JSON.stringify(point.toJSON()), + Object.hasOwn(DOMPointReadOnly.prototype, "x"), + Object.getOwnPropertyDescriptor(DOMPointReadOnly.prototype, "x").set === undefined, + Object.hasOwn(DOMPointReadOnly.prototype, "toJSON"), + Object.hasOwn(DOMPoint.prototype, "toJSON"), + new DOMPoint() instanceof DOMPointReadOnly + ].join("|"); +})() +"#, + ) + .expect("DOMPointReadOnly constructor should evaluate"); + + assert_eq!( + result, + "true|false|1,2,3,4|{\"x\":1,\"y\":2,\"z\":3,\"w\":4}|true|true|true|false|true" + ); +} + #[test] fn dom_matrix_objects_keep_declared_brand_and_own_slots() { let mut vm = new_storage_test_vm("https://dommatrix-declared-slots.test/");