// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h" #include "cc/layers/layer.h" #include "cc/layers/picture_layer.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/property_tree.h" #include "cc/trees/scroll_node.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/html/html_frame_owner_element.h" #include "third_party/blink/renderer/core/layout/layout_box_model_object.h" #include "third_party/blink/renderer/core/layout/layout_image.h" #include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/paint/paint_layer.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/graphics/paint/geometry_mapper.h" namespace blink { // TODO(wangxianzhu): Though these tests don't directly apply in // CompositeAfterPaint, we should ensure the cases are tested in // CompositeAfterPaint mode if applicable. class CompositedLayerMappingTest : public RenderingTest { public: CompositedLayerMappingTest() : RenderingTest(MakeGarbageCollected()) {} protected: IntRect RecomputeInterestRect(const GraphicsLayer* graphics_layer) { return static_cast(graphics_layer->Client()) .RecomputeInterestRect(graphics_layer); } IntRect ComputeInterestRect(GraphicsLayer* graphics_layer, IntRect previous_interest_rect) { return static_cast(graphics_layer->Client()) .ComputeInterestRect(graphics_layer, previous_interest_rect); } bool InterestRectChangedEnoughToRepaint(const IntRect& previous_interest_rect, const IntRect& new_interest_rect, const IntSize& layer_size) { return CompositedLayerMapping::InterestRectChangedEnoughToRepaint( previous_interest_rect, new_interest_rect, layer_size); } IntRect PreviousInterestRect(const GraphicsLayer* graphics_layer) { return graphics_layer->previous_interest_rect_; } private: void SetUp() override { EnableCompositing(); RenderingTest::SetUp(); } }; TEST_F(CompositedLayerMappingTest, SubpixelAccumulationChange) { SetBodyInnerHTML( "
"); Element* target = GetDocument().getElementById("target"); target->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.6px"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kTest); PaintLayer* paint_layer = ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); // Directly composited layers are not invalidated on subpixel accumulation // change. EXPECT_FALSE(paint_layer->GraphicsLayerBacking() ->GetPaintController() .GetPaintArtifact() .IsEmpty()); } TEST_F(CompositedLayerMappingTest, SubpixelAccumulationChangeUnderInvalidation) { ScopedPaintUnderInvalidationCheckingForTest test(true); SetBodyInnerHTML( "
"); Element* target = GetDocument().getElementById("target"); target->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.6px"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kTest); PaintLayer* paint_layer = ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); // Directly composited layers are not invalidated on subpixel accumulation // change. EXPECT_TRUE(paint_layer->GraphicsLayerBacking() ->GetPaintController() .GetPaintArtifact() .IsEmpty()); } TEST_F(CompositedLayerMappingTest, SubpixelAccumulationChangeIndirectCompositing) { SetBodyInnerHTML(R"HTML(
)HTML"); Element* target = GetDocument().getElementById("target"); target->SetInlineStyleProperty(CSSPropertyID::kLeft, "0.6px"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kTest); PaintLayer* paint_layer = ToLayoutBoxModelObject(target->GetLayoutObject())->Layer(); // The PaintArtifact should have been deleted because paint was // invalidated for subpixel accumulation change. EXPECT_TRUE(paint_layer->GraphicsLayerBacking() ->GetPaintController() .GetPaintArtifact() .IsEmpty()); } TEST_F(CompositedLayerMappingTest, SimpleInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); ASSERT_TRUE(paint_layer->GetCompositedLayerMapping()); EXPECT_EQ(IntRect(0, 0, 200, 200), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, TallLayerInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); // Screen-space visible content rect is [8, 8, 200, 600]. Mapping back to // local, adding 4000px in all directions, then clipping, yields this rect. EXPECT_EQ(IntRect(0, 0, 200, 4592), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, TallCompositedScrolledLayerInterestRect) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 8000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 2992, 200, 7008), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, TallNonCompositedScrolledLayerInterestRect) { SetHtmlInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 8000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); PaintLayer* paint_layer = GetDocument().GetLayoutView()->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 4000, 800, 7016), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, TallLayerWholeDocumentInterestRect) { SetBodyInnerHTML( "
"); GetDocument().GetSettings()->SetMainFrameClipsContent(false); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); ASSERT_TRUE(paint_layer->GetCompositedLayerMapping()); // Clipping is disabled in recomputeInterestRect. EXPECT_EQ(IntRect(0, 0, 200, 10000), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); EXPECT_EQ( IntRect(0, 0, 200, 10000), ComputeInterestRect(paint_layer->GraphicsLayerBacking(), IntRect())); } TEST_F(CompositedLayerMappingTest, VerticalRightLeftWritingModeDocument) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(-5000, 0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); PaintLayer* paint_layer = GetDocument().GetLayoutView()->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); ASSERT_TRUE(paint_layer->GetCompositedLayerMapping()); // A scroll by -5000px is equivalent to a scroll by (10000 - 5000 - 800)px = // 4200px in non-RTL mode. Expanding the resulting rect by 4000px in each // direction yields this result. EXPECT_EQ(IntRect(200, 0, 8800, 600), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, RotatedInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 200, 200), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, RotatedInterestRectNear90Degrees) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); // Because the layer is rotated to almost 90 degrees, floating-point error // leads to a reverse-projected rect that is much much larger than the // original layer size in certain dimensions. In such cases, we often fall // back to the 4000px interest rect padding amount. EXPECT_EQ(IntRect(0, 0, 4000, 200), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, LargeScaleInterestRect) { // It's rotated 90 degrees about the X axis, which means its visual content // rect is empty, and so the interest rect is the default (0, 0, 4000, 4000) // intersected with the layer bounds. SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 1920, 5300), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, PerspectiveInterestRect) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 1202, 837), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, RotationInterestRect) { SetBodyInnerHTML(R"HTML(
)HTML"); GetFrame().View()->Resize(2000, 3000); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 3000, 100), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, 3D90DegRotatedTallInterestRect) { // It's rotated 90 degrees about the X axis, which means its visual content // rect is empty, and so the interest rect is the default (0, 0, 4000, 4000) // intersected with the layer bounds. SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 200, 4000), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, 3D45DegRotatedTallInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 200, 6226), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, RotatedTallInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 200, 4000), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, WideLayerInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); // Screen-space visible content rect is [8, 8, 800, 200] (the screen is // 800x600). Mapping back to local, adding 4000px in all directions, then // clipping, yields this rect. EXPECT_EQ(IntRect(0, 0, 4792, 200), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, FixedPositionInterestRect) { SetBodyInnerHTML( "
"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); EXPECT_EQ(IntRect(0, 0, 300, 400), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, LayerOffscreenInterestRect) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(!!paint_layer->GraphicsLayerBacking()); // Offscreen layers are painted as usual. EXPECT_EQ(IntRect(0, 0, 200, 200), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, ScrollingLayerInterestRect) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); // Offscreen layers are painted as usual. ASSERT_TRUE( paint_layer->GetCompositedLayerMapping()->ScrollingContentsLayer()); // In screen space, the scroller is (8, 8, 195, 193) (because of overflow clip // of 'target', scrollbar and root margin). // Applying the viewport clip of the root has no effect because // the clip is already small. Mapping it down into the graphics layer // space yields (0, 0, 195, 193). This is then expanded by 4000px. EXPECT_EQ(IntRect(0, 0, 195, 4193), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, ClippedBigLayer) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer->GraphicsLayerBacking()); // Offscreen layers are painted as usual. EXPECT_EQ(IntRect(0, 0, 4001, 4001), RecomputeInterestRect(paint_layer->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, InterestRectChangedEnoughToRepaintEmpty) { IntSize layer_size(1000, 1000); // Both empty means there is nothing to do. EXPECT_FALSE( InterestRectChangedEnoughToRepaint(IntRect(), IntRect(), layer_size)); // Going from empty to non-empty means we must re-record because it could be // the first frame after construction or Clear. EXPECT_TRUE(InterestRectChangedEnoughToRepaint(IntRect(), IntRect(0, 0, 1, 1), layer_size)); // Going from non-empty to empty is not special-cased. EXPECT_FALSE(InterestRectChangedEnoughToRepaint(IntRect(0, 0, 1, 1), IntRect(), layer_size)); } TEST_F(CompositedLayerMappingTest, InterestRectChangedEnoughToRepaintNotBigEnough) { IntSize layer_size(1000, 1000); IntRect previous_interest_rect(100, 100, 100, 100); EXPECT_FALSE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(100, 100, 90, 90), layer_size)); EXPECT_FALSE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(100, 100, 100, 100), layer_size)); EXPECT_FALSE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(1, 1, 200, 200), layer_size)); } TEST_F(CompositedLayerMappingTest, InterestRectChangedEnoughToRepaintNotBigEnoughButNewAreaTouchesEdge) { IntSize layer_size(500, 500); IntRect previous_interest_rect(100, 100, 100, 100); // Top edge. EXPECT_TRUE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(100, 0, 100, 200), layer_size)); // Left edge. EXPECT_TRUE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(0, 100, 200, 100), layer_size)); // Bottom edge. EXPECT_TRUE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(100, 100, 100, 400), layer_size)); // Right edge. EXPECT_TRUE(InterestRectChangedEnoughToRepaint( previous_interest_rect, IntRect(100, 100, 400, 100), layer_size)); } // Verifies that having a current viewport that touches a layer edge does not // force re-recording. TEST_F(CompositedLayerMappingTest, InterestRectChangedEnoughToRepaintCurrentViewportTouchesEdge) { IntSize layer_size(500, 500); IntRect new_interest_rect(100, 100, 300, 300); // Top edge. EXPECT_FALSE(InterestRectChangedEnoughToRepaint( IntRect(100, 0, 100, 100), new_interest_rect, layer_size)); // Left edge. EXPECT_FALSE(InterestRectChangedEnoughToRepaint( IntRect(0, 100, 100, 100), new_interest_rect, layer_size)); // Bottom edge. EXPECT_FALSE(InterestRectChangedEnoughToRepaint( IntRect(300, 400, 100, 100), new_interest_rect, layer_size)); // Right edge. EXPECT_FALSE(InterestRectChangedEnoughToRepaint( IntRect(400, 300, 100, 100), new_interest_rect, layer_size)); } TEST_F(CompositedLayerMappingTest, InterestRectChangedEnoughToRepaintScrollScenarios) { IntSize layer_size(1000, 1000); IntRect previous_interest_rect(100, 100, 100, 100); IntRect new_interest_rect(previous_interest_rect); new_interest_rect.Move(512, 0); EXPECT_FALSE(InterestRectChangedEnoughToRepaint( previous_interest_rect, new_interest_rect, layer_size)); new_interest_rect.Move(0, 512); EXPECT_FALSE(InterestRectChangedEnoughToRepaint( previous_interest_rect, new_interest_rect, layer_size)); new_interest_rect.Move(1, 0); EXPECT_TRUE(InterestRectChangedEnoughToRepaint( previous_interest_rect, new_interest_rect, layer_size)); new_interest_rect.Move(-1, 1); EXPECT_TRUE(InterestRectChangedEnoughToRepaint( previous_interest_rect, new_interest_rect, layer_size)); } TEST_F(CompositedLayerMappingTest, InterestRectChangeOnViewportScroll) { SetBodyInnerHTML(R"HTML(
Text
)HTML"); UpdateAllLifecyclePhasesForTest(); GraphicsLayer* root_scrolling_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(IntRect(0, 0, 800, 4600), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 300), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because the recomputed rect hasn't // changed enough. EXPECT_EQ(IntRect(0, 0, 800, 4900), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 800, 4600), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 600), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Use recomputed interest rect because it changed enough. EXPECT_EQ(IntRect(0, 0, 800, 5200), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 800, 5200), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 5400), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1400, 800, 8600), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 1400, 800, 8600), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 9000), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because it contains the recomputed // interest rect. EXPECT_EQ(IntRect(0, 5000, 800, 5000), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 1400, 800, 8600), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0, 2000), mojom::blink::ScrollType::kProgrammatic); // Use recomputed interest rect because it changed enough. UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 0, 800, 6600), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 800, 6600), PreviousInterestRect(root_scrolling_layer)); } TEST_F(CompositedLayerMappingTest, InterestRectChangeOnShrunkenViewport) { SetBodyInnerHTML(R"HTML(
Text
)HTML"); UpdateAllLifecyclePhasesForTest(); GraphicsLayer* root_scrolling_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(IntRect(0, 0, 800, 4600), PreviousInterestRect(root_scrolling_layer)); GetDocument().View()->SetFrameRect(IntRect(0, 0, 800, 60)); UpdateAllLifecyclePhasesForTest(); // Repaint required, so interest rect should be updated to shrunken size. EXPECT_EQ(IntRect(0, 0, 800, 4060), RecomputeInterestRect(root_scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 800, 4060), PreviousInterestRect(root_scrolling_layer)); } TEST_F(CompositedLayerMappingTest, InterestRectChangeOnScroll) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML(
Text
GetLayoutBox()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(IntRect(0, 0, 400, 4400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(300); UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because the recomputed rect hasn't // changed enough. EXPECT_EQ(IntRect(0, 0, 400, 4700), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 400, 4400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(600); UpdateAllLifecyclePhasesForTest(); // Use recomputed interest rect because it changed enough. EXPECT_EQ(IntRect(0, 0, 400, 5000), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 400, 5000), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(5600); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1600, 400, 8400), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 1600, 400, 8400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(9000); UpdateAllLifecyclePhasesForTest(); // Still use the previous interest rect because it contains the recomputed // interest rect. EXPECT_EQ(IntRect(0, 5000, 400, 5000), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 1600, 400, 8400), PreviousInterestRect(scrolling_layer)); scroller->setScrollTop(2000); // Use recomputed interest rect because it changed enough. UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 0, 400, 6400), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 0, 400, 6400), PreviousInterestRect(scrolling_layer)); } TEST_F(CompositedLayerMappingTest, InterestRectShouldChangeOnPaintInvalidation) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML(
Text
GetLayoutBox()->Layer()->GraphicsLayerBacking(); scroller->setScrollTop(5400); UpdateAllLifecyclePhasesForTest(); scroller->setScrollTop(9400); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 5400, 400, 4600), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 5400, 400, 4600), PreviousInterestRect(scrolling_layer)); // Paint invalidation and repaint should change previous paint interest rect. GetDocument().getElementById("content")->setTextContent("Change"); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 5400, 400, 4600), RecomputeInterestRect(scrolling_layer)); EXPECT_EQ(IntRect(0, 5400, 400, 4600), PreviousInterestRect(scrolling_layer)); } TEST_F(CompositedLayerMappingTest, InterestRectOfSquashingLayerWithNegativeOverflow) { SetBodyInnerHTML(R"HTML(
text
)HTML"); EXPECT_EQ(GetDocument() .getElementById("inside") ->GetLayoutBox() ->VisualOverflowRect() .Size() .Height(), 100); CompositedLayerMapping* grouped_mapping = GetDocument() .getElementById("squashed") ->GetLayoutBox() ->Layer() ->GroupedMapping(); // The squashing layer is at (-10000, 190, 10100, 100) in viewport // coordinates. // The following rect is at (-4000, 190, 4100, 100) in viewport coordinates. EXPECT_EQ(IntRect(6000, 0, 4100, 100), grouped_mapping->ComputeInterestRect( grouped_mapping->SquashingLayer(), IntRect())); } TEST_F(CompositedLayerMappingTest, InterestRectOfSquashingLayerWithAncestorClip) { SetBodyInnerHTML( "" "
" "
" "
" // Above overflow:hidden div and two composited layers make the squashing // layer a child of an ancestor clipping layer. "
" "
" "
"); CompositedLayerMapping* grouped_mapping = GetDocument() .getElementById("squashed") ->GetLayoutBox() ->Layer() ->GroupedMapping(); // The squashing layer is at (-9600, 0, 10000, 1000) in viewport coordinates. // The following rect is at (-4000, 0, 4400, 1000) in viewport coordinates. EXPECT_EQ(IntRect(5600, 0, 4400, 1000), grouped_mapping->ComputeInterestRect( grouped_mapping->SquashingLayer(), IntRect())); } TEST_F(CompositedLayerMappingTest, InterestRectOfIframeInScrolledDiv) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); SetBodyInnerHTML(R"HTML(
)HTML"); SetChildFrameHTML( "
"); // Scroll 8000 pixels down to move the iframe into view. GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 8000.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); Element* target = ChildDocument().getElementById("target"); ASSERT_TRUE(target); EXPECT_EQ( IntRect(0, 0, 200, 200), RecomputeInterestRect( target->GetLayoutObject()->EnclosingLayer()->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, InterestRectOfScrolledIframe) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML( )HTML"); SetChildFrameHTML( "
"); UpdateAllLifecyclePhasesForTest(); // Scroll 7500 pixels down to bring the scrollable area to the bottom. ChildDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 7500.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(ChildDocument().View()->GetLayoutView()->HasLayer()); EXPECT_EQ(IntRect(0, 3500, 500, 4500), RecomputeInterestRect(ChildDocument() .View() ->GetLayoutView() ->EnclosingLayer() ->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithContentBoxOffset) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); // Set a 10px border in order to have a contentBoxOffset for the iframe // element. SetBodyInnerHTML(R"HTML( )HTML"); SetChildFrameHTML( "
"); UpdateAllLifecyclePhasesForTest(); // Scroll 3000 pixels down to bring the scrollable area to somewhere in the // middle. ChildDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 3000.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); ASSERT_TRUE(ChildDocument().View()->GetLayoutView()->HasLayer()); EXPECT_EQ(IntRect(0, 0, 500, 7500), RecomputeInterestRect(ChildDocument() .View() ->GetLayoutView() ->EnclosingLayer() ->GraphicsLayerBacking())); } TEST_F(CompositedLayerMappingTest, InterestRectOfIframeWithFixedContents) { GetDocument().SetBaseURLOverride(KURL("http://test.com")); GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML( )HTML"); SetChildFrameHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); auto* fixed = ChildDocument().getElementById("fixed")->GetLayoutObject(); auto* graphics_layer = fixed->EnclosingLayer()->GraphicsLayerBacking(fixed); // The graphics layer has dimensions 5400x300 but the interest rect clamps // this to the right-most 4000x4000 area. EXPECT_EQ(IntRect(1000, 0, 4400, 300), RecomputeInterestRect(graphics_layer)); ChildDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 3000.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Because the fixed element does not scroll, the interest rect is unchanged. EXPECT_EQ(IntRect(1000, 0, 4400, 300), RecomputeInterestRect(graphics_layer)); } TEST_F(CompositedLayerMappingTest, ScrolledFixedPositionInterestRect) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); auto* fixed = GetDocument().getElementById("fixed")->GetLayoutObject(); auto* graphics_layer = fixed->EnclosingLayer()->GraphicsLayerBacking(fixed); EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer)); GetDocument().View()->LayoutViewport()->SetScrollOffset( ScrollOffset(0.0, 200.0), mojom::blink::ScrollType::kProgrammatic); UpdateAllLifecyclePhasesForTest(); // Because the fixed element does not scroll, the interest rect is unchanged. EXPECT_EQ(IntRect(0, 500, 100, 4030), RecomputeInterestRect(graphics_layer)); } TEST_F(CompositedLayerMappingTest, ScrollingContentsAndForegroundLayerPaintingPhase) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML(
)HTML"); auto* mapping = To(GetLayoutObjectByElementId("container")) ->Layer() ->GetCompositedLayerMapping(); ASSERT_TRUE(mapping->ScrollingContentsLayer()); EXPECT_EQ(static_cast( kGraphicsLayerPaintOverflowContents | kGraphicsLayerPaintCompositedScroll), mapping->ScrollingContentsLayer()->PaintingPhase()); ASSERT_TRUE(mapping->ForegroundLayer()); EXPECT_EQ( static_cast( kGraphicsLayerPaintForeground | kGraphicsLayerPaintOverflowContents), mapping->ForegroundLayer()->PaintingPhase()); // Regression test for crbug.com/767908: a foreground layer should also // participates hit testing. EXPECT_TRUE(mapping->ForegroundLayer()->GetHitTestable()); Element* negative_composited_child = GetDocument().getElementById("negative-composited-child"); negative_composited_child->parentNode()->RemoveChild( negative_composited_child); UpdateAllLifecyclePhasesForTest(); mapping = To(GetLayoutObjectByElementId("container")) ->Layer() ->GetCompositedLayerMapping(); ASSERT_TRUE(mapping->ScrollingContentsLayer()); EXPECT_EQ( static_cast( kGraphicsLayerPaintOverflowContents | kGraphicsLayerPaintCompositedScroll | kGraphicsLayerPaintForeground), mapping->ScrollingContentsLayer()->PaintingPhase()); EXPECT_FALSE(mapping->ForegroundLayer()); } TEST_F(CompositedLayerMappingTest, DecorationOutlineLayerOnlyCreatedInCompositedScrolling) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* element = GetDocument().getElementById("target"); PaintLayer* paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); // Decoration outline layer is created when composited scrolling. EXPECT_TRUE(paint_layer->HasCompositedLayerMapping()); EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); CompositedLayerMapping* mapping = paint_layer->GetCompositedLayerMapping(); EXPECT_TRUE(mapping->DecorationOutlineLayer()); // No decoration outline layer is created when not composited scrolling. element->setAttribute(html_names::kStyleAttr, "overflow: visible;"); UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); mapping = paint_layer->GetCompositedLayerMapping(); EXPECT_FALSE(paint_layer->NeedsCompositedScrolling()); EXPECT_FALSE(mapping->DecorationOutlineLayer()); } TEST_F(CompositedLayerMappingTest, DecorationOutlineLayerCreatedAndDestroyedInCompositedScrolling) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); Element* scroller = GetDocument().getElementById("scroller"); PaintLayer* paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); CompositedLayerMapping* mapping = paint_layer->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->DecorationOutlineLayer()); // The decoration outline layer is created when composited scrolling // with an outline drawn over the composited scrolling region. scroller->setAttribute(html_names::kStyleAttr, "outline-offset: -2px;"); UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); mapping = paint_layer->GetCompositedLayerMapping(); EXPECT_TRUE(paint_layer->NeedsCompositedScrolling()); EXPECT_TRUE(mapping->DecorationOutlineLayer()); // The decoration outline layer is destroyed when the scrolling region // will not be covered up by the outline. scroller->removeAttribute(html_names::kStyleAttr); UpdateAllLifecyclePhasesForTest(); paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer(); ASSERT_TRUE(paint_layer); mapping = paint_layer->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->DecorationOutlineLayer()); } TEST_F(CompositedLayerMappingTest, BackgroundPaintedIntoGraphicsLayerIfNotCompositedScrolling) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML(
)HTML"); const auto* container = ToLayoutBox(GetLayoutObjectByElementId("container")); EXPECT_EQ(kBackgroundPaintInScrollingContents, container->ComputeBackgroundPaintLocationIfComposited()); // We currently don't use composited scrolling when the container has a // border-radius so even though we can paint the background onto the scrolling // contents layer we don't have a scrolling contents layer to paint into in // this case. const auto* mapping = container->Layer()->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->HasScrollingLayer()); EXPECT_EQ(kBackgroundPaintInGraphicsLayer, container->GetBackgroundPaintLocation()); } TEST_F(CompositedLayerMappingTest, StickyPositionMainThreadOffset) { SetBodyInnerHTML(R"HTML(
)HTML"); PaintLayer* sticky_layer = ToLayoutBox(GetLayoutObjectByElementId("sticky"))->Layer(); CompositedLayerMapping* sticky_mapping = sticky_layer->GetCompositedLayerMapping(); ASSERT_TRUE(sticky_mapping); // Now scroll the page - this should increase the main thread offset. LayoutBoxModelObject* scroller = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")); PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); scrollable_area->ScrollToAbsolutePosition( FloatPoint(scrollable_area->ScrollPosition().X(), 100)); ASSERT_EQ(100.0, scrollable_area->ScrollPosition().Y()); sticky_layer->SetNeedsCompositingInputsUpdate(); EXPECT_TRUE(sticky_layer->NeedsCompositingInputsUpdate()); GetDocument().View()->UpdateLifecycleToCompositingCleanPlusScrolling( DocumentUpdateReason::kTest); EXPECT_FALSE(sticky_layer->NeedsCompositingInputsUpdate()); } TEST_F(CompositedLayerMappingTest, StickyPositionNotSquashed) { SetBodyInnerHTML(R"HTML(
)HTML"); auto* sticky1 = To(GetLayoutObjectByElementId("sticky1"))->Layer(); auto* sticky2 = To(GetLayoutObjectByElementId("sticky2"))->Layer(); auto* sticky3 = To(GetLayoutObjectByElementId("sticky3"))->Layer(); // All three sticky-pos elements are composited, because we composite // all sticky elements which stick to scrollers. EXPECT_EQ(kPaintsIntoOwnBacking, sticky1->GetCompositingState()); EXPECT_EQ(kPaintsIntoOwnBacking, sticky2->GetCompositingState()); EXPECT_EQ(kPaintsIntoOwnBacking, sticky3->GetCompositingState()); } TEST_F(CompositedLayerMappingTest, LayerPositionForStickyElementInCompositedScroller) { SetBodyInnerHTML(R"HTML(
)HTML"); LayoutBoxModelObject* sticky = ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky")); CompositedLayerMapping* mapping = sticky->Layer()->GetCompositedLayerMapping(); ASSERT_TRUE(mapping); GraphicsLayer* main_graphics_layer = mapping->MainGraphicsLayer(); ASSERT_TRUE(main_graphics_layer); auto* scroller = To(GetLayoutObjectByElementId("scroller"))->Layer(); PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); scrollable_area->ScrollToAbsolutePosition( FloatPoint(scrollable_area->ScrollPosition().Y(), 100)); UpdateAllLifecyclePhasesForTest(); // On the blink side, a sticky offset of (0, 100) should have been applied to // the sticky element. EXPECT_EQ(PhysicalOffset(0, 100), sticky->StickyPositionOffset()); GraphicsLayer* root_scrolling_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); const PropertyTreeState& root_layer_state = root_scrolling_layer->GetPropertyTreeState(); const PropertyTreeState& sticky_layer_state = main_graphics_layer->GetPropertyTreeState(); auto transform_from_sticky_to_root = GeometryMapper::SourceToDestinationProjection( sticky_layer_state.Transform(), root_layer_state.Transform()); // Irrespective of if the ancestor scroller is composited or not, the sticky // position element should be at the same location. auto sticky_position_relative_to_root = transform_from_sticky_to_root.MapPoint( FloatPoint(main_graphics_layer->GetOffsetFromTransformNode())); EXPECT_FLOAT_EQ(8, sticky_position_relative_to_root.X()); EXPECT_FLOAT_EQ(8, sticky_position_relative_to_root.Y()); } TEST_F(CompositedLayerMappingTest, LayerPositionForStickyElementInNonCompositedScroller) { SetBodyInnerHTML(R"HTML(
)HTML"); auto* mapping = To(GetLayoutObjectByElementId("sticky")) ->Layer() ->GetCompositedLayerMapping(); ASSERT_TRUE(mapping); GraphicsLayer* main_graphics_layer = mapping->MainGraphicsLayer(); auto* scroller = To(GetLayoutObjectByElementId("scroller"))->Layer(); PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); ASSERT_TRUE(scrollable_area); scrollable_area->ScrollToAbsolutePosition( FloatPoint(scrollable_area->ScrollPosition().Y(), 100)); UpdateAllLifecyclePhasesForTest(); GraphicsLayer* root_scrolling_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); const PropertyTreeState& root_layer_state = root_scrolling_layer->GetPropertyTreeState(); const PropertyTreeState& sticky_layer_state = main_graphics_layer->GetPropertyTreeState(); auto transform_from_sticky_to_root = GeometryMapper::SourceToDestinationProjection( sticky_layer_state.Transform(), root_layer_state.Transform()); // Irrespective of if the ancestor scroller is composited or not, the sticky // position element should be at the same location. auto sticky_position_relative_to_root = transform_from_sticky_to_root.MapPoint( FloatPoint(main_graphics_layer->GetOffsetFromTransformNode())); EXPECT_FLOAT_EQ(8, sticky_position_relative_to_root.X()); EXPECT_FLOAT_EQ(8, sticky_position_relative_to_root.Y()); } TEST_F(CompositedLayerMappingTest, TransformedRasterizationDisallowedForDirectReasons) { // This test verifies layers with direct compositing reasons won't have // transformed rasterization, i.e. should raster in local space. SetBodyInnerHTML(R"HTML(
foo
bar
ham
)HTML"); { LayoutObject* target = GetLayoutObjectByElementId("target1"); ASSERT_TRUE(target && target->IsBox()); PaintLayer* target_layer = ToLayoutBox(target)->Layer(); GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); EXPECT_FALSE( target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } { LayoutObject* target = GetLayoutObjectByElementId("target2"); ASSERT_TRUE(target && target->IsBox()); PaintLayer* target_layer = ToLayoutBox(target)->Layer(); GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); EXPECT_FALSE( target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } { LayoutObject* target = GetLayoutObjectByElementId("target3"); ASSERT_TRUE(target && target->IsBox()); PaintLayer* target_layer = ToLayoutBox(target)->Layer(); GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); EXPECT_FALSE( target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } } TEST_F(CompositedLayerMappingTest, TransformedRasterizationForInlineTransform) { // This test verifies we allow layers that are indirectly composited due to // an inline transform (but no direct reason otherwise) to raster in the // device space for higher quality. SetBodyInnerHTML(R"HTML(
composited
indirectly composited due to inline transform
)HTML"); LayoutObject* target = GetLayoutObjectByElementId("target"); ASSERT_TRUE(target && target->IsBox()); PaintLayer* target_layer = ToLayoutBox(target)->Layer(); GraphicsLayer* target_graphics_layer = target_layer ? target_layer->GraphicsLayerBacking() : nullptr; ASSERT_TRUE(target_graphics_layer); EXPECT_TRUE( target_graphics_layer->CcLayer()->transformed_rasterization_allowed()); } TEST_F(CompositedLayerMappingTest, ScrollingContainerBoundsChange) { GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); SetBodyInnerHTML(R"HTML(
GetScrollableArea(); cc::Layer* scrolling_layer = scrollable_area->LayerForScrolling(); auto element_id = scrollable_area->GetScrollElementId(); auto& scroll_tree = scrolling_layer->layer_tree_host()->property_trees()->scroll_tree; EXPECT_EQ(0, scroll_tree.current_scroll_offset(element_id).y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); auto* scroll_node = scroll_tree.FindNodeFromElementId(element_id); EXPECT_EQ(100, scroll_node->container_bounds.height()); scrollerElement->setScrollTop(300); scrollerElement->setAttribute(html_names::kStyleAttr, "max-height: 25px;"); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, scroll_tree.current_scroll_offset(element_id).y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); scroll_node = scroll_tree.FindNodeFromElementId(element_id); EXPECT_EQ(25, scroll_node->container_bounds.height()); scrollerElement->setAttribute(html_names::kStyleAttr, "max-height: 300px;"); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(50, scroll_tree.current_scroll_offset(element_id).y()); EXPECT_EQ(150, scrolling_layer->bounds().height()); scroll_node = scroll_tree.FindNodeFromElementId(element_id); EXPECT_EQ(100, scroll_node->container_bounds.height()); } TEST_F(CompositedLayerMappingTest, MainFrameLayerBackgroundColor) { UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(Color::kWhite, GetDocument().View()->BaseBackgroundColor()); auto* view_layer = GetDocument().GetLayoutView()->Layer()->GraphicsLayerBacking(); EXPECT_EQ(Color::kWhite, view_layer->BackgroundColor()); Color base_background(255, 0, 0); GetDocument().View()->SetBaseBackgroundColor(base_background); GetDocument().body()->setAttribute(html_names::kStyleAttr, "background: rgba(0, 255, 0, 0.5)"); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(base_background, GetDocument().View()->BaseBackgroundColor()); EXPECT_EQ(Color(127, 128, 0, 255), view_layer->BackgroundColor()); } TEST_F(CompositedLayerMappingTest, ScrollingLayerBackgroundColor) { SetBodyInnerHTML(R"HTML(
)HTML"); auto* target = GetDocument().getElementById("target"); auto* mapping = ToLayoutBoxModelObject(target->GetLayoutObject()) ->Layer() ->GetCompositedLayerMapping(); auto* graphics_layer = mapping->MainGraphicsLayer(); auto* scrolling_contents_layer = mapping->ScrollingContentsLayer(); ASSERT_TRUE(graphics_layer); ASSERT_TRUE(scrolling_contents_layer); EXPECT_EQ(Color::kTransparent, graphics_layer->BackgroundColor()); EXPECT_EQ(Color::kTransparent, scrolling_contents_layer->BackgroundColor()); target->setAttribute(html_names::kClassAttr, "color"); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(Color(0, 0, 255), graphics_layer->BackgroundColor()); EXPECT_EQ(Color(0, 0, 255), scrolling_contents_layer->BackgroundColor()); } TEST_F(CompositedLayerMappingTest, ScrollLayerSizingSubpixelAccumulation) { // This test verifies that when subpixel accumulation causes snapping it // applies to both the scrolling and scrolling contents layers. Verify that // the mapping doesn't have any vertical scrolling introduced as a result of // the snapping behavior. https://crbug.com/801381. GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled( true); // The values below are chosen so that the subpixel accumulation causes the // pixel snapped height to be increased relative to snapping without it. SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); auto* mapping = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller")) ->Layer() ->GetCompositedLayerMapping(); ASSERT_TRUE(mapping); ASSERT_TRUE(mapping->ScrollingLayer()); ASSERT_TRUE(mapping->ScrollingContentsLayer()); EXPECT_EQ(mapping->ScrollingLayer()->Size().height(), mapping->ScrollingContentsLayer()->Size().height()); } TEST_F(CompositedLayerMappingTest, SquashingScrollInterestRect) { SetHtmlInnerHTML(R"HTML(
)HTML"); auto* squashed = ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer(); EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState()); GetDocument().View()->LayoutViewport()->ScrollBy( ScrollOffset(0, 5000), mojom::blink::ScrollType::kUser); UpdateAllLifecyclePhasesForTest(); EXPECT_EQ(IntRect(0, 1000, 200, 5000), squashed->GroupedMapping()->SquashingLayer()->InterestRect()); } TEST_F(CompositedLayerMappingTest, SquashingBoundsUnderCompositedScrollingWithTransform) { SetHtmlInnerHTML(R"HTML(
)HTML"); Element* scroller_element = GetDocument().getElementById("scroller"); auto* scroller = scroller_element->GetLayoutObject(); EXPECT_EQ(kPaintsIntoOwnBacking, scroller->GetCompositingState()); auto* squashing = ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashing"))->Layer(); EXPECT_EQ(kPaintsIntoOwnBacking, squashing->GetCompositingState()); auto* squashed = ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer(); EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState()); scroller_element->setScrollTop(300); UpdateAllLifecyclePhasesForTest(); // 100px down from squashing's main graphics layer. EXPECT_EQ(IntPoint(0, 100), squashed->GraphicsLayerBacking()->GetOffsetFromTransformNode()); } TEST_F(CompositedLayerMappingTest, ContentsNotOpaqueWithForegroundLayer) { SetHtmlInnerHTML(R"HTML(
)HTML"); PaintLayer* target_layer = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); CompositedLayerMapping* mapping = target_layer->GetCompositedLayerMapping(); EXPECT_TRUE(mapping->ForegroundLayer()); EXPECT_FALSE(mapping->MainGraphicsLayer()->ContentsOpaque()); } TEST_F(CompositedLayerMappingTest, EmptyBoundsDoesntDrawContent) { SetHtmlInnerHTML(R"HTML(
)HTML"); PaintLayer* target_layer = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); CompositedLayerMapping* mapping = target_layer->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->MainGraphicsLayer()->DrawsContent()); } TEST_F(CompositedLayerMappingTest, TouchActionRectsWithoutContent) { SetBodyInnerHTML( "
"); auto* box = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target")); auto* mapping = box->Layer()->GetCompositedLayerMapping(); const auto* layer = mapping->MainGraphicsLayer()->CcLayer(); auto expected = gfx::Rect(0, 0, 100, 100); EXPECT_EQ(layer->touch_action_region().GetAllRegions().bounds(), expected); EXPECT_TRUE(mapping->MainGraphicsLayer()->PaintsHitTest()); // The only painted content for the main graphics layer is the touch-action // rect which is not sent to cc, so the cc::layer should not draw content. EXPECT_FALSE(layer->DrawsContent()); EXPECT_FALSE(mapping->MainGraphicsLayer()->DrawsContent()); } TEST_F(CompositedLayerMappingTest, ContentsOpaque) { SetHtmlInnerHTML(R"HTML(
)HTML"); PaintLayer* target_layer = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer(); CompositedLayerMapping* mapping = target_layer->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->ForegroundLayer()); EXPECT_TRUE(mapping->MainGraphicsLayer()->ContentsOpaque()); } TEST_F(CompositedLayerMappingTest, NullOverflowControlsHostLayer) { SetHtmlInnerHTML("
"); CompositedLayerMapping* mapping = ToLayoutBoxModelObject(GetLayoutObjectByElementId("target")) ->Layer() ->GetCompositedLayerMapping(); EXPECT_FALSE(mapping->DetachLayerForOverflowControls()); } TEST_F(CompositedLayerMappingTest, CompositedHiddenAnimatingLayer) { SetHtmlInnerHTML(R"HTML(
)HTML"); PaintLayer* animated = ToLayoutBoxModelObject(GetLayoutObjectByElementId("animated"))->Layer(); CompositedLayerMapping* mapping = animated->GetCompositedLayerMapping(); ASSERT_TRUE(mapping); EXPECT_TRUE(mapping->MainGraphicsLayer()->GetCompositingReasons() & CompositingReason::kActiveTransformAnimation); // We still composite the animated layer even if visibility: hidden. // TODO(crbug.com/937573): Is this necessary? GetDocument() .getElementById("animated") ->setAttribute(html_names::kStyleAttr, "visibility: hidden"); UpdateAllLifecyclePhasesForTest(); mapping = animated->GetCompositedLayerMapping(); ASSERT_TRUE(mapping); EXPECT_TRUE(mapping->MainGraphicsLayer()->GetCompositingReasons() & CompositingReason::kActiveTransformAnimation); } TEST_F(CompositedLayerMappingTest, RepaintScrollableAreaLayersInMainThreadScrolling) { SetHtmlInnerHTML(R"HTML(
)HTML"); PaintLayer* scroller = ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller"))->Layer(); PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); ASSERT_TRUE(scrollable_area); ASSERT_TRUE(scrollable_area->VerticalScrollbar()->IsOverlayScrollbar()); ASSERT_FALSE(scrollable_area->NeedsCompositedScrolling()); EXPECT_FALSE(scrollable_area->VerticalScrollbar()->VisualRect().IsEmpty()); GraphicsLayer* vertical_scrollbar_layer = scrollable_area->GraphicsLayerForVerticalScrollbar(); ASSERT_TRUE(vertical_scrollbar_layer); CompositedLayerMapping* mapping = scroller->GetCompositedLayerMapping(); ASSERT_TRUE(mapping); // Input events, animations and DOM changes, etc, can trigger cc::ProxyMain:: // BeginMainFrame, which may check if all graphics layers need repaint. // // We shouldn't repaint scrollable area layer which has no paint invalidation // in many uncorrelated BeginMainFrame scenes, such as moving mouse over the // non-scrollbar area, animating or DOM changes in another composited layer. GetDocument() .getElementById("uncorrelated") ->setAttribute(html_names::kStyleAttr, "width: 200px"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kTest); EXPECT_FALSE(mapping->NeedsRepaint(*vertical_scrollbar_layer)); GetDocument().getElementById("child")->setAttribute(html_names::kStyleAttr, "height: 50px"); GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kTest); EXPECT_TRUE(mapping->NeedsRepaint(*vertical_scrollbar_layer)); } TEST_F(CompositedLayerMappingTest, IsolationClippingContainer) { SetBodyInnerHTML(R"HTML(
a
b
)HTML"); Element* hideable = GetDocument().getElementById("hideable"); hideable->SetInlineStyleProperty(CSSPropertyID::kOverflow, "visible"); UpdateAllLifecyclePhasesForTest(); auto* isolation_a = GetDocument().getElementById("isolation_a"); auto* isolation_a_object = isolation_a->GetLayoutObject(); auto* squash_container_a = GetDocument().getElementById("squash_container_a"); PaintLayer* squash_container_a_layer = ToLayoutBoxModelObject(squash_container_a->GetLayoutObject())->Layer(); EXPECT_EQ(squash_container_a_layer->ClippingContainer(), isolation_a_object); } TEST_F(CompositedLayerMappingTest, FrameAttribution) { SetBodyInnerHTML(R"HTML(
)HTML"); UpdateAllLifecyclePhasesForTest(); // Ensure that we correctly attribute child layers in the main frame to their // containing document. Element* child = GetDocument().getElementById("child"); PaintLayer* child_paint_layer = ToLayoutBoxModelObject(child->GetLayoutObject())->Layer(); auto* child_layer = child_paint_layer->GraphicsLayerBacking()->CcLayer(); EXPECT_TRUE(child_layer->frame_element_id()); EXPECT_EQ(child_layer->frame_element_id(), CompositorElementIdFromUniqueObjectId( DOMNodeIds::IdForNode(&GetDocument()), CompositorElementIdNamespace::kDOMNodeId)); // Test that a layerized subframe's element ID is that of its containing // document. auto* subframe = To(GetDocument().getElementById("subframe")); EXPECT_TRUE(subframe); PaintLayer* subframe_paint_layer = ToLayoutBoxModelObject(subframe->GetLayoutObject())->Layer(); auto* subframe_layer = subframe_paint_layer->GraphicsLayerBacking()->CcLayer(); EXPECT_TRUE(subframe_layer->frame_element_id()); EXPECT_EQ(subframe_layer->frame_element_id(), CompositorElementIdFromUniqueObjectId( DOMNodeIds::IdForNode(subframe->contentDocument()), CompositorElementIdNamespace::kDOMNodeId)); } } // namespace blink