blob: 0a4aab28bf95533794274a7db1e5ec6a50ee643f [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/html/html_iframe_element.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
namespace blink {
class PaintLayerTest : public PaintTestConfigurations, public RenderingTest {
public:
PaintLayerTest() : RenderingTest(SingleChildLocalFrameClient::Create()) {}
void SetUp() override {
RenderingTest::SetUp();
EnableCompositing();
}
protected:
PaintLayer* GetPaintLayerByElementId(const char* id) {
return ToLayoutBoxModelObject(GetLayoutObjectByElementId(id))->Layer();
}
};
INSTANTIATE_PAINT_TEST_CASE_P(PaintLayerTest);
TEST_P(PaintLayerTest, ChildWithoutPaintLayer) {
SetBodyInnerHTML(
"<div id='target' style='width: 200px; height: 200px;'></div>");
PaintLayer* paint_layer = GetPaintLayerByElementId("target");
PaintLayer* root_layer = GetLayoutView().Layer();
EXPECT_EQ(nullptr, paint_layer);
EXPECT_NE(nullptr, root_layer);
}
TEST_P(PaintLayerTest, CompositedBoundsAbsPosGrandchild) {
// BoundingBoxForCompositing is not used in SPv2 mode.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetBodyInnerHTML(
" <div id='parent'><div id='absposparent'><div id='absposchild'>"
" </div></div></div>"
"<style>"
" #parent { position: absolute; z-index: 0; overflow: hidden;"
" background: lightgray; width: 150px; height: 150px;"
" will-change: transform; }"
" #absposparent { position: absolute; z-index: 0; }"
" #absposchild { position: absolute; top: 0px; left: 0px; height: 200px;"
" width: 200px; background: lightblue; }</style>");
PaintLayer* parent_layer = GetPaintLayerByElementId("parent");
// Since "absposchild" is clipped by "parent", it should not expand the
// composited bounds for "parent" beyond its intrinsic size of 150x150.
EXPECT_EQ(LayoutRect(0, 0, 150, 150),
parent_layer->BoundingBoxForCompositing());
}
TEST_P(PaintLayerTest, CompositedBoundsTransformedChild) {
// TODO(chrishtr): fix this test for SPv2
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetBodyInnerHTML(R"HTML(
<div id=parent style='overflow: scroll; will-change: transform'>
<div class='target'
style='position: relative; transform: skew(-15deg);'>
</div>
<div style='width: 1000px; height: 500px; background: lightgray'>
</div>
</div>
)HTML");
PaintLayer* parent_layer = GetPaintLayerByElementId("parent");
EXPECT_EQ(LayoutRect(0, 0, 784, 500),
parent_layer->BoundingBoxForCompositing());
}
TEST_P(PaintLayerTest, RootLayerCompositedBounds) {
SetBodyInnerHTML(
"<style> body { width: 1000px; height: 1000px; margin: 0 } </style>");
EXPECT_EQ(LayoutRect(0, 0, 800, 600),
GetLayoutView().Layer()->BoundingBoxForCompositing());
}
TEST_P(PaintLayerTest, RootLayerScrollBounds) {
ScopedOverlayScrollbarsForTest overlay_scrollbars(false);
SetBodyInnerHTML(
"<style> body { width: 1000px; height: 1000px; margin: 0 } </style>");
PaintLayerScrollableArea* plsa = GetLayoutView().Layer()->GetScrollableArea();
int scrollbarThickness = plsa->VerticalScrollbarWidth();
EXPECT_EQ(scrollbarThickness, plsa->HorizontalScrollbarHeight());
EXPECT_GT(scrollbarThickness, 0);
EXPECT_EQ(ScrollOffset(200 + scrollbarThickness, 400 + scrollbarThickness),
plsa->MaximumScrollOffset());
EXPECT_EQ(IntRect(0, 0, 800 - scrollbarThickness, 600 - scrollbarThickness),
plsa->VisibleContentRect());
EXPECT_EQ(IntRect(0, 0, 800, 600),
plsa->VisibleContentRect(kIncludeScrollbars));
}
TEST_P(PaintLayerTest, PaintingExtentReflection) {
SetBodyInnerHTML(R"HTML(
<div id='target' style='background-color: blue; position: absolute;
width: 110px; height: 120px; top: 40px; left: 60px;
-webkit-box-reflect: below 3px'>
</div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_EQ(LayoutRect(60, 40, 110, 243),
layer->PaintingExtent(GetDocument().GetLayoutView()->Layer(),
LayoutSize(), 0));
}
TEST_P(PaintLayerTest, PaintingExtentReflectionWithTransform) {
SetBodyInnerHTML(R"HTML(
<div id='target' style='background-color: blue; position: absolute;
width: 110px; height: 120px; top: 40px; left: 60px;
-webkit-box-reflect: below 3px; transform: translateX(30px)'>
</div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_EQ(LayoutRect(90, 40, 110, 243),
layer->PaintingExtent(GetDocument().GetLayoutView()->Layer(),
LayoutSize(), 0));
}
TEST_P(PaintLayerTest, ScrollsWithViewportRelativePosition) {
SetBodyInnerHTML("<div id='target' style='position: relative'></div>");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_FALSE(layer->FixedToViewport());
}
TEST_P(PaintLayerTest, ScrollsWithViewportFixedPosition) {
SetBodyInnerHTML("<div id='target' style='position: fixed'></div>");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_TRUE(layer->FixedToViewport());
}
TEST_P(PaintLayerTest, ScrollsWithViewportFixedPositionInsideTransform) {
SetBodyInnerHTML(R"HTML(
<div style='transform: translateZ(0)'>
<div id='target' style='position: fixed'></div>
</div>
<div style='width: 10px; height: 1000px'></div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_FALSE(layer->FixedToViewport());
}
TEST_P(PaintLayerTest,
ScrollsWithViewportFixedPositionInsideTransformNoScroll) {
SetBodyInnerHTML(R"HTML(
<div style='transform: translateZ(0)'>
<div id='target' style='position: fixed'></div>
</div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
// In SPv2 mode, we correctly determine that the frame doesn't scroll at all,
// and so return true.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
EXPECT_TRUE(layer->FixedToViewport());
else
EXPECT_FALSE(layer->FixedToViewport());
}
TEST_P(PaintLayerTest, SticksToScrollerStickyPosition) {
SetBodyInnerHTML(R"HTML(
<div style='transform: translateZ(0)'>
<div id='target' style='position: sticky; top: 0;'></div>
</div>
<div style='width: 10px; height: 1000px'></div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_TRUE(layer->SticksToScroller());
}
TEST_P(PaintLayerTest, SticksToScrollerNoAnchor) {
SetBodyInnerHTML(R"HTML(
<div style='transform: translateZ(0)'>
<div id='target' style='position: sticky'></div>
</div>
<div style='width: 10px; height: 1000px'></div>
)HTML");
PaintLayer* layer =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer();
EXPECT_FALSE(layer->SticksToScroller());
}
TEST_P(PaintLayerTest, SticksToScrollerStickyPositionNoScroll) {
SetBodyInnerHTML(R"HTML(
<div style='transform: translateZ(0)'>
<div id='target' style='position: sticky; top: 0;'></div>
</div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_TRUE(layer->SticksToScroller());
}
TEST_P(PaintLayerTest, SticksToScrollerStickyPositionInsideScroller) {
SetBodyInnerHTML(R"HTML(
<div style='overflow:scroll; width: 100px; height: 100px;'>
<div id='target' style='position: sticky; top: 0;'></div>
<div style='width: 50px; height: 1000px;'></div>
</div>
)HTML");
PaintLayer* layer = GetPaintLayerByElementId("target");
EXPECT_TRUE(layer->SticksToScroller());
}
TEST_P(PaintLayerTest, CompositedScrollingNoNeedsRepaint) {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='scroll' style='width: 100px; height: 100px; overflow: scroll;
will-change: transform'>
<div id='content' style='position: relative; background: blue;
width: 2000px; height: 2000px'></div>
</div>
)HTML");
PaintLayer* scroll_layer = GetPaintLayerByElementId("scroll");
EXPECT_EQ(kPaintsIntoOwnBacking, scroll_layer->GetCompositingState());
PaintLayer* content_layer = GetPaintLayerByElementId("content");
EXPECT_EQ(kNotComposited, content_layer->GetCompositingState());
EXPECT_EQ(LayoutPoint(), content_layer->Location());
scroll_layer->GetScrollableArea()->SetScrollOffset(ScrollOffset(1000, 1000),
kProgrammaticScroll);
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
EXPECT_EQ(LayoutPoint(-1000, -1000), content_layer->Location());
EXPECT_FALSE(content_layer->NeedsRepaint());
EXPECT_FALSE(scroll_layer->NeedsRepaint());
GetDocument().View()->UpdateAllLifecyclePhases();
}
TEST_P(PaintLayerTest, NonCompositedScrollingNeedsRepaint) {
// SPV2 scrolling raster invalidation decisions are made in
// ContentLayerClientImpl::GenerateRasterInvalidations through
// PaintArtifactCompositor.
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetBodyInnerHTML(R"HTML(
<div id='scroll' style='width: 100px; height: 100px; overflow: scroll'>
<div id='content' style='position: relative; background: blue;
width: 2000px; height: 2000px'></div>
</div>
)HTML");
PaintLayer* scroll_layer = GetPaintLayerByElementId("scroll");
EXPECT_EQ(kNotComposited, scroll_layer->GetCompositingState());
PaintLayer* content_layer = GetPaintLayerByElementId("content");
EXPECT_EQ(kNotComposited, scroll_layer->GetCompositingState());
EXPECT_EQ(LayoutPoint(), content_layer->Location());
scroll_layer->GetScrollableArea()->SetScrollOffset(ScrollOffset(1000, 1000),
kProgrammaticScroll);
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
EXPECT_EQ(LayoutPoint(-1000, -1000), content_layer->Location());
EXPECT_TRUE(scroll_layer->NeedsRepaint());
EXPECT_FALSE(content_layer->NeedsRepaint());
GetDocument().View()->UpdateAllLifecyclePhases();
}
TEST_P(PaintLayerTest, HasNonIsolatedDescendantWithBlendMode) {
SetBodyInnerHTML(R"HTML(
<div id='stacking-grandparent' style='isolation: isolate'>
<div id='stacking-parent' style='isolation: isolate'>
<div id='non-stacking-parent' style='position:relative'>
<div id='blend-mode' style='mix-blend-mode: overlay'>
</div>
</div>
</div>
</div>
)HTML");
PaintLayer* stacking_grandparent =
GetPaintLayerByElementId("stacking-grandparent");
PaintLayer* stacking_parent = GetPaintLayerByElementId("stacking-parent");
PaintLayer* parent = GetPaintLayerByElementId("non-stacking-parent");
EXPECT_TRUE(parent->HasNonIsolatedDescendantWithBlendMode());
EXPECT_TRUE(stacking_parent->HasNonIsolatedDescendantWithBlendMode());
EXPECT_FALSE(stacking_grandparent->HasNonIsolatedDescendantWithBlendMode());
EXPECT_FALSE(parent->HasDescendantWithClipPath());
EXPECT_TRUE(parent->HasVisibleDescendant());
}
TEST_P(PaintLayerTest, HasDescendantWithSticky) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='isolation: isolate'>
<div id='child' style='position: sticky'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
EXPECT_TRUE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child->HasDescendantWithStickyOrFixed());
GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr,
"position: relative");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child->HasDescendantWithStickyOrFixed());
}
TEST_P(PaintLayerTest, HasDescendantWithFixed) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='isolation: isolate'>
<div id='child' style='position: fixed'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
EXPECT_TRUE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child->HasDescendantWithStickyOrFixed());
GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr,
"position: relative");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child->HasDescendantWithStickyOrFixed());
}
TEST_P(PaintLayerTest, HasDescendantWithFixedAndSticky) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='isolation: isolate'>
<div id='child1' style='position: sticky'>
</div>
<div id='child2' style='position: fixed'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child1 = GetPaintLayerByElementId("child1");
PaintLayer* child2 = GetPaintLayerByElementId("child2");
EXPECT_TRUE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child1->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child2->HasDescendantWithStickyOrFixed());
GetDocument().getElementById("child1")->setAttribute(HTMLNames::styleAttr,
"position: relative");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_TRUE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child1->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child2->HasDescendantWithStickyOrFixed());
GetDocument().getElementById("child2")->setAttribute(HTMLNames::styleAttr,
"position: relative");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(parent->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child1->HasDescendantWithStickyOrFixed());
EXPECT_FALSE(child2->HasDescendantWithStickyOrFixed());
}
TEST_P(PaintLayerTest, HasNonContainedAbsolutePositionDescendant) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='isolation: isolate'>
<div id='child' style='position: relative'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
EXPECT_FALSE(parent->HasNonContainedAbsolutePositionDescendant());
EXPECT_FALSE(child->HasNonContainedAbsolutePositionDescendant());
GetDocument().getElementById("child")->setAttribute(HTMLNames::styleAttr,
"position: absolute");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_TRUE(parent->HasNonContainedAbsolutePositionDescendant());
EXPECT_FALSE(child->HasNonContainedAbsolutePositionDescendant());
GetDocument().getElementById("parent")->setAttribute(HTMLNames::styleAttr,
"position: relative");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(parent->HasNonContainedAbsolutePositionDescendant());
EXPECT_FALSE(child->HasNonContainedAbsolutePositionDescendant());
}
TEST_P(PaintLayerTest, SubsequenceCachingStackingContexts) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position:relative'>
<div id='child1' style='position: relative'>
<div id='grandchild1' style='position: relative'></div>
</div>
<div id='child2' style='isolation: isolate'>
<div id='grandchild2' style='position: relative'></div>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child1 = GetPaintLayerByElementId("child1");
PaintLayer* child2 = GetPaintLayerByElementId("child2");
PaintLayer* grandchild1 = GetPaintLayerByElementId("grandchild1");
PaintLayer* grandchild2 = GetPaintLayerByElementId("grandchild2");
EXPECT_FALSE(parent->SupportsSubsequenceCaching());
EXPECT_FALSE(child1->SupportsSubsequenceCaching());
EXPECT_TRUE(child2->SupportsSubsequenceCaching());
EXPECT_FALSE(grandchild1->SupportsSubsequenceCaching());
EXPECT_FALSE(grandchild2->SupportsSubsequenceCaching());
GetDocument()
.getElementById("grandchild1")
->setAttribute(HTMLNames::styleAttr, "isolation: isolate");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(parent->SupportsSubsequenceCaching());
EXPECT_FALSE(child1->SupportsSubsequenceCaching());
EXPECT_TRUE(child2->SupportsSubsequenceCaching());
EXPECT_TRUE(grandchild1->SupportsSubsequenceCaching());
EXPECT_FALSE(grandchild2->SupportsSubsequenceCaching());
}
TEST_P(PaintLayerTest, SubsequenceCachingSVGRoot) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position: relative'>
<svg id='svgroot' style='position: relative'></svg>
</div>
)HTML");
PaintLayer* svgroot = GetPaintLayerByElementId("svgroot");
EXPECT_FALSE(svgroot->SupportsSubsequenceCaching());
}
TEST_P(PaintLayerTest, SubsequenceCachingMuticol) {
SetBodyInnerHTML(R"HTML(
<div style='columns: 2'>
<svg id='svgroot' style='position: relative'></svg>
</div>
)HTML");
PaintLayer* svgroot = GetPaintLayerByElementId("svgroot");
EXPECT_FALSE(svgroot->SupportsSubsequenceCaching());
}
TEST_P(PaintLayerTest, HasDescendantWithClipPath) {
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position:relative'>
<div id='clip-path' style='clip-path: circle(50px at 0 100px)'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* clip_path = GetPaintLayerByElementId("clip-path");
EXPECT_TRUE(parent->HasDescendantWithClipPath());
EXPECT_FALSE(clip_path->HasDescendantWithClipPath());
EXPECT_FALSE(parent->HasNonIsolatedDescendantWithBlendMode());
EXPECT_TRUE(parent->HasVisibleDescendant());
}
TEST_P(PaintLayerTest, HasVisibleDescendant) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='invisible' style='position:relative'>
<div id='visible' style='visibility: visible; position: relative'>
</div>
</div>
)HTML");
PaintLayer* invisible = GetPaintLayerByElementId("invisible");
PaintLayer* visible = GetPaintLayerByElementId("visible");
EXPECT_TRUE(invisible->HasVisibleDescendant());
EXPECT_FALSE(visible->HasVisibleDescendant());
EXPECT_FALSE(invisible->HasNonIsolatedDescendantWithBlendMode());
EXPECT_FALSE(invisible->HasDescendantWithClipPath());
}
TEST_P(PaintLayerTest, Has3DTransformedDescendant) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position:relative; z-index: 0'>
<div id='child' style='transform: translateZ(1px)'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
EXPECT_TRUE(parent->Has3DTransformedDescendant());
EXPECT_FALSE(child->Has3DTransformedDescendant());
}
TEST_P(PaintLayerTest, Has3DTransformedDescendantChangeStyle) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position:relative; z-index: 0'>
<div id='child' style='position:relative '>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
EXPECT_FALSE(parent->Has3DTransformedDescendant());
EXPECT_FALSE(child->Has3DTransformedDescendant());
GetDocument().getElementById("child")->setAttribute(
HTMLNames::styleAttr, "transform: translateZ(1px)");
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_TRUE(parent->Has3DTransformedDescendant());
EXPECT_FALSE(child->Has3DTransformedDescendant());
}
TEST_P(PaintLayerTest, Has3DTransformedDescendantNotStacking) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position:relative;'>
<div id='child' style='transform: translateZ(1px)'>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
// |child| is not a stacking child of |parent|, so it has no 3D transformed
// descendant.
EXPECT_FALSE(parent->Has3DTransformedDescendant());
EXPECT_FALSE(child->Has3DTransformedDescendant());
}
TEST_P(PaintLayerTest, Has3DTransformedGrandchildWithPreserve3d) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='parent' style='position:relative; z-index: 0'>
<div id='child' style='transform-style: preserve-3d'>
<div id='grandchild' style='transform: translateZ(1px)'>
</div>
</div>
</div>
)HTML");
PaintLayer* parent = GetPaintLayerByElementId("parent");
PaintLayer* child = GetPaintLayerByElementId("child");
PaintLayer* grandchild = GetPaintLayerByElementId("grandchild");
EXPECT_TRUE(parent->Has3DTransformedDescendant());
EXPECT_TRUE(child->Has3DTransformedDescendant());
EXPECT_FALSE(grandchild->Has3DTransformedDescendant());
}
TEST_P(PaintLayerTest, DescendantDependentFlagsStopsAtThrottledFrames) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<style>body { margin: 0; }</style>
<div id='transform' style='transform: translate3d(4px, 5px, 6px);'>
</div>
<iframe id='iframe' sandbox></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>body { margin: 0; }</style>
<div id='iframeTransform'
style='transform: translate3d(4px, 5px, 6px);'/>
)HTML");
// Move the child frame offscreen so it becomes available for throttling.
auto* iframe = ToHTMLIFrameElement(GetDocument().getElementById("iframe"));
iframe->setAttribute(HTMLNames::styleAttr, "transform: translateY(5555px)");
GetDocument().View()->UpdateAllLifecyclePhases();
// Ensure intersection observer notifications get delivered.
test::RunPendingTasks();
EXPECT_FALSE(GetDocument().View()->IsHiddenForThrottling());
EXPECT_TRUE(ChildDocument().View()->IsHiddenForThrottling());
{
DocumentLifecycle::AllowThrottlingScope throttling_scope(
GetDocument().Lifecycle());
EXPECT_FALSE(GetDocument().View()->ShouldThrottleRendering());
EXPECT_TRUE(ChildDocument().View()->ShouldThrottleRendering());
ChildDocument()
.View()
->GetLayoutView()
->Layer()
->DirtyVisibleContentStatus();
EXPECT_TRUE(ChildDocument()
.View()
->GetLayoutView()
->Layer()
->needs_descendant_dependent_flags_update_);
// Also check that the rest of the lifecycle succeeds without crashing due
// to a stale m_needsDescendantDependentFlagsUpdate.
GetDocument().View()->UpdateAllLifecyclePhases();
// Still dirty, because the frame was throttled.
EXPECT_TRUE(ChildDocument()
.View()
->GetLayoutView()
->Layer()
->needs_descendant_dependent_flags_update_);
}
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_FALSE(ChildDocument()
.View()
->GetLayoutView()
->Layer()
->needs_descendant_dependent_flags_update_);
}
TEST_P(PaintLayerTest, PaintInvalidationOnNonCompositedScroll) {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetBodyInnerHTML(R"HTML(
<style>* { margin: 0 } ::-webkit-scrollbar { display: none }</style>
<div id='scroller' style='overflow: scroll; width: 50px; height: 50px'>
<div style='height: 400px'>
<div id='content-layer' style='position: relative; height: 10px;
top: 30px; background: blue'>
<div id='content' style='height: 5px; background: yellow'></div>
</div>
</div>
</div>
)HTML");
LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
LayoutObject* content_layer = GetLayoutObjectByElementId("content-layer");
LayoutObject* content = GetLayoutObjectByElementId("content");
EXPECT_EQ(LayoutRect(0, 30, 50, 10),
content_layer->FirstFragment().VisualRect());
EXPECT_EQ(LayoutRect(0, 30, 50, 5), content->FirstFragment().VisualRect());
scroller->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 20),
kProgrammaticScroll);
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_EQ(LayoutRect(0, 30, 50, 10),
content_layer->FirstFragment().VisualRect());
EXPECT_EQ(LayoutRect(0, 30, 50, 5), content->FirstFragment().VisualRect());
}
TEST_P(PaintLayerTest, PaintInvalidationOnCompositedScroll) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<style>* { margin: 0 } ::-webkit-scrollbar { display: none }</style>
<div id='scroller' style='overflow: scroll; width: 50px; height: 50px;
will-change: transform'>
<div style='height: 400px'>
<div id='content-layer' style='position: relative; height: 10px;
top: 30px; background: blue'>
<div id='content' style='height: 5px; background: yellow'></div>
</div>
</div>
</div>
)HTML");
LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
LayoutObject* content_layer = GetLayoutObjectByElementId("content-layer");
LayoutObject* content = GetLayoutObjectByElementId("content");
EXPECT_EQ(LayoutRect(0, 30, 50, 10),
content_layer->FirstFragment().VisualRect());
EXPECT_EQ(LayoutRect(0, 30, 50, 5), content->FirstFragment().VisualRect());
scroller->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 20),
kProgrammaticScroll);
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_EQ(LayoutRect(0, 30, 50, 10),
content_layer->FirstFragment().VisualRect());
EXPECT_EQ(LayoutRect(0, 30, 50, 5), content->FirstFragment().VisualRect());
}
TEST_P(PaintLayerTest, CompositingContainerStackedFloatUnderStackingInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9'>
<div id='target' style='float: right; position: relative'></div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_EQ(GetPaintLayerByElementId("span"), target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(GetPaintLayerByElementId("compositedContainer"),
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest,
CompositingContainerStackedFloatUnderStackingCompositedInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9; will-change: transform'>
<div id='target' style='float: right; position: relative'></div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(span, target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(span,
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest, CompositingContainerNonStackedFloatUnderStackingInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9'>
<div id='target' style='float: right; overflow: hidden'></div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_EQ(GetPaintLayerByElementId("containingBlock"),
target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(GetPaintLayerByElementId("compositedContainer"),
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest,
CompositingContainerNonStackedFloatUnderStackingCompositedInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9; will-change: transform'>
<div id='target' style='float: right; overflow: hidden'></div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_EQ(GetPaintLayerByElementId("containingBlock"),
target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(GetPaintLayerByElementId("compositedContainer"),
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest,
CompositingContainerStackedUnderFloatUnderStackingInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9'>
<div style='float: right'>
<div id='target' style='position: relative'></div>
</div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_EQ(GetPaintLayerByElementId("span"), target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(GetPaintLayerByElementId("compositedContainer"),
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest,
CompositingContainerStackedUnderFloatUnderStackingCompositedInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9; will-change: transform'>
<div style='float: right'>
<div id='target' style='position: relative'></div>
</div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(span, target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(span,
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest,
CompositingContainerNonStackedUnderFloatUnderStackingInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9'>
<div style='float: right'>
<div id='target' style='overflow: hidden'></div>
</div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_EQ(GetPaintLayerByElementId("containingBlock"),
target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(GetPaintLayerByElementId("compositedContainer"),
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest,
CompositingContainerNonStackedUnderFloatUnderStackingCompositedInline) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<span id='span' style='opacity: 0.9; will-change: transform'>
<div style='float: right'>
<div id='target' style='overflow: hidden'></div>
</div>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_EQ(GetPaintLayerByElementId("containingBlock"),
target->CompositingContainer());
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(GetPaintLayerByElementId("compositedContainer"),
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest, FloatLayerAndAbsoluteUnderInlineLayer) {
SetBodyInnerHTML(R"HTML(
<div id='container' style='position: absolute; top: 20px; left: 20px'>
<div style='margin: 33px'>
<span id='span' style='position: relative; top: 100px; left: 100px'>
<div id='floating'
style='float: left; position: relative; top: 50px; left: 50px'>
</div>
<div id='absolute'
style='position: absolute; top: 50px; left: 50px'>
</div>
</span>
</div>
</div>
)HTML");
PaintLayer* floating = GetPaintLayerByElementId("floating");
PaintLayer* absolute = GetPaintLayerByElementId("absolute");
PaintLayer* span = GetPaintLayerByElementId("span");
PaintLayer* container = GetPaintLayerByElementId("container");
EXPECT_EQ(span, floating->Parent());
EXPECT_EQ(container, floating->ContainingLayer());
EXPECT_EQ(span, absolute->Parent());
EXPECT_EQ(span, absolute->ContainingLayer());
EXPECT_EQ(container, span->Parent());
EXPECT_EQ(container, span->ContainingLayer());
EXPECT_EQ(LayoutPoint(83, 83), floating->Location());
EXPECT_EQ(LayoutPoint(50, 50), absolute->Location());
EXPECT_EQ(LayoutPoint(133, 133), span->Location());
EXPECT_EQ(LayoutPoint(20, 20), container->Location());
EXPECT_EQ(LayoutPoint(-50, -50), floating->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(50, 50), absolute->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(83, 83), floating->VisualOffsetFromAncestor(container));
EXPECT_EQ(LayoutPoint(183, 183),
absolute->VisualOffsetFromAncestor(container));
}
TEST_P(PaintLayerTest, FloatLayerUnderInlineLayerScrolled) {
SetBodyInnerHTML(R"HTML(
<div id='container' style='overflow: scroll; width: 50px; height: 50px'>
<span id='span' style='position: relative; top: 100px; left: 100px'>
<div id='floating'
style='float: left; position: relative; top: 50px; left: 50px'>
</div>
</span>
<div style='height: 1000px'></div>
</div>
)HTML");
PaintLayer* floating = GetPaintLayerByElementId("floating");
PaintLayer* span = GetPaintLayerByElementId("span");
PaintLayer* container = GetPaintLayerByElementId("container");
container->GetScrollableArea()->SetScrollOffset(ScrollOffset(0, 400),
kProgrammaticScroll);
EXPECT_EQ(span, floating->Parent());
EXPECT_EQ(container, floating->ContainingLayer());
EXPECT_EQ(container, span->Parent());
EXPECT_EQ(container, span->ContainingLayer());
EXPECT_EQ(LayoutPoint(50, -350), floating->Location());
EXPECT_EQ(LayoutPoint(100, -300), span->Location());
EXPECT_EQ(LayoutPoint(-50, -50), floating->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(50, -350),
floating->VisualOffsetFromAncestor(container));
}
TEST_P(PaintLayerTest, FloatLayerUnderBlockUnderInlineLayer) {
SetBodyInnerHTML(R"HTML(
<style>body {margin: 0}</style>
<span id='span' style='position: relative; top: 100px; left: 100px'>
<div style='display: inline-block; margin: 33px'>
<div id='floating'
style='float: left; position: relative; top: 50px; left: 50px'>
</div>
</div>
</span>
)HTML");
PaintLayer* floating = GetPaintLayerByElementId("floating");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(span, floating->Parent());
EXPECT_EQ(span, floating->ContainingLayer());
EXPECT_EQ(LayoutPoint(83, 83), floating->Location());
EXPECT_EQ(LayoutPoint(100, 100), span->Location());
EXPECT_EQ(LayoutPoint(83, 83), floating->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(183, 183), floating->VisualOffsetFromAncestor(
GetDocument().GetLayoutView()->Layer()));
}
TEST_P(PaintLayerTest, FloatLayerUnderFloatUnderInlineLayer) {
SetBodyInnerHTML(R"HTML(
<style>body {margin: 0}</style>
<span id='span' style='position: relative; top: 100px; left: 100px'>
<div style='float: left; margin: 33px'>
<div id='floating'
style='float: left; position: relative; top: 50px; left: 50px'>
</div>
</div>
</span>
)HTML");
PaintLayer* floating = GetPaintLayerByElementId("floating");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(span, floating->Parent());
EXPECT_EQ(span->Parent(), floating->ContainingLayer());
EXPECT_EQ(LayoutPoint(83, 83), floating->Location());
EXPECT_EQ(LayoutPoint(100, 100), span->Location());
EXPECT_EQ(LayoutPoint(-17, -17), floating->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(83, 83), floating->VisualOffsetFromAncestor(
GetDocument().GetLayoutView()->Layer()));
}
TEST_P(PaintLayerTest, FloatLayerUnderFloatLayerUnderInlineLayer) {
SetBodyInnerHTML(R"HTML(
<style>body {margin: 0}</style>
<span id='span' style='position: relative; top: 100px; left: 100px'>
<div id='floatingParent'
style='float: left; position: relative; margin: 33px'>
<div id='floating'
style='float: left; position: relative; top: 50px; left: 50px'>
</div>
</div>
</span>
)HTML");
PaintLayer* floating = GetPaintLayerByElementId("floating");
PaintLayer* floating_parent = GetPaintLayerByElementId("floatingParent");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(floating_parent, floating->Parent());
EXPECT_EQ(floating_parent, floating->ContainingLayer());
EXPECT_EQ(span, floating_parent->Parent());
EXPECT_EQ(span->Parent(), floating_parent->ContainingLayer());
EXPECT_EQ(LayoutPoint(50, 50), floating->Location());
EXPECT_EQ(LayoutPoint(33, 33), floating_parent->Location());
EXPECT_EQ(LayoutPoint(100, 100), span->Location());
EXPECT_EQ(LayoutPoint(-17, -17), floating->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(-67, -67),
floating_parent->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(83, 83), floating->VisualOffsetFromAncestor(
GetDocument().GetLayoutView()->Layer()));
}
TEST_P(PaintLayerTest, LayerUnderFloatUnderInlineLayer) {
SetBodyInnerHTML(R"HTML(
<style>body {margin: 0}</style>
<span id='span' style='position: relative; top: 100px; left: 100px'>
<div style='float: left; margin: 33px'>
<div>
<div id='child' style='position: relative; top: 50px; left: 50px'>
</div>
</div>
</div>
</span>
)HTML");
PaintLayer* child = GetPaintLayerByElementId("child");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(span, child->Parent());
EXPECT_EQ(span->Parent(), child->ContainingLayer());
EXPECT_EQ(LayoutPoint(83, 83), child->Location());
EXPECT_EQ(LayoutPoint(100, 100), span->Location());
EXPECT_EQ(LayoutPoint(-17, -17), child->VisualOffsetFromAncestor(span));
EXPECT_EQ(LayoutPoint(83, 83), child->VisualOffsetFromAncestor(
GetDocument().GetLayoutView()->Layer()));
}
TEST_P(PaintLayerTest, CompositingContainerFloatingIframe) {
EnableCompositing();
SetBodyInnerHTML(R"HTML(
<div id='compositedContainer' style='position: relative;
will-change: transform'>
<div id='containingBlock' style='position: relative; z-index: 0'>
<div style='backface-visibility: hidden'></div>
<span id='span'
style='clip-path: polygon(0px 15px, 0px 54px, 100px 0px)'>
<iframe srcdoc='foo' id='target' style='float: right'></iframe>
</span>
</div>
</div>
)HTML");
PaintLayer* target = GetPaintLayerByElementId("target");
// A non-positioned iframe still gets a PaintLayer because PaintLayers are
// forced for all LayoutEmbeddedContent objects. However, such PaintLayers are
// not stacked.
PaintLayer* containing_block = GetPaintLayerByElementId("containingBlock");
EXPECT_EQ(containing_block, target->CompositingContainer());
PaintLayer* composited_container =
GetPaintLayerByElementId("compositedContainer");
// enclosingLayerWithCompositedLayerMapping is not needed or applicable to
// SPv2.
if (!RuntimeEnabledFeatures::SlimmingPaintV2Enabled()) {
EXPECT_EQ(composited_container,
target->EnclosingLayerWithCompositedLayerMapping(kExcludeSelf));
}
}
TEST_P(PaintLayerTest, CompositingContainerSelfPaintingNonStackedFloat) {
SetBodyInnerHTML(R"HTML(
<div id='container' style='position: relative'>
<span id='span' style='opacity: 0.9'>
<div id='target' style='columns: 1; float: left'></div>
</span>
</div>
)HTML");
// The target layer is self-painting, but not stacked.
PaintLayer* target = GetPaintLayerByElementId("target");
EXPECT_TRUE(target->IsSelfPaintingLayer());
EXPECT_FALSE(target->GetLayoutObject().StyleRef().IsStacked());
PaintLayer* container = GetPaintLayerByElementId("container");
PaintLayer* span = GetPaintLayerByElementId("span");
EXPECT_EQ(container, target->ContainingLayer());
EXPECT_EQ(span, target->CompositingContainer());
}
TEST_P(PaintLayerTest, ColumnSpanLayerUnderExtraLayerScrolled) {
SetBodyInnerHTML(R"HTML(
<div id='columns' style='overflow: hidden; width: 80px; height: 80px;
columns: 2; column-gap: 0'>
<div id='extraLayer'
style='position: relative; top: 100px; left: 100px'>
<div id='spanner' style='column-span: all; position: relative;
top: 50px; left: 50px'>
</div>
</div>
<div style='height: 1000px'></div>
</div>
)HTML");
PaintLayer* spanner = GetPaintLayerByElementId("spanner");
PaintLayer* extra_layer = GetPaintLayerByElementId("extraLayer");
PaintLayer* columns = GetPaintLayerByElementId("columns");
columns->GetScrollableArea()->SetScrollOffset(ScrollOffset(200, 0),
kProgrammaticScroll);
EXPECT_EQ(extra_layer, spanner->Parent());
EXPECT_EQ(columns, spanner->ContainingLayer());
EXPECT_EQ(columns, extra_layer->Parent()->Parent());
EXPECT_EQ(columns, extra_layer->ContainingLayer()->Parent());
EXPECT_EQ(LayoutPoint(-150, 50), spanner->Location());
EXPECT_EQ(LayoutPoint(100, 100), extra_layer->Location());
// -60 = 2nd-column-x(40) - scroll-offset-x(200) + x-location(100)
// 20 = y-location(100) - column-height(80)
EXPECT_EQ(LayoutPoint(-60, 20),
extra_layer->VisualOffsetFromAncestor(columns));
EXPECT_EQ(LayoutPoint(-150, 50), spanner->VisualOffsetFromAncestor(columns));
}
TEST_P(PaintLayerTest, PaintLayerTransformUpdatedOnStyleTransformAnimation) {
SetBodyInnerHTML("<div id='target' style='will-change: transform'></div>");
LayoutObject* target_object =
GetDocument().getElementById("target")->GetLayoutObject();
PaintLayer* target_paint_layer =
ToLayoutBoxModelObject(target_object)->Layer();
EXPECT_EQ(nullptr, target_paint_layer->Transform());
const ComputedStyle* old_style = target_object->Style();
scoped_refptr<ComputedStyle> new_style = ComputedStyle::Clone(*old_style);
new_style->SetHasCurrentTransformAnimation(true);
target_paint_layer->UpdateTransform(old_style, *new_style);
EXPECT_NE(nullptr, target_paint_layer->Transform());
}
TEST_P(PaintLayerTest, NeedsRepaintOnSelfPaintingStatusChange) {
SetBodyInnerHTML(R"HTML(
<span id='span' style='opacity: 0.1'>
<div id='target' style='overflow: hidden; float: left;
column-width: 10px'>
</div>
</span>
)HTML");
auto* span_layer =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("span"))->Layer();
auto* target_element = GetDocument().getElementById("target");
auto* target_object = target_element->GetLayoutObject();
auto* target_layer = ToLayoutBoxModelObject(target_object)->Layer();
// Target layer is self painting because it is a multicol container.
EXPECT_TRUE(target_layer->IsSelfPaintingLayer());
EXPECT_EQ(span_layer, target_layer->CompositingContainer());
EXPECT_FALSE(target_layer->NeedsRepaint());
EXPECT_FALSE(span_layer->NeedsRepaint());
// Removing column-width: 10px makes target layer no longer self-painting,
// and change its compositing container. The original compositing container
// span_layer should be marked NeedsRepaint.
target_element->setAttribute(HTMLNames::styleAttr,
"overflow: hidden; float: left");
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
EXPECT_FALSE(target_layer->IsSelfPaintingLayer());
EXPECT_EQ(span_layer->Parent(), target_layer->CompositingContainer());
EXPECT_TRUE(target_layer->NeedsRepaint());
EXPECT_TRUE(target_layer->CompositingContainer()->NeedsRepaint());
EXPECT_TRUE(span_layer->NeedsRepaint());
GetDocument().View()->UpdateAllLifecyclePhases();
}
TEST_P(PaintLayerTest, NeedsRepaintOnRemovingStackedLayer) {
EnableCompositing();
SetBodyInnerHTML(
"<style>body {margin-top: 200px; backface-visibility: hidden}</style>"
"<div id='target' style='position: absolute; top: 0'>Text</div>");
auto* body = GetDocument().body();
auto* body_layer = body->GetLayoutBox()->Layer();
auto* target_element = GetDocument().getElementById("target");
auto* target_object = target_element->GetLayoutObject();
auto* target_layer = ToLayoutBoxModelObject(target_object)->Layer();
// |container| is not the CompositingContainer of |target| because |target|
// is stacked but |container| is not a stacking context.
EXPECT_TRUE(target_layer->GetLayoutObject().StyleRef().IsStacked());
EXPECT_NE(body_layer, target_layer->CompositingContainer());
auto* old_compositing_container = target_layer->CompositingContainer();
body->setAttribute(HTMLNames::styleAttr, "margin-top: 0");
target_element->setAttribute(HTMLNames::styleAttr, "top: 0");
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
EXPECT_FALSE(target_object->HasLayer());
EXPECT_TRUE(body_layer->NeedsRepaint());
EXPECT_TRUE(old_compositing_container->NeedsRepaint());
GetDocument().View()->UpdateAllLifecyclePhases();
}
TEST_P(PaintLayerTest, FrameViewContentSize) {
SetBodyInnerHTML(
"<style> body { width: 1200px; height: 900px; margin: 0 } </style>");
EXPECT_EQ(IntSize(800, 600), GetDocument().View()->Size());
}
TEST_P(PaintLayerTest, ReferenceClipPathWithPageZoom) {
SetHtmlInnerHTML(R"HTML(
<style>
body { margin: 0; }
</style>
<div style='width: 200px; height: 200px; background-color: blue;
clip-path: url(#clip)' id='content'></div>
<svg>
<clipPath id='clip'>
<path d='M50,50h100v100h-100z'/>
</clipPath>
</svg>
)HTML");
auto* content = GetDocument().getElementById("content");
auto* body = GetDocument().body();
// A hit test on the content div within the clip should hit it.
EXPECT_EQ(content, GetDocument().ElementFromPoint(125, 75));
EXPECT_EQ(content, GetDocument().ElementFromPoint(75, 125));
// A hit test on the content div outside the clip should not hit it.
EXPECT_EQ(body, GetDocument().ElementFromPoint(151, 60));
EXPECT_EQ(body, GetDocument().ElementFromPoint(60, 151));
// Zoom the page by 2x,
GetDocument().GetFrame()->SetPageZoomFactor(2);
// A hit test on the content div within the clip should hit it.
EXPECT_EQ(content, GetDocument().ElementFromPoint(125, 75));
EXPECT_EQ(content, GetDocument().ElementFromPoint(75, 125));
// A hit test on the content div outside the clip should not hit it.
EXPECT_EQ(body, GetDocument().ElementFromPoint(151, 60));
EXPECT_EQ(body, GetDocument().ElementFromPoint(60, 151));
}
TEST_P(PaintLayerTest, FragmentedHitTest) {
SetHtmlInnerHTML(R"HTML(
<style>
div {
break-inside: avoid-column;
width: 50px;
height: 50px;
position: relative;
}
</style>
<ul style="column-count: 4; position: relative">
<div></div>
<div id=target style=" position: relative; transform: translateY(0px);">
</div>
</ul>
)HTML");
auto* target = GetDocument().getElementById("target");
EXPECT_EQ(target, GetDocument().ElementFromPoint(280, 30));
}
TEST_P(PaintLayerTest, SquashingOffsets) {
if (RuntimeEnabledFeatures::SlimmingPaintV2Enabled())
return;
SetHtmlInnerHTML(R"HTML(
<style>
* { margin: 0 }
</style>
<div id=target
style='width: 200px; height: 200px; position: relative; will-change: transform'></div>
<div id=squashed
style='width: 200px; height: 200px; top: -200px; position: relative;'></div>
<div style='width: 10px; height: 3000px'></div>
)HTML");
auto* squashed =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer();
EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState());
FloatPoint point;
LayoutRect rect(0, 0, 200, 200);
PaintLayer::MapPointInPaintInvalidationContainerToBacking(
squashed->GetLayoutObject(), point);
EXPECT_EQ(FloatPoint(), point);
PaintLayer::MapRectInPaintInvalidationContainerToBacking(
squashed->GetLayoutObject(), rect);
EXPECT_EQ(LayoutRect(0, 0, 200, 200), rect);
EXPECT_EQ(LayoutPoint(0, 0), squashed->ComputeOffsetFromAncestor(
squashed->TransformAncestorOrRoot()));
GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25),
kUserScroll);
GetDocument().View()->UpdateAllLifecyclePhases();
PaintLayer::MapPointInPaintInvalidationContainerToBacking(
squashed->GetLayoutObject(), point);
EXPECT_EQ(FloatPoint(), point);
PaintLayer::MapRectInPaintInvalidationContainerToBacking(
squashed->GetLayoutObject(), rect);
EXPECT_EQ(LayoutRect(0, 0, 200, 200), rect);
EXPECT_EQ(LayoutPoint(0, 0), squashed->ComputeOffsetFromAncestor(
squashed->TransformAncestorOrRoot()));
}
TEST_P(PaintLayerTest, HitTestWithIgnoreClipping) {
SetBodyInnerHTML("<div id='hit' style='width: 90px; height: 9000px;'></div>");
HitTestRequest request(HitTestRequest::kIgnoreClipping);
// (10, 900) is outside the viewport clip of 800x600.
HitTestResult result(request, IntPoint(10, 900));
GetDocument().GetLayoutView()->HitTest(result);
EXPECT_EQ(GetDocument().getElementById("hit"), result.InnerNode());
}
TEST_P(PaintLayerTest, HitTestWithStopNode) {
SetBodyInnerHTML(R"HTML(
<div id='hit' style='width: 100px; height: 100px;'>
<div id='child' style='width:100px;height:100px'></div>
</div>
<div id='overlap' style='position:relative;top:-50px;width:100px;height:100px'></div>
)HTML");
Element* hit = GetDocument().getElementById("hit");
Element* child = GetDocument().getElementById("child");
Element* overlap = GetDocument().getElementById("overlap");
// Regular hit test over 'child'
HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
HitTestResult result(request, LayoutPoint(50, 25));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(child, result.InnerNode());
// Same hit test, with stop node.
request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
hit->GetLayoutObject());
result = HitTestResult(request, LayoutPoint(50, 25));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(hit, result.InnerNode());
// Regular hit test over 'overlap'
request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive);
result = HitTestResult(request, LayoutPoint(50, 75));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(overlap, result.InnerNode());
// Same hit test, with stop node, should still hit 'overlap' because it's not
// a descendant of 'hit'.
request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
hit->GetLayoutObject());
result = HitTestResult(request, LayoutPoint(50, 75));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(overlap, result.InnerNode());
// List-based hit test with stop node
request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
HitTestRequest::kListBased,
hit->GetLayoutObject());
result = HitTestResult(request, LayoutRect(40, 15, 20, 20));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(1u, result.ListBasedTestResult().size());
EXPECT_EQ(hit, *result.ListBasedTestResult().begin());
}
TEST_P(PaintLayerTest, HitTestTableWithStopNode) {
SetBodyInnerHTML(R"HTML(
<style>
.cell {
width: 100px;
height: 100px;
}
</style>
<table id='table'>
<tr>
<td><div id='cell11' class='cell'></td>
<td><div id='cell12' class='cell'></td>
</tr>
<tr>
<td><div id='cell21' class='cell'></td>
<td><div id='cell22' class='cell'></td>
</tr>
</table>
)HTML");
Element* table = GetDocument().getElementById("table");
Element* cell11 = GetDocument().getElementById("cell11");
HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
HitTestResult result(request, LayoutPoint(50, 50));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(cell11, result.InnerNode());
request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
table->GetLayoutObject());
result = HitTestResult(request, LayoutPoint(50, 50));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(table, result.InnerNode());
}
TEST_P(PaintLayerTest, HitTestSVGWithStopNode) {
SetBodyInnerHTML(R"HTML(
<svg id='svg' style='width:100px;height:100px' viewBox='0 0 100 100'>
<circle id='circle' cx='50' cy='50' r='50' />
</svg>
)HTML");
Element* svg = GetDocument().getElementById("svg");
Element* circle = GetDocument().getElementById("circle");
HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
HitTestResult result(request, LayoutPoint(50, 50));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(circle, result.InnerNode());
request = HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive,
svg->GetLayoutObject());
result = HitTestResult(request, LayoutPoint(50, 50));
GetDocument().GetLayoutView()->Layer()->HitTest(result);
EXPECT_EQ(svg, result.InnerNode());
}
TEST_P(PaintLayerTest, SetNeedsRepaintSelfPaintingUnderNonSelfPainting) {
SetHtmlInnerHTML(R"HTML(
<span id='span' style='opacity: 0.5'>
<div id='floating' style='float: left; overflow: hidden'>
<div id='multicol' style='columns: 2'>A</div>
</div>
</span>
)HTML");
auto* html_layer =
ToLayoutBoxModelObject(GetDocument().documentElement()->GetLayoutObject())
->Layer();
auto* span_layer = GetPaintLayerByElementId("span");
auto* floating_layer = GetPaintLayerByElementId("floating");
auto* multicol_layer = GetPaintLayerByElementId("multicol");
EXPECT_FALSE(html_layer->NeedsRepaint());
EXPECT_FALSE(span_layer->NeedsRepaint());
EXPECT_FALSE(floating_layer->NeedsRepaint());
EXPECT_FALSE(multicol_layer->NeedsRepaint());
multicol_layer->SetNeedsRepaint();
EXPECT_TRUE(html_layer->NeedsRepaint());
EXPECT_TRUE(span_layer->NeedsRepaint());
EXPECT_TRUE(floating_layer->NeedsRepaint());
EXPECT_TRUE(multicol_layer->NeedsRepaint());
}
} // namespace blink