diff options
-rw-r--r-- | components/layout_2020/dom_traversal.rs | 5 | ||||
-rw-r--r-- | components/layout_2020/flow/inline.rs | 65 | ||||
-rw-r--r-- | components/layout_2020/flow/mod.rs | 19 | ||||
-rw-r--r-- | components/layout_2020/flow/root.rs | 91 | ||||
-rw-r--r-- | components/layout_2020/fragments.rs | 68 | ||||
-rw-r--r-- | components/layout_2020/geom.rs | 36 | ||||
-rw-r--r-- | components/layout_2020/positioned.rs | 20 | ||||
-rw-r--r-- | components/layout_2020/query.rs | 12 | ||||
-rw-r--r-- | components/layout_thread_2020/lib.rs | 21 | ||||
-rw-r--r-- | tests/wpt/mozilla/meta/MANIFEST.json | 4 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/scroll_root.html | 3 | ||||
-rw-r--r-- | tests/wpt/mozilla/tests/mozilla/scroll_root_ref.html | 7 |
12 files changed, 286 insertions, 65 deletions
diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs index 5ccf8310d53..9d8f54f057e 100644 --- a/components/layout_2020/dom_traversal.rs +++ b/components/layout_2020/dom_traversal.rs @@ -426,8 +426,13 @@ where node = child; continue; } + } else if node == self { + // If this is the root of the subtree and we aren't descending + // into our children return now. + return; } } + let mut next_is_a_sibling_of = node; node = loop { if let Some(sibling) = next_is_a_sibling_of.next_sibling() { diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 7c3fd9a29e3..ba9453a5b80 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -339,11 +339,12 @@ impl Lines { block: line_block_size, }; self.next_line_block_position += size.block; - self.fragments.push(Fragment::Anonymous(AnonymousFragment { - children: line_contents, - rect: Rect { start_corner, size }, - mode: containing_block.style.writing_mode, - })) + self.fragments + .push(Fragment::Anonymous(AnonymousFragment::new( + Rect { start_corner, size }, + line_contents, + containing_block.style.writing_mode, + ))) } } @@ -402,22 +403,24 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> { inline_position: &mut Length, at_line_break: bool, ) { - let mut fragment = BoxFragment { - tag: self.tag, - style: self.style.clone(), - children: std::mem::take(&mut nesting_level.fragments_so_far), - content_rect: Rect { - size: Vec2 { - inline: *inline_position - self.start_corner.inline, - block: nesting_level.max_block_size_of_fragments_so_far, - }, - start_corner: self.start_corner.clone(), + let content_rect = Rect { + size: Vec2 { + inline: *inline_position - self.start_corner.inline, + block: nesting_level.max_block_size_of_fragments_so_far, }, - padding: self.padding.clone(), - border: self.border.clone(), - margin: self.margin.clone(), - block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), + start_corner: self.start_corner.clone(), }; + + let mut fragment = BoxFragment::new( + self.tag, + self.style.clone(), + std::mem::take(&mut nesting_level.fragments_so_far), + content_rect, + self.padding.clone(), + self.border.clone(), + self.margin.clone(), + CollapsedBlockMargins::zero(), + ); let last_fragment = self.last_box_tree_fragment && !at_line_break; if last_fragment { *inline_position += fragment.padding.inline_end + @@ -470,16 +473,16 @@ fn layout_atomic<'box_tree>( let size = replaced.used_size_as_if_inline_element(ifc.containing_block, &atomic.style); let fragments = replaced.make_fragments(&atomic.style, size.clone()); let content_rect = Rect { start_corner, size }; - BoxFragment { - tag: atomic.tag, - style: atomic.style.clone(), - children: fragments, + BoxFragment::new( + atomic.tag, + atomic.style.clone(), + fragments, content_rect, padding, border, margin, - block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), - } + CollapsedBlockMargins::zero(), + ) }, Err(non_replaced) => { let box_size = atomic.style.box_size(); @@ -545,16 +548,16 @@ fn layout_atomic<'box_tree>( inline: inline_size, }, }; - BoxFragment { - tag: atomic.tag, - style: atomic.style.clone(), - children: independent_layout.fragments, + BoxFragment::new( + atomic.tag, + atomic.style.clone(), + independent_layout.fragments, content_rect, padding, border, margin, - block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), - } + CollapsedBlockMargins::zero(), + ) }, }; diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index b7373baf55a..b047fee78df 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -495,16 +495,16 @@ fn layout_in_flow_non_replaced_block_level<'a>( inline: inline_size, }, }; - BoxFragment { + BoxFragment::new( tag, - style: style.clone(), - children: fragments, + style.clone(), + fragments, content_rect, padding, border, margin, block_margins_collapsed_with_children, - } + ) } /// https://drafts.csswg.org/css2/visudet.html#block-replaced-width @@ -545,16 +545,17 @@ fn layout_in_flow_replaced_block_level<'a>( }, size, }; - BoxFragment { + let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin); + BoxFragment::new( tag, - style: style.clone(), - children: fragments, + style.clone(), + fragments, content_rect, padding, border, - block_margins_collapsed_with_children: CollapsedBlockMargins::from_margin(&margin), margin, - } + block_margins_collapsed_with_children, + ) } fn solve_inline_margins_for_in_flow_block_level( diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 14b1081c84a..f6466e99844 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -11,12 +11,15 @@ use crate::formatting_contexts::IndependentFormattingContext; use crate::fragments::Fragment; use crate::geom; use crate::geom::flow_relative::Vec2; +use crate::geom::physical; use crate::positioned::AbsolutelyPositionedBox; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContent; use crate::sizing::ContentSizesRequest; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside}; use crate::DefiniteContainingBlock; +use app_units::Au; +use euclid::default::{Point2D, Rect, Size2D}; use gfx_traits::print_tree::PrintTree; use script_layout_interface::wrapper_traits::LayoutNode; use servo_arc::Arc; @@ -26,7 +29,17 @@ use style::Zero; use style_traits::CSSPixel; pub struct BoxTreeRoot(BlockFormattingContext); -pub struct FragmentTreeRoot(Vec<Fragment>); + +pub struct FragmentTreeRoot { + /// The children of the root of the fragment tree. + children: Vec<Fragment>, + + /// The scrollable overflow of the root of the fragment tree. + scrollable_overflow: physical::Rect<Length>, + + /// The axis-aligned bounding box of the border box of all child fragments + bounding_box_of_border_boxes: physical::Rect<Length>, +} impl BoxTreeRoot { pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self @@ -131,7 +144,58 @@ impl BoxTreeRoot { &mut independent_layout.fragments, ); - FragmentTreeRoot(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 + .iter() + .fold(physical::Rect::zero(), |acc, child| { + let child_overflow = child.scrollable_overflow(); + + // https://drafts.csswg.org/css-overflow/#scrolling-direction + // We want to clip scrollable overflow on box-start and inline-start + // sides of the scroll container. + // + // FIXME(mrobinson, bug 25564): This should take into account writing + // mode. + let child_overflow = physical::Rect { + top_left: physical::Vec2::zero(), + size: physical::Vec2 { + x: child_overflow.size.x + child_overflow.top_left.x, + y: child_overflow.size.y + child_overflow.top_left.y, + }, + }; + acc.axis_aligned_bounding_box(&child_overflow) + }); + + let containing_block = physical::Rect::zero(); + let bounding_box_of_border_boxes = + independent_layout + .fragments + .iter() + .fold(physical::Rect::zero(), |acc, child| { + acc.axis_aligned_bounding_box(&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, + } } } @@ -151,15 +215,34 @@ impl FragmentTreeRoot { y: Length::new(viewport_size.height), }, }; - for fragment in &self.0 { + for fragment in &self.children { fragment.build_display_list(builder, &containing_block) } } pub fn print(&self) { let mut print_tree = PrintTree::new("Fragment Tree".to_string()); - for fragment in &self.0 { + for fragment in &self.children { fragment.print(&mut print_tree); } } + + pub fn scrollable_overflow(&self) -> webrender_api::units::LayoutSize { + webrender_api::units::LayoutSize::from_untyped(Size2D::new( + self.scrollable_overflow.size.x.px(), + self.scrollable_overflow.size.y.px(), + )) + } + + pub fn bounding_box_of_border_boxes(&self) -> Rect<Au> { + let origin = Point2D::new( + Au::from_f32_px(self.bounding_box_of_border_boxes.top_left.x.px()), + Au::from_f32_px(self.bounding_box_of_border_boxes.top_left.y.px()), + ); + let size = Size2D::new( + Au::from_f32_px(self.bounding_box_of_border_boxes.size.x.px()), + Au::from_f32_px(self.bounding_box_of_border_boxes.size.y.px()), + ); + Rect::new(origin, size) + } } diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragments.rs index 1bef85d16a7..887b8c3b04a 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragments.rs @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::geom::flow_relative::{Rect, Sides, Vec2}; +use crate::geom::physical; use gfx::text::glyph::GlyphStore; use gfx_traits::print_tree::PrintTree; use servo_arc::Arc as ServoArc; @@ -36,6 +37,9 @@ pub(crate) struct BoxFragment { pub margin: Sides<Length>, pub block_margins_collapsed_with_children: CollapsedBlockMargins, + + /// The scrollable overflow of this box fragment. + pub scrollable_overflow: physical::Rect<Length>, } pub(crate) struct CollapsedBlockMargins { @@ -55,6 +59,9 @@ pub(crate) struct AnonymousFragment { pub rect: Rect<Length>, pub children: Vec<Fragment>, pub mode: WritingMode, + + /// The scrollable overflow of this anonymous fragment's children. + pub scrollable_overflow: physical::Rect<Length>, } pub(crate) struct TextFragment { @@ -90,6 +97,21 @@ impl Fragment { Fragment::Image(fragment) => fragment.print(tree), } } + + pub fn scrollable_overflow(&self) -> physical::Rect<Length> { + // FIXME(mrobinson, bug 25564): We should be using the containing block + // here to properly convert scrollable overflow to physical geometry. + match self { + Fragment::Box(fragment) => fragment.scrollable_overflow.clone(), + Fragment::Anonymous(fragment) => fragment.scrollable_overflow.clone(), + Fragment::Text(fragment) => fragment + .rect + .to_physical(fragment.parent_style.writing_mode, &physical::Rect::zero()), + Fragment::Image(fragment) => fragment + .rect + .to_physical(fragment.style.writing_mode, &physical::Rect::zero()), + } + } } impl AnonymousFragment { @@ -98,6 +120,21 @@ impl AnonymousFragment { children: vec![], rect: Rect::zero(), mode, + scrollable_overflow: physical::Rect::zero(), + } + } + + pub fn new(rect: Rect<Length>, children: Vec<Fragment>, mode: WritingMode) -> Self { + // FIXME(mrobinson, bug 25564): We should be using the containing block + // here to properly convert scrollable overflow to physical geometry. + let scrollable_overflow = children.iter().fold(physical::Rect::zero(), |acc, child| { + acc.axis_aligned_bounding_box(&child.scrollable_overflow()) + }); + AnonymousFragment { + rect, + children, + mode, + scrollable_overflow, } } @@ -116,6 +153,37 @@ impl AnonymousFragment { } impl BoxFragment { + pub fn new( + tag: OpaqueNode, + style: ServoArc<ComputedValues>, + children: Vec<Fragment>, + content_rect: Rect<Length>, + padding: Sides<Length>, + border: Sides<Length>, + margin: Sides<Length>, + block_margins_collapsed_with_children: CollapsedBlockMargins, + ) -> BoxFragment { + // FIXME(mrobinson, bug 25564): We should be using the containing block + // here to properly convert scrollable overflow to physical geometry. + let scrollable_overflow = children.iter().fold( + content_rect + .inflate(&border) + .to_physical(style.writing_mode, &physical::Rect::zero()), + |acc, child| acc.axis_aligned_bounding_box(&child.scrollable_overflow()), + ); + BoxFragment { + tag, + style, + children, + content_rect, + padding, + border, + margin, + block_margins_collapsed_with_children, + scrollable_overflow, + } + } + pub fn padding_rect(&self) -> Rect<Length> { self.content_rect.inflate(&self.padding) } diff --git a/components/layout_2020/geom.rs b/components/layout_2020/geom.rs index 166ba7a0494..918cbf47fd2 100644 --- a/components/layout_2020/geom.rs +++ b/components/layout_2020/geom.rs @@ -60,6 +60,15 @@ pub(crate) mod flow_relative { } } +impl<T: Zero> physical::Vec2<T> { + pub fn zero() -> Self { + Self { + x: T::zero(), + y: T::zero(), + } + } +} + impl<T: fmt::Debug> fmt::Debug for physical::Vec2<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Not using f.debug_struct on purpose here, to keep {:?} output somewhat compact @@ -387,6 +396,33 @@ impl<T> physical::Rect<T> { } } +impl physical::Rect<Length> { + pub fn axis_aligned_bounding_box(&self, other: &Self) -> Self { + let top_left = physical::Vec2 { + x: self.top_left.x.min(other.top_left.x), + y: self.top_left.y.min(other.top_left.y), + }; + + let bottom_corner_x = (self.top_left.x + self.size.x).max(other.top_left.x + other.size.x); + let bottom_corner_y = (self.top_left.y + self.size.y).max(other.top_left.y + other.size.y); + let size = physical::Vec2 { + x: bottom_corner_x - top_left.x, + y: bottom_corner_y - top_left.y, + }; + + Self { top_left, size } + } +} + +impl<T: Zero> physical::Rect<T> { + pub fn zero() -> Self { + Self { + top_left: physical::Vec2::zero(), + size: physical::Vec2::zero(), + } + } +} + impl From<physical::Rect<Length>> for Rect<CSSPixel> { fn from(r: physical::Rect<Length>) -> Self { Rect { diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index b10b489bc1d..7b7d67bd122 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -290,11 +290,11 @@ impl<'box_tree> PositioningContext<'box_tree> { ); positioned_box_fragment .children - .push(Fragment::Anonymous(AnonymousFragment { + .push(Fragment::Anonymous(AnonymousFragment::new( + padding_rect, children, - rect: padding_rect, - mode: positioned_box_fragment.style.writing_mode, - })) + positioned_box_fragment.style.writing_mode, + ))) } } } @@ -470,16 +470,16 @@ impl<'box_tree> HoistedAbsolutelyPositionedBox<'box_tree> { size, }; - BoxFragment { - tag: self.absolutely_positioned_box.contents.tag, - style: style.clone(), - children: fragments, + BoxFragment::new( + self.absolutely_positioned_box.contents.tag, + style.clone(), + fragments, content_rect, padding, border, margin, - block_margins_collapsed_with_children: CollapsedBlockMargins::zero(), - } + CollapsedBlockMargins::zero(), + ) }) } } diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 1317759b33b..0a7225c3b3c 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -5,6 +5,7 @@ //! Utilities for querying the layout, as needed by the layout thread. use crate::context::LayoutContext; +use crate::flow::FragmentTreeRoot; use app_units::Au; use euclid::default::{Point2D, Rect}; use euclid::Size2D; @@ -163,8 +164,15 @@ impl LayoutRPC for LayoutRPCImpl { } } -pub fn process_content_box_request(_requested_node: OpaqueNode) -> Option<Rect<Au>> { - None +pub fn process_content_box_request( + _requested_node: OpaqueNode, + fragment_tree_root: Option<&FragmentTreeRoot>, +) -> Option<Rect<Au>> { + let fragment_tree_root = match fragment_tree_root { + Some(fragment_tree_root) => fragment_tree_root, + None => return None, + }; + Some(fragment_tree_root.bounding_box_of_border_boxes()) } pub fn process_content_boxes_request(_requested_node: OpaqueNode) -> Vec<Rect<Au>> { diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs index 70233892d63..295cdabda4e 100644 --- a/components/layout_thread_2020/lib.rs +++ b/components/layout_thread_2020/lib.rs @@ -46,7 +46,7 @@ use layout::query::{ process_text_index_request, }; use layout::traversal::RecalcStyle; -use layout::BoxTreeRoot; +use layout::{BoxTreeRoot, FragmentTreeRoot}; use layout_traits::LayoutThreadFactory; use libc::c_void; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; @@ -172,10 +172,10 @@ pub struct LayoutThread { outstanding_web_fonts: Arc<AtomicUsize>, /// The root of the box tree. - box_tree_root: RefCell<Option<layout::BoxTreeRoot>>, + box_tree_root: RefCell<Option<BoxTreeRoot>>, /// The root of the fragment tree. - fragment_tree_root: RefCell<Option<layout::FragmentTreeRoot>>, + fragment_tree_root: RefCell<Option<FragmentTreeRoot>>, /// The document-specific shared lock used for author-origin stylesheets document_shared_lock: Option<SharedRwLock>, @@ -1218,7 +1218,10 @@ impl LayoutThread { match *reflow_goal { ReflowGoal::LayoutQuery(ref querymsg, _) => match querymsg { &QueryMsg::ContentBoxQuery(node) => { - rw_data.content_box_response = process_content_box_request(node); + rw_data.content_box_response = process_content_box_request( + node, + (&*self.fragment_tree_root.borrow()).as_ref(), + ); }, &QueryMsg::ContentBoxesQuery(node) => { rw_data.content_boxes_response = process_content_boxes_request(node); @@ -1355,7 +1358,7 @@ impl LayoutThread { fn perform_post_style_recalc_layout_passes( &self, - fragment_tree: &layout::FragmentTreeRoot, + fragment_tree: &FragmentTreeRoot, reflow_goal: &ReflowGoal, document: Option<&ServoLayoutDocument>, context: &mut LayoutContext, @@ -1374,12 +1377,16 @@ impl LayoutThread { document.will_paint(); } + let mut display_list = DisplayListBuilder::new( + self.id.to_webrender(), + context, + fragment_tree.scrollable_overflow(), + ); + let viewport_size = webrender_api::units::LayoutSize::from_untyped(Size2D::new( self.viewport_size.width.to_f32_px(), self.viewport_size.height.to_f32_px(), )); - let mut display_list = - DisplayListBuilder::new(self.id.to_webrender(), context, viewport_size); fragment_tree.build_display_list(&mut display_list, viewport_size); if self.dump_flow_tree { diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 380ca0c7023..a683eeb8628 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -19368,11 +19368,11 @@ "testharness" ], "mozilla/scroll_root.html": [ - "b1a9cb590b0fcce9c883f99e17fa029a999b699b", + "5896eb3957da1eb85a26680053823d3f3bf9af49", "reftest" ], "mozilla/scroll_root_ref.html": [ - "6503ad5d5265c0698f61fc607e2e4e017b31cb6f", + "c7d4cfec7b9d9b5daf32841172721ddac3e4d071", "support" ], "mozilla/scroll_top_null_target.html": [ diff --git a/tests/wpt/mozilla/tests/mozilla/scroll_root.html b/tests/wpt/mozilla/tests/mozilla/scroll_root.html index b1a9cb590b0..5896eb3957d 100644 --- a/tests/wpt/mozilla/tests/mozilla/scroll_root.html +++ b/tests/wpt/mozilla/tests/mozilla/scroll_root.html @@ -7,6 +7,9 @@ <style> body { background: green; + + /* FIXME(mrobinson): Remove this workaround when #25559 is fixed. */ + margin: 0; } </style> </head> diff --git a/tests/wpt/mozilla/tests/mozilla/scroll_root_ref.html b/tests/wpt/mozilla/tests/mozilla/scroll_root_ref.html index 6503ad5d526..c7d4cfec7b9 100644 --- a/tests/wpt/mozilla/tests/mozilla/scroll_root_ref.html +++ b/tests/wpt/mozilla/tests/mozilla/scroll_root_ref.html @@ -5,7 +5,14 @@ <style> body { background: green; + + /* FIXME(mrobinson): Remove this workaround when #25559 is fixed. */ + margin: 0; } </style> </head> + <body> + <!-- FIXME(mrobinson): Remove this workaround div when #25559 is fixed. --> + <div style="height: 10000px;"></div> + </body> </html> |