diff options
Diffstat (limited to 'components/layout_2020/flow/root.rs')
-rw-r--r-- | components/layout_2020/flow/root.rs | 192 |
1 files changed, 137 insertions, 55 deletions
diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index dd647070d7a..454177c789e 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -10,7 +10,7 @@ use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::Fragment; use crate::geom::flow_relative::Vec2; -use crate::geom::PhysicalRect; +use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::positioned::AbsolutelyPositionedBox; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContent; @@ -22,6 +22,7 @@ use euclid::default::{Point2D, Rect, Size2D}; use gfx_traits::print_tree::PrintTree; use script_layout_interface::wrapper_traits::LayoutNode; use servo_arc::Arc; +use style::dom::OpaqueNode; use style::properties::ComputedValues; use style::values::computed::Length; use style_traits::CSSPixel; @@ -35,8 +36,8 @@ pub struct FragmentTreeRoot { /// The scrollable overflow of the root of the fragment tree. scrollable_overflow: PhysicalRect<Length>, - /// The axis-aligned bounding box of the border box of all child fragments - bounding_box_of_border_boxes: PhysicalRect<Length>, + /// The containing block used in the layout of this fragment tree. + initial_containing_block: PhysicalRect<Length>, } impl BoxTreeRoot { @@ -117,13 +118,18 @@ impl BoxTreeRoot { viewport: euclid::Size2D<f32, CSSPixel>, ) -> FragmentTreeRoot { let style = ComputedValues::initial_values(); + + // FIXME: use the document’s mode: + // https://drafts.csswg.org/css-writing-modes/#principal-flow + let physical_containing_block = PhysicalRect::new( + PhysicalPoint::zero(), + PhysicalSize::new(Length::new(viewport.width), Length::new(viewport.height)), + ); let initial_containing_block = DefiniteContainingBlock { size: Vec2 { - inline: Length::new(viewport.width), - block: Length::new(viewport.height), + inline: physical_containing_block.size.width, + block: physical_containing_block.size.height, }, - // FIXME: use the document’s mode: - // https://drafts.csswg.org/css-writing-modes/#principal-flow style, }; @@ -142,8 +148,6 @@ impl BoxTreeRoot { &mut independent_layout.fragments, ); - // FIXME(mrobinson, bug 25564): We should be using the containing block - // here to properly convert scrollable overflow to physical geometry. let scrollable_overflow = independent_layout .fragments @@ -167,51 +171,18 @@ impl BoxTreeRoot { acc.union(&child_overflow) }); - let containing_block = PhysicalRect::zero(); - let bounding_box_of_border_boxes = - independent_layout - .fragments - .iter() - .fold(PhysicalRect::zero(), |acc, child| { - acc.union(&match child { - Fragment::Box(fragment) => fragment - .border_rect() - .to_physical(fragment.style.writing_mode, &containing_block), - Fragment::Anonymous(fragment) => { - fragment.rect.to_physical(fragment.mode, &containing_block) - }, - Fragment::Text(fragment) => fragment - .rect - .to_physical(fragment.parent_style.writing_mode, &containing_block), - Fragment::Image(fragment) => fragment - .rect - .to_physical(fragment.style.writing_mode, &containing_block), - }) - }); - FragmentTreeRoot { children: independent_layout.fragments, scrollable_overflow, - bounding_box_of_border_boxes, + initial_containing_block: physical_containing_block, } } } impl FragmentTreeRoot { - pub fn build_display_list( - &self, - builder: &mut crate::display_list::DisplayListBuilder, - viewport_size: webrender_api::units::LayoutSize, - ) { - let containing_block = PhysicalRect::new( - euclid::Point2D::zero(), - euclid::Size2D::new( - Length::new(viewport_size.width), - Length::new(viewport_size.height), - ), - ); + pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) { for fragment in &self.children { - fragment.build_display_list(builder, &containing_block) + fragment.build_display_list(builder, &self.initial_containing_block) } } @@ -229,15 +200,126 @@ impl FragmentTreeRoot { )) } - pub fn bounding_box_of_border_boxes(&self) -> Rect<Au> { - let origin = Point2D::new( - Au::from_f32_px(self.bounding_box_of_border_boxes.origin.x.px()), - Au::from_f32_px(self.bounding_box_of_border_boxes.origin.y.px()), - ); - let size = Size2D::new( - Au::from_f32_px(self.bounding_box_of_border_boxes.size.width.px()), - Au::from_f32_px(self.bounding_box_of_border_boxes.size.height.px()), - ); - Rect::new(origin, size) + fn find<T>( + &self, + mut process_func: impl FnMut(&Fragment, &PhysicalRect<Length>) -> Option<T>, + ) -> Option<T> { + fn recur<T>( + fragments: &[Fragment], + containing_block: &PhysicalRect<Length>, + process_func: &mut impl FnMut(&Fragment, &PhysicalRect<Length>) -> Option<T>, + ) -> Option<T> { + for fragment in fragments { + if let Some(result) = process_func(fragment, containing_block) { + return Some(result); + } + + match fragment { + Fragment::Box(fragment) => { + let new_containing_block = fragment + .content_rect + .to_physical(fragment.style.writing_mode, containing_block) + .translate(containing_block.origin.to_vector()); + if let Some(result) = + recur(&fragment.children, &new_containing_block, process_func) + { + return Some(result); + } + }, + Fragment::Anonymous(fragment) => { + let new_containing_block = fragment + .rect + .to_physical(fragment.mode, containing_block) + .translate(containing_block.origin.to_vector()); + if let Some(result) = + recur(&fragment.children, &new_containing_block, process_func) + { + return Some(result); + } + }, + _ => {}, + } + } + None + } + recur( + &self.children, + &self.initial_containing_block, + &mut process_func, + ) + } + + pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect<Au> { + let mut bounding_box = PhysicalRect::zero(); + self.find(|fragment, containing_block| { + let fragment_relative_rect = match fragment { + Fragment::Box(fragment) if fragment.tag == requested_node => fragment + .border_rect() + .to_physical(fragment.style.writing_mode, &containing_block), + Fragment::Text(fragment) if fragment.tag == requested_node => fragment + .rect + .to_physical(fragment.parent_style.writing_mode, &containing_block), + Fragment::Box(_) | + Fragment::Text(_) | + Fragment::Image(_) | + Fragment::Anonymous(_) => return None, + }; + + bounding_box = fragment_relative_rect + .translate(containing_block.origin.to_vector()) + .union(&bounding_box); + None::<()> + }); + + Rect::new( + Point2D::new( + Au::from_f32_px(bounding_box.origin.x.px()), + Au::from_f32_px(bounding_box.origin.y.px()), + ), + Size2D::new( + Au::from_f32_px(bounding_box.size.width.px()), + Au::from_f32_px(bounding_box.size.height.px()), + ), + ) + } + + pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect<i32> { + self.find(|fragment, containing_block| { + let (style, padding_rect) = match fragment { + Fragment::Box(fragment) if fragment.tag == requested_node => { + (&fragment.style, fragment.padding_rect()) + }, + Fragment::Box(_) | + Fragment::Text(_) | + Fragment::Image(_) | + Fragment::Anonymous(_) => return None, + }; + + // 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 display = &style.get_box().display; + if display.inside() == style::values::specified::box_::DisplayInside::Flow && + display.outside() == style::values::specified::box_::DisplayOutside::Inline + { + return Some(Rect::zero()); + } + + let padding_rect = padding_rect.to_physical(style.writing_mode, &containing_block); + let border = style.get_border(); + Some(Rect::new( + Point2D::new( + border.border_left_width.px() as i32, + border.border_top_width.px() as i32, + ), + Size2D::new( + padding_rect.size.width.px() as i32, + padding_rect.size.height.px() as i32, + ), + )) + }) + .unwrap_or_else(Rect::zero) } } |