mirror of
https://github.com/lexmount/moli.git
synced 2026-10-08 16:01:05 +00:00
fix(geometry): return DOMRectList from geometry APIs
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<v8::Local<'s, v8::Array>> {
|
||||
get_private_object(scope, object, DOM_RECT_LIST_VALUES_SLOT)
|
||||
.and_then(|values| v8::Local::<v8::Array>::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<v8::Local<'s, v8::Array>> {
|
||||
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::<DomRectListItemArgs>(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::<Vec<_>>();
|
||||
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
|
||||
}
|
||||
@@ -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<v8::Local<'s, v8::Array>> {
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
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::<Vec<_>>();
|
||||
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>(
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Item = ClientRect>,
|
||||
) -> Option<v8::Local<'s, v8::Array>> {
|
||||
) -> v8::Local<'s, v8::Object> {
|
||||
let rects = rects
|
||||
.into_iter()
|
||||
.filter_map(|rect| client_rect_object(scope, rect))
|
||||
.collect::<Vec<_>>();
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -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"}"#
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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/",
|
||||
"<!doctype html><body><div id='node'>text</div></body>",
|
||||
@@ -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/",
|
||||
"<!doctype html><body><div id='node'>text</div></body>",
|
||||
);
|
||||
|
||||
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":""}"#
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ interfaces! {
|
||||
DOMPointReadOnly;
|
||||
DOMQuad;
|
||||
DOMRect: DOMRectReadOnly;
|
||||
DOMRectList;
|
||||
DOMRectReadOnly;
|
||||
DOMStringList;
|
||||
DOMStringMap;
|
||||
|
||||
Reference in New Issue
Block a user