Commit bd513eeb authored by trchen's avatar trchen Committed by Commit bot

[blink] Unify PaintLayerClipper behavior with kIgnoreOverflowClip

Prior to this CL, the behavior of GeometryMapper version and non-GM
version of PaintLayerClipper was inconsistent with kIgnoreOverflowClip
when both CSS clip and overflow clip present on the painting root.

       | CSS clip | Overflow clip | Both
-------+----------+---------------+--------
GM     | Applied  | Skipped       | Skipped
Non-GM | Applied  | Skipped       | Applied

Also the GM-version applies only CSS clip when both present and
context.root_layer == layer_. (While non-GM applies both)

This CL changes the behavior so that both CSS clip and overflow clip are
ignored whether both or either one present. It is believed that it will
also fix a bug that prevented us from enabling impl-side scrolling on
scrollers that has CSS clip.
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2

Review-Url: https://codereview.chromium.org/2856373002
Cr-Commit-Position: refs/heads/master@{#469449}
parent 3618dd34
...@@ -53,6 +53,25 @@ ...@@ -53,6 +53,25 @@
namespace blink { namespace blink {
static bool HasOverflowClip(
const PaintLayer& layer) {
if (!layer.GetLayoutObject().IsBox())
return false;
const LayoutBox& box = ToLayoutBox(layer.GetLayoutObject());
return box.ShouldClipOverflow();
}
bool ClipRectsContext::ShouldRespectRootLayerClip() const {
if (respect_overflow_clip == kIgnoreOverflowClip)
return false;
if (root_layer->IsRootLayer() &&
respect_overflow_clip_for_viewport == kIgnoreOverflowClip)
return false;
return true;
}
static void AdjustClipRectsForChildren( static void AdjustClipRectsForChildren(
const LayoutBoxModelObject& layout_object, const LayoutBoxModelObject& layout_object,
ClipRects& clip_rects) { ClipRects& clip_rects) {
...@@ -386,6 +405,13 @@ void PaintLayerClipper::CalculateClipRects(const ClipRectsContext& context, ...@@ -386,6 +405,13 @@ void PaintLayerClipper::CalculateClipRects(const ClipRectsContext& context,
bool is_clipping_root = &layer_ == context.root_layer; bool is_clipping_root = &layer_ == context.root_layer;
if (is_clipping_root && !context.ShouldRespectRootLayerClip()) {
clip_rects.Reset(LayoutRect(LayoutRect::InfiniteIntRect()));
if (layout_object.StyleRef().GetPosition() == EPosition::kFixed)
clip_rects.SetFixed(true);
return;
}
// For transformed layers, the root layer was shifted to be us, so there is no // For transformed layers, the root layer was shifted to be us, so there is no
// need to examine the parent. We want to cache clip rects with us as the // need to examine the parent. We want to cache clip rects with us as the
// root. // root.
...@@ -401,7 +427,9 @@ void PaintLayerClipper::CalculateClipRects(const ClipRectsContext& context, ...@@ -401,7 +427,9 @@ void PaintLayerClipper::CalculateClipRects(const ClipRectsContext& context,
AdjustClipRectsForChildren(layout_object, clip_rects); AdjustClipRectsForChildren(layout_object, clip_rects);
if (ShouldClipOverflow(context) || layout_object.HasClip()) { // Computing paint offset is expensive, skip the computation if the object
// is known to have no clip. This check is redundant otherwise.
if (HasOverflowClip(layer_) || layout_object.HasClip()) {
// This offset cannot use convertToLayerCoords, because sometimes our // This offset cannot use convertToLayerCoords, because sometimes our
// rootLayer may be across some transformed layer boundary, for example, in // rootLayer may be across some transformed layer boundary, for example, in
// the PaintLayerCompositor overlapMap, where clipRects are needed in view // the PaintLayerCompositor overlapMap, where clipRects are needed in view
...@@ -428,23 +456,18 @@ void PaintLayerClipper::CalculateBackgroundClipRectWithGeometryMapper( ...@@ -428,23 +456,18 @@ void PaintLayerClipper::CalculateBackgroundClipRectWithGeometryMapper(
const ClipRectsContext& context, const ClipRectsContext& context,
ClipRect& output) const { ClipRect& output) const {
DCHECK(use_geometry_mapper_); DCHECK(use_geometry_mapper_);
bool is_clipping_root = &layer_ == context.root_layer;
if (is_clipping_root && !context.ShouldRespectRootLayerClip()) {
output.SetRect(FloatClipRect());
return;
}
PropertyTreeState source_property_tree_state(nullptr, nullptr, nullptr); PropertyTreeState source_property_tree_state(nullptr, nullptr, nullptr);
PropertyTreeState destination_property_tree_state(nullptr, nullptr, nullptr); PropertyTreeState destination_property_tree_state(nullptr, nullptr, nullptr);
InitializeCommonClipRectState(context, source_property_tree_state, InitializeCommonClipRectState(context, source_property_tree_state,
destination_property_tree_state); destination_property_tree_state);
if (&layer_ != context.root_layer) {
auto* ancestor_properties =
context.root_layer->GetLayoutObject().PaintProperties();
const auto* ancestor_overflow_clip =
ancestor_properties ? ancestor_properties->OverflowClip() : nullptr;
// Set the clip of |destinationPropertyTreeState| to be inside the
// ancestor's overflow clip, so that that clip is not applied.
if (context.respect_overflow_clip == kIgnoreOverflowClip &&
ancestor_overflow_clip)
destination_property_tree_state.SetClip(ancestor_overflow_clip);
}
// The background rect applies all clips *above* m_layer, but not the overflow // The background rect applies all clips *above* m_layer, but not the overflow
// clip of m_layer. It also applies a clip to the total painting bounds // clip of m_layer. It also applies a clip to the total painting bounds
// of m_layer, because nothing in m_layer or its children within the clip can // of m_layer, because nothing in m_layer or its children within the clip can
...@@ -457,7 +480,7 @@ void PaintLayerClipper::CalculateBackgroundClipRectWithGeometryMapper( ...@@ -457,7 +480,7 @@ void PaintLayerClipper::CalculateBackgroundClipRectWithGeometryMapper(
// of transforms. Tight results are required for most use cases of these // of transforms. Tight results are required for most use cases of these
// rects, so we should add methods to GeometryMapper that guarantee there // rects, so we should add methods to GeometryMapper that guarantee there
// are tight results, or else signal an error. // are tight results, or else signal an error.
if (ShouldClipOverflow(context)) { if (HasOverflowClip(layer_)) {
FloatClipRect clip_rect((FloatRect(LocalVisualRect()))); FloatClipRect clip_rect((FloatRect(LocalVisualRect())));
clip_rect.MoveBy(FloatPoint(layer_.GetLayoutObject().PaintOffset())); clip_rect.MoveBy(FloatPoint(layer_.GetLayoutObject().PaintOffset()));
GeometryMapper::SourceToDestinationVisualRect( GeometryMapper::SourceToDestinationVisualRect(
...@@ -478,23 +501,34 @@ void PaintLayerClipper::InitializeCommonClipRectState( ...@@ -478,23 +501,34 @@ void PaintLayerClipper::InitializeCommonClipRectState(
PropertyTreeState& source_property_tree_state, PropertyTreeState& source_property_tree_state,
PropertyTreeState& destination_property_tree_state) const { PropertyTreeState& destination_property_tree_state) const {
DCHECK(use_geometry_mapper_); DCHECK(use_geometry_mapper_);
DCHECK(layer_.GetLayoutObject().LocalBorderBoxProperties());
DCHECK(layer_.GetLayoutObject().LocalBorderBoxProperties());
source_property_tree_state = source_property_tree_state =
*layer_.GetLayoutObject().LocalBorderBoxProperties(); *layer_.GetLayoutObject().LocalBorderBoxProperties();
DCHECK(context.root_layer->GetLayoutObject().LocalBorderBoxProperties()); DCHECK(context.root_layer->GetLayoutObject().LocalBorderBoxProperties());
destination_property_tree_state = destination_property_tree_state =
*context.root_layer->GetLayoutObject().LocalBorderBoxProperties(); *context.root_layer->GetLayoutObject().LocalBorderBoxProperties();
auto* ancestor_properties = auto* ancestor_properties =
context.root_layer->GetLayoutObject().PaintProperties(); context.root_layer->GetLayoutObject().PaintProperties();
const auto* ancestor_css_clip = if (!ancestor_properties)
ancestor_properties ? ancestor_properties->CssClip() : nullptr; return;
// CSS clip of the root is always applied.
if (ancestor_css_clip) { if (context.ShouldRespectRootLayerClip()) {
DCHECK(destination_property_tree_state.Clip() == const auto* ancestor_css_clip = ancestor_properties->CssClip();
ancestor_properties->CssClip()); if (ancestor_css_clip) {
destination_property_tree_state.SetClip(ancestor_css_clip->Parent()); DCHECK_EQ(destination_property_tree_state.Clip(),
ancestor_css_clip);
destination_property_tree_state.SetClip(ancestor_css_clip->Parent());
}
} else {
const auto* ancestor_overflow_clip = ancestor_properties->OverflowClip();
if (ancestor_overflow_clip) {
DCHECK_EQ(destination_property_tree_state.Clip(),
ancestor_overflow_clip->Parent());
destination_property_tree_state.SetClip(ancestor_overflow_clip);
}
} }
} }
...@@ -572,29 +606,9 @@ void PaintLayerClipper::GetOrCalculateClipRects(const ClipRectsContext& context, ...@@ -572,29 +606,9 @@ void PaintLayerClipper::GetOrCalculateClipRects(const ClipRectsContext& context,
bool PaintLayerClipper::ShouldClipOverflow( bool PaintLayerClipper::ShouldClipOverflow(
const ClipRectsContext& context) const { const ClipRectsContext& context) const {
if (!layer_.GetLayoutObject().IsBox()) if (&layer_ == context.root_layer && !context.ShouldRespectRootLayerClip())
return false;
const LayoutBox& box = ToLayoutBox(layer_.GetLayoutObject());
if (!ShouldRespectOverflowClip(context))
return false; return false;
return HasOverflowClip(layer_);
return box.ShouldClipOverflow();
}
bool PaintLayerClipper::ShouldRespectOverflowClip(
const ClipRectsContext& context) const {
if (&layer_ != context.root_layer)
return true;
if (context.respect_overflow_clip == kIgnoreOverflowClip)
return false;
if (layer_.IsRootLayer() &&
context.respect_overflow_clip_for_viewport == kIgnoreOverflowClip)
return false;
return true;
} }
ClipRects& PaintLayerClipper::PaintingClipRects( ClipRects& PaintLayerClipper::PaintingClipRects(
......
...@@ -96,6 +96,8 @@ class ClipRectsContext { ...@@ -96,6 +96,8 @@ class ClipRectsContext {
ClipRectsCacheSlot CacheSlot() const { return cache_slot_; } ClipRectsCacheSlot CacheSlot() const { return cache_slot_; }
bool ShouldRespectRootLayerClip() const;
const PaintLayer* root_layer; const PaintLayer* root_layer;
const OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior; const OverlayScrollbarClipBehavior overlay_scrollbar_clip_behavior;
...@@ -220,7 +222,6 @@ class CORE_EXPORT PaintLayerClipper { ...@@ -220,7 +222,6 @@ class CORE_EXPORT PaintLayerClipper {
void GetOrCalculateClipRects(const ClipRectsContext&, ClipRects&) const; void GetOrCalculateClipRects(const ClipRectsContext&, ClipRects&) const;
ALWAYS_INLINE bool ShouldClipOverflow(const ClipRectsContext&) const; ALWAYS_INLINE bool ShouldClipOverflow(const ClipRectsContext&) const;
ALWAYS_INLINE bool ShouldRespectOverflowClip(const ClipRectsContext&) const;
// Returned clip rect in |output| is in the space of the context's rootLayer. // Returned clip rect in |output| is in the space of the context's rootLayer.
ALWAYS_INLINE void CalculateBackgroundClipRectWithGeometryMapper( ALWAYS_INLINE void CalculateBackgroundClipRectWithGeometryMapper(
......
...@@ -512,4 +512,114 @@ TEST_P(PaintLayerClipperTest, Filter) { ...@@ -512,4 +512,114 @@ TEST_P(PaintLayerClipperTest, Filter) {
EXPECT_EQ(LayoutRect(0, 0, 100, 200), foreground_rect.Rect()); EXPECT_EQ(LayoutRect(0, 0, 100, 200), foreground_rect.Rect());
} }
// Computed infinite clip rects may not match LayoutRect::InfiniteIntRect()
// due to floating point errors.
static bool IsInfinite(const LayoutRect& rect) {
return rect.X().Round() < -10000000 && rect.MaxX().Round() > 10000000
&& rect.Y().Round() < -10000000 && rect.MaxY().Round() > 10000000;
}
TEST_P(PaintLayerClipperTest, IgnoreRootLayerClipWithCSSClip) {
SetBodyInnerHTML(
"<style>"
" #root { "
" width: 400px; height: 400px;"
" position: absolute; clip: rect(0, 50px, 100px, 0);"
" }"
" #target {"
" position: relative;"
" }"
"</style>"
"<div id='root'>"
" <div id='target'></div>"
"</div>");
PaintLayer* root =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("root"))->Layer();
PaintLayer* target =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer();
ClipRectsContext context(root, kPaintingClipRectsIgnoringOverflowClip);
PaintLayer::GeometryMapperOption option = PaintLayer::kDoNotUseGeometryMapper;
if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled())
option = PaintLayer::kUseGeometryMapper;
LayoutRect infinite_rect(LayoutRect::InfiniteIntRect());
LayoutRect layer_bounds(infinite_rect);
ClipRect background_rect(infinite_rect);
ClipRect foreground_rect(infinite_rect);
target->Clipper(option).CalculateRects(context, infinite_rect, layer_bounds,
background_rect, foreground_rect);
EXPECT_TRUE(IsInfinite(background_rect.Rect()));
EXPECT_TRUE(IsInfinite(foreground_rect.Rect()));
}
TEST_P(PaintLayerClipperTest, IgnoreRootLayerClipWithOverflowClip) {
SetBodyInnerHTML(
"<style>"
" #root { "
" width: 400px; height: 400px;"
" overflow: hidden;"
" }"
" #target {"
" position: relative;"
" }"
"</style>"
"<div id='root'>"
" <div id='target'></div>"
"</div>");
PaintLayer* root =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("root"))->Layer();
PaintLayer* target =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer();
ClipRectsContext context(root, kPaintingClipRectsIgnoringOverflowClip);
PaintLayer::GeometryMapperOption option = PaintLayer::kDoNotUseGeometryMapper;
if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled())
option = PaintLayer::kUseGeometryMapper;
LayoutRect infinite_rect(LayoutRect::InfiniteIntRect());
LayoutRect layer_bounds(infinite_rect);
ClipRect background_rect(infinite_rect);
ClipRect foreground_rect(infinite_rect);
target->Clipper(option).CalculateRects(context, infinite_rect, layer_bounds,
background_rect, foreground_rect);
EXPECT_TRUE(IsInfinite(background_rect.Rect()));
EXPECT_TRUE(IsInfinite(foreground_rect.Rect()));
}
TEST_P(PaintLayerClipperTest, IgnoreRootLayerClipWithBothClip) {
SetBodyInnerHTML(
"<style>"
" #root { "
" width: 400px; height: 400px;"
" position: absolute; clip: rect(0, 50px, 100px, 0);"
" overflow: hidden;"
" }"
" #target {"
" position: relative;"
" }"
"</style>"
"<div id='root'>"
" <div id='target'></div>"
"</div>");
PaintLayer* root =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("root"))->Layer();
PaintLayer* target =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer();
ClipRectsContext context(root, kPaintingClipRectsIgnoringOverflowClip);
PaintLayer::GeometryMapperOption option = PaintLayer::kDoNotUseGeometryMapper;
if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled())
option = PaintLayer::kUseGeometryMapper;
LayoutRect infinite_rect(LayoutRect::InfiniteIntRect());
LayoutRect layer_bounds(infinite_rect);
ClipRect background_rect(infinite_rect);
ClipRect foreground_rect(infinite_rect);
target->Clipper(option).CalculateRects(context, infinite_rect, layer_bounds,
background_rect, foreground_rect);
EXPECT_TRUE(IsInfinite(background_rect.Rect()));
EXPECT_TRUE(IsInfinite(foreground_rect.Rect()));
}
} // namespace blink } // namespace blink
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