From b63a1818c437cc748f0c0a0829ffe26339af706d Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Fri, 25 Apr 2025 15:38:05 +0200 Subject: [PATCH] layout: Implement node geometry queries against `BoxTree`'s `Fragment` (#36663) This is a followup to #36629, continuing to implement script-based layout queries using the `Fragment`s attached to the `BoxTree`. In this change, geometry queris (apart from parent offset) are calculated using `Fragment`s hanging of the `BoxTree`. In order to make this work, all `Fragment`s for inlines split by blocks, need to be accessible in the `BoxTree`. This required some changes to the way that box tree items were stored in DOM `BoxSlot`s. Now every inline level item can have more than a single `BoxTree` item. These are carefully collected by the `InlineFormattingContextBuilder` -- currently a bit fragile, but with more documentation. Testing: There are tests for these changes. Signed-off-by: Martin Robinson Co-authored-by: Oriol Brufau --- components/layout/dom.rs | 13 ++- components/layout/flow/construct.rs | 21 +++-- components/layout/flow/inline/construct.rs | 64 +++++++++++--- components/layout/flow/inline/inline_box.rs | 16 ++-- components/layout/flow/inline/mod.rs | 8 +- components/layout/flow/root.rs | 21 ++--- components/layout/fragment_tree/fragment.rs | 53 +++++++++++- .../layout/fragment_tree/fragment_tree.rs | 83 +------------------ components/layout/layout_impl.rs | 19 +++-- components/layout/query.rs | 48 +++++------ components/script/dom/window.rs | 12 ++- components/shared/script_layout/lib.rs | 6 +- .../getBoundingClientRect-shy.html.ini | 3 - 13 files changed, 197 insertions(+), 170 deletions(-) diff --git a/components/layout/dom.rs b/components/layout/dom.rs index a81a6eec545..add4b3ac2d5 100644 --- a/components/layout/dom.rs +++ b/components/layout/dom.rs @@ -61,7 +61,7 @@ impl InnerDOMLayoutData { pub(super) enum LayoutBox { DisplayContents, BlockLevel(ArcRefCell), - InlineLevel(ArcRefCell), + InlineLevel(Vec>), FlexLevel(ArcRefCell), TableLevelBox(TableLevelBox), TaffyItemBox(ArcRefCell), @@ -74,8 +74,10 @@ impl LayoutBox { LayoutBox::BlockLevel(block_level_box) => { block_level_box.borrow().invalidate_cached_fragment() }, - LayoutBox::InlineLevel(inline_item) => { - inline_item.borrow().invalidate_cached_fragment() + LayoutBox::InlineLevel(inline_items) => { + for inline_item in inline_items.iter() { + inline_item.borrow().invalidate_cached_fragment() + } }, LayoutBox::FlexLevel(flex_level_box) => { flex_level_box.borrow().invalidate_cached_fragment() @@ -91,7 +93,10 @@ impl LayoutBox { match self { LayoutBox::DisplayContents => vec![], LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().fragments(), - LayoutBox::InlineLevel(inline_item) => inline_item.borrow().fragments(), + LayoutBox::InlineLevel(inline_items) => inline_items + .iter() + .flat_map(|inline_item| inline_item.borrow().fragments()) + .collect(), LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().fragments(), LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().fragments(), LayoutBox::TableLevelBox(table_box) => table_box.fragments(), diff --git a/components/layout/flow/construct.rs b/components/layout/flow/construct.rs index a6471756db8..5ed567f513b 100644 --- a/components/layout/flow/construct.rs +++ b/components/layout/flow/construct.rs @@ -458,15 +458,14 @@ where self.propagated_data.without_text_decorations(), ), ); - box_slot.set(LayoutBox::InlineLevel(atomic)); + box_slot.set(LayoutBox::InlineLevel(vec![atomic])); return; }; // Otherwise, this is just a normal inline box. Whatever happened before, all we need to do // before recurring is to remember this ongoing inline level box. - let inline_item = self - .inline_formatting_context_builder - .start_inline_box(InlineBox::new(info)); + self.inline_formatting_context_builder + .start_inline_box(InlineBox::new(info), None); if is_list_item { if let Some((marker_info, marker_contents)) = @@ -486,8 +485,14 @@ where self.finish_anonymous_table_if_needed(); - self.inline_formatting_context_builder.end_inline_box(); - box_slot.set(LayoutBox::InlineLevel(inline_item)); + // As we are ending this inline box, during the course of the `traverse()` above, the ongoing + // inline formatting context may have been split around block-level elements. In that case, + // more than a single inline box tree item may have been produced for this inline-level box. + // `InlineFormattingContextBuilder::end_inline_box()` is returning all of those box tree + // items. + box_slot.set(LayoutBox::InlineLevel( + self.inline_formatting_context_builder.end_inline_box(), + )); } fn handle_block_level_element( @@ -574,7 +579,7 @@ where display_inside, contents, )); - box_slot.set(LayoutBox::InlineLevel(inline_level_box)); + box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box])); return; } @@ -607,7 +612,7 @@ where contents, self.propagated_data, )); - box_slot.set(LayoutBox::InlineLevel(inline_level_box)); + box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box])); return; } diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs index 7c668751ef6..61292701a9f 100644 --- a/components/layout/flow/inline/construct.rs +++ b/components/layout/flow/inline/construct.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use std::char::{ToLowercase, ToUppercase}; use icu_segmenter::WordSegmenter; +use itertools::izip; use servo_arc::Arc; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; use style::values::specified::text::TextTransformCase; @@ -67,6 +68,16 @@ pub(crate) struct InlineFormattingContextBuilder { /// When an inline box ends, it's removed from this stack. inline_box_stack: Vec, + /// Normally, an inline box produces a single box tree [`InlineItem`]. When a block + /// element causes an inline box [to be split], it can produce multiple + /// [`InlineItem`]s, all inserted into different [`InlineFormattingContext`]s. + /// [`Self::block_in_inline_splits`] is responsible for tracking all of these split + /// inline box results, so that they can be inserted into the [`crate::dom::BoxSlot`] + /// for the DOM element once it has been processed for BoxTree construction. + /// + /// [to be split]: https://www.w3.org/TR/CSS2/visuren.html#anonymous-block-level + block_in_inline_splits: Vec>>, + /// Whether or not the inline formatting context under construction has any /// uncollapsible text content. pub has_uncollapsible_text_content: bool, @@ -162,29 +173,42 @@ impl InlineFormattingContextBuilder { inline_level_box } - pub(crate) fn start_inline_box(&mut self, inline_box: InlineBox) -> ArcRefCell { + pub(crate) fn start_inline_box( + &mut self, + inline_box: InlineBox, + block_in_inline_splits: Option>>, + ) { self.push_control_character_string(inline_box.base.style.bidi_control_chars().0); let (identifier, inline_box) = self.inline_boxes.start_inline_box(inline_box); let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box)); self.inline_items.push(inline_level_box.clone()); self.inline_box_stack.push(identifier); - inline_level_box + + let mut block_in_inline_splits = block_in_inline_splits.unwrap_or_default(); + block_in_inline_splits.push(inline_level_box); + self.block_in_inline_splits.push(block_in_inline_splits); } - pub(crate) fn end_inline_box(&mut self) -> ArcRefCell { - let identifier = self.end_inline_box_internal(); + /// End the ongoing inline box in this [`InlineFormattingContextBuilder`], returning + /// shared references to all of the box tree items that were created for it. More than + /// a single box tree items may be produced for a single inline box when that inline + /// box is split around a block-level element. + pub(crate) fn end_inline_box(&mut self) -> Vec> { + let (identifier, block_in_inline_splits) = self.end_inline_box_internal(); let inline_level_box = self.inline_boxes.get(&identifier); - inline_level_box.borrow_mut().is_last_fragment = true; + { + let mut inline_level_box = inline_level_box.borrow_mut(); + inline_level_box.is_last_split = true; + self.push_control_character_string(inline_level_box.base.style.bidi_control_chars().1); + } - self.push_control_character_string( - inline_level_box.borrow().base.style.bidi_control_chars().1, - ); - - inline_level_box + block_in_inline_splits.unwrap_or_default() } - fn end_inline_box_internal(&mut self) -> InlineBoxIdentifier { + fn end_inline_box_internal( + &mut self, + ) -> (InlineBoxIdentifier, Option>>) { let identifier = self .inline_box_stack .pop() @@ -193,7 +217,12 @@ impl InlineFormattingContextBuilder { .push(ArcRefCell::new(InlineItem::EndInlineBox)); self.inline_boxes.end_inline_box(identifier); - identifier + + // This might be `None` if this builder has already drained its block-in-inline-splits + // into the new builder on the other side of a new block-in-inline split. + let block_in_inline_splits = self.block_in_inline_splits.pop(); + + (identifier, block_in_inline_splits) } pub(crate) fn push_text<'dom, Node: NodeExt<'dom>>( @@ -295,12 +324,21 @@ impl InlineFormattingContextBuilder { // marked as not being the first fragment. No inline content is carried over to this new // builder. let mut new_builder = InlineFormattingContextBuilder::new(); - for identifier in self.inline_box_stack.iter() { + let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits); + for (identifier, historical_inline_boxes) in + izip!(self.inline_box_stack.iter(), block_in_inline_splits) + { + // Start a new inline box for every ongoing inline box in this + // InlineFormattingContext once we are done processing this block element, + // being sure to give the block-in-inline-split to the new + // InlineFormattingContext. These will finally be inserted into the DOM's + // BoxSlot once the inline box has been fully processed. new_builder.start_inline_box( self.inline_boxes .get(identifier) .borrow() .split_around_block(), + Some(historical_inline_boxes), ); } let mut inline_builder_from_before_split = std::mem::replace(self, new_builder); diff --git a/components/layout/flow/inline/inline_box.rs b/components/layout/flow/inline/inline_box.rs index 97398d6e708..de79f876340 100644 --- a/components/layout/flow/inline/inline_box.rs +++ b/components/layout/flow/inline/inline_box.rs @@ -23,8 +23,12 @@ pub(crate) struct InlineBox { pub base: LayoutBoxBase, /// The identifier of this inline box in the containing [`super::InlineFormattingContext`]. pub(super) identifier: InlineBoxIdentifier, - pub is_first_fragment: bool, - pub is_last_fragment: bool, + /// Whether or not this is the first instance of an [`InlineBox`] before a possible + /// block-in-inline split. When no split occurs, this is always true. + pub is_first_split: bool, + /// Whether or not this is the last instance of an [`InlineBox`] before a possible + /// block-in-inline split. When no split occurs, this is always true. + pub is_last_split: bool, /// The index of the default font in the [`super::InlineFormattingContext`]'s font metrics store. /// This is initialized during IFC shaping. pub default_font_index: Option, @@ -36,8 +40,8 @@ impl InlineBox { base: LayoutBoxBase::new(info.into(), info.style.clone()), // This will be assigned later, when the box is actually added to the IFC. identifier: InlineBoxIdentifier::default(), - is_first_fragment: true, - is_last_fragment: false, + is_first_split: true, + is_last_split: false, default_font_index: None, } } @@ -45,8 +49,8 @@ impl InlineBox { pub(crate) fn split_around_block(&self) -> Self { Self { base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()), - is_first_fragment: false, - is_last_fragment: false, + is_first_split: false, + is_last_split: false, ..*self } } diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index 490917d95a3..dabb9773410 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -744,7 +744,7 @@ impl InlineFormattingContextLayout<'_> { self.containing_block, self.layout_context, self.current_inline_container_state(), - inline_box.is_last_fragment, + inline_box.is_last_split, inline_box .default_font_index .map(|index| &self.ifc.font_metrics[index].metrics), @@ -773,7 +773,7 @@ impl InlineFormattingContextLayout<'_> { ); } - if inline_box.is_first_fragment { + if inline_box.is_first_split { self.current_line_segment.inline_size += inline_box_state.pbm.padding.inline_start + inline_box_state.pbm.border.inline_start + inline_box_state.pbm.margin.inline_start.auto_is(Au::zero); @@ -2349,10 +2349,10 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { .auto_is(Au::zero); let pbm = margin + padding + border; - if inline_box.is_first_fragment { + if inline_box.is_first_split { self.add_inline_size(pbm.inline_start); } - if inline_box.is_last_fragment { + if inline_box.is_last_split { self.ending_inline_pbm_stack.push(pbm.inline_end); } else { self.ending_inline_pbm_stack.push(Au::zero()); diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs index e8b7b6f5402..187726595f8 100644 --- a/components/layout/flow/root.rs +++ b/components/layout/flow/root.rs @@ -195,16 +195,17 @@ impl BoxTree { }, _ => return None, }, - LayoutBox::InlineLevel(inline_level_box) => match &*inline_level_box.borrow() { - InlineItem::OutOfFlowAbsolutelyPositionedBox(_, text_offset_index) - if box_style.position.is_absolutely_positioned() => - { - UpdatePoint::AbsolutelyPositionedInlineLevelBox( - inline_level_box.clone(), - *text_offset_index, - ) - }, - _ => return None, + LayoutBox::InlineLevel(inline_level_items) => { + let inline_level_box = inline_level_items.first()?; + let InlineItem::OutOfFlowAbsolutelyPositionedBox(_, text_offset_index) = + &*inline_level_box.borrow() + else { + return None; + }; + UpdatePoint::AbsolutelyPositionedInlineLevelBox( + inline_level_box.clone(), + *text_offset_index, + ) }, LayoutBox::FlexLevel(flex_level_box) => match &*flex_level_box.borrow() { FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs index c08ddae55e9..ceccd1ec304 100644 --- a/components/layout/fragment_tree/fragment.rs +++ b/components/layout/fragment_tree/fragment.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use app_units::Au; use base::id::PipelineId; use base::print_tree::PrintTree; +use euclid::{Point2D, Rect, Size2D, UnknownUnit}; use fonts::{ByteIndex, FontMetrics, GlyphStore}; use malloc_size_of_derive::MallocSizeOf; use range::Range as ServoRange; @@ -21,7 +22,7 @@ use super::{ Tag, }; use crate::cell::ArcRefCell; -use crate::geom::{LogicalSides, PhysicalRect}; +use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect}; use crate::style_ext::ComputedValuesExt; #[derive(Clone, MallocSizeOf)] @@ -190,6 +191,56 @@ impl Fragment { } } + pub(crate) fn cumulative_content_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::Text(_) | + Fragment::AbsoluteOrFixedPositioned(_) | + Fragment::Image(_) | + Fragment::IFrame(_) => None, + } + } + + pub(crate) fn client_rect(&self) -> Rect { + let rect = match self { + Fragment::Box(fragment) | Fragment::Float(fragment) => { + // https://drafts.csswg.org/cssom-view/#dom-element-clienttop + // " If the element has no associated CSS layout box or if the + // CSS layout box is inline, return zero." For this check we + // also explicitly ignore the list item portion of the display + // style. + let fragment = fragment.borrow(); + if fragment.is_inline_box() { + return Rect::zero(); + } + + if fragment.is_table_wrapper() { + // For tables the border actually belongs to the table grid box, + // so we need to include it in the dimension of the table wrapper box. + let mut rect = fragment.border_rect(); + rect.origin = PhysicalPoint::zero(); + rect + } else { + let mut rect = fragment.padding_rect(); + rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top); + rect + } + }, + _ => return Rect::zero(), + } + .to_untyped(); + + let rect = Rect::new( + Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()), + Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()), + ); + rect.round().to_i32() + } + pub(crate) fn find( &self, manager: &ContainingBlockManager>, diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs index 589ae69e8e5..3a082c99389 100644 --- a/components/layout/fragment_tree/fragment_tree.rs +++ b/components/layout/fragment_tree/fragment_tree.rs @@ -5,18 +5,17 @@ use app_units::Au; use base::print_tree::PrintTree; use compositing_traits::display_list::AxesScrollSensitivity; -use euclid::default::{Point2D, Rect, Size2D}; +use euclid::default::Size2D; use fxhash::FxHashSet; use malloc_size_of_derive::MallocSizeOf; use style::animation::AnimationSetKey; -use style::dom::OpaqueNode; use webrender_api::units; -use super::{ContainingBlockManager, Fragment, Tag}; +use super::{ContainingBlockManager, Fragment}; use crate::context::LayoutContext; use crate::display_list::StackingContext; use crate::flow::CanvasBackground; -use crate::geom::{PhysicalPoint, PhysicalRect}; +use crate::geom::PhysicalRect; #[derive(MallocSizeOf)] pub struct FragmentTree { @@ -139,82 +138,6 @@ impl FragmentTree { .find_map(|child| child.find(&info, 0, &mut process_func)) } - /// Get the vector of rectangles that surrounds the fragments of the node with the given address. - /// This function answers the `getClientRects()` query and the union of the rectangles answers - /// the `getBoundingClientRect()` query. - /// - /// TODO: This function is supposed to handle scroll offsets, but that isn't happening at all. - pub fn get_content_boxes_for_node(&self, requested_node: OpaqueNode) -> Vec> { - let mut content_boxes = Vec::new(); - let tag_to_find = Tag::new(requested_node); - self.find(|fragment, _, containing_block| { - if fragment.tag() != Some(tag_to_find) { - return None::<()>; - } - - let fragment_relative_rect = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => { - fragment.borrow().border_rect() - }, - Fragment::Positioning(fragment) => fragment.borrow().rect, - Fragment::Text(fragment) => fragment.borrow().rect, - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Image(_) | - Fragment::IFrame(_) => return None, - }; - - let rect = fragment_relative_rect.translate(containing_block.origin.to_vector()); - - content_boxes.push(rect.to_untyped()); - None::<()> - }); - content_boxes - } - - pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect { - let tag_to_find = Tag::new(requested_node); - self.find(|fragment, _, _containing_block| { - if fragment.tag() != Some(tag_to_find) { - return None; - } - - let rect = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => { - // https://drafts.csswg.org/cssom-view/#dom-element-clienttop - // " If the element has no associated CSS layout box or if the - // CSS layout box is inline, return zero." For this check we - // also explicitly ignore the list item portion of the display - // style. - let fragment = fragment.borrow(); - if fragment.is_inline_box() { - return Some(Rect::zero()); - } - if fragment.is_table_wrapper() { - // For tables the border actually belongs to the table grid box, - // so we need to include it in the dimension of the table wrapper box. - let mut rect = fragment.border_rect(); - rect.origin = PhysicalPoint::zero(); - rect - } else { - let mut rect = fragment.padding_rect(); - rect.origin = PhysicalPoint::new(fragment.border.left, fragment.border.top); - rect - } - }, - Fragment::Positioning(fragment) => fragment.borrow().rect.cast_unit(), - Fragment::Text(text_fragment) => text_fragment.borrow().rect, - _ => return None, - }; - - let rect = Rect::new( - Point2D::new(rect.origin.x.to_f32_px(), rect.origin.y.to_f32_px()), - Size2D::new(rect.size.width.to_f32_px(), rect.size.height.to_f32_px()), - ); - Some(rect.round().to_i32().to_untyped()) - }) - .unwrap_or_else(Rect::zero) - } - pub fn get_scrolling_area_for_viewport(&self) -> PhysicalRect { let mut scroll_area = self.initial_containing_block; for fragment in self.root_fragments.iter() { diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index 361760692d2..941fa641cc9 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -81,8 +81,8 @@ use webrender_api::{ExternalScrollId, HitTestFlags}; use crate::context::LayoutContext; use crate::display_list::{DisplayList, WebRenderImageInfo}; use crate::query::{ - get_the_text_steps, process_content_box_request, process_content_boxes_request, - process_node_geometry_request, process_node_scroll_area_request, process_offset_parent_query, + get_the_text_steps, process_client_rect_request, process_content_box_request, + process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query, process_resolved_font_style_query, process_resolved_style_request, process_text_index_request, }; use crate::traversal::RecalcStyle; @@ -235,24 +235,27 @@ impl Layout for LayoutThread { feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn query_content_box(&self, node: OpaqueNode) -> Option> { - process_content_box_request(node, self.fragment_tree.borrow().clone()) + fn query_content_box(&self, node: TrustedNodeAddress) -> Option> { + let node = unsafe { ServoLayoutNode::new(&node) }; + process_content_box_request(node) } #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn query_content_boxes(&self, node: OpaqueNode) -> Vec> { - process_content_boxes_request(node, self.fragment_tree.borrow().clone()) + fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec> { + let node = unsafe { ServoLayoutNode::new(&node) }; + process_content_boxes_request(node) } #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn query_client_rect(&self, node: OpaqueNode) -> UntypedRect { - process_node_geometry_request(node, self.fragment_tree.borrow().clone()) + fn query_client_rect(&self, node: TrustedNodeAddress) -> UntypedRect { + let node = unsafe { ServoLayoutNode::new(&node) }; + process_client_rect_request(node) } #[cfg_attr( diff --git a/components/layout/query.rs b/components/layout/query.rs index 3409f7c1923..3badff83672 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -46,42 +46,36 @@ use crate::fragment_tree::{ use crate::geom::{PhysicalRect, PhysicalVec}; use crate::taffy::SpecificTaffyGridInfo; -pub fn process_content_box_request( - requested_node: OpaqueNode, - fragment_tree: Option>, -) -> Option> { - let rects = fragment_tree?.get_content_boxes_for_node(requested_node); +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) + .collect(); if rects.is_empty() { return None; } - Some( - rects - .iter() - .fold(Rect::zero(), |unioned_rect, rect| rect.union(&unioned_rect)), - ) + Some(rects.iter().fold(Rect::zero(), |unioned_rect, rect| { + rect.to_untyped().union(&unioned_rect) + })) } -pub fn process_content_boxes_request( - requested_node: OpaqueNode, - fragment_tree: Option>, -) -> Vec> { - fragment_tree - .map(|tree| tree.get_content_boxes_for_node(requested_node)) +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) + .map(|rect| rect.to_untyped()) + .collect() +} + +pub fn process_client_rect_request<'dom>(node: impl LayoutNode<'dom> + 'dom) -> Rect { + node.fragments_for_pseudo(None) + .first() + .map(Fragment::client_rect) .unwrap_or_default() } -pub fn process_node_geometry_request( - requested_node: OpaqueNode, - fragment_tree: Option>, -) -> Rect { - if let Some(fragment_tree) = fragment_tree { - fragment_tree.get_border_dimensions_for_node(requested_node) - } else { - Rect::zero() - } -} - /// pub fn process_node_scroll_area_request<'dom>( requested_node: Option + 'dom>, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 9053f7f7e86..418c737acd4 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -2261,7 +2261,9 @@ impl Window { // Query content box without considering any reflow pub(crate) fn content_box_query_unchecked(&self, node: &Node) -> Option> { - self.layout.borrow().query_content_box(node.to_opaque()) + self.layout + .borrow() + .query_content_box(node.to_trusted_node_address()) } pub(crate) fn content_box_query(&self, node: &Node, can_gc: CanGc) -> Option> { @@ -2275,14 +2277,18 @@ impl Window { if !self.layout_reflow(QueryMsg::ContentBoxes, can_gc) { return vec![]; } - self.layout.borrow().query_content_boxes(node.to_opaque()) + self.layout + .borrow() + .query_content_boxes(node.to_trusted_node_address()) } pub(crate) fn client_rect_query(&self, node: &Node, can_gc: CanGc) -> UntypedRect { if !self.layout_reflow(QueryMsg::ClientRectQuery, can_gc) { return Rect::zero(); } - self.layout.borrow().query_client_rect(node.to_opaque()) + self.layout + .borrow() + .query_client_rect(node.to_trusted_node_address()) } /// Find the scroll area of the given node, if it is not None. If the node diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 233d02dfd47..499d99753fe 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -243,9 +243,9 @@ pub trait Layout { /// Set the scroll states of this layout after a compositor scroll. fn set_scroll_offsets(&mut self, scroll_states: &[ScrollState]); - fn query_content_box(&self, node: OpaqueNode) -> Option>; - fn query_content_boxes(&self, node: OpaqueNode) -> Vec>; - fn query_client_rect(&self, node: OpaqueNode) -> Rect; + fn query_content_box(&self, node: TrustedNodeAddress) -> Option>; + fn query_content_boxes(&self, node: TrustedNodeAddress) -> Vec>; + fn query_client_rect(&self, node: TrustedNodeAddress) -> Rect; fn query_element_inner_outer_text(&self, node: TrustedNodeAddress) -> String; fn query_nodes_from_point( &self, diff --git a/tests/wpt/meta/css/cssom-view/getBoundingClientRect-shy.html.ini b/tests/wpt/meta/css/cssom-view/getBoundingClientRect-shy.html.ini index 536f7aaacff..b8b7564f112 100644 --- a/tests/wpt/meta/css/cssom-view/getBoundingClientRect-shy.html.ini +++ b/tests/wpt/meta/css/cssom-view/getBoundingClientRect-shy.html.ini @@ -29,8 +29,5 @@ [Rendered soft-hyphen should have a width.] expected: FAIL - [Collapsed soft-hyphen in a span should be 0 width.] - expected: FAIL - [Rendered soft-hyphen in a span should have a width.] expected: FAIL