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 @@ + + + + + + + + +
+
+ + +