diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index 1bd2e8e0b8..e63c8b40b7 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -2468,12 +2468,7 @@ css/cssom/getComputedStyle-layout-dependent-replaced-into-ib-split.html css/cssom/idlharness.html css/cssom/style-sheet-interfaces-001.html css/geometry/DOMMatrix2DInit-validate-fixup.html -css/geometry/DOMQuad-001.html -css/geometry/DOMQuad-002.html -css/geometry/DOMQuad-nan.html css/geometry/DOMRectList.html -css/geometry/historical.html -css/geometry/spec-examples.html css/geometry/structured-serialization.html css/mediaqueries/at-custom-media-cssom.html css/mediaqueries/at-custom-media-parsing.html diff --git a/moli-benchmark/wpt-cross-current/passed-cases.txt b/moli-benchmark/wpt-cross-current/passed-cases.txt index 4da52f1fb9..923a7ab75c 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -3356,10 +3356,15 @@ css/geometry/DOMMatrix-stringifier.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 +css/geometry/DOMQuad-nan.html css/geometry/DOMRect-001.html css/geometry/DOMRect-002.html css/geometry/DOMRect-nan.html css/geometry/WebKitCSSMatrix.html +css/geometry/historical.html +css/geometry/spec-examples.html css/mediaqueries/aspect-ratio-serialization.html css/mediaqueries/duplicate-media-stylesheet-crash.html css/mediaqueries/dynamic-range.html diff --git a/moli-renderer-v8/src/context_bootstrap.rs b/moli-renderer-v8/src/context_bootstrap.rs index 5ebde32a21..f520d41d8b 100644 --- a/moli-renderer-v8/src/context_bootstrap.rs +++ b/moli-renderer-v8/src/context_bootstrap.rs @@ -13,6 +13,7 @@ mod crypto; mod css_fontface_runtime; mod css_runtime; pub(crate) mod css_stylesheet_runtime; +mod dom_quad; mod dom_rect; mod event_document; mod event_legacy; 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 70b1b30b87..935b1205b6 100644 --- a/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs +++ b/moli-renderer-v8/src/context_bootstrap/assets/constructor_templates.rs @@ -12,6 +12,7 @@ use super::super::{ css_fontface_runtime::font_face_constructor_callback, css_runtime::{css_keyword_value_constructor_callback, css_unit_value_constructor_callback}, css_stylesheet_runtime::css_style_sheet_constructor_callback, + dom_quad::dom_quad_constructor_callback, events::{ EventSubclassKind, ValueEventKind, build_event_subclass_template, build_value_event_template, event_constructor_callback, @@ -527,6 +528,14 @@ pub(in crate::context_bootstrap) fn build_constructor_template<'s>( .length(0) .build(scope) } + ConstructorKind::DomQuad => { + v8::FunctionTemplate::builder(moli_webapi_declare::web_api_constructor!( + web_api_interfaces::DOMQuad, + dom_quad_constructor_callback + )) + .length(0) + .build(scope) + } ConstructorKind::DomMatrix => { v8::FunctionTemplate::builder(dom_matrix_constructor_callback) .length(0) diff --git a/moli-renderer-v8/src/context_bootstrap/assets/prototype_bindings.rs b/moli-renderer-v8/src/context_bootstrap/assets/prototype_bindings.rs index bb5cbdf9d7..4093d87cd5 100644 --- a/moli-renderer-v8/src/context_bootstrap/assets/prototype_bindings.rs +++ b/moli-renderer-v8/src/context_bootstrap/assets/prototype_bindings.rs @@ -14,6 +14,7 @@ use super::super::{ crypto::install_crypto_template_bindings, css_runtime::install_css_typed_om_template_bindings, css_stylesheet_runtime::install_css_stylesheet_template_bindings, + dom_quad::install_dom_quad_template_bindings, dom_rect::install_dom_rect_template_bindings, event_template::install_event_template_bindings, file_api::install_file_api_template_bindings, @@ -408,6 +409,7 @@ pub(super) fn install_constructor_template_bindings<'s>( install_css_style_declaration_template_accessors(scope, template, spec.interface.name()); install_abort_template_bindings(scope, template, spec.interface.name()); install_attr_template_bindings(scope, template, spec.interface.name()); + install_dom_quad_template_bindings(scope, template, spec.interface.name()); install_dom_rect_template_bindings(scope, template, spec.interface.name()); install_dom_exception_template_bindings(scope, template, spec.interface.name()); install_dom_implementation_template_bindings(scope, template, spec.interface.name()); diff --git a/moli-renderer-v8/src/context_bootstrap/dom_quad.rs b/moli-renderer-v8/src/context_bootstrap/dom_quad.rs new file mode 100644 index 0000000000..31b7ccd7ae --- /dev/null +++ b/moli-renderer-v8/src/context_bootstrap/dom_quad.rs @@ -0,0 +1,346 @@ +use super::*; +use crate::{ + util::{callback_data_index_value, get_private_object}, + web_api_interfaces, webidl, +}; +use moli_webapi_declare::{WebApiFunctionTemplate, WebApiObject}; + +const DOM_QUAD_P1_SLOT: &str = "__moliDomQuadP1"; +const DOM_QUAD_P2_SLOT: &str = "__moliDomQuadP2"; +const DOM_QUAD_P3_SLOT: &str = "__moliDomQuadP3"; +const DOM_QUAD_P4_SLOT: &str = "__moliDomQuadP4"; + +#[derive(WebApiObject)] +#[webapi(interface = web_api_interfaces::DOMQuad, fallback_to_string_tag = "DOMQuad")] +struct DomQuadObjectDeclaration<'scope> { + #[webapi(slot = DOM_QUAD_P1_SLOT)] + p1: v8::Local<'scope, v8::Object>, + #[webapi(slot = DOM_QUAD_P2_SLOT)] + p2: v8::Local<'scope, v8::Object>, + #[webapi(slot = DOM_QUAD_P3_SLOT)] + p3: v8::Local<'scope, v8::Object>, + #[webapi(slot = DOM_QUAD_P4_SLOT)] + p4: v8::Local<'scope, v8::Object>, +} + +#[derive(WebApiObject)] +#[webapi(plain, data_properties, enumerable)] +struct DomQuadJsonDeclaration<'scope> { + p1: v8::Local<'scope, v8::Object>, + p2: v8::Local<'scope, v8::Object>, + p3: v8::Local<'scope, v8::Object>, + p4: v8::Local<'scope, v8::Object>, +} + +#[derive(WebApiFunctionTemplate)] +#[webapi(interface = web_api_interfaces::DOMQuad)] +struct DomQuadPrototypeDeclaration { + #[webapi( + accessor_property, + getter = dom_quad_point_getter_callback, + data = callback_data_index_value(scope, 0), + enumerable + )] + p1: (), + #[webapi( + accessor_property, + getter = dom_quad_point_getter_callback, + data = callback_data_index_value(scope, 1), + enumerable + )] + p2: (), + #[webapi( + accessor_property, + getter = dom_quad_point_getter_callback, + data = callback_data_index_value(scope, 2), + enumerable + )] + p3: (), + #[webapi( + accessor_property, + getter = dom_quad_point_getter_callback, + data = callback_data_index_value(scope, 3), + enumerable + )] + p4: (), + + #[webapi( + method = "getBounds", + callback = dom_quad_get_bounds_callback, + length = 0, + enumerable + )] + get_bounds: (), + + #[webapi( + method = "toJSON", + callback = dom_quad_to_json_callback, + length = 0, + enumerable + )] + to_json: (), +} + +#[derive(WebApiFunctionTemplate)] +#[webapi(interface = web_api_interfaces::DOMQuad)] +struct DomQuadConstructorDeclaration { + #[webapi( + static_method = "fromRect", + callback = dom_quad_from_rect_callback, + length = 0 + )] + from_rect: (), + + #[webapi( + static_method = "fromQuad", + callback = dom_quad_from_quad_callback, + length = 0 + )] + from_quad: (), +} + +#[derive(Default, webidl::WebIdlDictionary)] +#[webidl(prefix = "DOMQuadInit")] +struct DomQuadInit { + #[webidl(with = optional_dom_point_member)] + p1: Option, + #[webidl(with = optional_dom_point_member)] + p2: Option, + #[webidl(with = optional_dom_point_member)] + p3: Option, + #[webidl(with = optional_dom_point_member)] + p4: Option, +} + +pub(super) fn dom_quad_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 'DOMQuad': Please use the 'new' operator.", + ); + return; + } + + let mut points = [geometry_runtime::DomPointInit::default(); 4]; + for (index, point) in points.iter_mut().enumerate() { + let Some(init) = + geometry_runtime::optional_dom_point_init_arg(scope, &args, index as i32, "DOMQuad") + else { + return; + }; + *point = init; + } + + let [p1, p2, p3, p4] = points.map(|point| build_dom_point(scope, point)); + DomQuadObjectDeclaration::new(p1, p2, p3, p4) + .initialize(scope, args.this()) + .expect("DOMQuad declaration should initialize object"); + rv.set(args.this().into()); +} + +pub(in crate::context_bootstrap) fn install_dom_quad_template_bindings<'s>( + scope: &mut v8::PinScope<'s, '_, ()>, + template: v8::Local<'s, v8::FunctionTemplate>, + interface_name: &str, +) { + if interface_name != "DOMQuad" { + return; + } + DomQuadConstructorDeclaration::initialize_template(scope, template); + DomQuadPrototypeDeclaration::initialize_prototype_template( + scope, + template.prototype_template(scope), + ); +} + +fn dom_quad_from_rect_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(rect) = dom_rect::dom_rect_init_arg(scope, &args, "DOMQuad.fromRect") else { + return; + }; + let p1 = point(rect.x, rect.y); + let p2 = point(rect.x + rect.width, rect.y); + let p3 = point(rect.x + rect.width, rect.y + rect.height); + let p4 = point(rect.x, rect.y + rect.height); + rv.set(build_dom_quad(scope, [p1, p2, p3, p4]).into()); +} + +fn dom_quad_from_quad_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(init) = dom_quad_init_arg(scope, &args) else { + return; + }; + rv.set( + build_dom_quad( + scope, + [ + init.p1.unwrap_or_default(), + init.p2.unwrap_or_default(), + init.p3.unwrap_or_default(), + init.p4.unwrap_or_default(), + ], + ) + .into(), + ); +} + +fn dom_quad_point_getter_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(slot) = callback_data_item(scope, &args, DOM_QUAD_POINT_SLOTS, "DOMQuad point slots") + else { + rv.set_undefined(); + return; + }; + if !dom_quad_receiver_branded(scope, args.this()) { + throw_type_error(scope, "Illegal invocation"); + return; + } + let Some(point) = get_private_object(scope, args.this(), slot) else { + rv.set_undefined(); + return; + }; + rv.set(point.into()); +} + +fn dom_quad_get_bounds_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(points) = dom_quad_points(scope, args.this()) else { + throw_type_error(scope, "Illegal invocation"); + return; + }; + let points = points.map(|point| geometry_runtime::dom_point_init_from_object(scope, point)); + let (x, width) = coordinate_bounds(points.map(|point| point.x)); + let (y, height) = coordinate_bounds(points.map(|point| point.y)); + rv.set(dom_rect::build_dom_rect_object(scope, x, y, width, height).into()); +} + +fn dom_quad_to_json_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some([p1, p2, p3, p4]) = dom_quad_points(scope, args.this()) else { + throw_type_error(scope, "Illegal invocation"); + return; + }; + let Ok(object) = DomQuadJsonDeclaration { p1, p2, p3, p4 }.bind(scope) else { + return; + }; + rv.set(object.into()); +} + +fn build_dom_quad<'s>( + scope: &mut v8::PinScope<'s, '_>, + points: [geometry_runtime::DomPointInit; 4], +) -> v8::Local<'s, v8::Object> { + let [p1, p2, p3, p4] = points.map(|point| build_dom_point(scope, point)); + DomQuadObjectDeclaration::new(p1, p2, p3, p4) + .bind(scope) + .expect("DOMQuad declaration should bind") +} + +fn build_dom_point<'s>( + scope: &mut v8::PinScope<'s, '_>, + point: geometry_runtime::DomPointInit, +) -> v8::Local<'s, v8::Object> { + geometry_runtime::build_dom_point_object(scope, point.x, point.y, point.z, point.w) +} + +fn point(x: f64, y: f64) -> geometry_runtime::DomPointInit { + geometry_runtime::DomPointInit { + x, + y, + ..Default::default() + } +} + +fn dom_quad_init_arg<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: &v8::FunctionCallbackArguments<'s>, +) -> Option { + if args.length() == 0 || args.get(0).is_undefined() { + return Some(DomQuadInit::default()); + } + match webidl::parse_dictionary::( + scope, + args.get(0), + webidl::Context::argument("DOMQuad.fromQuad", 1), + ) { + Ok(Some(init)) => Some(init), + Ok(None) => Some(DomQuadInit::default()), + Err(error) => { + webidl::throw_error(scope, &error); + None + } + } +} + +fn optional_dom_point_member<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, + name: &'static str, +) -> Result, webidl::WebIdlError> { + let context = webidl::Context::member("DOMQuadInit", name); + let Some(value) = webidl::property_result(scope, object, name, context)? else { + return Ok(None); + }; + if value.is_undefined() { + return Ok(None); + } + webidl::parse_dictionary::(scope, value, context) + .map(|init| Some(init.unwrap_or_default())) +} + +fn dom_quad_points<'s>( + scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, +) -> Option<[v8::Local<'s, v8::Object>; 4]> { + if !dom_quad_receiver_branded(scope, receiver) { + return None; + } + Some([ + get_private_object(scope, receiver, DOM_QUAD_P1_SLOT)?, + get_private_object(scope, receiver, DOM_QUAD_P2_SLOT)?, + get_private_object(scope, receiver, DOM_QUAD_P3_SLOT)?, + get_private_object(scope, receiver, DOM_QUAD_P4_SLOT)?, + ]) +} + +fn dom_quad_receiver_branded<'s>( + scope: &mut v8::PinScope<'s, '_>, + receiver: v8::Local<'s, v8::Object>, +) -> bool { + web_api_interfaces::DOMQuad::is_instance(scope, receiver) +} + +fn coordinate_bounds(values: [f64; 4]) -> (f64, f64) { + if values.iter().any(|value| value.is_nan()) { + return (f64::NAN, f64::NAN); + } + let minimum = values.into_iter().fold(f64::INFINITY, f64::min); + let maximum = values.into_iter().fold(f64::NEG_INFINITY, f64::max); + (minimum, maximum - minimum) +} + +const DOM_QUAD_POINT_SLOTS: &[&str] = &[ + DOM_QUAD_P1_SLOT, + DOM_QUAD_P2_SLOT, + DOM_QUAD_P3_SLOT, + DOM_QUAD_P4_SLOT, +]; diff --git a/moli-renderer-v8/src/context_bootstrap/dom_rect.rs b/moli-renderer-v8/src/context_bootstrap/dom_rect.rs index c91bdb8861..161c3d71a7 100644 --- a/moli-renderer-v8/src/context_bootstrap/dom_rect.rs +++ b/moli-renderer-v8/src/context_bootstrap/dom_rect.rs @@ -214,15 +214,15 @@ struct DomRectReadOnlyConstructorArgs { #[derive(Clone, Copy, Default, webidl::WebIdlDictionary)] #[webidl(prefix = "DOMRectInit")] -struct DomRectInit { +pub(super) struct DomRectInit { #[webidl(default = 0.0)] - x: f64, + pub(super) x: f64, #[webidl(default = 0.0)] - y: f64, + pub(super) y: f64, #[webidl(default = 0.0)] - width: f64, + pub(super) width: f64, #[webidl(default = 0.0)] - height: f64, + pub(super) height: f64, } pub(super) fn dom_rect_readonly_constructor_callback<'s>( @@ -368,7 +368,7 @@ fn dom_rect_from_rect_callback<'s>( rv.set(build_dom_rect_object(scope, init.x, init.y, init.width, init.height).into()); } -fn dom_rect_init_arg<'s>( +pub(super) fn dom_rect_init_arg<'s>( scope: &mut v8::PinScope<'s, '_>, args: &v8::FunctionCallbackArguments<'s>, prefix: &'static str, diff --git a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs index 864deec0c0..d8023bfad5 100644 --- a/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs +++ b/moli-renderer-v8/src/context_bootstrap/geometry_runtime.rs @@ -1156,6 +1156,18 @@ fn dom_point_slot<'s>( .unwrap_or(default) } +pub(super) fn dom_point_init_from_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> DomPointInit { + DomPointInit { + x: dom_point_slot(scope, object, DOM_POINT_X_SLOT, 0.0), + y: dom_point_slot(scope, object, DOM_POINT_Y_SLOT, 0.0), + z: dom_point_slot(scope, object, DOM_POINT_Z_SLOT, 0.0), + w: dom_point_slot(scope, object, DOM_POINT_W_SLOT, 1.0), + } +} + fn dom_point_receiver_branded<'s>( scope: &mut v8::PinScope<'s, '_>, receiver: v8::Local<'s, v8::Object>, diff --git a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs index 9b6a6719c6..b5f101c6df 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs @@ -1097,7 +1097,7 @@ const CONSTRUCTOR_SPECS_AFTER_STREAMS: &[ConstructorSpec] = &[ }, ConstructorSpec { interface: web_api_interfaces::DOMQuad::DESCRIPTOR, - kind: ConstructorKind::Illegal, + kind: ConstructorKind::DomQuad, }, ConstructorSpec { interface: web_api_interfaces::DOMMatrixReadOnly::DESCRIPTOR, diff --git a/moli-renderer-v8/src/context_bootstrap/specs/types.rs b/moli-renderer-v8/src/context_bootstrap/specs/types.rs index da3b195bb9..c62fdfc1a8 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/types.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/types.rs @@ -115,6 +115,7 @@ pub(in crate::context_bootstrap) enum ConstructorKind { DomRect, DomPointReadOnly, DomPoint, + DomQuad, DomMatrix, ResizeObserver, PerformanceObserver, diff --git a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs index c0262902cb..2974d799e0 100644 --- a/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs +++ b/moli-renderer-v8/src/script_vm/tests/browser_api/misc/extracted/document_navigator.rs @@ -518,3 +518,140 @@ fn dom_point_matrix_transform_validates_matrix_init_and_returns_in_the_function_ "0|true|false|30,52,12,10|TypeError|TypeError|no throw|no throw" ); } + +#[test] +fn dom_quad_uses_live_same_object_points_and_geometry_dictionary_factories() { + let mut vm = new_storage_test_vm("https://domquad-geometry.test/"); + + let result = vm + .eval( + r#" +(() => { + const probe = callback => { + try { + return String(callback()); + } catch (error) { + return error.name; + } + }; + const q = new DOMQuad( + {x: 1, y: 2, z: 3, w: 4}, + {x: 11, y: 2}, + {x: 11, y: 22}, + {x: 1, y: 22} + ); + const initial = [q.p1.x, q.p1.y, q.p1.z, q.p1.w, + q.getBounds().x, q.getBounds().y, q.getBounds().width, + q.getBounds().height].join(","); + const copy = DOMQuad.fromQuad(q); + const copyInitial = [copy.p1.x, copy.p1.y, copy.p1.z, copy.p1.w, + copy.p4.x, copy.p4.y].join(","); + const originalP1 = q.p1; + q.p1.x = -5; + q.p4.y = 30; + q.p1 = new DOMPoint(999, 999); + const live = [q.p1 === originalP1, q.p1.x, q.p4.y, + q.getBounds().x, q.getBounds().y, q.getBounds().width, + q.getBounds().height].join(","); + const copyAfterMutation = [copy.p1.x, copy.p4.y, copy.p1 !== q.p1].join(","); + const negative = DOMQuad.fromRect({x: 10, y: 20, width: -100, height: -200}); + const negativeValues = [negative.p1.x, negative.p1.y, negative.p2.x, + negative.p3.y, negative.getBounds().x, negative.getBounds().y, + negative.getBounds().width, negative.getBounds().height].join(","); + const nanBounds = new DOMQuad({x: 0, y: 0}, {x: 0, y: 0}, + {x: NaN, y: 0}, {x: 0, y: 0}).getBounds(); + const nanValues = [nanBounds.x, nanBounds.y, nanBounds.width, + nanBounds.height].join(","); + const json = q.toJSON(); + const pointDescriptor = Object.getOwnPropertyDescriptor(DOMQuad.prototype, "p1"); + const fake = Object.assign(Object.create(DOMQuad.prototype), { + __moliDomQuadBrand: true, + __moliDomQuadP1: new DOMPoint() + }); + return JSON.stringify({ + interfaceShape: [DOMQuad.length, DOMQuad.fromRect.length, + DOMQuad.fromQuad.length, q.getBounds.length, q.toJSON.length, + Object.prototype.toString.call(q), "bounds" in q].join("|"), + prototypeKeys: Object.keys(DOMQuad.prototype).join(","), + pointDescriptor: [typeof pointDescriptor.get, pointDescriptor.get.name, + pointDescriptor.get.length, pointDescriptor.set === undefined, + pointDescriptor.enumerable, pointDescriptor.configurable].join("|"), + sameObject: q.p1 === q.p1, + initial, + live, + copyInitial, + copyAfterMutation, + negativeValues, + nanValues, + jsonShape: [Object.getPrototypeOf(json) === Object.prototype, + Object.keys(json).join(","), json.p1 === q.p1, + JSON.stringify(json)].join("|"), + forged: [probe(() => pointDescriptor.get.call(fake)), + probe(() => DOMQuad.prototype.getBounds.call(fake)), + probe(() => DOMQuad.prototype.toJSON.call(fake))].join(","), + visibleSlots: Object.getOwnPropertyNames(q) + .filter(name => name.startsWith("__moliDomQuad")).join(",") + }); +})() +"#, + ) + .expect("DOMQuad geometry probe should evaluate"); + + assert_eq!( + result, + concat!( + r#"{"interfaceShape":"0|0|0|0|0|[object DOMQuad]|false","prototypeKeys":"p1,p2,p3,p4,getBounds,toJSON","pointDescriptor":"function|get p1|0|true|true|true","sameObject":true,"initial":"1,2,3,4,1,2,10,20","live":"true,-5,30,-5,2,16,28","copyInitial":"1,2,3,4,1,22","copyAfterMutation":"1,22,true","negativeValues":"10,20,-90,-180,-90,-180,100,200","nanValues":"NaN,0,NaN,0","jsonShape":"true|p1,p2,p3,p4|true|"#, + r#"{\"p1\":{\"x\":-5,\"y\":2,\"z\":3,\"w\":4},\"p2\":{\"x\":11,\"y\":2,\"z\":0,\"w\":1},\"p3\":{\"x\":11,\"y\":22,\"z\":0,\"w\":1},\"p4\":{\"x\":1,\"y\":30,\"z\":0,\"w\":1}}","forged":"TypeError,TypeError,TypeError","visibleSlots":""}"# + ) + ); +} + +#[test] +fn dom_quad_factories_and_default_to_json_use_the_function_realm() { + let mut vm = new_storage_test_vm("https://domquad-function-realm.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 = "domquad-function-realm"; + body.appendChild(frame); +})() +"#, + ) + .expect("DOMQuad factory child frame should be created"); + materialize_single_child_default_realm_for_test(&mut vm, "DOMQuad factory child Realm"); + + let result = vm + .eval( + r#" +(() => { + const other = document.getElementById("domquad-function-realm").contentWindow; + const quad = other.DOMQuad.fromRect({x: 1, y: 2, width: 3, height: 4}); + const source = new DOMQuad({x: 7, y: 8}); + const json = other.DOMQuad.prototype.toJSON.call(source); + return [ + quad instanceof other.DOMQuad, + quad instanceof DOMQuad, + quad.p1 instanceof other.DOMPoint, + quad.p1 instanceof DOMPoint, + quad.getBounds() instanceof other.DOMRect, + quad.getBounds() instanceof DOMRect, + Object.getPrototypeOf(json) === other.Object.prototype, + json.p1 === source.p1, + json.p1 instanceof DOMPoint, + json.p1 instanceof other.DOMPoint + ].join("|"); +})() +"#, + ) + .expect("DOMQuad function realm probe should evaluate"); + + assert_eq!( + result, + "true|false|true|false|true|false|true|true|true|false" + ); +}