diff options
author | Martin Robinson <mrobinson@igalia.com> | 2025-04-18 16:19:24 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2025-04-22 16:37:03 +0200 |
commit | 57428bc5da0b73f6fc81510a1aa7816e720baf14 (patch) | |
tree | b1587a3e79a2984bea0e3a9f80ebc87830472c51 /components/layout/fragment_tree | |
parent | 73703e75ba17ca2b4f7999c62c1585360bda5ed6 (diff) | |
download | servo-57428bc5da0b73f6fc81510a1aa7816e720baf14.tar.gz servo-57428bc5da0b73f6fc81510a1aa7816e720baf14.zip |
layout: Implement node geometry queries against `BoxTree`'s `Fragment`
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.
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Diffstat (limited to 'components/layout/fragment_tree')
-rw-r--r-- | components/layout/fragment_tree/fragment.rs | 53 | ||||
-rw-r--r-- | components/layout/fragment_tree/fragment_tree.rs | 83 |
2 files changed, 55 insertions, 81 deletions
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<PhysicalRect<Au>> { + 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<i32, UnknownUnit> { + 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<T>( &self, manager: &ContainingBlockManager<PhysicalRect<Au>>, 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<Rect<Au>> { - 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<i32> { - 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<Au> { let mut scroll_area = self.initial_containing_block; for fragment in self.root_fragments.iter() { |