aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout_2020/dom_traversal.rs5
-rw-r--r--components/layout_2020/flow/inline.rs65
-rw-r--r--components/layout_2020/flow/mod.rs19
-rw-r--r--components/layout_2020/flow/root.rs91
-rw-r--r--components/layout_2020/fragments.rs68
-rw-r--r--components/layout_2020/geom.rs36
-rw-r--r--components/layout_2020/positioned.rs20
-rw-r--r--components/layout_2020/query.rs12
-rw-r--r--components/layout_thread_2020/lib.rs21
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json4
-rw-r--r--tests/wpt/mozilla/tests/mozilla/scroll_root.html3
-rw-r--r--tests/wpt/mozilla/tests/mozilla/scroll_root_ref.html7
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>