blob: e0152cd3379226b0dab5c26bac6da5738cc30df8 [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/ng/ng_paint_fragment.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"
using testing::ElementsAre;
namespace blink {
INSTANTIATE_PAINT_TEST_CASE_P(PaintControllerPaintTest);
using PaintControllerPaintTestForCAP = PaintControllerPaintTest;
INSTANTIATE_CAP_TEST_CASE_P(PaintControllerPaintTestForCAP);
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_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&text_inline_box, kForegroundType)));
div.focus();
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&text_inline_box, kForegroundType),
// New!
IsSameId(&CaretDisplayItemClientForTesting(), DisplayItem::kCaret)));
}
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());
DisplayItemClient& first_text_box =
text.FirstInlineFragment()
? (DisplayItemClient&)*text.FirstInlineFragment()
: (DisplayItemClient&)*text.FirstTextBox();
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&first_text_box, kForegroundType)));
div.setAttribute(html_names::kStyleAttr, "width: 10px; height: 200px");
UpdateAllLifecyclePhasesForTest();
LayoutText& new_text = *ToLayoutText(div_block.FirstChild());
DisplayItemClient& new_first_text_box =
new_text.FirstInlineFragment()
? (DisplayItemClient&)*new_text.FirstInlineFragment()
: (DisplayItemClient&)*text.FirstTextBox();
DisplayItemClient& second_text_box =
new_text.FirstInlineFragment()
? (DisplayItemClient&)*NGPaintFragment::
TraverseNextForSameLayoutObject::Next(
new_text.FirstInlineFragment())
: (DisplayItemClient&)*new_text.FirstTextBox()
->NextForSameLayoutObject();
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&new_first_text_box, kForegroundType),
IsSameId(&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_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&sub_div, kBackgroundType),
IsSameId(&sub_div2, kBackgroundType)));
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_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&sub_div, kBackgroundType)));
}
TEST_P(PaintControllerPaintTestForCAP, 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");
const auto& div1 = *GetLayoutObjectByElementId("div1");
const auto& div2 = *GetLayoutObjectByElementId("div2");
const auto& div3 = *GetLayoutObjectByElementId("div3");
const auto& div4 = *GetLayoutObjectByElementId("div4");
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&GetLayoutView(), kScrollHitTestType),
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&div1, kBackgroundType), IsSameId(&div2, kBackgroundType)));
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(5000, 5000), kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&GetLayoutView(), kScrollHitTestType),
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&div2, kBackgroundType), IsSameId(&div3, kBackgroundType),
IsSameId(&div4, kBackgroundType)));
}
TEST_P(PaintControllerPaintTestForCAP, 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_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&div1, kBackgroundType), IsSameId(&div2, kBackgroundType)));
container.GetScrollableArea()->SetScrollOffset(ScrollOffset(5000, 5000),
kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
// Cull rect after scroll: (1000,1000 8100x8100)
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&div2, kBackgroundType), IsSameId(&div3, kBackgroundType),
IsSameId(&div4, kBackgroundType)));
}
TEST_P(PaintControllerPaintTestForCAP, 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_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&GetLayoutView(), kScrollHitTestType),
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&container, kBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&container.GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient(),
kBackgroundType),
IsSameId(&child, kBackgroundType)));
}
TEST_P(PaintControllerPaintTestForCAP, 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_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&neg_z_child, kBackgroundType),
IsSameId(&container, kBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&container.GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient(),
kBackgroundType),
IsSameId(&child, kBackgroundType),
IsSameId(&pos_z_child, kBackgroundType)));
}
TEST_P(PaintControllerPaintTestForCAP, 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_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&container, kBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&container.GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient(),
kBackgroundType),
IsSameId(&neg_z_child, kBackgroundType),
IsSameId(&child, kBackgroundType),
IsSameId(&pos_z_child, kBackgroundType)));
}
TEST_P(PaintControllerPaintTestForCAP,
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_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&neg_z_child, kBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&child, kBackgroundType),
IsSameId(&pos_z_child, kBackgroundType)));
}
} // namespace blink