Skip to content

Commit

Permalink
Use real clips when generating scroll roots
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mrobinson committed Apr 6, 2017
1 parent 21eafeb commit 0353aad
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 12 deletions.
2 changes: 1 addition & 1 deletion components/gfx/display_list/mod.rs
Expand Up @@ -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<Au>,
pub clip: ClippingRegion,

/// The rect of the contents that can be scrolled inside of the scroll root.
pub content_rect: Rect<Au>,
Expand Down
43 changes: 41 additions & 2 deletions components/layout/display_list_builder.rs
Expand Up @@ -520,7 +520,9 @@ fn handle_overlapping_radii(size: &Size2D<Au>, radii: &BorderRadii<Au>) -> Borde
}
}

fn build_border_radius(abs_bounds: &Rect<Au>, border_style: &style_structs::Border) -> BorderRadii<Au> {
fn build_border_radius(abs_bounds: &Rect<Au>,
border_style: &style_structs::Border)
-> BorderRadii<Au> {
// 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.
Expand All @@ -537,6 +539,34 @@ fn build_border_radius(abs_bounds: &Rect<Au>, 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<Au>,
style: Arc<ServoComputedValues>)
-> BorderRadii<Au> {
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,
Expand Down Expand Up @@ -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)),
},
Expand Down
14 changes: 5 additions & 9 deletions components/layout/webrender_helpers.rs
Expand Up @@ -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(),
}
Expand Down
25 changes: 25 additions & 0 deletions tests/wpt/mozilla/meta/MANIFEST.json
Expand Up @@ -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",
Expand Down Expand Up @@ -8415,6 +8427,11 @@
{}
]
],
"css/overflow_border_radius_ref.html": [
[
{}
]
],
"css/overflow_position_abs_inline_block_ref.html": [
[
{}
Expand Down Expand Up @@ -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"
Expand Down
36 changes: 36 additions & 0 deletions tests/wpt/mozilla/tests/css/overflow_border_radius.html
@@ -0,0 +1,36 @@
<!doctype html>
<html>
<meta charset="utf-8">
<title>Test to ensure that clipped overflow is clipped to border radius</title>
<link rel="match" href="overflow_border_radius_ref.html">

<body>
<style>
.box {
width: 50px;
height: 50px;
border-radius: 20px;
border: 5px transparent solid;
margin-right: 10px;
float: left;
}

.overflowingbox {
margin-left: -25px;
margin-top: -25px;
width: 100px;
height: 100px;
background: gray;
}
</style>

<div class="box border" style="overflow: auto;">
<div class="overflowingbox"></div>
</div>

<div class="box border" style="overflow: scroll;">
<div class="overflowingbox"></div>
</div>
</body>

</html>
26 changes: 26 additions & 0 deletions tests/wpt/mozilla/tests/css/overflow_border_radius_ref.html
@@ -0,0 +1,26 @@
<!doctype html>
<html>
<meta charset="utf-8">
<title></title>

<body>
<style>
.box {
width: 50px;
height: 50px;
margin-left: 5px;
margin-top: 5px;

border-radius: 15px;
background: gray;
float: left;

margin-right: 15px;
}
</style>

<div class="box"></div>
<div class="box"></div>
</body>

</html>

0 comments on commit 0353aad

Please sign in to comment.