From a62f0eaf0a7bfdea47e6132f9042d4b3adb6d3e3 Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Fri, 25 Apr 2025 22:25:10 +0200 Subject: [PATCH] layout: Use box tree `Fragment`s for offset parent queries (#36681) This change switches `offsetParent`, `offsetLeft`, etc queries to use the BoxTree fragments instead of walking the entire fragment tree. In addition, fragments are stored for columns and colgroups. In general, this greatly simplifies the flow of the query and prevents having to do expensive tree walks. Testing: This change is covered by newly passing WPT tests and three new failures: - /css/filter-effects/backdrop-filter-edge-clipping-2.html - /css/filter-effects/backdrop-filter-edge-mirror.html - /css/filter-effects/backdrop-filter-edge-pixels-2.html These failures are actually progressions, because now the references start to render properly whereas before they did not. Fixes: This is part of #36525 and #36665. Signed-off-by: Martin Robinson Co-authored-by: Oriol Brufau --- .../layout/fragment_tree/base_fragment.rs | 5 - .../layout/fragment_tree/box_fragment.rs | 8 +- components/layout/fragment_tree/fragment.rs | 11 +- .../fragment_tree/positioning_fragment.rs | 14 + components/layout/layout_impl.rs | 5 +- components/layout/query.rs | 364 +++++++----------- components/layout/table/layout.rs | 12 +- components/script/dom/window.rs | 5 +- components/shared/script_layout/lib.rs | 2 +- .../offsetParent-block-in-inline.html.ini | 3 - .../backdrop-filter-edge-clipping-2.html.ini | 2 + .../backdrop-filter-edge-mirror.html.ini | 2 + .../backdrop-filter-edge-pixels-2.html.ini | 2 + ...etParent-across-shadow-boundaries.html.ini | 24 -- ...fsetLeft-across-shadow-boundaries.html.ini | 3 - 15 files changed, 193 insertions(+), 269 deletions(-) delete mode 100644 tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini create mode 100644 tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini create mode 100644 tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini create mode 100644 tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini delete mode 100644 tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini diff --git a/components/layout/fragment_tree/base_fragment.rs b/components/layout/fragment_tree/base_fragment.rs index 48d672a8547..0cf6ee511cb 100644 --- a/components/layout/fragment_tree/base_fragment.rs +++ b/components/layout/fragment_tree/base_fragment.rs @@ -132,11 +132,6 @@ impl Tag { Tag { node, pseudo } } - /// Returns true if this tag is for a pseudo element. - pub(crate) fn is_pseudo(&self) -> bool { - self.pseudo.is_some() - } - pub(crate) fn to_display_list_fragment_id(self) -> u64 { combine_id_with_fragment_type(self.node.id(), self.pseudo.into()) } diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs index 0e83c0d71a6..e87826ec3ca 100644 --- a/components/layout/fragment_tree/box_fragment.rs +++ b/components/layout/fragment_tree/box_fragment.rs @@ -212,6 +212,10 @@ impl BoxFragment { ) } + pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect) { + self.cumulative_containing_block_rect = *containing_block; + } + pub fn offset_by_containing_block(&self, rect: &PhysicalRect) -> PhysicalRect { rect.translate(self.cumulative_containing_block_rect.origin.to_vector()) } @@ -405,8 +409,4 @@ impl BoxFragment { _ => CollapsedBlockMargins::zero(), } } - - pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect) { - self.cumulative_containing_block_rect = *containing_block; - } } diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs index ceccd1ec304..7708b0893ee 100644 --- a/components/layout/fragment_tree/fragment.rs +++ b/components/layout/fragment_tree/fragment.rs @@ -134,7 +134,9 @@ impl Fragment { Fragment::Float(float_fragment) => float_fragment .borrow_mut() .set_containing_block(containing_block), - Fragment::Positioning(_) => {}, + Fragment::Positioning(positioning_fragment) => positioning_fragment + .borrow_mut() + .set_containing_block(containing_block), Fragment::AbsoluteOrFixedPositioned(hoisted_shared_fragment) => { if let Some(ref fragment) = hoisted_shared_fragment.borrow().fragment { fragment.set_containing_block(containing_block); @@ -191,13 +193,16 @@ impl Fragment { } } - pub(crate) fn cumulative_content_box_rect(&self) -> Option> { + pub(crate) fn cumulative_border_box_rect(&self) -> Option> { match self { Fragment::Box(fragment) | Fragment::Float(fragment) => { let fragment = fragment.borrow(); Some(fragment.offset_by_containing_block(&fragment.border_rect())) }, - Fragment::Positioning(_) | + Fragment::Positioning(fragment) => { + let fragment = fragment.borrow(); + Some(fragment.offset_by_containing_block(&fragment.rect)) + }, Fragment::Text(_) | Fragment::AbsoluteOrFixedPositioned(_) | Fragment::Image(_) | diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs index 853caed6709..1fe968eb484 100644 --- a/components/layout/fragment_tree/positioning_fragment.rs +++ b/components/layout/fragment_tree/positioning_fragment.rs @@ -20,12 +20,17 @@ pub(crate) struct PositioningFragment { pub base: BaseFragment, pub rect: PhysicalRect, pub children: Vec, + /// The scrollable overflow of this anonymous fragment's children. pub scrollable_overflow: PhysicalRect, /// If this fragment was created with a style, the style of the fragment. #[conditional_malloc_size_of] pub style: Option>, + + /// This [`PositioningFragment`]'s containing block rectangle in coordinates relative to + /// the initial containing block, but not taking into account any transforms. + pub cumulative_containing_block_rect: PhysicalRect, } impl PositioningFragment { @@ -61,9 +66,18 @@ impl PositioningFragment { rect, children, scrollable_overflow, + cumulative_containing_block_rect: PhysicalRect::zero(), }) } + pub(crate) fn set_containing_block(&mut self, containing_block: &PhysicalRect) { + self.cumulative_containing_block_rect = *containing_block; + } + + pub fn offset_by_containing_block(&self, rect: &PhysicalRect) -> PhysicalRect { + rect.translate(self.cumulative_containing_block_rect.origin.to_vector()) + } + pub fn print(&self, tree: &mut PrintTree) { tree.new_level(format!( "PositioningFragment\ diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 941fa641cc9..3110899d76e 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -300,8 +300,9 @@ impl Layout for LayoutThread { feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse { - process_offset_parent_query(node, self.fragment_tree.borrow().clone()) + fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse { + let node = unsafe { ServoLayoutNode::new(&node) }; + process_offset_parent_query(node).unwrap_or_default() } #[cfg_attr( diff --git a/components/layout/query.rs b/components/layout/query.rs index 3badff83672..e78acdd0ca8 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use app_units::Au; use euclid::default::{Point2D, Rect}; -use euclid::{SideOffsets2D, Size2D, Vector2D}; +use euclid::{SideOffsets2D, Size2D}; use itertools::Itertools; use script_layout_interface::wrapper_traits::{ LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode, @@ -38,19 +38,19 @@ use style::values::specified::GenericGridTemplateComponent; use style::values::specified::box_::DisplayInside; use style_traits::{ParsingMode, ToCss}; +use crate::ArcRefCell; use crate::dom::NodeExt; use crate::flow::inline::construct::{TextTransformation, WhitespaceCollapse}; use crate::fragment_tree::{ - BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, Tag, + BoxFragment, Fragment, FragmentFlags, FragmentTree, SpecificLayoutInfo, }; -use crate::geom::{PhysicalRect, PhysicalVec}; use crate::taffy::SpecificTaffyGridInfo; pub fn process_content_box_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Option> { let rects: Vec<_> = node .fragments_for_pseudo(None) .iter() - .filter_map(Fragment::cumulative_content_box_rect) + .filter_map(Fragment::cumulative_border_box_rect) .collect(); if rects.is_empty() { return None; @@ -64,7 +64,7 @@ pub fn process_content_box_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> pub fn process_content_boxes_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Vec> { node.fragments_for_pseudo(None) .iter() - .filter_map(Fragment::cumulative_content_box_rect) + .filter_map(Fragment::cumulative_border_box_rect) .map(|rect| rect.to_untyped()) .collect() } @@ -428,229 +428,155 @@ fn shorthand_to_css_string( } } -pub fn process_offset_parent_query( - node: OpaqueNode, - fragment_tree: Option>, -) -> OffsetParentResponse { - process_offset_parent_query_inner(node, fragment_tree).unwrap_or_default() +struct OffsetParentFragments { + parent: ArcRefCell, + grandparent: Option, +} + +/// +fn offset_parent_fragments<'dom>( + node: impl LayoutNode<'dom> + 'dom, +) -> Option { + // 1. If any of the following holds true return null and terminate this algorithm: + // * The element does not have an associated CSS layout box. + // * The element is the root element. + // * The element is the HTML body element. + // * The element’s computed value of the position property is fixed. + let fragment = node.fragments_for_pseudo(None).first().cloned()?; + let flags = fragment.base()?.flags; + if flags.intersects( + FragmentFlags::IS_ROOT_ELEMENT | FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT, + ) { + return None; + } + if matches!( + fragment, Fragment::Box(fragment) if fragment.borrow().style.get_box().position == Position::Fixed + ) { + return None; + } + + // 2. Return the nearest ancestor element of the element for which at least one of + // the following is true and terminate this algorithm if such an ancestor is found: + // * The computed value of the position property is not static. + // * It is the HTML body element. + // * The computed value of the position property of the element is static and the + // ancestor is one of the following HTML elements: td, th, or table. + let mut maybe_parent_node = node.parent_node(); + while let Some(parent_node) = maybe_parent_node { + maybe_parent_node = parent_node.parent_node(); + + if let Some(parent_fragment) = parent_node.fragments_for_pseudo(None).first() { + let parent_fragment = match parent_fragment { + Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment, + _ => continue, + }; + + let grandparent_fragment = + maybe_parent_node.and_then(|node| node.fragments_for_pseudo(None).first().cloned()); + + if parent_fragment.borrow().style.get_box().position != Position::Static { + return Some(OffsetParentFragments { + parent: parent_fragment.clone(), + grandparent: grandparent_fragment, + }); + } + + let flags = parent_fragment.borrow().base.flags; + if flags.intersects( + FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT | + FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT, + ) { + return Some(OffsetParentFragments { + parent: parent_fragment.clone(), + grandparent: grandparent_fragment, + }); + } + } + } + + None } #[inline] -fn process_offset_parent_query_inner( - node: OpaqueNode, - fragment_tree: Option>, +pub fn process_offset_parent_query<'dom>( + node: impl LayoutNode<'dom> + 'dom, ) -> Option { - let fragment_tree = fragment_tree?; + // Only consider the first fragment of the node found as per a + // possible interpretation of the specification: "[...] return the + // y-coordinate of the top border edge of the first CSS layout box + // associated with the element [...]" + // + // FIXME: Browsers implement this all differently (e.g., [1]) - + // Firefox does returns the union of all layout elements of some + // sort. Chrome returns the first fragment for a block element (the + // same as ours) or the union of all associated fragments in the + // first containing block fragment for an inline element. We could + // implement Chrome's behavior, but our fragment tree currently + // provides insufficient information. + // + // [1]: https://github.com/w3c/csswg-drafts/issues/4541 + // > 1. If the element is the HTML body element or does not have any associated CSS + // layout box return zero and terminate this algorithm. + let fragment = node.fragments_for_pseudo(None).first().cloned()?; + let mut border_box = fragment.cumulative_border_box_rect()?; - struct NodeOffsetBoxInfo { - border_box: Rect, - offset_parent_node_address: Option, - is_static_body_element: bool, - } - - // https://www.w3.org/TR/2016/WD-cssom-view-1-20160317/#extensions-to-the-htmlelement-interface - let mut parent_node_addresses: Vec> = Vec::new(); - let tag_to_find = Tag::new(node); - let node_offset_box = fragment_tree.find(|fragment, level, containing_block| { - let base = fragment.base()?; - let is_body_element = base - .flags - .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT); - - if fragment.tag() == Some(tag_to_find) { - // Only consider the first fragment of the node found as per a - // possible interpretation of the specification: "[...] return the - // y-coordinate of the top border edge of the first CSS layout box - // associated with the element [...]" - // - // FIXME: Browsers implement this all differently (e.g., [1]) - - // Firefox does returns the union of all layout elements of some - // sort. Chrome returns the first fragment for a block element (the - // same as ours) or the union of all associated fragments in the - // first containing block fragment for an inline element. We could - // implement Chrome's behavior, but our fragment tree currently - // provides insufficient information. - // - // [1]: https://github.com/w3c/csswg-drafts/issues/4541 - let fragment_relative_rect = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => fragment.borrow().border_rect(), - Fragment::Text(fragment) => fragment.borrow().rect, - Fragment::Positioning(fragment) => fragment.borrow().rect, - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Image(_) | - Fragment::IFrame(_) => unreachable!(), - }; - - let mut border_box = fragment_relative_rect.translate(containing_block.origin.to_vector()).to_untyped(); - - // "If any of the following holds true return null and terminate - // this algorithm: [...] The element’s computed value of the - // `position` property is `fixed`." - let is_fixed = matches!( - fragment, Fragment::Box(fragment) if fragment.borrow().style.get_box().position == Position::Fixed - ); - - if is_body_element { - // "If the element is the HTML body element or [...] return zero - // and terminate this algorithm." - border_box.origin = Point2D::zero(); - } - - let offset_parent_node = if is_fixed { - None - } else { - // Find the nearest ancestor element eligible as `offsetParent`. - parent_node_addresses[..level] - .iter() - .rev() - .cloned() - .find_map(std::convert::identity) - }; - - Some(NodeOffsetBoxInfo { - border_box, - offset_parent_node_address: offset_parent_node.map(|node| node.0), - is_static_body_element: offset_parent_node.is_some_and(|node| node.1), - }) - } else { - // Record the paths of the nodes being traversed. - let parent_node_address = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => { - let fragment = &*fragment.borrow(); - let is_eligible_parent = is_eligible_parent(fragment); - let is_static_body_element = is_body_element && - fragment.style.get_box().position == Position::Static; - match base.tag { - Some(tag) if is_eligible_parent && !tag.is_pseudo() => { - Some((tag.node, is_static_body_element)) - }, - _ => None, - } - }, - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::IFrame(_) | - Fragment::Image(_) | - Fragment::Positioning(_) | - Fragment::Text(_) => None, - }; - - while parent_node_addresses.len() <= level { - parent_node_addresses.push(None); - } - parent_node_addresses[level] = parent_node_address; - None - } - }); - - // Bail out if the element doesn't have an associated fragment. - // "If any of the following holds true return null and terminate this - // algorithm: [...] The element does not have an associated CSS layout box." - // (`offsetParent`) "If the element is the HTML body element [...] return - // zero and terminate this algorithm." (others) - let node_offset_box = node_offset_box?; - - let offset_parent_padding_box_corner = if let Some(offset_parent_node_address) = - node_offset_box.offset_parent_node_address - { - // The spec (https://www.w3.org/TR/cssom-view-1/#extensions-to-the-htmlelement-interface) - // says that offsetTop/offsetLeft are always relative to the padding box of the offsetParent. - // However, in practice this is not true in major browsers in the case that the offsetParent is the body - // element and the body element is position:static. In that case offsetLeft/offsetTop are computed - // relative to the root node's border box. - if node_offset_box.is_static_body_element { - fn extract_box_fragment( - fragment: &Fragment, - containing_block: &PhysicalRect, - ) -> PhysicalVec { - let (Fragment::Box(fragment) | Fragment::Float(fragment)) = fragment else { - unreachable!(); - }; - // Again, take the *first* associated CSS layout box. - fragment.borrow().border_rect().origin.to_vector() + - containing_block.origin.to_vector() - } - - let containing_block = &fragment_tree.initial_containing_block; - let fragment = &fragment_tree.root_fragments[0]; - if let Fragment::AbsoluteOrFixedPositioned(shared_fragment) = fragment { - let shared_fragment = &*shared_fragment.borrow(); - let fragment = shared_fragment.fragment.as_ref().unwrap(); - extract_box_fragment(fragment, containing_block) - } else { - extract_box_fragment(fragment, containing_block) - } - } else { - // Find the top and left padding edges of "the first CSS layout box - // associated with the `offsetParent` of the element". - // - // Since we saw `offset_parent_node_address` once, we should be able - // to find it again. - let offset_parent_node_tag = Tag::new(offset_parent_node_address); - fragment_tree - .find(|fragment, _, containing_block| { - match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => { - let fragment = fragment.borrow(); - if fragment.base.tag == Some(offset_parent_node_tag) { - // Again, take the *first* associated CSS layout box. - let padding_box_corner = fragment.padding_rect().origin.to_vector() - + containing_block.origin.to_vector(); - Some(padding_box_corner) - } else { - None - } - }, - Fragment::AbsoluteOrFixedPositioned(_) - | Fragment::Text(_) - | Fragment::Image(_) - | Fragment::IFrame(_) - | Fragment::Positioning(_) => None, - } - }) - .unwrap() - } - } else { - // "If the offsetParent of the element is null," subtract zero in the - // following step. - Vector2D::zero() + // 2. If the offsetParent of the element is null return the x-coordinate of the left + // border edge of the first CSS layout box associated with the element, relative to + // the initial containing block origin, ignoring any transforms that apply to the + // element and its ancestors, and terminate this algorithm. + let Some(offset_parent_fragment) = offset_parent_fragments(node) else { + return Some(OffsetParentResponse { + node_address: None, + rect: border_box.to_untyped(), + }); }; - Some(OffsetParentResponse { - node_address: node_offset_box.offset_parent_node_address.map(Into::into), - // "Return the result of subtracting the x-coordinate of the left - // padding edge of the first CSS layout box associated with the - // `offsetParent` of the element from the x-coordinate of the left - // border edge of the first CSS layout box associated with the element, - // relative to the initial containing block origin, ignoring any - // transforms that apply to the element and its ancestors." (and vice - // versa for the top border edge) - rect: node_offset_box - .border_box - .translate(-offset_parent_padding_box_corner.to_untyped()), - }) -} - -/// Returns whether or not the element with the given style and body element determination -/// is eligible to be a parent element for offset* queries. -/// -/// From : -/// -/// > Return the nearest ancestor element of the element for which at least one of the following is -/// > true and terminate this algorithm if such an ancestor is found: -/// > 1. The computed value of the position property is not static. -/// > 2. It is the HTML body element. -/// > 3. The computed value of the position property of the element is static and the ancestor is -/// > one of the following HTML elements: td, th, or table. -fn is_eligible_parent(fragment: &BoxFragment) -> bool { - fragment + let parent_fragment = offset_parent_fragment.parent.borrow(); + let parent_is_static_body_element = parent_fragment .base .flags - .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT) || - fragment.style.get_box().position != Position::Static || - fragment - .base - .flags - .contains(FragmentFlags::IS_TABLE_TH_OR_TD_ELEMENT) + .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT) && + parent_fragment.style.get_box().position == Position::Static; + + // For `offsetLeft`: + // 3. Return the result of subtracting the y-coordinate of the top padding edge of the + // first CSS layout box associated with the offsetParent of the element from the + // y-coordinate of the top border edge of the first CSS layout box associated with the + // element, relative to the initial containing block origin, ignoring any transforms + // that apply to the element and its ancestors. + // + // We generalize this for `offsetRight` as described in the specification. + let grandparent_box_fragment = || match offset_parent_fragment.grandparent { + Some(Fragment::Box(box_fragment)) | Some(Fragment::Float(box_fragment)) => { + Some(box_fragment) + }, + _ => None, + }; + + // The spec (https://www.w3.org/TR/cssom-view-1/#extensions-to-the-htmlelement-interface) + // says that offsetTop/offsetLeft are always relative to the padding box of the offsetParent. + // However, in practice this is not true in major browsers in the case that the offsetParent is the body + // element and the body element is position:static. In that case offsetLeft/offsetTop are computed + // relative to the root node's border box. + // + // See . + let parent_offset_rect = if parent_is_static_body_element { + if let Some(grandparent_fragment) = grandparent_box_fragment() { + let grandparent_fragment = grandparent_fragment.borrow(); + grandparent_fragment.offset_by_containing_block(&grandparent_fragment.border_rect()) + } else { + parent_fragment.offset_by_containing_block(&parent_fragment.padding_rect()) + } + } else { + parent_fragment.offset_by_containing_block(&parent_fragment.padding_rect()) + }; + + border_box = border_box.translate(-parent_offset_rect.origin.to_vector()); + + Some(OffsetParentResponse { + node_address: parent_fragment.base.tag.map(|tag| tag.node.into()), + rect: border_box.to_untyped(), + }) } /// diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs index 57b48ae0bca..2261f7d165c 100644 --- a/components/layout/table/layout.rs +++ b/components/layout/table/layout.rs @@ -2142,23 +2142,27 @@ impl<'a> TableLayout<'a> { for column_group in self.table.column_groups.iter() { let column_group = column_group.borrow(); if !column_group.is_empty() { - fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + let fragment = Fragment::Positioning(PositioningFragment::new_empty( column_group.base.base_fragment_info, dimensions .get_column_group_rect(&column_group) .as_physical(None), column_group.base.style.clone(), - ))); + )); + column_group.base.set_fragment(fragment.clone()); + fragments.push(fragment); } } for (column_index, column) in self.table.columns.iter().enumerate() { let column = column.borrow(); - fragments.push(Fragment::Positioning(PositioningFragment::new_empty( + let fragment = Fragment::Positioning(PositioningFragment::new_empty( column.base.base_fragment_info, dimensions.get_column_rect(column_index).as_physical(None), column.base.style.clone(), - ))); + )); + column.base.set_fragment(fragment.clone()); + fragments.push(fragment); } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 418c737acd4..932a9ec7f2d 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -2392,7 +2392,10 @@ impl Window { return (None, Rect::zero()); } - let response = self.layout.borrow().query_offset_parent(node.to_opaque()); + let response = self + .layout + .borrow() + .query_offset_parent(node.to_trusted_node_address()); let element = response.node_address.and_then(|parent_node_address| { let node = unsafe { from_untrusted_node_address(parent_node_address) }; DomRoot::downcast(node) diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 499d99753fe..a40b8c403c1 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -252,7 +252,7 @@ pub trait Layout { point: Point2D, query_type: NodesFromPointQueryType, ) -> Vec; - fn query_offset_parent(&self, node: OpaqueNode) -> OffsetParentResponse; + fn query_offset_parent(&self, node: TrustedNodeAddress) -> OffsetParentResponse; fn query_resolved_style( &self, node: TrustedNodeAddress, diff --git a/tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini b/tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini deleted file mode 100644 index aa1109f1600..00000000000 --- a/tests/wpt/meta/css/cssom-view/offsetParent-block-in-inline.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[offsetParent-block-in-inline.html] - [offsetParent-block-in-inline] - expected: FAIL diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini new file mode 100644 index 00000000000..4a08b41acc2 --- /dev/null +++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-clipping-2.html.ini @@ -0,0 +1,2 @@ +[backdrop-filter-edge-clipping-2.html] + expected: FAIL diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini new file mode 100644 index 00000000000..51318cbb1d2 --- /dev/null +++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-mirror.html.ini @@ -0,0 +1,2 @@ +[backdrop-filter-edge-mirror.html] + expected: FAIL diff --git a/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini new file mode 100644 index 00000000000..19d53f587ad --- /dev/null +++ b/tests/wpt/meta/css/filter-effects/backdrop-filter-edge-pixels-2.html.ini @@ -0,0 +1,2 @@ +[backdrop-filter-edge-pixels-2.html] + expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini b/tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini deleted file mode 100644 index afd8ee2cf06..00000000000 --- a/tests/wpt/meta/shadow-dom/offsetParent-across-shadow-boundaries.html.ini +++ /dev/null @@ -1,24 +0,0 @@ -[offsetParent-across-shadow-boundaries.html] - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode] - expected: FAIL - - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode] - expected: FAIL - - [offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of open mode] - expected: FAIL - - [offsetParent must skip multiple offset parents of an element when the context object is assigned to a slot in a shadow tree of closed mode] - expected: FAIL - - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of open mode] - expected: FAIL - - [offsetParent must skip offset parents of an element when the context object is assigned to a slot in nested shadow trees of closed mode] - expected: FAIL - - [offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of open mode did not have any offset parent] - expected: FAIL - - [offsetParent must find the first offset parent which is a shadow-including ancestor of the context object even some shadow tree of closed mode did not have any offset parent] - expected: FAIL diff --git a/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini b/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini index 88f45f3b6a7..5ebeb149de3 100644 --- a/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini +++ b/tests/wpt/meta/shadow-dom/offsetTop-offsetLeft-across-shadow-boundaries.html.ini @@ -4,6 +4,3 @@ [Verifies that HTMLElement.offsetTop accounts for shadow boundaries when nested in multiple shadow roots.] expected: FAIL - - [Verifies that HTMLElement.offsetLeft accounts for shadow boundaries.] - expected: FAIL