diff --git a/moli-benchmark/wpt-cross-current/failed-cases.txt b/moli-benchmark/wpt-cross-current/failed-cases.txt index 188fca6eed..360cdaca26 100644 --- a/moli-benchmark/wpt-cross-current/failed-cases.txt +++ b/moli-benchmark/wpt-cross-current/failed-cases.txt @@ -2349,7 +2349,6 @@ css/css-variables/variable-reference-perspective-origin.html css/css-variables/variable-reference.html css/css-variables/variable-substitution-variable-declaration.html css/css-variables/variables-animation-math-functions.html -css/cssom-view/DOMRectList.html css/cssom-view/HTMLBody-ScrollArea_quirksmode.html css/cssom-view/MediaQueryList-addListener-handleEvent.html css/cssom-view/MediaQueryList-addListener-removeListener.html @@ -2470,7 +2469,6 @@ 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/DOMRectList.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 3ff684aac7..89889ac5d0 100644 --- a/moli-benchmark/wpt-cross-current/passed-cases.txt +++ b/moli-benchmark/wpt-cross-current/passed-cases.txt @@ -3076,6 +3076,7 @@ css/css-will-change/parsing/will-change-valid.html css/css-will-change/will-change-contents-crash.html css/css-will-change/will-change-inherit-dynamic.html css/cssom-view/CaretPosition-001.html +css/cssom-view/DOMRectList.html css/cssom-view/Element-currentCSSZoom.html css/cssom-view/GetBoundingRect.html css/cssom-view/HTMLImageElement-x-and-y-ignore-transforms.html @@ -3359,6 +3360,7 @@ css/geometry/DOMQuad-nan.html css/geometry/DOMRect-001.html css/geometry/DOMRect-002.html css/geometry/DOMRect-nan.html +css/geometry/DOMRectList.html css/geometry/WebKitCSSMatrix.html css/geometry/historical.html css/geometry/spec-examples.html diff --git a/moli-renderer-v8/src/context_bootstrap.rs b/moli-renderer-v8/src/context_bootstrap.rs index 092657d69b..93f437eca1 100644 --- a/moli-renderer-v8/src/context_bootstrap.rs +++ b/moli-renderer-v8/src/context_bootstrap.rs @@ -11,6 +11,7 @@ mod css_runtime; pub(crate) mod css_stylesheet_runtime; mod dom_quad; mod dom_rect; +mod dom_rect_list; mod event_document; mod event_legacy; mod event_template; @@ -231,6 +232,7 @@ pub(crate) use self::css_stylesheet_runtime::{ sync_css_style_sheet_shadow_root_adopted_owner_tracking, }; pub(crate) use self::dom_rect::build_dom_rect_object; +pub(crate) use self::dom_rect_list::{build_dom_rect_list_object}; pub(crate) use self::events::{ EVENT_DISPATCHING_SLOT, EVENT_PASSIVE_SLOT, EVENT_STOP_IMMEDIATE_PROPAGATION_SLOT, EVENT_STOP_PROPAGATION_SLOT, EventHandlerType, apply_event_handler_return_value, 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 dc54b398b7..0234b35a42 100644 --- a/moli-renderer-v8/src/context_bootstrap/assets/prototype_bindings.rs +++ b/moli-renderer-v8/src/context_bootstrap/assets/prototype_bindings.rs @@ -16,6 +16,7 @@ use super::super::{ css_stylesheet_runtime::install_css_stylesheet_template_bindings, dom_quad::install_dom_quad_template_bindings, dom_rect::install_dom_rect_template_bindings, + dom_rect_list::install_dom_rect_list_template_bindings, event_template::install_event_template_bindings, file_api::install_file_api_template_bindings, geometry_runtime::install_geometry_template_bindings, @@ -411,6 +412,7 @@ pub(super) fn install_constructor_template_bindings<'s>( 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_rect_list_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()); install_custom_element_registry_template_bindings(scope, template, spec.interface.name()); diff --git a/moli-renderer-v8/src/context_bootstrap/dom_rect_list.rs b/moli-renderer-v8/src/context_bootstrap/dom_rect_list.rs new file mode 100644 index 0000000000..3768aafcb2 --- /dev/null +++ b/moli-renderer-v8/src/context_bootstrap/dom_rect_list.rs @@ -0,0 +1,229 @@ +use crate::{ + util::{get_private_object, serialize_v8_array, throw_type_error}, + web_api_interfaces, webidl, +}; +use moli_webapi_declare::{ + DataPropertyDescriptorDeclaration, WebApiFunctionTemplate, WebApiObject, +}; + +const DOM_RECT_LIST_VALUES_SLOT: &str = "__moliDomRectListValues"; + +#[derive(WebApiFunctionTemplate)] +#[webapi(interface = web_api_interfaces::DOMRectList, enumerable)] +struct DomRectListPrototypeDeclaration { + #[webapi(accessor_property, getter = dom_rect_list_length_getter)] + length: (), + + #[webapi(method, length = 1, callback = dom_rect_list_item_callback)] + item: (), +} + +#[derive(WebApiObject)] +#[webapi( + interface = web_api_interfaces::DOMRectList, + require_prototype, + fallback_to_string_tag = "DOMRectList" +)] +struct DomRectListObjectDeclaration<'scope> { + #[webapi(slot = DOM_RECT_LIST_VALUES_SLOT)] + values: v8::Local<'scope, v8::Array>, +} + +#[derive(webidl::WebIdlArgs)] +#[webidl(prefix = "DOMRectList.item")] +struct DomRectListItemArgs { + #[webidl(required)] + index: u32, +} + +pub(crate) fn build_dom_rect_list_object<'s>( + scope: &mut v8::PinScope<'s, '_>, + rects: &[v8::Local<'s, v8::Object>], +) -> v8::Local<'s, v8::Object> { + let values = serialize_v8_array(scope, rects).unwrap_or_else(|| v8::Array::new(scope, 0)); + let template = v8::ObjectTemplate::new(scope); + template.set_indexed_property_handler( + v8::IndexedPropertyHandlerConfiguration::new() + .getter(dom_rect_list_indexed_getter) + .setter(dom_rect_list_indexed_setter) + .query(dom_rect_list_indexed_query) + .deleter(dom_rect_list_indexed_deleter) + .enumerator(dom_rect_list_indexed_enumerator) + .definer(dom_rect_list_indexed_definer) + .descriptor(dom_rect_list_indexed_descriptor), + ); + let object = template + .new_instance(scope) + .expect("DOMRectList object template should instantiate"); + DomRectListObjectDeclaration::new(values) + .bind_into(scope, object) + .expect("DOMRectList declaration should bind"); + object +} + +pub(in crate::context_bootstrap) fn install_dom_rect_list_template_bindings<'s>( + scope: &mut v8::PinScope<'s, '_, ()>, + template: v8::Local<'s, v8::FunctionTemplate>, + interface_name: &str, +) { + if interface_name == "DOMRectList" { + DomRectListPrototypeDeclaration::initialize_prototype_template( + scope, + template.prototype_template(scope), + ); + } +} + +fn dom_rect_list_values<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, +) -> Option> { + get_private_object(scope, object, DOM_RECT_LIST_VALUES_SLOT) + .and_then(|values| v8::Local::::try_from(values).ok()) +} + +fn require_dom_rect_list_values<'s>( + scope: &mut v8::PinScope<'s, '_>, + object: v8::Local<'s, v8::Object>, + member: &str, +) -> Option> { + dom_rect_list_values(scope, object).or_else(|| { + throw_type_error( + scope, + &format!("Failed to execute '{member}' on 'DOMRectList': Illegal invocation."), + ); + None + }) +} + +fn dom_rect_list_length_getter<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(values) = require_dom_rect_list_values(scope, args.this(), "get length") else { + return; + }; + rv.set_uint32(values.length()); +} + +fn dom_rect_list_item_callback<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) { + let Some(values) = require_dom_rect_list_values(scope, args.this(), "item") else { + return; + }; + let Some(parsed) = webidl::parse_args::(scope, &args) else { + return; + }; + match values.get_index(scope, parsed.index) { + Some(value) if !value.is_null_or_undefined() => rv.set(value), + _ => rv.set(v8::null(scope).into()), + } +} + +fn dom_rect_list_indexed_getter<'s>( + scope: &mut v8::PinScope<'s, '_>, + index: u32, + args: v8::PropertyCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) -> v8::Intercepted { + let Some(value) = dom_rect_list_values(scope, args.holder()) + .filter(|values| index < values.length()) + .and_then(|values| values.get_index(scope, index)) + else { + return v8::Intercepted::kNo; + }; + rv.set(value); + v8::Intercepted::kYes +} + +fn dom_rect_list_indexed_query<'s>( + scope: &mut v8::PinScope<'s, '_>, + index: u32, + args: v8::PropertyCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Integer>, +) -> v8::Intercepted { + let Some(values) = dom_rect_list_values(scope, args.holder()) else { + return v8::Intercepted::kNo; + }; + if index >= values.length() { + return v8::Intercepted::kNo; + } + rv.set_int32(v8::PropertyAttribute::READ_ONLY.as_u32() as i32); + v8::Intercepted::kYes +} + +fn dom_rect_list_indexed_setter( + _scope: &mut v8::PinScope<'_, '_>, + _index: u32, + _value: v8::Local<'_, v8::Value>, + _args: v8::PropertyCallbackArguments<'_>, + mut rv: v8::ReturnValue<'_, v8::Boolean>, +) -> v8::Intercepted { + rv.set_bool(false); + v8::Intercepted::kYes +} + +fn dom_rect_list_indexed_deleter<'s>( + scope: &mut v8::PinScope<'s, '_>, + index: u32, + args: v8::PropertyCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Boolean>, +) -> v8::Intercepted { + let Some(values) = dom_rect_list_values(scope, args.holder()) else { + return v8::Intercepted::kNo; + }; + if index >= values.length() { + return v8::Intercepted::kNo; + } + rv.set_bool(false); + v8::Intercepted::kYes +} + +fn dom_rect_list_indexed_definer( + _scope: &mut v8::PinScope<'_, '_>, + _index: u32, + _descriptor: &v8::PropertyDescriptor, + _args: v8::PropertyCallbackArguments<'_>, + mut rv: v8::ReturnValue<'_, v8::Boolean>, +) -> v8::Intercepted { + rv.set_bool(true); + v8::Intercepted::kYes +} + +fn dom_rect_list_indexed_enumerator<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::PropertyCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Array>, +) { + let length = dom_rect_list_values(scope, args.holder()) + .map(|values| values.length()) + .unwrap_or(0); + let keys = (0..length) + .map(|index| v8::Integer::new_from_unsigned(scope, index).into()) + .collect::>(); + rv.set(v8::Array::new_with_elements(scope, &keys)); +} + +fn dom_rect_list_indexed_descriptor<'s>( + scope: &mut v8::PinScope<'s, '_>, + index: u32, + args: v8::PropertyCallbackArguments<'s>, + mut rv: v8::ReturnValue<'_, v8::Value>, +) -> v8::Intercepted { + let Some(value) = dom_rect_list_values(scope, args.holder()) + .filter(|values| index < values.length()) + .and_then(|values| values.get_index(scope, index)) + else { + return v8::Intercepted::kNo; + }; + let Ok(descriptor) = DataPropertyDescriptorDeclaration::new(value, false, true).bind(scope) + else { + return v8::Intercepted::kNo; + }; + rv.set(descriptor.into()); + v8::Intercepted::kYes +} diff --git a/moli-renderer-v8/src/context_bootstrap/range_algorithms/geometry.rs b/moli-renderer-v8/src/context_bootstrap/range_algorithms/geometry.rs index 3aa51fed64..5befda8033 100644 --- a/moli-renderer-v8/src/context_bootstrap/range_algorithms/geometry.rs +++ b/moli-renderer-v8/src/context_bootstrap/range_algorithms/geometry.rs @@ -1,5 +1,5 @@ use super::*; -use crate::util::{call_object_method, object_string_property, serialize_v8_array}; +use crate::util::{call_object_method, object_string_property}; enum NativeRangeGeometryQuery { Text, @@ -330,7 +330,7 @@ pub(in crate::context_bootstrap) fn range_geometry_dom_rect<'s>( pub(in crate::context_bootstrap) fn range_geometry_client_rects<'s>( scope: &mut v8::PinScope<'s, '_>, range: v8::Local<'s, v8::Object>, -) -> Option> { +) -> Option> { if let Some(rects) = native_range_client_rects(scope, range) { return match rects { Ok(rects) => { @@ -338,7 +338,7 @@ pub(in crate::context_bootstrap) fn range_geometry_client_rects<'s>( .into_iter() .filter_map(|rect| new_dom_rect_from_client_rect(scope, rect)) .collect::>(); - serialize_v8_array(scope, rects) + Some(dom_rect_list::build_dom_rect_list_object(scope, &rects)) } Err(error) => { throw_range_layout_error(scope, error); @@ -348,12 +348,12 @@ pub(in crate::context_bootstrap) fn range_geometry_client_rects<'s>( } if let Some(rect) = collapsed_text_range_client_rect(scope, range) { let rect = new_dom_rect_from_client_rect(scope, rect)?; - return serialize_v8_array(scope, [rect]); + return Some(dom_rect_list::build_dom_rect_list_object(scope, &[rect])); } let target = range_geometry_target(scope, range)?; let rect = client_rect_for_object(scope, target)?; let rect = new_dom_rect_from_client_rect(scope, rect)?; - serialize_v8_array(scope, [rect]) + Some(dom_rect_list::build_dom_rect_list_object(scope, &[rect])) } pub(in crate::context_bootstrap) fn new_dom_rect_zero<'s>( diff --git a/moli-renderer-v8/src/context_bootstrap/range_surface/geometry.rs b/moli-renderer-v8/src/context_bootstrap/range_surface/geometry.rs index e8ea622acf..81cabbf48c 100644 --- a/moli-renderer-v8/src/context_bootstrap/range_surface/geometry.rs +++ b/moli-renderer-v8/src/context_bootstrap/range_surface/geometry.rs @@ -18,11 +18,10 @@ pub(super) fn range_get_client_rects_callback<'s>( args: v8::FunctionCallbackArguments<'s>, mut rv: v8::ReturnValue<'_, v8::Value>, ) { - let rects = - range_geometry_client_rects(scope, args.this()).or_else(|| Some(v8::Array::new(scope, 0))); - if let Some(rects) = rects { - rv.set(rects.into()); - } else { - rv.set(v8::undefined(scope).into()); - } + // Build the no-geometry fallback before querying layout. The query may + // throw, and allocating a wrapper after that would disturb V8's pending + // exception state. + let empty = dom_rect_list::build_dom_rect_list_object(scope, &[]); + let rects = range_geometry_client_rects(scope, args.this()).unwrap_or(empty); + rv.set(rects.into()); } diff --git a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs index 98d14488c5..e95aecae4d 100644 --- a/moli-renderer-v8/src/context_bootstrap/specs/registry.rs +++ b/moli-renderer-v8/src/context_bootstrap/specs/registry.rs @@ -40,6 +40,10 @@ const CONSTRUCTOR_SPECS_BEFORE_STREAMS: &[ConstructorSpec] = &[ interface: web_api_interfaces::DOMStringList::DESCRIPTOR, kind: ConstructorKind::Illegal, }, + ConstructorSpec { + interface: web_api_interfaces::DOMRectList::DESCRIPTOR, + kind: ConstructorKind::Illegal, + }, ConstructorSpec { interface: web_api_interfaces::PluginArray::DESCRIPTOR, kind: ConstructorKind::Illegal, diff --git a/moli-renderer-v8/src/native_bridge/element/geometry/rects.rs b/moli-renderer-v8/src/native_bridge/element/geometry/rects.rs index 16c8c4a626..f8299fb525 100644 --- a/moli-renderer-v8/src/native_bridge/element/geometry/rects.rs +++ b/moli-renderer-v8/src/native_bridge/element/geometry/rects.rs @@ -3,8 +3,8 @@ use super::super::super::node::{ throw_incompatible_method_receiver, }; use super::{ClientRect, observable_bounding_client_rect, observable_client_rects}; -use crate::context_bootstrap::build_dom_rect_object; -use crate::util::{serialize_v8_array, v8_string}; +use crate::context_bootstrap::{build_dom_rect_list_object, build_dom_rect_object}; +use crate::util::v8_string; pub(crate) fn client_rect_object<'s>( scope: &mut v8::PinScope<'s, '_>, @@ -22,12 +22,12 @@ pub(crate) fn client_rect_object<'s>( fn client_rect_list<'s>( scope: &mut v8::PinScope<'s, '_>, rects: impl IntoIterator, -) -> Option> { +) -> v8::Local<'s, v8::Object> { let rects = rects .into_iter() .filter_map(|rect| client_rect_object(scope, rect)) .collect::>(); - serialize_v8_array(scope, rects) + build_dom_rect_list_object(scope, &rects) } fn throw_layout_error(scope: &mut v8::PinScope<'_, '_>, error: moli_layout::LayoutError) { @@ -83,7 +83,6 @@ pub(in crate::native_bridge) fn node_get_client_rects_callback( let Ok((runtime_ptr, handle)) = node_runtime_and_handle_from_args_or_detached(scope, &args) else { throw_incompatible_method_receiver(scope, "Element", "getClientRects"); - rv.set(v8::Array::new(scope, 0).into()); return; }; if !require_element_method_receiver(scope, unsafe { &*runtime_ptr }, handle, "getClientRects") { @@ -97,12 +96,8 @@ pub(in crate::native_bridge) fn node_get_client_rects_callback( Ok(rects) => rects, Err(error) => { throw_layout_error(scope, error); - rv.set(v8::Array::new(scope, 0).into()); return; } }; - let value = client_rect_list(scope, rects) - .map(Into::into) - .unwrap_or_else(|| v8::Array::new(scope, 0).into()); - rv.set(value); + rv.set(client_rect_list(scope, rects).into()); } diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/detached.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/detached.rs index c690ccc45d..a620b8acba 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/detached.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/detached.rs @@ -681,7 +681,7 @@ fn detached_interaction_brand_checks_accept_standard_prototype_methods() { rootSame: root === host.shadowRoot, rectType: Object.prototype.toString.call(rect), rectWidthType: typeof rect.width, - rectsArray: Array.isArray(rects), + rectsType: Object.prototype.toString.call(rects), activeAfterFocus, activeAfterBlur, events: events.join(","), @@ -703,7 +703,7 @@ fn detached_interaction_brand_checks_accept_standard_prototype_methods() { assert_eq!( result, - r#"{"rootType":"[object ShadowRoot]","rootSame":true,"rectType":"[object DOMRect]","rectWidthType":"number","rectsArray":true,"activeAfterFocus":true,"activeAfterBlur":true,"events":"focus,click,blur","attachOwn":false,"boundingOwn":false,"rectsOwn":false,"focusOwn":false,"blurOwn":false,"clickOwn":false,"elementOwners":"Element,Element,Element,Element,Element,Element,Element,Element","elementShapes":"true:function:1:true:true:true|true:function:0:true:true:true|true:function:0:true:true:true|true:function:1:true:true:true|true:function:1:true:true:true|true:function:1:true:true:true|true:function:2:true:true:true|true:function:1:true:true:true","actionOwners":"HTMLElement,HTMLElement,HTMLElement","actionShapes":"true:function:0:true:true:true|true:function:0:true:true:true|true:function:0:true:true:true"}"# + r#"{"rootType":"[object ShadowRoot]","rootSame":true,"rectType":"[object DOMRect]","rectWidthType":"number","rectsType":"[object DOMRectList]","activeAfterFocus":true,"activeAfterBlur":true,"events":"focus,click,blur","attachOwn":false,"boundingOwn":false,"rectsOwn":false,"focusOwn":false,"blurOwn":false,"clickOwn":false,"elementOwners":"Element,Element,Element,Element,Element,Element,Element,Element","elementShapes":"true:function:1:true:true:true|true:function:0:true:true:true|true:function:0:true:true:true|true:function:1:true:true:true|true:function:1:true:true:true|true:function:1:true:true:true|true:function:2:true:true:true|true:function:1:true:true:true","actionOwners":"HTMLElement,HTMLElement,HTMLElement","actionShapes":"true:function:0:true:true:true|true:function:0:true:true:true|true:function:0:true:true:true"}"# ); } diff --git a/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs b/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs index c0cd87c379..220f3b8738 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_xhr/misc.rs @@ -1667,7 +1667,7 @@ fn child_content_document_created_elements_expose_geometry_methods() { assert_eq!(result, "function|function|0|true"); } #[test] -fn geometry_rect_objects_expose_domrect_to_json() { +fn geometry_rect_objects_and_lists_expose_their_webidl_shapes() { let mut vm = new_parsed_test_vm( "https://geometry-domrect-shape.test/", "
text
", @@ -1698,12 +1698,16 @@ fn geometry_rect_objects_expose_domrect_to_json() { [ 'x', 'y', 'width', 'height', 'top', 'right', 'bottom', 'left' ].every(name => json[name] === rect[name]), + rects instanceof DOMRectList, Array.isArray(rects), + Object.prototype.toString.call(rects), rects.length, rects[0] instanceof DOMRect, + rects.item(0) === rects[0], typeof rects[0].toJSON, rangeRect instanceof DOMRect, typeof rangeRect.toJSON, + rangeRects instanceof DOMRectList, rangeRects[0] instanceof DOMRect, contentRect instanceof DOMRect, typeof contentRect.toJSON, @@ -1717,7 +1721,81 @@ fn geometry_rect_objects_expose_domrect_to_json() { assert_eq!( result, - "true|true||function|x,y,width,height,top,right,bottom,left|true|true|1|true|function|true|function|true|true|function|true|false" + concat!( + "true|true||function|x,y,width,height,top,right,bottom,left|true|", + "true|false|[object DOMRectList]|1|true|true|function|true|function|", + "true|true|true|function|true|false" + ) + ); +} + +#[test] +fn dom_rect_list_has_readonly_indexed_semantics_and_rejects_structured_clone() { + let mut vm = new_parsed_test_vm( + "https://domrect-list.test/", + "
text
", + ); + + let result = vm + .eval( + r#" +(() => { + const probe = callback => { + try { + callback(); + return "ok"; + } catch (error) { + return error.name; + } + }; + const node = document.getElementById("node"); + const list = node.getClientRects(); + const range = document.createRange(); + range.selectNodeContents(node); + const rangeList = range.getClientRects(); + const indexDescriptor = Object.getOwnPropertyDescriptor(list, "0"); + const lengthDescriptor = Object.getOwnPropertyDescriptor( + DOMRectList.prototype, + "length" + ); + const itemDescriptor = Object.getOwnPropertyDescriptor( + DOMRectList.prototype, + "item" + ); + const fake = Object.create(DOMRectList.prototype); + return JSON.stringify({ + constructor: [typeof DOMRectList, DOMRectList.length, + probe(() => DOMRectList()), probe(() => new DOMRectList())].join("|"), + instance: [list instanceof DOMRectList, list instanceof Array, + Object.getPrototypeOf(list) === DOMRectList.prototype, + Object.prototype.toString.call(list), list.constructor === DOMRectList].join("|"), + values: [list.length, list[-1], list[1], list.item(-1), list.item(1), + list.item(0) === list[0], list[0] instanceof DOMRect, + Object.keys(list).join(",")].join("|"), + indexDescriptor: [indexDescriptor.value === list[0], indexDescriptor.writable, + indexDescriptor.enumerable, indexDescriptor.configurable].join("|"), + prototypeDescriptors: [typeof lengthDescriptor.get, lengthDescriptor.get.name, + lengthDescriptor.get.length, lengthDescriptor.set === undefined, + lengthDescriptor.enumerable, lengthDescriptor.configurable, + itemDescriptor.value.name, itemDescriptor.value.length, + itemDescriptor.enumerable, itemDescriptor.writable, + itemDescriptor.configurable].join("|"), + brands: [probe(() => lengthDescriptor.get.call(fake)), + probe(() => itemDescriptor.value.call(fake, 0))].join("|"), + range: [rangeList instanceof DOMRectList, + Object.prototype.toString.call(rangeList), rangeList.item(0) === rangeList[0]].join("|"), + clone: probe(() => structuredClone(list)), + visibleSlots: Object.getOwnPropertyNames(list) + .filter(name => name.startsWith("__moliDomRectList")).join(",") + }); +})() +"#, + ) + .expect("DOMRectList WebIDL probe should evaluate"); + + assert_eq!( + result, + r#"{"constructor":"function|0|TypeError|TypeError","instance":"true|false|true|[object DOMRectList]|true","values":"1|||||true|true|0","indexDescriptor":"true|false|true|true","prototypeDescriptors":"function|get length|0|true|true|true|item|1|true|true|true","brands":"TypeError|TypeError","range":"true|[object DOMRectList]|true","clone":"DataCloneError","visibleSlots":""}"# ); } diff --git a/moli-renderer-v8/src/web_api_interfaces.rs b/moli-renderer-v8/src/web_api_interfaces.rs index 616a9950f5..d15b735feb 100644 --- a/moli-renderer-v8/src/web_api_interfaces.rs +++ b/moli-renderer-v8/src/web_api_interfaces.rs @@ -113,6 +113,7 @@ interfaces! { DOMPointReadOnly; DOMQuad; DOMRect: DOMRectReadOnly; + DOMRectList; DOMRectReadOnly; DOMStringList; DOMStringMap;