From 0353aad4e390565efb42c7f0927fcc09301c2c4c Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Thu, 6 Apr 2017 14:43:29 +0200 Subject: [PATCH] Use real clips when generating scroll roots This is the first step toward removing inherited clips in favor of scroll roots for handling overflow and CSS clipping. This will allow us to more easily handle elements that should not be clipped. While we are still using inherited clips here, we now properly clip some types of content that wasn't clipped before. --- components/gfx/display_list/mod.rs | 2 +- components/layout/display_list_builder.rs | 43 ++++++++++++++++++- components/layout/webrender_helpers.rs | 14 +++--- tests/wpt/mozilla/meta/MANIFEST.json | 25 +++++++++++ .../tests/css/overflow_border_radius.html | 36 ++++++++++++++++ .../tests/css/overflow_border_radius_ref.html | 26 +++++++++++ 6 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 tests/wpt/mozilla/tests/css/overflow_border_radius.html create mode 100644 tests/wpt/mozilla/tests/css/overflow_border_radius_ref.html diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index e43767ddadd..6c7242e1146 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -508,7 +508,7 @@ pub struct ScrollRoot { pub parent_id: ScrollRootId, /// The position of this scroll root's frame in the parent stacking context. - pub clip: Rect, + pub clip: ClippingRegion, /// The rect of the contents that can be scrolled inside of the scroll root. pub content_rect: Rect, diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 8c390487f5c..380517d9924 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -520,7 +520,9 @@ fn handle_overlapping_radii(size: &Size2D, radii: &BorderRadii) -> Borde } } -fn build_border_radius(abs_bounds: &Rect, border_style: &style_structs::Border) -> BorderRadii { +fn build_border_radius(abs_bounds: &Rect, + border_style: &style_structs::Border) + -> BorderRadii { // TODO(cgaebel): Support border radii even in the case of multiple border widths. // This is an extension of supporting elliptical radii. For now, all percentage // radii will be relative to the width. @@ -537,6 +539,34 @@ fn build_border_radius(abs_bounds: &Rect, border_style: &style_structs::Bord }) } +/// Get the border radius for the rectangle inside of a rounded border. This is useful +/// for building the clip for the content inside the border. +fn build_border_radius_for_inner_rect(outer_rect: &Rect, + style: Arc) + -> BorderRadii { + let mut radii = build_border_radius(&outer_rect, style.get_border()); + if radii.is_square() { + return radii; + } + + // Since we are going to using the inner rectangle (outer rectangle minus + // border width), we need to adjust to border radius so that we are smaller + // rectangle with the same border curve. + let border_widths = style.logical_border_width().to_physical(style.writing_mode); + radii.top_left.width = cmp::max(Au(0), radii.top_left.width - border_widths.left); + radii.bottom_left.width = cmp::max(Au(0), radii.bottom_left.width - border_widths.left); + + radii.top_right.width = cmp::max(Au(0), radii.top_right.width - border_widths.right); + radii.bottom_right.width = cmp::max(Au(0), radii.bottom_right.width - border_widths.right); + + radii.top_left.height = cmp::max(Au(0), radii.top_left.height - border_widths.top); + radii.top_right.height = cmp::max(Au(0), radii.top_right.height - border_widths.top); + + radii.bottom_left.height = cmp::max(Au(0), radii.bottom_left.height - border_widths.bottom); + radii.bottom_right.height = cmp::max(Au(0), radii.bottom_right.height - border_widths.bottom); + radii +} + impl FragmentDisplayListBuilding for Fragment { fn build_display_list_for_background_if_applicable(&self, state: &mut DisplayListBuildState, @@ -1978,12 +2008,21 @@ impl BlockFlowDisplayListBuilding for BlockFlow { let new_scroll_root_id = ScrollRootId::new_of_type(self.fragment.node.id() as usize, self.fragment.fragment_type()); + let clip_rect = Rect::new(Point2D::zero(), content_box.size); + let mut clip = ClippingRegion::from_rect(&clip_rect); + + let border_radii = build_border_radius_for_inner_rect(&border_box, + self.fragment.style.clone()); + if !border_radii.is_square() { + clip.intersect_with_rounded_rect(&clip_rect, &border_radii) + } + let content_size = self.base.overflow.scroll.origin + self.base.overflow.scroll.size; state.add_scroll_root( ScrollRoot { id: new_scroll_root_id, parent_id: containing_scroll_root_id, - clip: Rect::new(Point2D::zero(), content_box.size), + clip: clip, content_rect: Rect::new(content_box.origin, Size2D::new(content_size.x, content_size.y)), }, diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index dbbf3f68b79..73dde47c3ed 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -421,15 +421,11 @@ impl WebRenderDisplayItemConverter for DisplayItem { } DisplayItem::PopStackingContext(_) => builder.pop_stacking_context(), DisplayItem::PushScrollRoot(ref item) => { - let clip = builder.new_clip_region(&item.scroll_root.clip.to_rectf(), - vec![], - None); - - let provided_id = ScrollLayerId::new(item.scroll_root.id.0 as u64, builder.pipeline_id); - let id = builder.define_clip(item.scroll_root.content_rect.to_rectf(), - clip, - Some(provided_id)); - debug_assert!(provided_id == id); + let our_id = ScrollLayerId::new(item.scroll_root.id.0 as u64, builder.pipeline_id); + let clip = item.scroll_root.clip.to_clip_region(builder); + let content_rect = item.scroll_root.content_rect.to_rectf(); + let webrender_id = builder.define_clip(content_rect, clip, Some(our_id)); + debug_assert!(our_id == webrender_id); } DisplayItem::PopScrollRoot(_) => {} //builder.pop_scroll_layer(), } diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 870984d2d50..74a6afd08f6 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -4079,6 +4079,18 @@ {} ] ], + "css/overflow_border_radius.html": [ + [ + "/_mozilla/css/overflow_border_radius.html", + [ + [ + "/_mozilla/css/overflow_border_radius_ref.html", + "==" + ] + ], + {} + ] + ], "css/overflow_position_abs_inline_block.html": [ [ "/_mozilla/css/overflow_position_abs_inline_block.html", @@ -8415,6 +8427,11 @@ {} ] ], + "css/overflow_border_radius_ref.html": [ + [ + {} + ] + ], "css/overflow_position_abs_inline_block_ref.html": [ [ {} @@ -22689,6 +22706,14 @@ "195201950fd56bd139e71971d66f92ee4fef815d", "support" ], + "css/overflow_border_radius.html": [ + "2c8a650f518ccff78517556540416f7e0e798033", + "reftest" + ], + "css/overflow_border_radius_ref.html": [ + "3798d0efb1b9858ad47ecf6f09357c3c0dae1b80", + "support" + ], "css/overflow_position_abs_inline_block.html": [ "7550f9c9f3e91635c15554d9ae21e172944054e6", "reftest" diff --git a/tests/wpt/mozilla/tests/css/overflow_border_radius.html b/tests/wpt/mozilla/tests/css/overflow_border_radius.html new file mode 100644 index 00000000000..338a2f603e6 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/overflow_border_radius.html @@ -0,0 +1,36 @@ + + + +Test to ensure that clipped overflow is clipped to border radius + + + + + +
+
+
+ +
+
+
+ + + diff --git a/tests/wpt/mozilla/tests/css/overflow_border_radius_ref.html b/tests/wpt/mozilla/tests/css/overflow_border_radius_ref.html new file mode 100644 index 00000000000..1c29c1fa3d6 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/overflow_border_radius_ref.html @@ -0,0 +1,26 @@ + + + + + + + + +
+
+ + +