Commit e977dd86 authored by Vladimir Levin's avatar Vladimir Levin Committed by Commit Bot

multicol: Ensure that clipped out elements don't get painted.

This patch keep track of whether the logical bounding box of an
object was empty in fragmentainer iterator. If it was, then it is
likely to be placed in the first column (since typically empty rects
are 0,0 0x0). The first column doesn't have the top edge clip so
we might end up painting contents outside of the container
(see testcase).

By using the portion rect, we ensure that the fragment clip we put
in place is bounded on all sides.

R=pdr@chromium.org, wangxianzhu@chromium.org

Bug: 1017599
Change-Id: I436802f7410124ab2251bfff13ce7dcff49b335b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1879564
Commit-Queue: vmpstr <vmpstr@chromium.org>
Reviewed-by: default avatarXianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709509}
parent ecef75fb
......@@ -28,6 +28,7 @@ FragmentainerIterator::FragmentainerIterator(
logical_top_in_flow_thread_ = bounds_in_flow_thread.X();
logical_bottom_in_flow_thread_ = bounds_in_flow_thread.MaxX();
}
bounding_box_is_empty_ = bounds_in_flow_thread.IsEmpty();
// Jump to the first interesting column set.
current_column_set_ = flow_thread.ColumnSetAtBlockOffset(
......@@ -81,8 +82,22 @@ LayoutUnit FragmentainerIterator::FragmentainerLogicalTopInFlowThread() const {
LayoutRect FragmentainerIterator::ClipRectInFlowThread() const {
DCHECK(!AtEnd());
LayoutRect clip_rect = CurrentGroup().FlowThreadPortionOverflowRectAt(
current_fragmentainer_index_);
LayoutRect clip_rect;
// An empty bounding box rect would typically be 0,0 0x0, so it would be
// placed in the first column always. However, the first column might not have
// a top edge clip (see FlowThreadPortionOverflowRectAt()). This might cause
// artifacts to paint outside of the column container. To avoid this
// situation, and since the logical bounding box is empty anyway, use the
// portion rect instead which is bounded on all sides. Note that we don't
// return an empty clip here, because an empty clip indicates that we have an
// empty column which may be treated differently by the calling code.
if (bounding_box_is_empty_) {
clip_rect =
CurrentGroup().FlowThreadPortionRectAt(current_fragmentainer_index_);
} else {
clip_rect = CurrentGroup().FlowThreadPortionOverflowRectAt(
current_fragmentainer_index_);
}
flow_thread_.DeprecatedFlipForWritingMode(clip_rect);
return clip_rect;
}
......
......@@ -60,6 +60,8 @@ class FragmentainerIterator {
LayoutUnit logical_top_in_flow_thread_;
LayoutUnit logical_bottom_in_flow_thread_;
bool bounding_box_is_empty_;
const MultiColumnFragmentainerGroup& CurrentGroup() const;
void MoveToNextFragmentainerGroup();
bool SetFragmentainersOfInterest();
......
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>CSS Test: Multi-column element with scrolled content clipping</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"/>
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
<link rel="match" href="reference/multicol-clip-scrolled-content-001-ref.html"/>
<style>
#columns {
column-width: 350px;
}
.spacer {
height: 200px;
width: 10px;
}
#outer {
border: 1px solid black;
overflow: scroll;
height: 300px;
width: 300px;
}
.inner {
overflow: scroll;
}
.clipped_target {
overflow: scroll;
background: red;
width: 50px;
height: 50px;
}
</style>
<div class=spacer></div>
<div id=columns>
<div class=spacer></div>
<div id=outer>
<div class=inner>
<pre class=clipped_target>
scrollable
content
goes
here
</pre>
</div>
<div class=spacer></div>
<div class=spacer></div>
</div>
</div>
<script>
window.onload = () => { outer.scrollTop = 100; };
</script>
</html>
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<title>CSS Test: Multi-column element with scrolled content clipping (reference)</title>
<link rel="author" title="Vladimir Levin" href="mailto:vmpstr@chromium.org"/>
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
<style>
#columns {
column-width: 350px;
}
.spacer {
height: 200px;
width: 10px;
}
#outer {
border: 1px solid black;
overflow: scroll;
height: 300px;
width: 300px;
}
.inner {
overflow: scroll;
visibility: hidden;
}
.clipped_target {
width: 50px;
height: 50px;
}
</style>
<div class=spacer></div>
<div id=columns>
<div class=spacer></div>
<div id=outer>
<div class=inner>
<pre class=clipped_target>
scrollable
content
goes
here
</pre>
</div>
<div class=spacer></div>
<div class=spacer></div>
</div>
</div>
<script>
window.onload = () => { outer.scrollTop = 100; };
</script>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment