| // Copyright 2018 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_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/core/paint/compositing/composited_layer_mapping.h" |
| |
| namespace blink { |
| |
| using ::testing::MatchesRegex; |
| using ::testing::UnorderedElementsAre; |
| |
| 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; |
| } |
| .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_CASE_P(PaintAndRasterInvalidationTest); |
| |
| 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 get_debug_info = [&]() -> std::string { |
| auto* cc_layer = |
| RuntimeEnabledFeatures::CompositeAfterPaintEnabled() |
| ? GetDocument() |
| .View() |
| ->GetPaintArtifactCompositorForTesting() |
| ->RootLayer() |
| ->children()[0] |
| .get() |
| : GetLayoutView().Layer()->GraphicsLayerBacking()->CcLayer(); |
| return cc_layer->GetLayerClientForTesting() |
| ->TakeDebugInfo(cc_layer) |
| ->ToString(); |
| }; |
| |
| { |
| // This is equivalent to enabling disabled-by-default-blink.invalidation |
| // for tracing. |
| ScopedPaintUnderInvalidationCheckingForTest checking(true); |
| |
| target->setAttribute(html_names::kStyleAttr, "height: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT( |
| get_debug_info(), |
| MatchesRegex( |
| "\\{\"layer_name\":.*\"annotated_invalidation_rects\":\\[" |
| "\\{\"geometry_rect\":\\[8,108,100,100\\]," |
| "\"reason\":\"incremental\"," |
| "\"client\":\"LayoutN?G?BlockFlow DIV id='target'\"\\}\\]\\}")); |
| |
| target->setAttribute(html_names::kStyleAttr, "height: 200px; width: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT( |
| get_debug_info(), |
| MatchesRegex( |
| "\\{\"layer_name\":.*\"annotated_invalidation_rects\":\\[" |
| "\\{\"geometry_rect\":\\[108,8,100,200\\]," |
| "\"reason\":\"incremental\"," |
| "\"client\":\"LayoutN?G?BlockFlow DIV id='target'\"\\}\\]\\}")); |
| } |
| |
| target->setAttribute(html_names::kStyleAttr, "height: 300px; width: 300px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(std::string::npos, get_debug_info().find("invalidation_rects")); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationExpand) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| auto* object = target->GetLayoutObject(); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(50, 0, 50, 200), |
| PaintInvalidationReason::kIncremental}, |
| RasterInvalidationInfo{ |
| object, object->DebugName(), IntRect(0, 100, 100, 100), |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationShrink) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| auto* object = target->GetLayoutObject(); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 20px; height: 80px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(20, 0, 30, 100), |
| PaintInvalidationReason::kIncremental}, |
| RasterInvalidationInfo{ |
| object, object->DebugName(), IntRect(0, 80, 50, 20), |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, IncrementalInvalidationMixed) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| auto* object = target->GetLayoutObject(); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 100px; height: 80px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(50, 0, 50, 80), |
| PaintInvalidationReason::kIncremental}, |
| RasterInvalidationInfo{ |
| object, object->DebugName(), IntRect(0, 80, 50, 20), |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, SubpixelVisualRectChagne) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| auto* object = target->GetLayoutObject(); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, |
| "width: 100.6px; height: 70.3px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 50, 100), |
| PaintInvalidationReason::kGeometry}, |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 101, 71), |
| PaintInvalidationReason::kGeometry})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 50px; height: 100px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 50, 100), |
| PaintInvalidationReason::kGeometry}, |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 101, 71), |
| PaintInvalidationReason::kGeometry})); |
| GetDocument().View()->SetTracksPaintInvalidations(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()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, |
| "width: 100.6px; height: 70.3px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 100, 200), |
| PaintInvalidationReason::kGeometry}, |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 202, 142), |
| PaintInvalidationReason::kGeometry})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 50px; height: 100px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 100, 200), |
| PaintInvalidationReason::kGeometry}, |
| RasterInvalidationInfo{object, object->DebugName(), |
| IntRect(0, 0, 202, 142), |
| PaintInvalidationReason::kGeometry})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, SubpixelWithinPixelsChange) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| LayoutObject* object = target->GetLayoutObject(); |
| EXPECT_EQ(LayoutRect(0, 0, 50, 100), object->FirstFragment().VisualRect()); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, |
| "margin-top: 0.6px; width: 50px; height: 99.3px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(LayoutRect(0, 0, 50, 100), object->FirstFragment().VisualRect()); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| object, object->DebugName(), IntRect(0, 0, 50, 100), |
| PaintInvalidationReason::kGeometry})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, |
| "margin-top: 0.6px; width: 49.3px; height: 98.5px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(LayoutRect(0, 0, 50, 100), object->FirstFragment().VisualRect()); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| object, object->DebugName(), IntRect(0, 0, 50, 100), |
| PaintInvalidationReason::kGeometry})); |
| GetDocument().View()->SetTracksPaintInvalidations(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()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, |
| "transform: rotate(45deg); width: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| auto expected_rect = EnclosingIntRect( |
| TransformationMatrix().Rotate(45).MapRect(FloatRect(50, 0, 150, 100))); |
| expected_rect.Intersect(IntRect(0, 0, 800, 600)); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| object, object->DebugName(), expected_rect, |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, ResizeRotatedChild) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| target->setAttribute(html_names::kStyleAttr, |
| "transform: rotate(45deg); width: 200px"); |
| target->SetInnerHTMLFromString( |
| "<div id=child style='width: 50px; height: 50px; background: " |
| "red'></div>"); |
| UpdateAllLifecyclePhasesForTest(); |
| Element* child = GetDocument().getElementById("child"); |
| auto* child_object = child->GetLayoutObject(); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| child->setAttribute(html_names::kStyleAttr, |
| "width: 100px; height: 50px; background: red"); |
| UpdateAllLifecyclePhasesForTest(); |
| auto expected_rect = EnclosingIntRect( |
| TransformationMatrix().Rotate(45).MapRect(FloatRect(50, 0, 50, 50))); |
| expected_rect.Intersect(IntRect(0, 0, 800, 600)); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| child_object, child_object->DebugName(), expected_rect, |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, CompositedLayoutViewResize) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| target->setAttribute(html_names::kClassAttr, ""); |
| target->setAttribute(html_names::kStyleAttr, "height: 2000px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(kBackgroundPaintInScrollingContents, |
| GetLayoutView().GetBackgroundPaintLocation()); |
| if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| const auto* mapping = GetLayoutView().Layer()->GetCompositedLayerMapping(); |
| EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); |
| EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); |
| } |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "height: 3000px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT( |
| GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| &ViewScrollingBackgroundClient(), |
| ViewScrollingBackgroundClient().DebugName(), |
| IntRect(0, 2000, 800, 1000), PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| // Resize the viewport. No invalidation. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| GetDocument().View()->Resize(800, 1000); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| GetDocument().View()->SetTracksPaintInvalidations(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(kBackgroundPaintInScrollingContents, |
| GetLayoutView().GetBackgroundPaintLocation()); |
| if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| const auto* mapping = GetLayoutView().Layer()->GetCompositedLayerMapping(); |
| EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); |
| EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); |
| } |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "height: 3000px"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| EXPECT_THAT( |
| GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| &ViewScrollingBackgroundClient(), |
| ViewScrollingBackgroundClient().DebugName(), IntRect(0, 0, 800, 3000), |
| PaintInvalidationReason::kBackground})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| // Resize the viewport. No invalidation. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| GetDocument().View()->Resize(800, 1000); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewResize) { |
| 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"); |
| Element* content = ChildDocument().getElementById("content"); |
| EXPECT_EQ(GetLayoutView(), |
| content->GetLayoutObject()->ContainerForPaintInvalidation()); |
| EXPECT_EQ(kBackgroundPaintInScrollingContents, |
| content->GetLayoutObject() |
| ->View() |
| ->GetBackgroundPaintLocation()); |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(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()->SetTracksPaintInvalidations(false); |
| |
| // Resize the iframe. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| iframe->setAttribute(html_names::kStyleAttr, "height: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| // TODO(wangxianzhu): This is probably incorrect, but for now we assume |
| // any scrolling contents as composited during CAP painting. Perhaps we |
| // need some heuristic about composited scrolling during painting. |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| } else { |
| // The iframe doesn't have anything visible by itself, so we only issue |
| // raster invalidation for the frame contents. |
| EXPECT_THAT( |
| GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| content->GetLayoutObject()->View(), |
| content->GetLayoutObject()->View()->DebugName(), |
| IntRect(0, 100, 100, 100), PaintInvalidationReason::kIncremental})); |
| } |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, NonCompositedLayoutViewGradientResize) { |
| 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"); |
| Element* content = ChildDocument().getElementById("content"); |
| LayoutView* frame_layout_view = content->GetLayoutObject()->View(); |
| EXPECT_EQ(GetLayoutView(), |
| content->GetLayoutObject()->ContainerForPaintInvalidation()); |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| content->setAttribute(html_names::kStyleAttr, "height: 500px"); |
| UpdateAllLifecyclePhasesForTest(); |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| // TODO(wangxianzhu): This is probably incorrect, but for now we assume |
| // any scrolling contents as composited during CAP painting. Perhaps we |
| // need some heuristic about composited scrolling during painting. |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| } else { |
| EXPECT_THAT( |
| GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| frame_layout_view, frame_layout_view->DebugName(), |
| IntRect(0, 0, 100, 100), PaintInvalidationReason::kBackground})); |
| } |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| // Resize the iframe. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| iframe->setAttribute(html_names::kStyleAttr, "height: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| // TODO(wangxianzhu): This is probably incorrect, but for now we assume |
| // any scrolling contents as composited during CAP painting. Perhaps we |
| // need some heuristic about composited scrolling during painting. |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| } else { |
| // The iframe doesn't have anything visible by itself, so we only issue |
| // raster invalidation for the frame contents. |
| EXPECT_THAT( |
| GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| frame_layout_view, frame_layout_view->DebugName(), |
| IntRect(0, 100, 100, 100), PaintInvalidationReason::kIncremental})); |
| } |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, |
| CompositedBackgroundAttachmentLocalResize) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| target->setAttribute(html_names::kClassAttr, |
| "solid composited scroll local-attachment border"); |
| target->SetInnerHTMLFromString( |
| "<div id=child style='width: 500px; height: 500px'></div>", |
| ASSERT_NO_EXCEPTION); |
| Element* child = GetDocument().getElementById("child"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| auto* target_obj = ToLayoutBoxModelObject(target->GetLayoutObject()); |
| EXPECT_EQ(kBackgroundPaintInScrollingContents, |
| target_obj->GetBackgroundPaintLocation()); |
| if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| const auto* mapping = target_obj->Layer()->GetCompositedLayerMapping(); |
| EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); |
| EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); |
| } |
| |
| auto container_raster_invalidation_tracking = |
| [&]() -> const RasterInvalidationTracking* { |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) |
| return GetRasterInvalidationTracking(1); |
| return target_obj->Layer() |
| ->GraphicsLayerBacking(target_obj) |
| ->GetRasterInvalidationTracking(); |
| }; |
| auto contents_raster_invalidation_tracking = |
| [&]() -> const RasterInvalidationTracking* { |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) |
| return GetRasterInvalidationTracking(2); |
| return target_obj->Layer() |
| ->GraphicsLayerBacking() |
| ->GetRasterInvalidationTracking(); |
| }; |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(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, client.DebugName(), IntRect(0, 500, 500, 500), |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| // Resize the container. |
| GetDocument().View()->SetTracksPaintInvalidations(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, target_obj->DebugName(), IntRect(0, 0, 70, 220), |
| PaintInvalidationReason::kGeometry})); |
| // No invalidation on scrolling contents for container resize. |
| EXPECT_FALSE(contents_raster_invalidation_tracking()->HasInvalidations()); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, |
| CompositedBackgroundAttachmentLocalGradientResize) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| target->setAttribute(html_names::kClassAttr, |
| "gradient composited scroll local-attachment border"); |
| target->SetInnerHTMLFromString( |
| "<div id='child' style='width: 500px; height: 500px'></div>", |
| ASSERT_NO_EXCEPTION); |
| Element* child = GetDocument().getElementById("child"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| LayoutBoxModelObject* target_obj = |
| ToLayoutBoxModelObject(target->GetLayoutObject()); |
| auto container_raster_invalidation_tracking = |
| [&]() -> const RasterInvalidationTracking* { |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) |
| return GetRasterInvalidationTracking(1); |
| return target_obj->Layer() |
| ->GraphicsLayerBacking(target_obj) |
| ->GetRasterInvalidationTracking(); |
| }; |
| auto contents_raster_invalidation_tracking = |
| [&]() -> const RasterInvalidationTracking* { |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) |
| return GetRasterInvalidationTracking(2); |
| return target_obj->Layer() |
| ->GraphicsLayerBacking() |
| ->GetRasterInvalidationTracking(); |
| }; |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| child->setAttribute(html_names::kStyleAttr, "width: 500px; height: 1000px"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(kBackgroundPaintInScrollingContents, |
| target_obj->GetBackgroundPaintLocation()); |
| if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| const auto* mapping = target_obj->Layer()->GetCompositedLayerMapping(); |
| EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); |
| EXPECT_FALSE(mapping->BackgroundPaintsOntoGraphicsLayer()); |
| } |
| |
| // 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, client.DebugName(), IntRect(0, 0, 500, 1000), |
| PaintInvalidationReason::kBackground})); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| // Resize the container. |
| GetDocument().View()->SetTracksPaintInvalidations(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, target_obj->DebugName(), IntRect(0, 0, 70, 220), |
| PaintInvalidationReason::kGeometry})); |
| // No invalidation on scrolling contents for container resize. |
| EXPECT_FALSE(contents_raster_invalidation_tracking()->HasInvalidations()); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, |
| NonCompositedBackgroundAttachmentLocalResize) { |
| SetUpHTML(*this); |
| Element* target = GetDocument().getElementById("target"); |
| auto* object = target->GetLayoutObject(); |
| target->setAttribute(html_names::kClassAttr, "solid local-attachment scroll"); |
| target->SetInnerHTMLFromString( |
| "<div id=child style='width: 500px; height: 500px'></div>", |
| ASSERT_NO_EXCEPTION); |
| Element* child = GetDocument().getElementById("child"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(&GetLayoutView(), object->ContainerForPaintInvalidation()); |
| EXPECT_EQ(kBackgroundPaintInScrollingContents, |
| ToLayoutBoxModelObject(object)->GetBackgroundPaintLocation()); |
| |
| // Resize the content. |
| GetDocument().View()->SetTracksPaintInvalidations(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()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "height: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| // TODO(wangxianzhu): This is probably incorrect, but for now we assume |
| // any scrolling contents as composited during CAP painting. Perhaps we |
| // need some heuristic about composited scrolling during painting. |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| } else { |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| object, object->DebugName(), IntRect(0, 100, 50, 100), |
| PaintInvalidationReason::kIncremental})); |
| } |
| GetDocument().View()->SetTracksPaintInvalidations(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->SetInnerHTMLFromString("<div style='height: 500px'></div>", |
| ASSERT_NO_EXCEPTION); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| // Resize the scroller. |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 100px"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| LayoutBoxModelObject* target_object = |
| ToLayoutBoxModelObject(target->GetLayoutObject()); |
| EXPECT_EQ( |
| kBackgroundPaintInScrollingContents | kBackgroundPaintInGraphicsLayer, |
| target_object->GetBackgroundPaintLocation()); |
| if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| const auto* mapping = target_object->Layer()->GetCompositedLayerMapping(); |
| EXPECT_TRUE(mapping->BackgroundPaintsOntoScrollingContentsLayer()); |
| EXPECT_TRUE(mapping->BackgroundPaintsOntoGraphicsLayer()); |
| } |
| |
| const auto* contents_raster_invalidation_tracking = |
| RuntimeEnabledFeatures::CompositeAfterPaintEnabled() |
| ? GetRasterInvalidationTracking(2) |
| : target_object->Layer() |
| ->GraphicsLayerBacking() |
| ->GetRasterInvalidationTracking(); |
| const auto& client = target_object->GetScrollableArea() |
| ->GetScrollingBackgroundDisplayItemClient(); |
| EXPECT_THAT(contents_raster_invalidation_tracking->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| &client, client.DebugName(), IntRect(50, 0, 50, 500), |
| PaintInvalidationReason::kIncremental})); |
| const auto* container_raster_invalidation_tracking = |
| RuntimeEnabledFeatures::CompositeAfterPaintEnabled() |
| ? GetRasterInvalidationTracking(1) |
| : target_object->Layer() |
| ->GraphicsLayerBacking(target_object) |
| ->GetRasterInvalidationTracking(); |
| EXPECT_THAT( |
| container_raster_invalidation_tracking->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| target_object, target_object->DebugName(), IntRect(50, 0, 50, 100), |
| PaintInvalidationReason::kIncremental})); |
| GetDocument().View()->SetTracksPaintInvalidations(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().Height(), 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().Height(), 1000); |
| EXPECT_TRUE(GetDocument().GetLayoutView()->ShouldCheckForPaintInvalidation()); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, |
| UpdateVisualRectOnFrameBorderWidthChange) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| body { margin: 10px } |
| iframe { width: 100px; height: 100px; border: none; } |
| </style> |
| <iframe id='iframe'></iframe> |
| )HTML"); |
| |
| Element* iframe = GetDocument().getElementById("iframe"); |
| LayoutView* child_layout_view = ChildDocument().GetLayoutView(); |
| EXPECT_EQ(GetDocument().GetLayoutView(), |
| &child_layout_view->ContainerForPaintInvalidation()); |
| EXPECT_EQ(LayoutRect(0, 0, 100, 100), |
| child_layout_view->FirstFragment().VisualRect()); |
| |
| iframe->setAttribute(html_names::kStyleAttr, "border: 20px solid blue"); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_EQ(GetDocument().GetLayoutView(), |
| &child_layout_view->ContainerForPaintInvalidation()); |
| EXPECT_EQ(LayoutRect(0, 0, 100, 100), |
| child_layout_view->FirstFragment().VisualRect()); |
| }; |
| |
| TEST_P(PaintAndRasterInvalidationTest, DelayedFullPaintInvalidation) { |
| EnableCompositing(); |
| 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->SetShouldDoFullPaintInvalidationWithoutGeometryChange( |
| PaintInvalidationReason::kForTesting); |
| target->SetShouldDelayFullPaintInvalidation(); |
| EXPECT_FALSE(target->ShouldDoFullPaintInvalidation()); |
| EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation()); |
| EXPECT_EQ(PaintInvalidationReason::kForTesting, |
| target->FullPaintInvalidationReason()); |
| EXPECT_FALSE(target->NeedsPaintOffsetAndVisualRectUpdate()); |
| EXPECT_TRUE(target->ShouldCheckForPaintInvalidation()); |
| EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation()); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| EXPECT_FALSE(target->ShouldDoFullPaintInvalidation()); |
| EXPECT_TRUE(target->ShouldDelayFullPaintInvalidation()); |
| EXPECT_EQ(PaintInvalidationReason::kForTesting, |
| target->FullPaintInvalidationReason()); |
| EXPECT_FALSE(target->NeedsPaintOffsetAndVisualRectUpdate()); |
| EXPECT_TRUE(target->ShouldCheckForPaintInvalidation()); |
| EXPECT_TRUE(target->Parent()->ShouldCheckForPaintInvalidation()); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| // Scroll target into view. |
| GetDocument().domWindow()->scrollTo(0, 4000); |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| target, target->DebugName(), IntRect(0, 4000, 100, 100), |
| PaintInvalidationReason::kForTesting})); |
| EXPECT_EQ(PaintInvalidationReason::kNone, |
| target->FullPaintInvalidationReason()); |
| EXPECT_FALSE(target->ShouldDelayFullPaintInvalidation()); |
| EXPECT_FALSE(target->ShouldCheckForPaintInvalidation()); |
| EXPECT_FALSE(target->Parent()->ShouldCheckForPaintInvalidation()); |
| EXPECT_FALSE(target->NeedsPaintOffsetAndVisualRectUpdate()); |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| }; |
| |
| TEST_P(PaintAndRasterInvalidationTest, SVGHiddenContainer) { |
| EnableCompositing(); |
| 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"); |
| |
| // mask_rect's visual rect is in coordinates of the mask. |
| auto* mask_rect = GetLayoutObjectByElementId("mask-rect"); |
| EXPECT_EQ(LayoutRect(), mask_rect->FirstFragment().VisualRect()); |
| |
| // real_rect's visual rect is in coordinates of its paint invalidation |
| // container (the view). |
| auto* real_rect = GetLayoutObjectByElementId("real-rect"); |
| EXPECT_EQ(LayoutRect(55, 66, 7, 8), real_rect->FirstFragment().VisualRect()); |
| |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| ToElement(mask_rect->GetNode())->setAttribute("x", "20"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| EXPECT_EQ(LayoutRect(), mask_rect->FirstFragment().VisualRect()); |
| EXPECT_EQ(LayoutRect(55, 66, 7, 8), real_rect->FirstFragment().VisualRect()); |
| |
| // Should invalidate raster for real_rect only. |
| if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) { |
| // CAP creates composited layers for the rect and its mask. |
| EXPECT_THAT(GetRasterInvalidationTracking(1)->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| real_rect, real_rect->DebugName(), IntRect(0, 0, 7, 8), |
| PaintInvalidationReason::kFull})); |
| EXPECT_THAT(GetRasterInvalidationTracking(2)->Invalidations(), |
| UnorderedElementsAre(RasterInvalidationInfo{ |
| real_rect, real_rect->DebugName(), IntRect(0, 0, 7, 8), |
| PaintInvalidationReason::kFull})); |
| } else { |
| EXPECT_THAT(GetRasterInvalidationTracking(1)->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{real_rect, real_rect->DebugName(), |
| IntRect(155, 166, 7, 8), |
| PaintInvalidationReason::kFull}, |
| RasterInvalidationInfo{real_rect, real_rect->DebugName(), |
| IntRect(155, 166, 7, 8), |
| PaintInvalidationReason::kFull})); |
| } |
| |
| GetDocument().View()->SetTracksPaintInvalidations(false); |
| } |
| |
| TEST_P(PaintAndRasterInvalidationTest, UpdateVisualRectWhenPrinting) { |
| SetBodyInnerHTML(R"HTML( |
| <style> |
| * { margin: 0;} |
| span { |
| display: inline-block; |
| width: 150px; |
| height: 20px; |
| background: rebeccapurple; |
| } |
| </style> |
| <div><span id="a"></span><span id="b"></span><span id="c"></div> |
| )HTML"); |
| |
| auto* a = GetDocument().getElementById("a")->GetLayoutObject(); |
| EXPECT_EQ(LayoutRect(0, 0, 150, 20), a->FirstFragment().VisualRect()); |
| auto* b = GetDocument().getElementById("b")->GetLayoutObject(); |
| EXPECT_EQ(LayoutRect(150, 0, 150, 20), b->FirstFragment().VisualRect()); |
| auto* c = GetDocument().getElementById("c")->GetLayoutObject(); |
| EXPECT_EQ(LayoutRect(300, 0, 150, 20), c->FirstFragment().VisualRect()); |
| |
| // Print the page with a width of 400px which will require wrapping 'c'. |
| FloatSize page_size(400, 200); |
| GetFrame().StartPrinting(page_size, page_size, 1); |
| GetDocument().View()->UpdateLifecyclePhasesForPrinting(); |
| // In LayoutNG these may be different layout objects, so get them again |
| a = GetDocument().getElementById("a")->GetLayoutObject(); |
| b = GetDocument().getElementById("b")->GetLayoutObject(); |
| c = GetDocument().getElementById("c")->GetLayoutObject(); |
| |
| EXPECT_EQ(LayoutRect(0, 0, 150, 20), a->FirstFragment().VisualRect()); |
| EXPECT_EQ(LayoutRect(150, 0, 150, 20), b->FirstFragment().VisualRect()); |
| // 'c' should be on the next line. |
| EXPECT_EQ(LayoutRect(0, 20, 150, 20), c->FirstFragment().VisualRect()); |
| |
| GetFrame().EndPrinting(); |
| GetDocument().View()->UpdateLifecyclePhasesForPrinting(); |
| a = GetDocument().getElementById("a")->GetLayoutObject(); |
| b = GetDocument().getElementById("b")->GetLayoutObject(); |
| c = GetDocument().getElementById("c")->GetLayoutObject(); |
| |
| EXPECT_EQ(LayoutRect(0, 0, 150, 20), a->FirstFragment().VisualRect()); |
| EXPECT_EQ(LayoutRect(150, 0, 150, 20), b->FirstFragment().VisualRect()); |
| EXPECT_EQ(LayoutRect(300, 0, 150, 20), c->FirstFragment().VisualRect()); |
| }; |
| |
| 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 = ToLayoutBoxModelObject(object)->Layer(); |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "transform: scale(3)"); |
| GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint(); |
| EXPECT_FALSE(layer->NeedsRepaint()); |
| const auto* transform = |
| object->FirstFragment().PaintProperties()->Transform(); |
| EXPECT_TRUE(transform->Changed(*transform->Parent())); |
| |
| UpdateAllLifecyclePhasesForTest(); |
| EXPECT_THAT(GetRasterInvalidationTracking()->Invalidations(), |
| UnorderedElementsAre( |
| RasterInvalidationInfo{ |
| layer, layer->DebugName(), IntRect(0, 0, 100, 200), |
| PaintInvalidationReason::kPaintProperty}, |
| RasterInvalidationInfo{ |
| layer, layer->DebugName(), IntRect(0, 0, 150, 300), |
| PaintInvalidationReason::kPaintProperty})); |
| EXPECT_FALSE(transform->Changed(*transform->Parent())); |
| GetDocument().View()->SetTracksPaintInvalidations(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"); |
| GetDocument().View()->SetTracksPaintInvalidations(true); |
| target->setAttribute(html_names::kStyleAttr, "width: 200px; height: 200px"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| // No raster invalidations because the resized-div doesn't paint anything by |
| // itself, and the svg is fixed sized. |
| EXPECT_FALSE(GetRasterInvalidationTracking()->HasInvalidations()); |
| // At least we don't invalidate paint of the SVG rect. |
| for (const auto& paint_invalidation : |
| *GetDocument().View()->TrackedObjectPaintInvalidations()) { |
| EXPECT_NE(GetLayoutObjectByElementId("rect")->DebugName(), |
| paint_invalidation.name); |
| } |
| |
| GetDocument().View()->SetTracksPaintInvalidations(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(LayoutPoint(0, 0), sticky->FirstFragment().PaintOffset()); |
| EXPECT_EQ( |
| TransformationMatrix().Translate(0.f, 50.f), |
| sticky->FirstFragment().PaintProperties()->StickyTranslation()->Matrix()); |
| const auto* inner = GetLayoutObjectByElementId("inner"); |
| EXPECT_EQ(LayoutPoint(0, 0), inner->FirstFragment().PaintOffset()); |
| |
| UpdateAllLifecyclePhasesForTest(); |
| |
| EXPECT_FALSE(sticky->NeedsPaintPropertyUpdate()); |
| EXPECT_EQ(LayoutPoint(0, 0), sticky->FirstFragment().PaintOffset()); |
| EXPECT_EQ( |
| TransformationMatrix().Translate(0.f, 150.f), |
| sticky->FirstFragment().PaintProperties()->StickyTranslation()->Matrix()); |
| EXPECT_EQ(LayoutPoint(0, 0), inner->FirstFragment().PaintOffset()); |
| } |
| |
| class PaintInvalidatorTestClient : public EmptyChromeClient { |
| public: |
| void InvalidateRect(const IntRect&) 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(EmptyLocalFrameClient::Create()), |
| 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(R"HTML( |
| <div id=target style="opacity: 0.99"></div> |
| )HTML"); |
| |
| auto* target = GetDocument().getElementById("target"); |
| ASSERT_TRUE(target); |
| |
| ResetInvalidationRecorded(); |
| |
| target->setAttribute(html_names::kStyleAttr, "opacity: 0.98"); |
| UpdateAllLifecyclePhasesForTest(); |
| |
| EXPECT_TRUE(InvalidationRecorded()); |
| } |
| |
| } // namespace blink |