blob: 9f99dedc3f1732172bf16de5f320e20ae28887ca [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
#include "third_party/blink/renderer/core/editing/frame_caret.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/paint/object_paint_properties.h"
#include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
#include "third_party/blink/renderer/platform/graphics/graphics_context.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
namespace blink {
INSTANTIATE_PAINT_TEST_CASE_P(PaintControllerPaintTest);
using PaintControllerPaintTestForSPv2 = PaintControllerPaintTest;
INSTANTIATE_SPV2_TEST_CASE_P(PaintControllerPaintTestForSPv2);
TEST_P(PaintControllerPaintTest, FullDocumentPaintingWithCaret) {
SetBodyInnerHTML(
"<div id='div' contentEditable='true' style='outline:none'>XYZ</div>");
GetDocument().GetPage()->GetFocusController().SetActive(true);
GetDocument().GetPage()->GetFocusController().SetFocused(true);
Element& div = *ToElement(GetDocument().body()->firstChild());
InlineTextBox& text_inline_box =
*ToLayoutText(div.firstChild()->GetLayoutObject())->FirstTextBox();
EXPECT_DISPLAY_LIST(
RootPaintController().GetDisplayItemList(), 2,
TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType),
TestDisplayItem(text_inline_box, kForegroundType));
div.focus();
GetDocument().View()->UpdateAllLifecyclePhases();
EXPECT_DISPLAY_LIST(
RootPaintController().GetDisplayItemList(), 3,
TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType),
TestDisplayItem(text_inline_box, kForegroundType),
TestDisplayItem(CaretDisplayItemClientForTesting(),
DisplayItem::kCaret)); // New!
}
TEST_P(PaintControllerPaintTest, InlineRelayout) {
SetBodyInnerHTML(
"<div id='div' style='width:100px; height: 200px'>AAAAAAAAAA "
"BBBBBBBBBB</div>");
Element& div = *ToElement(GetDocument().body()->firstChild());
LayoutBlock& div_block =
*ToLayoutBlock(GetDocument().body()->firstChild()->GetLayoutObject());
LayoutText& text = *ToLayoutText(div_block.FirstChild());
InlineTextBox& first_text_box = *text.FirstTextBox();
EXPECT_DISPLAY_LIST(
RootPaintController().GetDisplayItemList(), 2,
TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType),
TestDisplayItem(first_text_box, kForegroundType));
div.setAttribute(HTMLNames::styleAttr, "width: 10px; height: 200px");
GetDocument().View()->UpdateAllLifecyclePhases();
LayoutText& new_text = *ToLayoutText(div_block.FirstChild());
InlineTextBox& new_first_text_box = *new_text.FirstTextBox();
InlineTextBox& second_text_box =
*new_text.FirstTextBox()->NextForSameLayoutObject();
EXPECT_DISPLAY_LIST(
RootPaintController().GetDisplayItemList(), 3,
TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType),
TestDisplayItem(new_first_text_box, kForegroundType),
TestDisplayItem(second_text_box, kForegroundType));
}
TEST_P(PaintControllerPaintTest, ChunkIdClientCacheFlag) {
SetBodyInnerHTML(R"HTML(
<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>
<div style='width: 100px; height: 100px; background-color:
blue'></div>
<div style='width: 100px; height: 100px; background-color:
blue'></div>
</div>
)HTML");
LayoutBlock& div = *ToLayoutBlock(GetLayoutObjectByElementId("div"));
LayoutObject& sub_div = *div.FirstChild();
LayoutObject& sub_div2 = *sub_div.NextSibling();
EXPECT_DISPLAY_LIST(
RootPaintController().GetDisplayItemList(), 3,
TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType),
TestDisplayItem(sub_div, kBackgroundType),
TestDisplayItem(sub_div2, kBackgroundType));
// Verify that the background does not scroll.
const PaintChunk& background_chunk = RootPaintController().PaintChunks()[0];
auto* transform = background_chunk.properties.Transform();
EXPECT_EQ(nullptr, transform->ScrollNode());
const EffectPaintPropertyNode* effect_node =
div.FirstFragment().PaintProperties()->Effect();
EXPECT_EQ(0.5f, effect_node->Opacity());
const PaintChunk& chunk = RootPaintController().PaintChunks()[1];
EXPECT_EQ(*div.Layer(), chunk.id.client);
EXPECT_EQ(effect_node, chunk.properties.Effect());
EXPECT_FALSE(div.Layer()->IsJustCreated());
// Client used by only paint chunks and non-cachaeable display items but not
// by any cacheable display items won't be marked as validly cached.
EXPECT_TRUE(ClientCacheIsValid(*div.Layer()));
EXPECT_FALSE(ClientCacheIsValid(div));
EXPECT_TRUE(ClientCacheIsValid(sub_div));
}
TEST_P(PaintControllerPaintTest, CompositingNoFold) {
SetBodyInnerHTML(R"HTML(
<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>
<div style='width: 100px; height: 100px; background-color:
blue'></div>
</div>
)HTML");
LayoutBlock& div = *ToLayoutBlock(GetLayoutObjectByElementId("div"));
LayoutObject& sub_div = *div.FirstChild();
EXPECT_DISPLAY_LIST(
RootPaintController().GetDisplayItemList(), 2,
TestDisplayItem(ViewBackgroundClient(), kDocumentBackgroundType),
TestDisplayItem(sub_div, kBackgroundType));
}
TEST_P(PaintControllerPaintTestForSPv2, FrameScrollingContents) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0; width: 10000px; height: 1000px }
div { position: absolute; width: 100px; height: 100px;
background: blue; }
</style>
<div id='div1' style='top: 0; left: 0'></div>
<div id='div2' style='top: 3000px; left: 3000px'></div>
<div id='div3' style='top: 6000px; left: 6000px'></div>
<div id='div4' style='top: 9000px; left: 9000px'></div>
)HTML");
auto& div1 = *GetLayoutObjectByElementId("div1");
// TODO(crbug.com/792577): Cull rect for frame scrolling contents is too
// small?
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 3,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(GetLayoutView(), kScrollHitTestType),
TestDisplayItem(div1, kBackgroundType));
GetDocument().View()->LayoutViewportScrollableArea()->SetScrollOffset(
ScrollOffset(5000, 5000), kProgrammaticScroll);
GetDocument().View()->UpdateAllLifecyclePhases();
// TODO(crbug.com/792577): Cull rect for frame scrolling contents is too
// small?
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 2,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(GetLayoutView(), kScrollHitTestType));
}
TEST_P(PaintControllerPaintTestForSPv2, BlockScrollingNonLayeredContents) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0 }
div { width: 100px; height: 100px; background: blue; }
container { display: block; width: 200px; height: 200px;
overflow: scroll }
</style>
<container id='container'>
<div id='div1'></div>
<div id='div2' style='margin-top: 2900px; margin-left: 3000px'></div>
<div id='div3' style='margin-top: 2900px; margin-left: 6000px'></div>
<div id='div4' style='margin-top: 2900px; margin-left: 9000px'></div>
</container>
)HTML");
auto& container = *ToLayoutBlock(GetLayoutObjectByElementId("container"));
auto& div1 = *GetLayoutObjectByElementId("div1");
auto& div2 = *GetLayoutObjectByElementId("div2");
auto& div3 = *GetLayoutObjectByElementId("div3");
auto& div4 = *GetLayoutObjectByElementId("div4");
// Initial cull rect: (0,0 4200x4200)
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 4,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(container, kScrollHitTestType),
TestDisplayItem(div1, kBackgroundType),
TestDisplayItem(div2, kBackgroundType));
container.GetScrollableArea()->SetScrollOffset(ScrollOffset(5000, 5000),
kProgrammaticScroll);
GetDocument().View()->UpdateAllLifecyclePhases();
// Cull rect after scroll: (1000,1000 8100x8100)
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 5,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(container, kScrollHitTestType),
TestDisplayItem(div2, kBackgroundType),
TestDisplayItem(div3, kBackgroundType),
TestDisplayItem(div4, kBackgroundType));
}
TEST_P(PaintControllerPaintTestForSPv2, ScrollHitTestOrder) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0 }
#container { width: 200px; height: 200px;
overflow: scroll; background: red; }
#child { width: 100px; height: 300px; background: green; }
#forceDocumentScroll { height: 1000px; }
</style>
<div id='container'>
<div id='child'></div>
</div>
<div id='forceDocumentScroll'/>
)HTML");
auto& container = *ToLayoutBlock(GetLayoutObjectByElementId("container"));
auto& child = *GetLayoutObjectByElementId("child");
// The container's items should all be after the document's scroll hit test
// to ensure the container is hit before the document. Similarly, the child's
// items should all be after the container's scroll hit test.
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 5,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(GetLayoutView(), kScrollHitTestType),
TestDisplayItem(container, kBackgroundType),
TestDisplayItem(container, kScrollHitTestType),
TestDisplayItem(child, kBackgroundType));
}
TEST_P(PaintControllerPaintTestForSPv2, NonStackingScrollHitTestOrder) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0 }
#container { width: 200px; height: 200px;
overflow: scroll; background: blue;
position: relative; z-index: auto; }
#child { width: 80px; height: 20px; background: white; }
#negZChild { width: 60px; height: 300px; background: purple;
position: absolute; z-index: -1; top: 0; }
#posZChild { width: 40px; height: 300px; background: yellow;
position: absolute; z-index: 1; top: 0; }
</style>
<div id='container'>
<div id='child'></div>
<div id='negZChild'></div>
<div id='posZChild'></div>
</div>
)HTML");
auto& container = *ToLayoutBlock(GetLayoutObjectByElementId("container"));
auto& child = *GetLayoutObjectByElementId("child");
auto& neg_z_child = *GetLayoutObjectByElementId("negZChild");
auto& pos_z_child = *GetLayoutObjectByElementId("posZChild");
// Container is not a stacking context because no z-index is auto.
// Negative z-index descendants are painted before the background and
// positive z-index descendants are painted after the background. Scroll hit
// testing should hit positive descendants, the container, and then negative
// descendants so the ScrollHitTest item should be immediately after the
// background.
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 6,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(neg_z_child, kBackgroundType),
TestDisplayItem(container, kBackgroundType),
TestDisplayItem(container, kScrollHitTestType),
TestDisplayItem(child, kBackgroundType),
TestDisplayItem(pos_z_child, kBackgroundType));
}
TEST_P(PaintControllerPaintTestForSPv2, StackingScrollHitTestOrder) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0 }
#container { width: 200px; height: 200px;
overflow: scroll; background: blue;
position: relative; z-index: 0; }
#child { width: 80px; height: 20px; background: white; }
#negZChild { width: 60px; height: 300px; background: purple;
position: absolute; z-index: -1; top: 0; }
#posZChild { width: 40px; height: 300px; background: yellow;
position: absolute; z-index: 1; top: 0; }
</style>
<div id='container'>
<div id='child'></div>
<div id='negZChild'></div>
<div id='posZChild'></div>
</div>
)HTML");
auto& container = *ToLayoutBlock(GetLayoutObjectByElementId("container"));
auto& child = *GetLayoutObjectByElementId("child");
auto& neg_z_child = *GetLayoutObjectByElementId("negZChild");
auto& pos_z_child = *GetLayoutObjectByElementId("posZChild");
// Container is a stacking context because z-index is non-auto.
// Both positive and negative z-index descendants are painted after the
// background. The scroll hit test should be after the background but before
// the z-index descendants to ensure hit test order is correct.
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 6,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(container, kBackgroundType),
TestDisplayItem(container, kScrollHitTestType),
TestDisplayItem(neg_z_child, kBackgroundType),
TestDisplayItem(child, kBackgroundType),
TestDisplayItem(pos_z_child, kBackgroundType));
}
TEST_P(PaintControllerPaintTestForSPv2,
NonStackingScrollHitTestOrderWithoutBackground) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none }
body { margin: 0 }
#container { width: 200px; height: 200px;
overflow: scroll; background: transparent;
position: relative; z-index: auto; }
#child { width: 80px; height: 20px; background: white; }
#negZChild { width: 60px; height: 300px; background: purple;
position: absolute; z-index: -1; top: 0; }
#posZChild { width: 40px; height: 300px; background: yellow;
position: absolute; z-index: 1; top: 0; }
</style>
<div id='container'>
<div id='child'></div>
<div id='negZChild'></div>
<div id='posZChild'></div>
</div>
)HTML");
auto& container = *ToLayoutBlock(GetLayoutObjectByElementId("container"));
auto& child = *GetLayoutObjectByElementId("child");
auto& neg_z_child = *GetLayoutObjectByElementId("negZChild");
auto& pos_z_child = *GetLayoutObjectByElementId("posZChild");
// Even though container does not paint a background, the scroll hit test item
// should still be between the negative z-index child and the regular child.
EXPECT_DISPLAY_LIST(RootPaintController().GetDisplayItemList(), 5,
TestDisplayItem(GetLayoutView(), kDocumentBackgroundType),
TestDisplayItem(neg_z_child, kBackgroundType),
TestDisplayItem(container, kScrollHitTestType),
TestDisplayItem(child, kBackgroundType),
TestDisplayItem(pos_z_child, kBackgroundType));
}
} // namespace blink