blob: 2d672c8c3f2494a90aeb89ff35dc07bef080dccb [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// 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_and_raster_invalidation_test.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/testing/find_cc_layer.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
using ::testing::MatchesRegex;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
const RasterInvalidationTracking* GetRasterInvalidationTracking(
const LocalFrameView& root_frame_view,
wtf_size_t index,
const String& name_regex) {
if (auto* client = root_frame_view.GetPaintArtifactCompositor()
->ContentLayerClientForTesting(index)) {
DCHECK(client->Layer().draws_content());
DCHECK(::testing::Matcher<std::string>(
::testing::ContainsRegex(name_regex.Utf8()))
.Matches(client->Layer().DebugName()));
return client->GetRasterInvalidator().GetTracking();
}
return nullptr;
}
void SetUpHTML(PaintAndRasterInvalidationTest& test) {
test.SetBodyInnerHTML(R"HTML(
<style>
body {
margin: 0;
height: 0;
}
::-webkit-scrollbar { display: none }
#target {
width: 50px;
height: 100px;
transform-origin: 0 0;
}
.solid {
background: blue;
}
.translucent {
background: rgba(0, 0, 255, 0.5);
}
.gradient {
background-image: linear-gradient(blue, yellow);
}
.scroll {
overflow: scroll;
}
.solid-composited-scroller {
overflow: scroll;
will-change: transform;
background: blue;
}
.local-attachment {
background-attachment: local;
}
.transform {
transform: scale(2);
}
.border {
border: 10px solid black;
}
.composited {
will-change: transform;
}
</style>
<div id='target' class='solid'></div>
)HTML");
}
INSTANTIATE_PAINT_TEST_SUITE_P(PaintAndRasterInvalidationTest);
class ScopedEnablePaintInvalidationTracing {
public:
ScopedEnablePaintInvalidationTracing() {
trace_event::EnableTracing(TRACE_DISABLED_BY_DEFAULT("blink.invalidation"));
}
~ScopedEnablePaintInvalidationTracing() { trace_event::DisableTracing(); }
};
TEST_P(PaintAndRasterInvalidationTest, TrackingForTracing) {
SetBodyInnerHTML(R"HTML(
<style>#target { width: 100px; height: 100px; background: blue }</style>
<div id="target"></div>
)HTML");
auto* target = GetDocument().getElementById("target");
auto& cc_layer = *GetDocument()
.View()
->GetPaintArtifactCompositor()
->RootLayer()
->children()[1];
{
ScopedEnablePaintInvalidationTracing tracing;
target->setAttribute(html_names::kStyleAttr, "height: 200px");
UpdateAllLifecyclePhasesForTest();
ASSERT_TRUE(cc_layer.debug_info());
EXPECT_EQ(1u, cc_layer.debug_info()->invalidations.size());
target->setAttribute(html_names::kStyleAttr, "height: 200px; width: 200px");
UpdateAllLifecyclePhasesForTest();
ASSERT_TRUE(cc_layer.debug_info());
EXPECT_EQ(2u, cc_layer.debug_info()->invalidations.size());
}
target->setAttribute(html_names::kStyleAttr, "height: 300px; width: 300px");
UpdateAllLifecyclePhasesForTest();
ASSERT_TRUE(cc_layer.debug_info());
// No new invalidations tracked.
EXPECT_EQ(2u, cc_layer.debug_info()->invalidations.size());
}
TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationExpand) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 200px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(50, 0, 50, 200),
PaintInvalidationReason::kIncremental},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 100, 100, 100),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationShrink) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 20px; height: 80px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(20, 0, 30, 100),
PaintInvalidationReason::kIncremental},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 80, 50, 20),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationMixed) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 80px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(50, 0, 50, 80),
PaintInvalidationReason::kIncremental},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 80, 50, 20),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, ResizeEmptyContent) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
// Make the view not solid color so that we can track raster invalidations
// in SolidColorLayers.
GetDocument().body()->setAttribute(
html_names::kStyleAttr,
"height: 400px; background: linear-gradient(red, blue)");
// Make the box empty.
target->setAttribute(html_names::kClassAttr, "");
UpdateAllLifecyclePhasesForTest();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 80px");
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, SubpixelChange) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr,
"width: 100.6px; height: 70.3px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 50, 100),
PaintInvalidationReason::kLayout},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 101, 70),
PaintInvalidationReason::kLayout}));
GetDocument().View()->SetTracksRasterInvalidations(false);
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 50px; height: 100px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 50, 100),
PaintInvalidationReason::kLayout},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 101, 70),
PaintInvalidationReason::kLayout}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChangeWithTransform) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
target->setAttribute(html_names::kClassAttr, "solid transform");
UpdateAllLifecyclePhasesForTest();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr,
"width: 100.6px; height: 70.3px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 100, 200),
PaintInvalidationReason::kLayout},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 202, 140),
PaintInvalidationReason::kLayout}));
GetDocument().View()->SetTracksRasterInvalidations(false);
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 50px; height: 100px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 100, 200),
PaintInvalidationReason::kLayout},
RasterInvalidationInfo{object->Id(), object->DebugName(),
gfx::Rect(0, 0, 202, 140),
PaintInvalidationReason::kLayout}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, SubpixelWithinPixelsChange) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
LayoutObject* object = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr,
"margin-top: 0.6px; width: 50px; height: 99.3px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
object->Id(), object->DebugName(), gfx::Rect(0, 0, 50, 100),
PaintInvalidationReason::kLayout}));
GetDocument().View()->SetTracksRasterInvalidations(false);
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr,
"margin-top: 0.6px; width: 49.3px; height: 98.5px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
object->Id(), object->DebugName(), gfx::Rect(0, 1, 50, 99),
PaintInvalidationReason::kLayout}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, ResizeRotated) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
target->setAttribute(html_names::kStyleAttr, "transform: rotate(45deg)");
UpdateAllLifecyclePhasesForTest();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr,
"transform: rotate(45deg); width: 200px");
UpdateAllLifecyclePhasesForTest();
auto expected_rect =
MakeRotationMatrix(45).MapRect(gfx::Rect(50, 0, 150, 100));
expected_rect.Intersect(gfx::Rect(0, 0, 800, 600));
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
object->Id(), object->DebugName(), expected_rect,
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, ResizeRotatedChild) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
target->setAttribute(html_names::kStyleAttr,
"transform: rotate(45deg); width: 200px");
target->setInnerHTML(
"<div id=child style='width: 50px; height: 50px; background: "
"red'></div>");
UpdateAllLifecyclePhasesForTest();
Element* child = GetDocument().getElementById("child");
auto* child_object = child->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
child->setAttribute(html_names::kStyleAttr,
"width: 100px; height: 50px; background: red");
UpdateAllLifecyclePhasesForTest();
auto expected_rect = MakeRotationMatrix(45).MapRect(gfx::Rect(50, 0, 50, 50));
expected_rect.Intersect(gfx::Rect(0, 0, 800, 600));
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
child_object->Id(), child_object->DebugName(), expected_rect,
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewResize) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
target->setAttribute(html_names::kClassAttr, "");
target->setAttribute(html_names::kStyleAttr, "height: 2000px");
// Make the scrolling contents layer not solid color so that we can track
// raster invalidations in SolidColorLayers.
target->setInnerHTML("<div style='height: 20px'>Text</div>");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(kBackgroundPaintInContentsSpace,
GetLayoutView().GetBackgroundPaintLocation());
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "height: 3000px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
ViewScrollingBackgroundClient().Id(),
ViewScrollingBackgroundClient().DebugName(),
gfx::Rect(0, 2000, 800, 1000),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
// Resize the viewport. No invalidation.
GetDocument().View()->SetTracksRasterInvalidations(true);
GetDocument().View()->Resize(800, 1000);
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewGradientResize) {
SetUpHTML(*this);
GetDocument().body()->setAttribute(html_names::kClassAttr, "gradient");
Element* target = GetDocument().getElementById("target");
target->setAttribute(html_names::kClassAttr, "");
target->setAttribute(html_names::kStyleAttr, "height: 2000px");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(kBackgroundPaintInContentsSpace,
GetLayoutView().GetBackgroundPaintLocation());
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "height: 3000px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
ViewScrollingBackgroundClient().Id(),
ViewScrollingBackgroundClient().DebugName(),
gfx::Rect(0, 0, 800, 3000), PaintInvalidationReason::kBackground}));
GetDocument().View()->SetTracksRasterInvalidations(false);
// Resize the viewport. No invalidation.
GetDocument().View()->SetTracksRasterInvalidations(true);
GetDocument().View()->Resize(800, 1000);
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) {
ScopedPreferNonCompositedScrollingForTest non_composited_scrolling(true);
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0 }
iframe { display: block; width: 100px; height: 100px; border: none; }
</style>
<iframe id='iframe'></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0; background: green; height: 0 }
</style>
<div id='content' style='width: 200px; height: 200px'></div>
)HTML");
UpdateAllLifecyclePhasesForTest();
Element* iframe = GetDocument().getElementById("iframe");
LayoutView* iframe_layout_view = ChildDocument().View()->GetLayoutView();
Element* content = ChildDocument().getElementById("content");
EXPECT_EQ(kBackgroundPaintInContentsSpace,
iframe_layout_view->ComputeBackgroundPaintLocationIfComposited());
EXPECT_EQ(RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
? kBackgroundPaintInContentsSpace
: kBackgroundPaintInBorderBoxSpace,
iframe_layout_view->GetBackgroundPaintLocation());
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
content->setAttribute(html_names::kStyleAttr, "height: 500px");
UpdateAllLifecyclePhasesForTest();
// No invalidation because the changed part of layout overflow is clipped.
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
// Resize the iframe.
GetDocument().View()->SetTracksRasterInvalidations(true);
iframe->setAttribute(html_names::kStyleAttr, "height: 200px");
UpdateAllLifecyclePhasesForTest();
// The iframe doesn't have anything visible by itself, so we only issue
// raster invalidation for the frame contents.
const auto* client =
RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
? &iframe_layout_view->GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient()
: iframe_layout_view;
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client->Id(), client->DebugName(), gfx::Rect(0, 100, 100, 100),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, FullInvalidationWithHTMLTransform) {
GetDocument().documentElement()->setAttribute(html_names::kStyleAttr,
"transform: scale(0.5)");
const DisplayItemClient& client = ViewScrollingBackgroundClient();
UpdateAllLifecyclePhasesForTest();
GetDocument().View()->SetTracksRasterInvalidations(true);
GetDocument().View()->Resize(gfx::Size(500, 500));
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{client.Id(), client.DebugName(),
gfx::Rect(0, 0, 500, 500),
PaintInvalidationReason::kBackground},
RasterInvalidationInfo{client.Id(), client.DebugName(),
gfx::Rect(0, 0, 500, 500),
PaintInvalidationReason::kBackground}));
}
TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) {
ScopedPreferNonCompositedScrollingForTest non_composited_scrolling(true);
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0 }
iframe { display: block; width: 100px; height: 100px; border: none; }
</style>
<iframe id='iframe'></iframe>
)HTML");
SetChildFrameHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body {
margin: 0;
height: 0;
background-image: linear-gradient(blue, yellow);
}
</style>
<div id='content' style='width: 200px; height: 200px'></div>
)HTML");
UpdateAllLifecyclePhasesForTest();
Element* iframe = GetDocument().getElementById("iframe");
const auto* iframe_layout_view = ChildDocument().View()->GetLayoutView();
Element* content = ChildDocument().getElementById("content");
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
content->setAttribute(html_names::kStyleAttr, "height: 500px");
UpdateAllLifecyclePhasesForTest();
const auto* client =
RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
? &iframe_layout_view->GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient()
: iframe_layout_view;
if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
// The two invalidations are for the old background and the new background.
// The rects are the same because they are clipped by the layer bounds.
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{client->Id(), client->DebugName(),
gfx::Rect(0, 0, 100, 100),
PaintInvalidationReason::kBackground},
RasterInvalidationInfo{client->Id(), client->DebugName(),
gfx::Rect(0, 0, 100, 100),
PaintInvalidationReason::kBackground}));
} else {
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client->Id(), client->DebugName(), gfx::Rect(0, 0, 100, 100),
PaintInvalidationReason::kBackground}));
}
GetDocument().View()->SetTracksRasterInvalidations(false);
// Resize the iframe.
GetDocument().View()->SetTracksRasterInvalidations(true);
iframe->setAttribute(html_names::kStyleAttr, "height: 200px");
UpdateAllLifecyclePhasesForTest();
// The iframe doesn't have anything visible by itself, so we only issue
// raster invalidation for the frame contents.
if (RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()) {
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{client->Id(), client->DebugName(),
gfx::Rect(0, 100, 100, 100),
PaintInvalidationReason::kIncremental},
RasterInvalidationInfo{client->Id(), client->DebugName(),
gfx::Rect(0, 0, 100, 200),
PaintInvalidationReason::kBackground}));
} else {
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client->Id(), client->DebugName(), gfx::Rect(0, 0, 100, 200),
PaintInvalidationReason::kBackground}));
}
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest,
CompositedBackgroundAttachmentLocalResize) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
target->setAttribute(html_names::kClassAttr,
"solid composited scroll local-attachment border");
UpdateAllLifecyclePhasesForTest();
target->setInnerHTML(
"<div id=child style='width: 500px; height: 500px'></div>",
ASSERT_NO_EXCEPTION);
Element* child = GetDocument().getElementById("child");
UpdateAllLifecyclePhasesForTest();
auto* target_obj = To<LayoutBoxModelObject>(target->GetLayoutObject());
EXPECT_EQ(kBackgroundPaintInContentsSpace,
target_obj->GetBackgroundPaintLocation());
auto container_raster_invalidation_tracking =
[&]() -> const RasterInvalidationTracking* {
return GetRasterInvalidationTracking(
RuntimeEnabledFeatures::SolidColorLayersEnabled() ? 0 : 1, "target");
};
auto contents_raster_invalidation_tracking =
[&]() -> const RasterInvalidationTracking* {
return GetRasterInvalidationTracking(
RuntimeEnabledFeatures::SolidColorLayersEnabled() ? 1 : 2, "target");
};
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px");
UpdateAllLifecyclePhasesForTest();
// No invalidation on the container layer.
EXPECT_FALSE(container_raster_invalidation_tracking()->HasInvalidations());
// Incremental invalidation of background on contents layer.
const auto& client = target_obj->GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient();
EXPECT_THAT(contents_raster_invalidation_tracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client.Id(), client.DebugName(), gfx::Rect(0, 500, 500, 500),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
// Resize the container.
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "height: 200px");
UpdateAllLifecyclePhasesForTest();
// Border invalidated in the container layer.
EXPECT_THAT(container_raster_invalidation_tracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
target_obj->Id(), target_obj->DebugName(),
gfx::Rect(0, 0, 70, 220), PaintInvalidationReason::kLayout}));
// No invalidation on scrolling contents for container resize.
EXPECT_FALSE(contents_raster_invalidation_tracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest,
CompositedBackgroundAttachmentLocalGradientResize) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
target->setAttribute(html_names::kClassAttr,
"gradient composited scroll local-attachment border");
target->setInnerHTML(
"<div id='child' style='width: 500px; height: 500px'></div>",
ASSERT_NO_EXCEPTION);
Element* child = GetDocument().getElementById("child");
UpdateAllLifecyclePhasesForTest();
auto* target_obj = To<LayoutBoxModelObject>(target->GetLayoutObject());
auto container_raster_invalidation_tracking =
[&]() -> const RasterInvalidationTracking* {
return GetRasterInvalidationTracking(
RuntimeEnabledFeatures::SolidColorLayersEnabled() ? 0 : 1, "target");
};
auto contents_raster_invalidation_tracking =
[&]() -> const RasterInvalidationTracking* {
return GetRasterInvalidationTracking(
RuntimeEnabledFeatures::SolidColorLayersEnabled() ? 1 : 2, "target");
};
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(kBackgroundPaintInContentsSpace,
target_obj->GetBackgroundPaintLocation());
// No invalidation on the container layer.
EXPECT_FALSE(container_raster_invalidation_tracking()->HasInvalidations());
// Full invalidation of background on contents layer because the gradient
// background is resized.
const auto& client = target_obj->GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient();
EXPECT_THAT(contents_raster_invalidation_tracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client.Id(), client.DebugName(), gfx::Rect(0, 0, 500, 1000),
PaintInvalidationReason::kBackground}));
GetDocument().View()->SetTracksRasterInvalidations(false);
// Resize the container.
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "height: 200px");
UpdateAllLifecyclePhasesForTest();
// Border invalidated in the container layer.
EXPECT_THAT(container_raster_invalidation_tracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
target_obj->Id(), target_obj->DebugName(),
gfx::Rect(0, 0, 70, 220), PaintInvalidationReason::kLayout}));
// No invalidation on scrolling contents for container resize.
EXPECT_FALSE(contents_raster_invalidation_tracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest,
NonCompositedBackgroundAttachmentLocalResize) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutBox();
target->setAttribute(html_names::kClassAttr,
"translucent local-attachment scroll");
target->setInnerHTML(
"<div id=child style='width: 500px; height: 500px'></div>",
ASSERT_NO_EXCEPTION);
Element* child = GetDocument().getElementById("child");
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(kBackgroundPaintInContentsSpace,
object->ComputeBackgroundPaintLocationIfComposited());
EXPECT_EQ(RuntimeEnabledFeatures::CompositeScrollAfterPaintEnabled()
? kBackgroundPaintInContentsSpace
: kBackgroundPaintInBorderBoxSpace,
object->GetBackgroundPaintLocation());
// Resize the content.
GetDocument().View()->SetTracksRasterInvalidations(true);
child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px");
UpdateAllLifecyclePhasesForTest();
// No invalidation because the changed part is invisible.
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
// Resize the container.
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "height: 200px");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
object->Id(), object->DebugName(), gfx::Rect(0, 100, 50, 100),
PaintInvalidationReason::kIncremental}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, CompositedSolidBackgroundResize) {
// To trigger background painting on both container and contents layer.
// Note that the test may need update when we change the background paint
// location rules.
SetPreferCompositingToLCDText(false);
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
target->setAttribute(html_names::kClassAttr, "solid composited scroll");
target->setInnerHTML(
"<div style='width: 50px; height: 500px; background: yellow'></div>");
UpdateAllLifecyclePhasesForTest();
// Resize the scroller.
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 100px");
UpdateAllLifecyclePhasesForTest();
auto* target_object = To<LayoutBoxModelObject>(target->GetLayoutObject());
EXPECT_EQ(kBackgroundPaintInBothSpaces,
target_object->GetBackgroundPaintLocation());
const auto* contents_raster_invalidation_tracking =
GetRasterInvalidationTracking(
RuntimeEnabledFeatures::SolidColorLayersEnabled() ? 0 : 2, "target");
// Only the contents layer is eligible for blink-side raster invalidation.
if (RuntimeEnabledFeatures::SolidColorLayersEnabled()) {
EXPECT_FALSE(GetRasterInvalidationTracking(1, ""));
}
const auto& client = target_object->GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient();
EXPECT_THAT(contents_raster_invalidation_tracking->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client.Id(), client.DebugName(), gfx::Rect(50, 0, 50, 500),
PaintInvalidationReason::kIncremental}));
if (!RuntimeEnabledFeatures::SolidColorLayersEnabled()) {
const auto* container_raster_invalidation_tracking =
GetRasterInvalidationTracking(1, "target");
EXPECT_THAT(
container_raster_invalidation_tracking->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
target_object->Id(), target_object->DebugName(),
gfx::Rect(50, 0, 50, 100), PaintInvalidationReason::kIncremental}));
}
GetDocument().View()->SetTracksRasterInvalidations(false);
}
// Changing style in a way that changes overflow without layout should cause
// the layout view to possibly need a paint invalidation since we may have
// revealed additional background that can be scrolled into view.
TEST_P(PaintAndRasterInvalidationTest, RecalcOverflowInvalidatesBackground) {
GetDocument().GetPage()->GetSettings().SetViewportEnabled(true);
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style type='text/css'>
body, html {
width: 100%;
height: 100%;
margin: 0px;
}
#container {
will-change: transform;
width: 100%;
height: 100%;
}
</style>
<div id='container'></div>
)HTML");
UpdateAllLifecyclePhasesForTest();
ScrollableArea* scrollable_area = GetDocument().View()->LayoutViewport();
ASSERT_EQ(scrollable_area->MaximumScrollOffset().y(), 0);
EXPECT_FALSE(
GetDocument().GetLayoutView()->ShouldCheckForPaintInvalidation());
Element* container = GetDocument().getElementById("container");
container->setAttribute(html_names::kStyleAttr,
"transform: translateY(1000px);");
GetDocument().UpdateStyleAndLayoutTree();
EXPECT_EQ(scrollable_area->MaximumScrollOffset().y(), 1000);
EXPECT_TRUE(GetDocument().GetLayoutView()->ShouldCheckForPaintInvalidation());
}
TEST_P(PaintAndRasterInvalidationTest, DelayedFullPaintInvalidation) {
SetBodyInnerHTML(R"HTML(
<style>body { margin: 0 }</style>
<div style='height: 4000px'></div>
<div id='target' style='width: 100px; height: 100px; background: blue'>
</div>
)HTML");
auto* target = GetLayoutObjectByElementId("target");
target->SetShouldDoFullPaintInvalidationWithoutLayoutChange(
PaintInvalidationReason::kStyle);
target->SetShouldDelayFullPaintInvalidation();
EXPECT_FALSE(target->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation());
EXPECT_EQ(PaintInvalidationReason::kStyle,
target->FullPaintInvalidationReason());
EXPECT_FALSE(target->ShouldCheckLayoutForPaintInvalidation());
EXPECT_TRUE(target->ShouldCheckForPaintInvalidation());
EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation());
GetDocument().View()->SetTracksRasterInvalidations(true);
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
EXPECT_FALSE(target->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation());
EXPECT_EQ(PaintInvalidationReason::kStyle,
target->FullPaintInvalidationReason());
EXPECT_FALSE(target->ShouldCheckLayoutForPaintInvalidation());
EXPECT_TRUE(target->ShouldCheckForPaintInvalidation());
EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation());
GetDocument().View()->SetTracksRasterInvalidations(false);
GetDocument().View()->SetTracksRasterInvalidations(true);
// Scroll target into view.
GetDocument().domWindow()->scrollTo(0, 4000);
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
target->Id(), target->DebugName(), gfx::Rect(0, 4000, 100, 100),
PaintInvalidationReason::kStyle}));
EXPECT_EQ(PaintInvalidationReason::kNone,
target->FullPaintInvalidationReason());
EXPECT_FALSE(target->ShouldDelayFullPaintInvalidation());
EXPECT_FALSE(target->ShouldCheckForPaintInvalidation());
EXPECT_FALSE(target->Parent()->ShouldCheckForPaintInvalidation());
EXPECT_FALSE(target->ShouldCheckLayoutForPaintInvalidation());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, SVGHiddenContainer) {
SetBodyInnerHTML(R"HTML(
<svg style='position: absolute; top: 100px; left: 100px'>
<mask id='mask'>
<g transform='scale(2)'>
<rect id='mask-rect' x='11' y='22' width='33' height='44'/>
</g>
</mask>
<rect id='real-rect' x='55' y='66' width='7' height='8'
mask='url(#mask)'/>
</svg>
)HTML");
auto* mask_rect = GetLayoutObjectByElementId("mask-rect");
auto* real_rect = GetLayoutObjectByElementId("real-rect");
GetDocument().View()->SetTracksRasterInvalidations(true);
To<Element>(mask_rect->GetNode())->setAttribute("x", "20");
UpdateAllLifecyclePhasesForTest();
// Should invalidate raster for real_rect only.
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{real_rect->Id(), real_rect->DebugName(),
gfx::Rect(155, 166, 7, 8),
PaintInvalidationReason::kSubtree},
RasterInvalidationInfo{real_rect->Id(), real_rect->DebugName(),
gfx::Rect(154, 165, 9, 10),
PaintInvalidationReason::kSubtree}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, SVGWithFilterNoOpStyleUpdate) {
SetBodyInnerHTML(R"HTML(
<svg>
<filter id="f">
<feGaussianBlur stdDeviation="5"/>
</filter>
<rect width="100" height="100" style="filter: url(#f)"/>
</svg>
)HTML");
GetDocument().View()->SetTracksRasterInvalidations(true);
GetDocument().body()->setAttribute(html_names::kStyleAttr, "--x: 42");
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, PaintPropertyChange) {
SetUpHTML(*this);
Element* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
target->setAttribute(html_names::kClassAttr, "solid transform");
UpdateAllLifecyclePhasesForTest();
auto* layer = To<LayoutBoxModelObject>(object)->Layer();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "transform: scale(3)");
UpdateAllLifecyclePhasesExceptPaint();
EXPECT_FALSE(layer->SelfNeedsRepaint());
const auto* transform =
object->FirstFragment().PaintProperties()->Transform();
EXPECT_TRUE(transform->Changed(
PaintPropertyChangeType::kChangedOnlySimpleValues, *transform->Parent()));
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(
RasterInvalidationInfo{layer->Id(), layer->DebugName(),
gfx::Rect(0, 0, 100, 200),
PaintInvalidationReason::kPaintProperty},
RasterInvalidationInfo{layer->Id(), layer->DebugName(),
gfx::Rect(0, 0, 150, 300),
PaintInvalidationReason::kPaintProperty}));
EXPECT_FALSE(transform->Changed(PaintPropertyChangeType::kChangedOnlyValues,
*transform->Parent()));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, ResizeContainerOfFixedSizeSVG) {
SetBodyInnerHTML(R"HTML(
<div id="target" style="width: 100px; height: 100px">
<svg viewBox="0 0 200 200" width="100" height="100">
<rect id="rect" width="100%" height="100%"/>
</svg>
</div>
)HTML");
Element* target = GetDocument().getElementById("target");
LayoutObject* rect = GetLayoutObjectByElementId("rect");
EXPECT_TRUE(static_cast<const DisplayItemClient*>(rect)->IsValid());
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 200px; height: 200px");
UpdateAllLifecyclePhasesExceptPaint();
// We don't invalidate paint of the SVG rect.
EXPECT_TRUE(static_cast<const DisplayItemClient*>(rect)->IsValid());
UpdateAllLifecyclePhasesForTest();
// No raster invalidations because the resized-div doesn't paint anything by
// itself, and the svg is fixed sized.
EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, ScrollingInvalidatesStickyOffset) {
SetBodyInnerHTML(R"HTML(
<div id="scroller" style="width:300px; height:200px; overflow:scroll">
<div id="sticky" style="position:sticky; top:50px;
width:50px; height:100px; background:red;">
<div id="inner" style="width:100px; height:50px; background:red;">
</div>
</div>
<div style="height:1000px;"></div>
</div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
scroller->setScrollTop(100);
const auto* sticky = GetLayoutObjectByElementId("sticky");
EXPECT_TRUE(sticky->NeedsPaintPropertyUpdate());
EXPECT_EQ(PhysicalOffset(), sticky->FirstFragment().PaintOffset());
EXPECT_EQ(gfx::Vector2dF(0, 50), sticky->FirstFragment()
.PaintProperties()
->StickyTranslation()
->Get2dTranslation());
const auto* inner = GetLayoutObjectByElementId("inner");
EXPECT_EQ(PhysicalOffset(), inner->FirstFragment().PaintOffset());
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(sticky->NeedsPaintPropertyUpdate());
EXPECT_EQ(PhysicalOffset(), sticky->FirstFragment().PaintOffset());
EXPECT_EQ(gfx::Vector2dF(0, 150), sticky->FirstFragment()
.PaintProperties()
->StickyTranslation()
->Get2dTranslation());
EXPECT_EQ(PhysicalOffset(), inner->FirstFragment().PaintOffset());
}
TEST_P(PaintAndRasterInvalidationTest, NoDamageDueToFloatingPointError) {
SetBodyInnerHTML(R"HTML(
<style>
#canvas {
position: absolute;
top: 0;
left: 0;
width: 0;
height: 0;
will-change: transform;
transform-origin: top left;
}
.initial { transform: translateX(0px) scale(1.8); }
.updated { transform: translateX(47.22222222222222px) scale(1.8); }
#tile {
position: absolute;
will-change: transform;
transform-origin: top left;
transform: scale(0.55555555555556);
}
#tileInner {
transform-origin: top left;
transform: scale(1.8);
width: 200px;
height: 200px;
background: lightblue;
}
</style>
<div id="canvas" class="initial">
<div id="tile">
<div id="tileInner"></div>
</div>
</div>
)HTML");
GetDocument().View()->SetTracksRasterInvalidations(true);
auto* canvas = GetDocument().getElementById("canvas");
canvas->setAttribute(html_names::kClassAttr, "updated");
GetDocument().View()->SetPaintArtifactCompositorNeedsUpdate(
PaintArtifactCompositorUpdateReason::kTest);
UpdateAllLifecyclePhasesForTest();
EXPECT_FALSE(
GetRasterInvalidationTracking(
RuntimeEnabledFeatures::SolidColorLayersEnabled() ? 0 : 1, "tile")
->HasInvalidations());
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, ResizeElementWhichHasNonCustomResizer) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
* { margin: 0;}
div {
width: 100px;
height: 100px;
background-color: red;
overflow: hidden;
resize: both;
}
</style>
<div id='target'></div>
)HTML");
auto* target = GetDocument().getElementById("target");
auto* object = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "width: 200px");
UpdateAllLifecyclePhasesForTest();
Vector<RasterInvalidationInfo> invalidations;
// This is for DisplayItem::kResizerScrollHitTest.
invalidations.push_back(RasterInvalidationInfo{
object->Id(), object->DebugName(), gfx::Rect(100, 0, 100, 100),
PaintInvalidationReason::kIncremental});
const auto& scroll_corner = To<LayoutBoxModelObject>(object)
->GetScrollableArea()
->GetScrollCornerDisplayItemClient();
invalidations.push_back(RasterInvalidationInfo{
scroll_corner.Id(), scroll_corner.DebugName(), gfx::Rect(93, 93, 7, 7),
PaintInvalidationReason::kLayout});
invalidations.push_back(RasterInvalidationInfo{
scroll_corner.Id(), scroll_corner.DebugName(), gfx::Rect(193, 93, 7, 7),
PaintInvalidationReason::kLayout});
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAreArray(invalidations));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
TEST_P(PaintAndRasterInvalidationTest, VisibilityChange) {
SetBodyInnerHTML(R"HTML(
<style>
/* Make the view not solid color so that we can track raster
invalidations in SolidColorLayers. */
body { background: linear-gradient(red, blue); }
#target { width: 100px; height: 100px; background: blue; }
</style>
<div id="target"></div>
)HTML");
auto* target = GetDocument().getElementById("target");
const DisplayItemClient* client = target->GetLayoutObject();
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "visibility: hidden");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client->Id(), client->DebugName(), gfx::Rect(8, 8, 100, 100),
PaintInvalidationReason::kDisappeared}));
GetDocument().View()->SetTracksRasterInvalidations(false);
GetDocument().View()->SetTracksRasterInvalidations(true);
target->setAttribute(html_names::kStyleAttr, "visibility: visible");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(),
UnorderedElementsAre(RasterInvalidationInfo{
client->Id(), client->DebugName(), gfx::Rect(8, 8, 100, 100),
PaintInvalidationReason::kAppeared}));
GetDocument().View()->SetTracksRasterInvalidations(false);
}
class PaintInvalidatorTestClient : public RenderingTestChromeClient {
public:
void InvalidateContainer() override { invalidation_recorded_ = true; }
bool InvalidationRecorded() { return invalidation_recorded_; }
void ResetInvalidationRecorded() { invalidation_recorded_ = false; }
private:
bool invalidation_recorded_ = false;
};
class PaintInvalidatorCustomClientTest : public RenderingTest {
public:
PaintInvalidatorCustomClientTest()
: RenderingTest(MakeGarbageCollected<EmptyLocalFrameClient>()),
chrome_client_(MakeGarbageCollected<PaintInvalidatorTestClient>()) {}
PaintInvalidatorTestClient& GetChromeClient() const override {
return *chrome_client_;
}
bool InvalidationRecorded() { return chrome_client_->InvalidationRecorded(); }
void ResetInvalidationRecorded() {
chrome_client_->ResetInvalidationRecorded();
}
private:
Persistent<PaintInvalidatorTestClient> chrome_client_;
};
TEST_F(PaintInvalidatorCustomClientTest,
NonCompositedInvalidationChangeOpacity) {
// This test runs in a non-composited mode, so invalidations should
// be issued via InvalidateChromeClient.
SetBodyInnerHTML("<div id=target style='opacity: 0.99'></div>");
auto* target = GetDocument().getElementById("target");
ASSERT_TRUE(target);
ResetInvalidationRecorded();
target->setAttribute(html_names::kStyleAttr, "opacity: 0.98");
UpdateAllLifecyclePhasesForTest();
EXPECT_TRUE(InvalidationRecorded());
}
TEST_F(PaintInvalidatorCustomClientTest,
NoInvalidationRepeatedUpdateLifecyleExceptPaint) {
SetBodyInnerHTML("<div id=target style='opacity: 0.99'></div>");
auto* target = GetDocument().getElementById("target");
ASSERT_TRUE(target);
ResetInvalidationRecorded();
target->setAttribute(html_names::kStyleAttr, "opacity: 0.98");
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
DocumentUpdateReason::kTest);
// Only paint property change doesn't need repaint.
EXPECT_FALSE(
GetDocument().View()->GetLayoutView()->Layer()->DescendantNeedsRepaint());
// Just needs to invalidate the chrome client.
EXPECT_TRUE(InvalidationRecorded());
ResetInvalidationRecorded();
// Let PrePaintTreeWalk do something instead of no-op, without any real
// change.
GetDocument().View()->SetNeedsPaintPropertyUpdate();
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(
DocumentUpdateReason::kTest);
EXPECT_FALSE(
GetDocument().View()->GetLayoutView()->Layer()->DescendantNeedsRepaint());
EXPECT_FALSE(InvalidationRecorded());
}
} // namespace blink