blob: f20e8160fbf2881374367e680c24213c6652d88f [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/layout/compositing/CompositedLayerMapping.h"
#include "core/frame/FrameView.h"
#include "core/layout/LayoutBoxModelObject.h"
#include "core/layout/LayoutTestHelper.h"
#include "core/layout/api/LayoutViewItem.h"
#include "core/page/scrolling/TopDocumentRootScrollerController.h"
#include "core/paint/PaintLayer.h"
#include "platform/testing/RuntimeEnabledFeaturesTestHelpers.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
typedef bool TestParamRootLayerScrolling;
class CompositedLayerMappingTest
: public testing::WithParamInterface<TestParamRootLayerScrolling>,
private ScopedRootLayerScrollingForTest,
public RenderingTest {
public:
CompositedLayerMappingTest()
: ScopedRootLayerScrollingForTest(GetParam()),
RenderingTest(SingleChildFrameLoaderClient::create()) {}
protected:
IntRect recomputeInterestRect(const GraphicsLayer* graphicsLayer) {
return static_cast<CompositedLayerMapping*>(graphicsLayer->client())
->recomputeInterestRect(graphicsLayer);
}
IntRect computeInterestRect(
const CompositedLayerMapping* compositedLayerMapping,
GraphicsLayer* graphicsLayer,
IntRect previousInterestRect) {
return compositedLayerMapping->computeInterestRect(graphicsLayer,
previousInterestRect);
}
bool shouldFlattenTransform(const GraphicsLayer& layer) const {
return layer.shouldFlattenTransform();
}
bool interestRectChangedEnoughToRepaint(const IntRect& previousInterestRect,
const IntRect& newInterestRect,
const IntSize& layerSize) {
return CompositedLayerMapping::interestRectChangedEnoughToRepaint(
previousInterestRect, newInterestRect, layerSize);
}
IntRect previousInterestRect(const GraphicsLayer* graphicsLayer) {
return graphicsLayer->m_previousInterestRect;
}
private:
void SetUp() override {
RenderingTest::SetUp();
enableCompositing();
}
void TearDown() override { RenderingTest::TearDown(); }
};
#define EXPECT_RECT_EQ(expected, actual) \
do { \
const IntRect& actualRect = actual; \
EXPECT_EQ(expected.x(), actualRect.x()); \
EXPECT_EQ(expected.y(), actualRect.y()); \
EXPECT_EQ(expected.width(), actualRect.width()); \
EXPECT_EQ(expected.height(), actualRect.height()); \
} while (false)
INSTANTIATE_TEST_CASE_P(All, CompositedLayerMappingTest, ::testing::Bool());
TEST_P(CompositedLayerMappingTest, SimpleInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 200px; will-change: "
"transform'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
EXPECT_RECT_EQ(IntRect(0, 0, 200, 200),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, TallLayerInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 10000px; will-change: "
"transform'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(paintLayer->graphicsLayerBacking());
// Screen-space visible content rect is [8, 8, 200, 600]. Mapping back to
// local, adding 4000px in all directions, then clipping, yields this rect.
EXPECT_RECT_EQ(IntRect(0, 0, 200, 4592),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, TallLayerWholeDocumentInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 10000px; will-change: "
"transform'></div>");
document().settings()->setMainFrameClipsContent(false);
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(paintLayer->graphicsLayerBacking());
ASSERT_TRUE(paintLayer->compositedLayerMapping());
// Clipping is disabled in recomputeInterestRect.
EXPECT_RECT_EQ(IntRect(0, 0, 200, 10000),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
EXPECT_RECT_EQ(
IntRect(0, 0, 200, 10000),
computeInterestRect(paintLayer->compositedLayerMapping(),
paintLayer->graphicsLayerBacking(), IntRect()));
}
TEST_P(CompositedLayerMappingTest, VerticalRightLeftWritingModeDocument) {
setBodyInnerHTML(
"<style>html,body { margin: 0px } html { -webkit-writing-mode: "
"vertical-rl}</style> <div id='target' style='width: 10000px; height: "
"200px;'></div>");
document().view()->updateAllLifecyclePhases();
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(-5000, 0), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
PaintLayer* paintLayer = document().layoutViewItem().layer();
ASSERT_TRUE(paintLayer->graphicsLayerBacking());
ASSERT_TRUE(paintLayer->compositedLayerMapping());
// A scroll by -5000px is equivalent to a scroll by (10000 - 5000 - 800)px =
// 4200px in non-RTL mode. Expanding the resulting rect by 4000px in each
// direction yields this result.
EXPECT_RECT_EQ(
IntRect(200, 0, 8800, 600),
recomputeInterestRect(paintLayer->graphicsLayerBackingForScrolling()));
}
TEST_P(CompositedLayerMappingTest, RotatedInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 200px; will-change: "
"transform; transform: rotateZ(45deg)'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
EXPECT_RECT_EQ(IntRect(0, 0, 200, 200),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, RotatedInterestRectNear90Degrees) {
setBodyInnerHTML(
"<div id='target' style='width: 10000px; height: 200px; will-change: "
"transform; transform: rotateY(89.9999deg)'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
// Because the layer is rotated to almost 90 degrees, floating-point error
// leads to a reverse-projected rect that is much much larger than the
// original layer size in certain dimensions. In such cases, we often fall
// back to the 4000px interest rect padding amount.
EXPECT_RECT_EQ(IntRect(0, 0, 4000, 200),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, 3D90DegRotatedTallInterestRect) {
// It's rotated 90 degrees about the X axis, which means its visual content
// rect is empty, and so the interest rect is the default (0, 0, 4000, 4000)
// intersected with the layer bounds.
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 10000px; will-change: "
"transform; transform: rotateY(90deg)'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
EXPECT_RECT_EQ(IntRect(0, 0, 200, 4000),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, 3D45DegRotatedTallInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 10000px; will-change: "
"transform; transform: rotateY(45deg)'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
EXPECT_RECT_EQ(IntRect(0, 0, 200, 4592),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, RotatedTallInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 10000px; will-change: "
"transform; transform: rotateZ(45deg)'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
EXPECT_RECT_EQ(IntRect(0, 0, 200, 4000),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, WideLayerInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 10000px; height: 200px; will-change: "
"transform'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
// Screen-space visible content rect is [8, 8, 800, 200] (the screen is
// 800x600). Mapping back to local, adding 4000px in all directions, then
// clipping, yields this rect.
EXPECT_RECT_EQ(IntRect(0, 0, 4792, 200),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, FixedPositionInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 300px; height: 400px; will-change: "
"transform; position: fixed; top: 100px; left: 200px;'></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
EXPECT_RECT_EQ(IntRect(0, 0, 300, 400),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, LayerOffscreenInterestRect) {
setBodyInnerHTML(
"<div id='target' style='width: 200px; height: 200px; will-change: "
"transform; position: absolute; top: 9000px; left: 0px;'>"
"</div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
// Offscreen layers are painted as usual.
EXPECT_RECT_EQ(IntRect(0, 0, 200, 200),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, ScrollingLayerInterestRect) {
setBodyInnerHTML(
"<style>div::-webkit-scrollbar{ width: 5px; }</style>"
"<div id='target' style='width: 200px; height: 200px; will-change: "
"transform; overflow: scroll'>"
"<div style='width: 100px; height: 10000px'></div></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(paintLayer->graphicsLayerBacking());
// Offscreen layers are painted as usual.
ASSERT_TRUE(paintLayer->compositedLayerMapping()->scrollingLayer());
EXPECT_RECT_EQ(
IntRect(0, 0, 195, 4592),
recomputeInterestRect(paintLayer->graphicsLayerBackingForScrolling()));
}
TEST_P(CompositedLayerMappingTest, ClippedBigLayer) {
setBodyInnerHTML(
"<div style='width: 1px; height: 1px; overflow: hidden'>"
"<div id='target' style='width: 10000px; height: 10000px; will-change: "
"transform'></div></div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(paintLayer->graphicsLayerBacking());
// Offscreen layers are painted as usual.
EXPECT_RECT_EQ(IntRect(0, 0, 4001, 4001),
recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, ClippingMaskLayer) {
if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
return;
const AtomicString styleWithoutClipping =
"backface-visibility: hidden; width: 200px; height: 200px";
const AtomicString styleWithBorderRadius =
styleWithoutClipping + "; border-radius: 10px";
const AtomicString styleWithClipPath =
styleWithoutClipping + "; -webkit-clip-path: inset(10px)";
setBodyInnerHTML("<video id='video' src='x' style='" + styleWithoutClipping +
"'></video>");
document().view()->updateAllLifecyclePhases();
Element* videoElement = document().getElementById("video");
GraphicsLayer* graphicsLayer =
toLayoutBoxModelObject(videoElement->layoutObject())
->layer()
->graphicsLayerBacking();
EXPECT_FALSE(graphicsLayer->maskLayer());
EXPECT_FALSE(graphicsLayer->contentsClippingMaskLayer());
videoElement->setAttribute(HTMLNames::styleAttr, styleWithBorderRadius);
document().view()->updateAllLifecyclePhases();
EXPECT_FALSE(graphicsLayer->maskLayer());
EXPECT_TRUE(graphicsLayer->contentsClippingMaskLayer());
videoElement->setAttribute(HTMLNames::styleAttr, styleWithClipPath);
document().view()->updateAllLifecyclePhases();
EXPECT_TRUE(graphicsLayer->maskLayer());
EXPECT_FALSE(graphicsLayer->contentsClippingMaskLayer());
videoElement->setAttribute(HTMLNames::styleAttr, styleWithoutClipping);
document().view()->updateAllLifecyclePhases();
EXPECT_FALSE(graphicsLayer->maskLayer());
EXPECT_FALSE(graphicsLayer->contentsClippingMaskLayer());
}
TEST_P(CompositedLayerMappingTest, ScrollContentsFlattenForScroller) {
setBodyInnerHTML(
"<style>div::-webkit-scrollbar{ width: 5px; }</style>"
"<div id='scroller' style='width: 100px; height: 100px; overflow: "
"scroll; will-change: transform'>"
"<div style='width: 1000px; height: 1000px;'>Foo</div>Foo</div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("scroller");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
CompositedLayerMapping* compositedLayerMapping =
paintLayer->compositedLayerMapping();
ASSERT_TRUE(compositedLayerMapping);
EXPECT_FALSE(
shouldFlattenTransform(*compositedLayerMapping->mainGraphicsLayer()));
EXPECT_FALSE(
shouldFlattenTransform(*compositedLayerMapping->scrollingLayer()));
EXPECT_TRUE(shouldFlattenTransform(
*compositedLayerMapping->scrollingContentsLayer()));
}
TEST_P(CompositedLayerMappingTest, InterestRectChangedEnoughToRepaintEmpty) {
IntSize layerSize(1000, 1000);
// Both empty means there is nothing to do.
EXPECT_FALSE(
interestRectChangedEnoughToRepaint(IntRect(), IntRect(), layerSize));
// Going from empty to non-empty means we must re-record because it could be
// the first frame after construction or Clear.
EXPECT_TRUE(interestRectChangedEnoughToRepaint(IntRect(), IntRect(0, 0, 1, 1),
layerSize));
// Going from non-empty to empty is not special-cased.
EXPECT_FALSE(interestRectChangedEnoughToRepaint(IntRect(0, 0, 1, 1),
IntRect(), layerSize));
}
TEST_P(CompositedLayerMappingTest,
InterestRectChangedEnoughToRepaintNotBigEnough) {
IntSize layerSize(1000, 1000);
IntRect previousInterestRect(100, 100, 100, 100);
EXPECT_FALSE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(100, 100, 90, 90), layerSize));
EXPECT_FALSE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(100, 100, 100, 100), layerSize));
EXPECT_FALSE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(1, 1, 200, 200), layerSize));
}
TEST_P(CompositedLayerMappingTest,
InterestRectChangedEnoughToRepaintNotBigEnoughButNewAreaTouchesEdge) {
IntSize layerSize(500, 500);
IntRect previousInterestRect(100, 100, 100, 100);
// Top edge.
EXPECT_TRUE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(100, 0, 100, 200), layerSize));
// Left edge.
EXPECT_TRUE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(0, 100, 200, 100), layerSize));
// Bottom edge.
EXPECT_TRUE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(100, 100, 100, 400), layerSize));
// Right edge.
EXPECT_TRUE(interestRectChangedEnoughToRepaint(
previousInterestRect, IntRect(100, 100, 400, 100), layerSize));
}
// Verifies that having a current viewport that touches a layer edge does not
// force re-recording.
TEST_P(CompositedLayerMappingTest,
InterestRectChangedEnoughToRepaintCurrentViewportTouchesEdge) {
IntSize layerSize(500, 500);
IntRect newInterestRect(100, 100, 300, 300);
// Top edge.
EXPECT_FALSE(interestRectChangedEnoughToRepaint(IntRect(100, 0, 100, 100),
newInterestRect, layerSize));
// Left edge.
EXPECT_FALSE(interestRectChangedEnoughToRepaint(IntRect(0, 100, 100, 100),
newInterestRect, layerSize));
// Bottom edge.
EXPECT_FALSE(interestRectChangedEnoughToRepaint(IntRect(300, 400, 100, 100),
newInterestRect, layerSize));
// Right edge.
EXPECT_FALSE(interestRectChangedEnoughToRepaint(IntRect(400, 300, 100, 100),
newInterestRect, layerSize));
}
TEST_P(CompositedLayerMappingTest,
InterestRectChangedEnoughToRepaintScrollScenarios) {
IntSize layerSize(1000, 1000);
IntRect previousInterestRect(100, 100, 100, 100);
IntRect newInterestRect(previousInterestRect);
newInterestRect.move(512, 0);
EXPECT_FALSE(interestRectChangedEnoughToRepaint(previousInterestRect,
newInterestRect, layerSize));
newInterestRect.move(0, 512);
EXPECT_FALSE(interestRectChangedEnoughToRepaint(previousInterestRect,
newInterestRect, layerSize));
newInterestRect.move(1, 0);
EXPECT_TRUE(interestRectChangedEnoughToRepaint(previousInterestRect,
newInterestRect, layerSize));
newInterestRect.move(-1, 1);
EXPECT_TRUE(interestRectChangedEnoughToRepaint(previousInterestRect,
newInterestRect, layerSize));
}
TEST_P(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) {
setBodyInnerHTML(
"<style>"
" ::-webkit-scrollbar { width: 0; height: 0; }"
" body { margin: 0; }"
"</style>"
"<div id='div' style='width: 100px; height: 10000px'>Text</div>");
document().view()->updateAllLifecyclePhases();
GraphicsLayer* rootScrollingLayer =
document().layoutViewItem().layer()->graphicsLayerBackingForScrolling();
EXPECT_RECT_EQ(IntRect(0, 0, 800, 4600),
previousInterestRect(rootScrollingLayer));
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0, 300), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
// Still use the previous interest rect because the recomputed rect hasn't
// changed enough.
EXPECT_RECT_EQ(IntRect(0, 0, 800, 4900),
recomputeInterestRect(rootScrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 800, 4600),
previousInterestRect(rootScrollingLayer));
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0, 600), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
// Use recomputed interest rect because it changed enough.
EXPECT_RECT_EQ(IntRect(0, 0, 800, 5200),
recomputeInterestRect(rootScrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 800, 5200),
previousInterestRect(rootScrollingLayer));
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0, 5400), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
EXPECT_RECT_EQ(IntRect(0, 1400, 800, 8600),
recomputeInterestRect(rootScrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 1400, 800, 8600),
previousInterestRect(rootScrollingLayer));
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0, 9000), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
// Still use the previous interest rect because it contains the recomputed
// interest rect.
EXPECT_RECT_EQ(IntRect(0, 5000, 800, 5000),
recomputeInterestRect(rootScrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 1400, 800, 8600),
previousInterestRect(rootScrollingLayer));
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0, 2000), ProgrammaticScroll);
// Use recomputed interest rect because it changed enough.
document().view()->updateAllLifecyclePhases();
EXPECT_RECT_EQ(IntRect(0, 0, 800, 6600),
recomputeInterestRect(rootScrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 800, 6600),
previousInterestRect(rootScrollingLayer));
}
TEST_P(CompositedLayerMappingTest, InterestRectChangeOnShrunkenViewport) {
setBodyInnerHTML(
"<style>"
" ::-webkit-scrollbar { width: 0; height: 0; }"
" body { margin: 0; }"
"</style>"
"<div id='div' style='width: 100px; height: 10000px'>Text</div>");
document().view()->updateAllLifecyclePhases();
GraphicsLayer* rootScrollingLayer =
document().layoutViewItem().layer()->graphicsLayerBackingForScrolling();
EXPECT_RECT_EQ(IntRect(0, 0, 800, 4600),
previousInterestRect(rootScrollingLayer));
document().view()->setFrameRect(IntRect(0, 0, 800, 60));
document().view()->updateAllLifecyclePhases();
// Repaint required, so interest rect should be updated to shrunken size.
EXPECT_RECT_EQ(IntRect(0, 0, 800, 4060),
recomputeInterestRect(rootScrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 800, 4060),
previousInterestRect(rootScrollingLayer));
}
TEST_P(CompositedLayerMappingTest, InterestRectChangeOnScroll) {
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);
setBodyInnerHTML(
"<style>"
" ::-webkit-scrollbar { width: 0; height: 0; }"
" body { margin: 0; }"
"</style>"
"<div id='scroller' style='width: 400px; height: 400px; overflow: "
"scroll'>"
" <div id='content' style='width: 100px; height: 10000px'>Text</div>"
"</div");
document().view()->updateAllLifecyclePhases();
Element* scroller = document().getElementById("scroller");
GraphicsLayer* scrollingLayer =
scroller->layoutBox()->layer()->graphicsLayerBackingForScrolling();
EXPECT_RECT_EQ(IntRect(0, 0, 400, 4600),
previousInterestRect(scrollingLayer));
scroller->setScrollTop(300);
document().view()->updateAllLifecyclePhases();
// Still use the previous interest rect because the recomputed rect hasn't
// changed enough.
EXPECT_RECT_EQ(IntRect(0, 0, 400, 4900),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 400, 4600),
previousInterestRect(scrollingLayer));
scroller->setScrollTop(600);
document().view()->updateAllLifecyclePhases();
// Use recomputed interest rect because it changed enough.
EXPECT_RECT_EQ(IntRect(0, 0, 400, 5200),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 400, 5200),
previousInterestRect(scrollingLayer));
scroller->setScrollTop(5400);
document().view()->updateAllLifecyclePhases();
EXPECT_RECT_EQ(IntRect(0, 1400, 400, 8600),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 1400, 400, 8600),
previousInterestRect(scrollingLayer));
scroller->setScrollTop(9000);
document().view()->updateAllLifecyclePhases();
// Still use the previous interest rect because it contains the recomputed
// interest rect.
EXPECT_RECT_EQ(IntRect(0, 5000, 400, 5000),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 1400, 400, 8600),
previousInterestRect(scrollingLayer));
scroller->setScrollTop(2000);
// Use recomputed interest rect because it changed enough.
document().view()->updateAllLifecyclePhases();
EXPECT_RECT_EQ(IntRect(0, 0, 400, 6600),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 0, 400, 6600),
previousInterestRect(scrollingLayer));
}
TEST_P(CompositedLayerMappingTest,
InterestRectShouldChangeOnPaintInvalidation) {
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);
setBodyInnerHTML(
"<style>"
" ::-webkit-scrollbar { width: 0; height: 0; }"
" body { margin: 0; }"
"</style>"
"<div id='scroller' style='width: 400px; height: 400px; overflow: "
"scroll'>"
" <div id='content' style='width: 100px; height: 10000px'>Text</div>"
"</div");
document().view()->updateAllLifecyclePhases();
Element* scroller = document().getElementById("scroller");
GraphicsLayer* scrollingLayer =
scroller->layoutBox()->layer()->graphicsLayerBackingForScrolling();
scroller->setScrollTop(5400);
document().view()->updateAllLifecyclePhases();
scroller->setScrollTop(9400);
// The above code creates an interest rect bigger than the interest rect if
// recomputed now.
document().view()->updateAllLifecyclePhases();
EXPECT_RECT_EQ(IntRect(0, 5400, 400, 4600),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 1400, 400, 8600),
previousInterestRect(scrollingLayer));
// Paint invalidation and repaint should change previous paint interest rect.
document().getElementById("content")->setTextContent("Change");
document().view()->updateAllLifecyclePhases();
EXPECT_RECT_EQ(IntRect(0, 5400, 400, 4600),
recomputeInterestRect(scrollingLayer));
EXPECT_RECT_EQ(IntRect(0, 5400, 400, 4600),
previousInterestRect(scrollingLayer));
}
TEST_P(CompositedLayerMappingTest,
InterestRectOfSquashingLayerWithNegativeOverflow) {
setBodyInnerHTML(
"<style>body { margin: 0; font-size: 16px; }</style>"
"<div style='position: absolute; top: -500px; width: 200px; height: "
"700px; will-change: transform'></div>"
"<div id='squashed' style='position: absolute; top: 190px;'>"
" <div id='inside' style='width: 100px; height: 100px; text-indent: "
"-10000px'>text</div>"
"</div>");
EXPECT_EQ(document()
.getElementById("inside")
->layoutBox()
->visualOverflowRect()
.size()
.height(),
100);
CompositedLayerMapping* groupedMapping = document()
.getElementById("squashed")
->layoutBox()
->layer()
->groupedMapping();
// The squashing layer is at (-10000, 190, 10100, 100) in viewport
// coordinates.
// The following rect is at (-4000, 190, 4100, 100) in viewport coordinates.
EXPECT_RECT_EQ(IntRect(6000, 0, 4100, 100),
groupedMapping->computeInterestRect(
groupedMapping->squashingLayer(), IntRect()));
}
TEST_P(CompositedLayerMappingTest,
InterestRectOfSquashingLayerWithAncestorClip) {
setBodyInnerHTML(
"<style>body { margin: 0; }</style>"
"<div style='overflow: hidden; width: 400px; height: 400px'>"
" <div style='position: relative; backface-visibility: hidden'>"
" <div style='position: absolute; top: -500px; width: 200px; height: "
"700px; backface-visibility: hidden'></div>"
// Above overflow:hidden div and two composited layers make the squashing
// layer a child of an ancestor clipping layer.
" <div id='squashed' style='height: 1000px; width: 10000px; right: 0; "
"position: absolute'></div>"
" </div>"
"</div>");
CompositedLayerMapping* groupedMapping = document()
.getElementById("squashed")
->layoutBox()
->layer()
->groupedMapping();
// The squashing layer is at (-9600, 0, 10000, 1000) in viewport coordinates.
// The following rect is at (-4000, 0, 4400, 1000) in viewport coordinates.
EXPECT_RECT_EQ(IntRect(5600, 0, 4400, 1000),
groupedMapping->computeInterestRect(
groupedMapping->squashingLayer(), IntRect()));
}
TEST_P(CompositedLayerMappingTest, InterestRectOfIframeInScrolledDiv) {
document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
setBodyInnerHTML(
"<style>body { margin: 0; }</style>"
"<div style='width: 200; height: 8000px'></div>"
"<iframe src='http://test.com' width='500' height='500' "
"frameBorder='0'>"
"</iframe>");
setChildFrameHTML(
"<style>body { margin: 0; } #target { width: 200px; height: 200px; "
"will-change: transform}</style><div id=target></div>");
// Scroll 8000 pixels down to move the iframe into view.
document().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0.0, 8000.0), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
Element* target = childDocument().getElementById("target");
ASSERT_TRUE(target);
EXPECT_RECT_EQ(
IntRect(0, 0, 200, 200),
recomputeInterestRect(
target->layoutObject()->enclosingLayer()->graphicsLayerBacking()));
}
TEST_P(CompositedLayerMappingTest, InterestRectOfScrolledIframe) {
document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);
setBodyInnerHTML(
"<style>body { margin: 0; } ::-webkit-scrollbar { display: none; "
"}</style>"
"<iframe src='http://test.com' width='500' height='500' "
"frameBorder='0'>"
"</iframe>");
setChildFrameHTML(
"<style>body { margin: 0; } #target { width: 200px; "
"height: 8000px;}</style><div id=target></div>");
document().view()->updateAllLifecyclePhases();
// Scroll 7500 pixels down to bring the scrollable area to the bottom.
childDocument().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0.0, 7500.0), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
ASSERT_TRUE(childDocument().view()->layoutViewItem().hasLayer());
EXPECT_RECT_EQ(
IntRect(0, 3500, 500, 4500),
recomputeInterestRect(childDocument()
.view()
->layoutViewItem()
.enclosingLayer()
->graphicsLayerBackingForScrolling()));
}
TEST_P(CompositedLayerMappingTest, InterestRectOfIframeWithContentBoxOffset) {
document().setBaseURLOverride(KURL(ParsedURLString, "http://test.com"));
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);
// Set a 10px border in order to have a contentBoxOffset for the iframe
// element.
setBodyInnerHTML(
"<style>body { margin: 0; } #frame { border: 10px solid black; } "
"::-webkit-scrollbar { display: none; }</style>"
"<iframe src='http://test.com' width='500' height='500' "
"frameBorder='0'>"
"</iframe>");
setChildFrameHTML(
"<style>body { margin: 0; } #target { width: 200px; "
"height: 8000px;}</style> <div id=target></div>");
document().view()->updateAllLifecyclePhases();
// Scroll 3000 pixels down to bring the scrollable area to somewhere in the
// middle.
childDocument().view()->layoutViewportScrollableArea()->setScrollOffset(
ScrollOffset(0.0, 3000.0), ProgrammaticScroll);
document().view()->updateAllLifecyclePhases();
ASSERT_TRUE(childDocument().view()->layoutViewItem().hasLayer());
// The width is 485 pixels due to the size of the scrollbar.
EXPECT_RECT_EQ(
IntRect(0, 0, 500, 7500),
recomputeInterestRect(childDocument()
.view()
->layoutViewItem()
.enclosingLayer()
->graphicsLayerBackingForScrolling()));
}
TEST_P(CompositedLayerMappingTest,
ScrollingContentsAndForegroundLayerPaintingPhase) {
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);
setBodyInnerHTML(
"<div id='container' style='position: relative; z-index: 1; overflow: "
"scroll; width: 300px; height: 300px'>"
" <div id='negative-composited-child' style='background-color: red; "
"width: 1px; height: 1px; position: absolute; backface-visibility: "
"hidden; z-index: -1'></div>"
" <div style='background-color: blue; width: 2000px; height: 2000px; "
"position: relative; top: 10px'></div>"
"</div>");
CompositedLayerMapping* mapping =
toLayoutBlock(getLayoutObjectByElementId("container"))
->layer()
->compositedLayerMapping();
ASSERT_TRUE(mapping->scrollingContentsLayer());
EXPECT_EQ(static_cast<GraphicsLayerPaintingPhase>(
GraphicsLayerPaintOverflowContents |
GraphicsLayerPaintCompositedScroll),
mapping->scrollingContentsLayer()->paintingPhase());
ASSERT_TRUE(mapping->foregroundLayer());
EXPECT_EQ(
static_cast<GraphicsLayerPaintingPhase>(
GraphicsLayerPaintForeground | GraphicsLayerPaintOverflowContents),
mapping->foregroundLayer()->paintingPhase());
Element* negativeCompositedChild =
document().getElementById("negative-composited-child");
negativeCompositedChild->parentNode()->removeChild(negativeCompositedChild);
document().view()->updateAllLifecyclePhases();
mapping = toLayoutBlock(getLayoutObjectByElementId("container"))
->layer()
->compositedLayerMapping();
ASSERT_TRUE(mapping->scrollingContentsLayer());
EXPECT_EQ(
static_cast<GraphicsLayerPaintingPhase>(
GraphicsLayerPaintOverflowContents |
GraphicsLayerPaintCompositedScroll | GraphicsLayerPaintForeground),
mapping->scrollingContentsLayer()->paintingPhase());
EXPECT_FALSE(mapping->foregroundLayer());
}
TEST_P(CompositedLayerMappingTest,
DecorationOutlineLayerOnlyCreatedInCompositedScrolling) {
setBodyInnerHTML(
"<style>"
"#target { overflow: scroll; height: 200px; width: 200px; will-change: "
"transform; background: white local content-box; "
"outline: 1px solid blue; outline-offset: -2px;}"
"#scrolled { height: 300px; }"
"</style>"
"<div id=\"parent\">"
" <div id=\"target\"><div id=\"scrolled\"></div></div>"
"</div>");
document().view()->updateAllLifecyclePhases();
Element* element = document().getElementById("target");
PaintLayer* paintLayer =
toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(paintLayer);
// Decoration outline layer is created when composited scrolling.
EXPECT_TRUE(paintLayer->hasCompositedLayerMapping());
EXPECT_TRUE(paintLayer->needsCompositedScrolling());
CompositedLayerMapping* mapping = paintLayer->compositedLayerMapping();
EXPECT_TRUE(mapping->decorationOutlineLayer());
// No decoration outline layer is created when not composited scrolling.
element->setAttribute(HTMLNames::styleAttr, "overflow: visible;");
document().view()->updateAllLifecyclePhases();
paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer();
ASSERT_TRUE(paintLayer);
mapping = paintLayer->compositedLayerMapping();
EXPECT_FALSE(paintLayer->needsCompositedScrolling());
EXPECT_FALSE(mapping->decorationOutlineLayer());
}
TEST_P(CompositedLayerMappingTest,
DecorationOutlineLayerCreatedAndDestroyedInCompositedScrolling) {
setBodyInnerHTML(
"<style>"
"#scroller { overflow: scroll; height: 200px; width: 200px; background: "
"white local content-box; outline: 1px solid blue; contain: paint; }"
"#scrolled { height: 300px; }"
"</style>"
"<div id=\"parent\">"
" <div id=\"scroller\"><div id=\"scrolled\"></div></div>"
"</div>");
document().view()->updateAllLifecyclePhases();
Element* scroller = document().getElementById("scroller");
PaintLayer* paintLayer =
toLayoutBoxModelObject(scroller->layoutObject())->layer();
ASSERT_TRUE(paintLayer);
CompositedLayerMapping* mapping = paintLayer->compositedLayerMapping();
EXPECT_FALSE(mapping->decorationOutlineLayer());
// The decoration outline layer is created when composited scrolling
// with an outline drawn over the composited scrolling region.
scroller->setAttribute(HTMLNames::styleAttr, "outline-offset: -2px;");
document().view()->updateAllLifecyclePhases();
paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer();
ASSERT_TRUE(paintLayer);
mapping = paintLayer->compositedLayerMapping();
EXPECT_TRUE(paintLayer->needsCompositedScrolling());
EXPECT_TRUE(mapping->decorationOutlineLayer());
// The decoration outline layer is destroyed when the scrolling region
// will not be covered up by the outline.
scroller->removeAttribute(HTMLNames::styleAttr);
document().view()->updateAllLifecyclePhases();
paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer();
ASSERT_TRUE(paintLayer);
mapping = paintLayer->compositedLayerMapping();
EXPECT_FALSE(mapping->decorationOutlineLayer());
}
TEST_P(CompositedLayerMappingTest,
BackgroundPaintedIntoGraphicsLayerIfNotCompositedScrolling) {
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);
setBodyInnerHTML(
"<div id='container' style='overflow: scroll; width: 300px; height: "
"300px; border-radius: 5px; background: white; will-change: transform;'>"
" <div style='background-color: blue; width: 2000px; height: "
"2000px;'></div>"
"</div>");
PaintLayer* layer =
toLayoutBlock(getLayoutObjectByElementId("container"))->layer();
EXPECT_EQ(BackgroundPaintInScrollingContents,
layer->backgroundPaintLocation());
// We currently don't use composited scrolling when the container has a
// border-radius so even though we can paint the background onto the scrolling
// contents layer we don't have a scrolling contents layer to paint into in
// this case.
CompositedLayerMapping* mapping = layer->compositedLayerMapping();
EXPECT_FALSE(mapping->hasScrollingLayer());
EXPECT_FALSE(mapping->backgroundPaintsOntoScrollingContentsLayer());
}
// Make sure that clipping layers are removed or their masking bit turned off
// when they're an ancestor of the root scroller element.
TEST_P(CompositedLayerMappingTest, RootScrollerAncestorsNotClipped) {
NonThrowableExceptionState nonThrow;
TopDocumentRootScrollerController& rootScrollerController =
document().frameHost()->globalRootScrollerController();
setBodyInnerHTML(
// The container DIV is composited with scrolling contents and a
// non-composited parent that clips it.
"<div id='clip' style='overflow: hidden; width: 200px; height: 200px; "
"position: absolute; left: 0px; top: 0px;'>"
" <div id='container' style='transform: translateZ(0); overflow: "
"scroll; width: 300px; height: 300px'>"
" <div style='width: 2000px; height: 2000px;'>lorem ipsum</div>"
" <div id='innerScroller' style='width: 800px; height: 600px; "
"left: 0px; top: 0px; position: absolute; overflow: scroll'>"
" <div style='height: 2000px; width: 2000px'></div>"
" </div>"
" </div>"
"</div>"
// The container DIV is composited with scrolling contents and a
// composited parent that clips it.
"<div id='clip2' style='transform: translateZ(0); position: absolute; "
"left: 0px; top: 0px; overflow: hidden; width: 200px; height: 200px'>"
" <div id='container2' style='transform: translateZ(0); overflow: "
"scroll; width: 300px; height: 300px'>"
" <div style='width: 2000px; height: 2000px;'>lorem ipsum</div>"
" <div id='innerScroller2' style='width: 800px; height: 600px; "
"left: 0px; top: 0px; position: absolute; overflow: scroll'>"
" <div style='height: 2000px; width: 2000px'></div>"
" </div>"
" </div>"
"</div>"
// The container DIV is composited without scrolling contents but
// composited children that it clips.
"<div id='container3' style='translateZ(0); position: absolute; left: "
"0px; top: 0px; z-index: 1; overflow: hidden; width: 300px; height: "
"300px'>"
" <div style='transform: translateZ(0); z-index: -1; width: 2000px; "
"height: 2000px;'>lorem ipsum</div>"
" <div id='innerScroller3' style='width: 800px; height: 600px; "
"left: 0px; top: 0px; position: absolute; overflow: scroll'>"
" <div style='height: 2000px; width: 2000px'></div>"
" </div>"
"</div>");
CompositedLayerMapping* mapping =
toLayoutBlock(getLayoutObjectByElementId("container"))
->layer()
->compositedLayerMapping();
CompositedLayerMapping* mapping2 =
toLayoutBlock(getLayoutObjectByElementId("container2"))
->layer()
->compositedLayerMapping();
CompositedLayerMapping* mapping3 =
toLayoutBlock(getLayoutObjectByElementId("container3"))
->layer()
->compositedLayerMapping();
Element* innerScroller = document().getElementById("innerScroller");
Element* innerScroller2 = document().getElementById("innerScroller2");
Element* innerScroller3 = document().getElementById("innerScroller3");
ASSERT_TRUE(mapping);
ASSERT_TRUE(mapping2);
ASSERT_TRUE(mapping3);
ASSERT_TRUE(innerScroller);
ASSERT_TRUE(innerScroller2);
ASSERT_TRUE(innerScroller3);
// Since there's no need to composite the clip and we prefer LCD text, the
// mapping should create an ancestorClippingLayer.
ASSERT_TRUE(mapping->scrollingLayer());
ASSERT_TRUE(mapping->ancestorClippingLayer());
// Since the clip has a transform it should be composited so there's no
// need for an ancestor clipping layer.
ASSERT_TRUE(mapping2->scrollingLayer());
// The third <div> should have a clipping layer since it's composited and
// clips composited children.
ASSERT_TRUE(mapping3->clippingLayer());
// All scrolling and clipping layers should have masksToBounds set on them.
{
EXPECT_TRUE(mapping->scrollingLayer()->platformLayer()->masksToBounds());
EXPECT_TRUE(
mapping->ancestorClippingLayer()->platformLayer()->masksToBounds());
EXPECT_TRUE(mapping2->scrollingLayer()->platformLayer()->masksToBounds());
EXPECT_TRUE(mapping3->clippingLayer()->platformLayer()->masksToBounds());
}
// Set the inner scroller in the first container as the root scroller. Its
// clipping layer should be removed and the scrolling layer should not
// mask.
{
document().setRootScroller(innerScroller, nonThrow);
document().view()->updateAllLifecyclePhases();
ASSERT_EQ(innerScroller, rootScrollerController.globalRootScroller());
EXPECT_FALSE(mapping->ancestorClippingLayer());
EXPECT_FALSE(mapping->scrollingLayer()->platformLayer()->masksToBounds());
}
// Set the inner scroller in the second container as the root scroller. Its
// scrolling layer should no longer mask. The clipping and scrolling layers
// on the first container should now reset back.
{
document().setRootScroller(innerScroller2, nonThrow);
document().view()->updateAllLifecyclePhases();
ASSERT_EQ(innerScroller2, rootScrollerController.globalRootScroller());
EXPECT_TRUE(mapping->ancestorClippingLayer());
EXPECT_TRUE(
mapping->ancestorClippingLayer()->platformLayer()->masksToBounds());
EXPECT_TRUE(mapping->scrollingLayer()->platformLayer()->masksToBounds());
EXPECT_FALSE(mapping2->scrollingLayer()->platformLayer()->masksToBounds());
}
// Set the inner scroller in the third container as the root scroller. Its
// clipping layer should be removed.
{
document().setRootScroller(innerScroller3, nonThrow);
document().view()->updateAllLifecyclePhases();
ASSERT_EQ(innerScroller3, rootScrollerController.globalRootScroller());
EXPECT_TRUE(mapping2->scrollingLayer()->platformLayer()->masksToBounds());
EXPECT_FALSE(mapping3->clippingLayer());
}
// Unset the root scroller. The clipping layer on the third container should
// be restored.
{
document().setRootScroller(nullptr, nonThrow);
document().view()->updateAllLifecyclePhases();
ASSERT_EQ(document().documentElement(),
rootScrollerController.globalRootScroller());
EXPECT_TRUE(mapping3->clippingLayer());
EXPECT_TRUE(mapping3->clippingLayer()->platformLayer()->masksToBounds());
}
}
TEST_P(CompositedLayerMappingTest, AncestorClippingMaskLayerUpdates) {
setBodyInnerHTML(
"<style>"
"#ancestor { width: 100px; height: 100px; overflow: hidden; }"
"#child { width: 120px; height: 120px; background-color: green; }"
"</style>"
"<div id='ancestor'><div id='child'></div></div>");
document().view()->updateAllLifecyclePhases();
Element* ancestor = document().getElementById("ancestor");
ASSERT_TRUE(ancestor);
PaintLayer* ancestorPaintLayer =
toLayoutBoxModelObject(ancestor->layoutObject())->layer();
ASSERT_TRUE(ancestorPaintLayer);
CompositedLayerMapping* ancestorMapping =
ancestorPaintLayer->compositedLayerMapping();
ASSERT_FALSE(ancestorMapping);
Element* child = document().getElementById("child");
ASSERT_TRUE(child);
PaintLayer* childPaintLayer =
toLayoutBoxModelObject(child->layoutObject())->layer();
ASSERT_FALSE(childPaintLayer);
// Making the child conposited causes creation of an AncestorClippingLayer.
child->setAttribute(HTMLNames::styleAttr, "will-change: transform");
document().view()->updateAllLifecyclePhases();
childPaintLayer = toLayoutBoxModelObject(child->layoutObject())->layer();
ASSERT_TRUE(childPaintLayer);
CompositedLayerMapping* childMapping =
childPaintLayer->compositedLayerMapping();
ASSERT_TRUE(childMapping);
EXPECT_TRUE(childMapping->ancestorClippingLayer());
EXPECT_FALSE(childMapping->ancestorClippingLayer()->maskLayer());
EXPECT_FALSE(childMapping->ancestorClippingMaskLayer());
// Adding border radius to the ancestor requires an
// ancestorClippingMaskLayer for the child
ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 40px;");
document().view()->updateAllLifecyclePhases();
childPaintLayer = toLayoutBoxModelObject(child->layoutObject())->layer();
ASSERT_TRUE(childPaintLayer);
childMapping = childPaintLayer->compositedLayerMapping();
ASSERT_TRUE(childMapping);
EXPECT_TRUE(childMapping->ancestorClippingLayer());
EXPECT_TRUE(childMapping->ancestorClippingLayer()->maskLayer());
EXPECT_TRUE(childMapping->ancestorClippingMaskLayer());
// Removing the border radius should remove the ancestorClippingMaskLayer
// for the child
ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 0px;");
document().view()->updateAllLifecyclePhases();
childPaintLayer = toLayoutBoxModelObject(child->layoutObject())->layer();
ASSERT_TRUE(childPaintLayer);
childMapping = childPaintLayer->compositedLayerMapping();
ASSERT_TRUE(childMapping);
EXPECT_TRUE(childMapping->ancestorClippingLayer());
EXPECT_FALSE(childMapping->ancestorClippingLayer()->maskLayer());
EXPECT_FALSE(childMapping->ancestorClippingMaskLayer());
// Add border radius back so we can test one more case
ancestor->setAttribute(HTMLNames::styleAttr, "border-radius: 40px;");
document().view()->updateAllLifecyclePhases();
// Now change the overflow to remove the need for an ancestor clip
// on the child
ancestor->setAttribute(HTMLNames::styleAttr, "overflow: visible");
document().view()->updateAllLifecyclePhases();
childPaintLayer = toLayoutBoxModelObject(child->layoutObject())->layer();
ASSERT_TRUE(childPaintLayer);
childMapping = childPaintLayer->compositedLayerMapping();
ASSERT_TRUE(childMapping);
EXPECT_FALSE(childMapping->ancestorClippingLayer());
EXPECT_FALSE(childMapping->ancestorClippingMaskLayer());
}
} // namespace blink