blob: bcdfdf273321b6af3474b2d921811e943469ee1e [file] [log] [blame]
// Copyright 2017 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/block_painter.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h"
#include "third_party/blink/renderer/core/paint/paint_controller_paint_test.h"
#include "third_party/blink/renderer/platform/graphics/paint/drawing_display_item.h"
#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk.h"
#include "third_party/blink/renderer/platform/graphics/paint/scroll_hit_test_display_item.h"
using testing::ElementsAre;
namespace blink {
using BlockPainterTest = PaintControllerPaintTest;
INSTANTIATE_PAINT_TEST_SUITE_P(BlockPainterTest);
TEST_P(BlockPainterTest, ScrollHitTestProperties) {
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0 }
#container { width: 200px; height: 200px;
overflow: scroll; background: blue; }
#child { width: 100px; height: 300px; background: green; }
</style>
<div id='container'>
<div id='child'></div>
</div>
)HTML");
auto& container = ToLayoutBlock(*GetLayoutObjectByElementId("container"));
auto& child = *GetLayoutObjectByElementId("child");
// The scroll hit test should be after the container background but before the
// scrolled contents.
EXPECT_EQ(
kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents,
container.GetBackgroundPaintLocation());
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(
IsSameId(&ViewScrollingBackgroundClient(), kDocumentBackgroundType),
IsSameId(&container, kBackgroundType),
IsSameId(&container, kScrollHitTestType),
IsSameId(&container.GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient(),
kBackgroundType),
IsSameId(&child, kBackgroundType)));
const auto& paint_chunks = RootPaintController().PaintChunks();
EXPECT_THAT(
paint_chunks,
ElementsAre(
IsPaintChunk(0, 1,
PaintChunk::Id(ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
GetLayoutView().FirstFragment().ContentsProperties()),
IsPaintChunk(1, 2,
PaintChunk::Id(*container.Layer(),
kNonScrollingBackgroundChunkType),
container.FirstFragment().LocalBorderBoxProperties()),
IsPaintChunk(2, 3, PaintChunk::Id(container, kScrollHitTestType),
container.FirstFragment().LocalBorderBoxProperties()),
IsPaintChunk(3, 5,
PaintChunk::Id(container, kScrollingBackgroundChunkType),
container.FirstFragment().ContentsProperties())));
// The document should not scroll so there should be no scroll offset
// transform.
const auto& root_transform = paint_chunks[0].properties.Transform();
EXPECT_EQ(nullptr, root_transform.ScrollNode());
// The container's background chunk should not scroll and therefore should use
// the root transform. Its local transform is actually a paint offset
// transform.
const auto& container_transform = paint_chunks[1].properties.Transform();
EXPECT_EQ(&root_transform, container_transform.Parent());
EXPECT_EQ(nullptr, container_transform.ScrollNode());
// The scroll hit test should not be scrolled and should not be clipped.
// Its local transform is actually a paint offset transform.
const auto& scroll_hit_test_chunk = paint_chunks[2];
const auto& scroll_hit_test_transform =
scroll_hit_test_chunk.properties.Transform();
EXPECT_EQ(nullptr, scroll_hit_test_transform.ScrollNode());
EXPECT_EQ(&root_transform, scroll_hit_test_transform.Parent());
const auto& scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip();
EXPECT_EQ(FloatRect(0, 0, 800, 600), scroll_hit_test_clip.ClipRect().Rect());
// The scrolled contents should be scrolled and clipped.
const auto& contents_chunk = RootPaintController().PaintChunks()[3];
const auto& contents_transform = contents_chunk.properties.Transform();
const auto* contents_scroll = contents_transform.ScrollNode();
EXPECT_EQ(IntSize(200, 300), contents_scroll->ContentsSize());
EXPECT_EQ(IntRect(0, 0, 200, 200), contents_scroll->ContainerRect());
const auto& contents_clip = contents_chunk.properties.Clip();
EXPECT_EQ(FloatRect(0, 0, 200, 200), contents_clip.ClipRect().Rect());
// The scroll hit test display item maintains a reference to a scroll offset
// translation node and the contents should be scrolled by this node.
const auto& scroll_hit_test_display_item =
static_cast<const ScrollHitTestDisplayItem&>(
RootPaintController()
.GetDisplayItemList()[scroll_hit_test_chunk.begin_index]);
EXPECT_EQ(&contents_transform,
&scroll_hit_test_display_item.scroll_offset_node());
}
TEST_P(BlockPainterTest, FrameScrollHitTestProperties) {
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
return;
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; }
#child { width: 100px; height: 2000px; background: green; }
</style>
<div id='child'></div>
)HTML");
auto& html =
ToLayoutBlock(*GetDocument().documentElement()->GetLayoutObject());
auto& child = *GetLayoutObjectByElementId("child");
// The scroll hit test should be after the document background but before the
// scrolled contents.
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&GetLayoutView(), kScrollHitTestType),
IsSameId(&ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
IsSameId(&child, kBackgroundType)));
const auto& paint_chunks = RootPaintController().PaintChunks();
EXPECT_THAT(
paint_chunks,
ElementsAre(
IsPaintChunk(
0, 1,
PaintChunk::Id(*GetLayoutView().Layer(),
DisplayItem::kLayerChunkBackground),
GetLayoutView().FirstFragment().LocalBorderBoxProperties()),
IsPaintChunk(1, 2,
PaintChunk::Id(ViewScrollingBackgroundClient(),
kDocumentBackgroundType),
GetLayoutView().FirstFragment().ContentsProperties()),
IsPaintChunk(2, 3,
PaintChunk::Id(*html.Layer(),
kNonScrollingContentsBackgroundChunkType),
html.FirstFragment().ContentsProperties())));
// The scroll hit test should not be scrolled and should not be clipped.
const auto& scroll_hit_test_chunk = RootPaintController().PaintChunks()[0];
const auto& scroll_hit_test_transform =
scroll_hit_test_chunk.properties.Transform();
EXPECT_EQ(nullptr, scroll_hit_test_transform.ScrollNode());
const auto& scroll_hit_test_clip = scroll_hit_test_chunk.properties.Clip();
EXPECT_EQ(FloatRect(LayoutRect::InfiniteIntRect()),
scroll_hit_test_clip.ClipRect().Rect());
// The scrolled contents should be scrolled and clipped.
const auto& contents_chunk = RootPaintController().PaintChunks()[2];
const auto& contents_transform = contents_chunk.properties.Transform();
const auto* contents_scroll = contents_transform.ScrollNode();
EXPECT_EQ(IntSize(800, 2000), contents_scroll->ContentsSize());
EXPECT_EQ(IntRect(0, 0, 800, 600), contents_scroll->ContainerRect());
const auto& contents_clip = contents_chunk.properties.Clip();
EXPECT_EQ(FloatRect(0, 0, 800, 600), contents_clip.ClipRect().Rect());
// The scroll hit test display item maintains a reference to a scroll offset
// translation node and the contents should be scrolled by this node.
const auto& scroll_hit_test_display_item =
static_cast<const ScrollHitTestDisplayItem&>(
RootPaintController()
.GetDisplayItemList()[scroll_hit_test_chunk.begin_index]);
EXPECT_EQ(&contents_transform,
&scroll_hit_test_display_item.scroll_offset_node());
}
TEST_P(BlockPainterTest, OverflowRectForCullRectTesting) {
SetBodyInnerHTML(R"HTML(
<div id='scroller' style='width: 50px; height: 50px; overflow: scroll'>
<div style='width: 50px; height: 5000px'></div>
</div>
)HTML");
auto* scroller = ToLayoutBlock(GetLayoutObjectByElementId("scroller"));
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
EXPECT_EQ(LayoutRect(0, 0, 50, 5000),
BlockPainter(*scroller).OverflowRectForCullRectTesting(false));
} else {
EXPECT_EQ(LayoutRect(0, 0, 50, 50),
BlockPainter(*scroller).OverflowRectForCullRectTesting(false));
}
}
TEST_P(BlockPainterTest, OverflowRectCompositedScrollingForCullRectTesting) {
SetBodyInnerHTML(R"HTML(
<div id='scroller' style='width: 50px; height: 50px; overflow: scroll; will-change: transform'>
<div style='width: 50px; height: 5000px'></div>
</div>
)HTML");
auto* scroller = ToLayoutBlock(GetLayoutObjectByElementId("scroller"));
EXPECT_EQ(LayoutRect(0, 0, 50, 5000),
BlockPainter(*scroller).OverflowRectForCullRectTesting(false));
}
class BlockPainterTestWithPaintTouchAction
: public PaintControllerPaintTestBase,
private ScopedPaintTouchActionRectsForTest {
public:
BlockPainterTestWithPaintTouchAction()
: PaintControllerPaintTestBase(),
ScopedPaintTouchActionRectsForTest(true) {}
};
TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectsWithoutPaint) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; }
#parent { width: 100px; height: 100px; }
.touchActionNone { touch-action: none; }
#childVisible { width: 200px; height: 25px; }
#childHidden { width: 200px; height: 30px; visibility: hidden; }
#childDisplayNone { width: 200px; height: 30px; display: none; }
</style>
<div id='parent'>
<div id='childVisible'></div>
<div id='childHidden'></div>
</div>
)HTML");
// Initially there should be no hit test display items because there is no
// touch action.
const auto& scrolling_client = ViewScrollingBackgroundClient();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType)));
// Add a touch action to parent and ensure that hit test display items are
// created for both the parent and the visible child.
auto* parent_element = GetElementById("parent");
parent_element->setAttribute(html_names::kClassAttr, "touchActionNone");
UpdateAllLifecyclePhasesForTest();
auto* parent = GetLayoutObjectByElementId("parent");
auto* child_visible = GetLayoutObjectByElementId("childVisible");
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(parent, DisplayItem::kHitTest),
IsSameId(child_visible, DisplayItem::kHitTest)));
// Remove the touch action from parent and ensure no hit test display items
// are left.
parent_element->removeAttribute(html_names::kClassAttr);
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType)));
}
TEST_F(BlockPainterTestWithPaintTouchAction,
TouchActionRectSubsequenceCaching) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#touchaction {
width: 100px;
height: 100px;
touch-action: none;
}
#sibling {
width: 100px;
height: 100px;
background: blue;
}
</style>
<div id='touchaction'></div>
)HTML");
const auto& scrolling_client = ViewScrollingBackgroundClient();
const auto* touchaction = GetLayoutObjectByElementId("touchaction");
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(touchaction, DisplayItem::kHitTest)));
const auto& hit_test_client = *touchaction->EnclosingLayer();
EXPECT_SUBSEQUENCE(hit_test_client, 1, 2);
PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType);
auto root_chunk_properties =
GetLayoutView().FirstFragment().ContentsProperties();
PaintChunk::Id hit_test_chunk_id(hit_test_client,
kNonScrollingBackgroundChunkType);
auto hit_test_chunk_properties = touchaction->EnclosingLayer()
->GetLayoutObject()
.FirstFragment()
.ContentsProperties();
HitTestData hit_test_data;
hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 100));
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties),
IsPaintChunk(1, 2, hit_test_chunk_id,
hit_test_chunk_properties, hit_test_data)));
// Trigger a repaint with the whole HTML subsequence cached.
GetLayoutView().Layer()->SetNeedsRepaint();
EXPECT_TRUE(PaintWithoutCommit());
EXPECT_EQ(2, NumCachedNewItems());
CommitAndFinishCycle();
EXPECT_SUBSEQUENCE(hit_test_client, 1, 2);
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties),
IsPaintChunk(1, 2, hit_test_chunk_id,
hit_test_chunk_properties, hit_test_data)));
}
TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectPaintCaching) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#touchaction {
width: 100px;
height: 100px;
touch-action: none;
}
#sibling {
width: 100px;
height: 100px;
background: blue;
}
</style>
<div id='touchaction'></div>
<div id='sibling'></div>
)HTML");
const auto& scrolling_client = ViewScrollingBackgroundClient();
const auto* touchaction = GetLayoutObjectByElementId("touchaction");
auto* sibling_element = GetElementById("sibling");
const auto* sibling = sibling_element->GetLayoutObject();
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(touchaction, DisplayItem::kHitTest),
IsSameId(sibling, kBackgroundType)));
PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType);
auto root_chunk_properties =
GetLayoutView().FirstFragment().ContentsProperties();
PaintChunk::Id hit_test_chunk_id(*touchaction->EnclosingLayer(),
kNonScrollingBackgroundChunkType);
auto hit_test_chunk_properties = touchaction->EnclosingLayer()
->GetLayoutObject()
.FirstFragment()
.ContentsProperties();
HitTestData hit_test_data;
hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 100));
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties),
IsPaintChunk(1, 3, hit_test_chunk_id,
hit_test_chunk_properties, hit_test_data)));
sibling_element->setAttribute(html_names::kStyleAttr, "background: green;");
GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint();
EXPECT_TRUE(PaintWithoutCommit());
// Only the background display item of the sibling should be invalidated.
EXPECT_EQ(2, NumCachedNewItems());
CommitAndFinishCycle();
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties),
IsPaintChunk(1, 3, hit_test_chunk_id,
hit_test_chunk_properties, hit_test_data)));
}
TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectScrollingContents) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; }
#scroller {
width: 100px;
height: 100px;
overflow: scroll;
touch-action: none;
will-change: transform;
background-color: blue;
}
#child {
width: 10px;
height: 400px;
}
</style>
<div id='scroller'>
<div id='child'></div>
</div>
)HTML");
const auto& root_client = GetLayoutView()
.GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient();
auto* scroller_element = GetElementById("scroller");
auto* scroller = ToLayoutBoxModelObject(scroller_element->GetLayoutObject());
const auto& scroller_client =
scroller->GetScrollableArea()->GetScrollingBackgroundDisplayItemClient();
auto* child_element = GetElementById("child");
auto* child = child_element->GetLayoutObject();
auto& non_scroller_paint_controller = RootPaintController();
auto& scroller_paint_controller = scroller->GetScrollableArea()
->Layer()
->GraphicsLayerBacking()
->GetPaintController();
EXPECT_THAT(scroller_paint_controller.GetDisplayItemList(),
ElementsAre(IsSameId(&scroller_client, kBackgroundType),
IsSameId(&scroller_client, DisplayItem::kHitTest),
IsSameId(child, DisplayItem::kHitTest)));
HitTestData hit_test_data;
hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 400));
hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 10, 400));
EXPECT_THAT(
scroller_paint_controller.PaintChunks(),
ElementsAre(IsPaintChunk(
0, 3, PaintChunk::Id(*scroller, kScrollingBackgroundChunkType),
scroller->FirstFragment().ContentsProperties(), hit_test_data)));
EXPECT_THAT(non_scroller_paint_controller.GetDisplayItemList(),
ElementsAre(IsSameId(&root_client, kDocumentBackgroundType)));
EXPECT_THAT(non_scroller_paint_controller.PaintChunks(),
ElementsAre(IsPaintChunk(
0, 1, PaintChunk::Id(root_client, kDocumentBackgroundType),
GetLayoutView().FirstFragment().ContentsProperties())));
}
TEST_F(BlockPainterTestWithPaintTouchAction, TouchActionRectPaintChunkChanges) {
SetBodyInnerHTML(R"HTML(
<style>
body { margin: 0; }
#touchaction {
width: 100px;
height: 100px;
}
</style>
<div id='touchaction'></div>
)HTML");
const auto& scrolling_client = ViewScrollingBackgroundClient();
auto* touchaction_element = GetElementById("touchaction");
auto* touchaction = touchaction_element->GetLayoutObject();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType)));
PaintChunk::Id root_chunk_id(scrolling_client, kDocumentBackgroundType);
auto root_chunk_properties =
GetLayoutView().FirstFragment().ContentsProperties();
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties)));
touchaction_element->setAttribute(html_names::kStyleAttr,
"touch-action: none;");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(touchaction, DisplayItem::kHitTest)));
PaintChunk::Id hit_test_chunk_id(*touchaction->EnclosingLayer(),
kNonScrollingBackgroundChunkType);
auto hit_test_chunk_properties = touchaction->EnclosingLayer()
->GetLayoutObject()
.FirstFragment()
.ContentsProperties();
HitTestData hit_test_data;
hit_test_data.touch_action_rects.emplace_back(LayoutRect(0, 0, 100, 100));
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties),
IsPaintChunk(1, 2, hit_test_chunk_id,
hit_test_chunk_properties, hit_test_data)));
touchaction_element->removeAttribute(html_names::kStyleAttr);
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType)));
EXPECT_THAT(
RootPaintController().PaintChunks(),
ElementsAre(IsPaintChunk(0, 1, root_chunk_id, root_chunk_properties)));
}
namespace {
class BlockPainterMockEventListener final : public NativeEventListener {
public:
void Invoke(ExecutionContext*, Event*) final {}
};
} // namespace
TEST_F(BlockPainterTestWithPaintTouchAction, TouchHandlerRectsWithoutPaint) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; }
#parent { width: 100px; height: 100px; }
#child { width: 200px; height: 50px; }
</style>
<div id='parent'>
<div id='child'></div>
</div>
)HTML");
// Initially there should be no hit test display items because there are no
// event handlers.
const auto& scrolling_client = ViewScrollingBackgroundClient();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType)));
// Add an event listener to parent and ensure that hit test display items are
// created for both the parent and child.
BlockPainterMockEventListener* callback =
MakeGarbageCollected<BlockPainterMockEventListener>();
auto* parent_element = GetElementById("parent");
parent_element->addEventListener(event_type_names::kTouchstart, callback);
UpdateAllLifecyclePhasesForTest();
auto* parent = GetLayoutObjectByElementId("parent");
auto* child = GetLayoutObjectByElementId("child");
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(parent, DisplayItem::kHitTest),
IsSameId(child, DisplayItem::kHitTest)));
// Remove the event handler from parent and ensure no hit test display items
// are left.
parent_element->RemoveAllEventListeners();
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(
RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType)));
}
TEST_F(BlockPainterTestWithPaintTouchAction,
TouchActionRectsAcrossPaintChanges) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; }
#parent { width: 100px; height: 100px; touch-action: none; }
#child { width: 200px; height: 50px; }
</style>
<div id='parent'>
<div id='child'></div>
</div>
)HTML");
const auto& scrolling_client = ViewScrollingBackgroundClient();
auto* parent = GetLayoutObjectByElementId("parent");
auto* child = GetLayoutObjectByElementId("child");
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(parent, DisplayItem::kHitTest),
IsSameId(child, DisplayItem::kHitTest)));
auto* child_element = GetElementById("parent");
child_element->setAttribute("style", "background: blue;");
UpdateAllLifecyclePhasesForTest();
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(parent, kBackgroundType),
IsSameId(parent, DisplayItem::kHitTest),
IsSameId(child, DisplayItem::kHitTest)));
}
TEST_F(BlockPainterTestWithPaintTouchAction, ScrolledHitTestChunkProperties) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
body { margin: 0; }
#scroller {
width: 100px;
height: 100px;
overflow: scroll;
touch-action: none;
}
#child {
width: 200px;
height: 50px;
touch-action: none;
}
</style>
<div id='scroller'>
<div id='child'></div>
</div>
)HTML");
const auto& scrolling_client = ViewScrollingBackgroundClient();
const auto* scroller = ToLayoutBlock(GetLayoutObjectByElementId("scroller"));
const auto* child = GetLayoutObjectByElementId("child");
EXPECT_THAT(RootPaintController().GetDisplayItemList(),
ElementsAre(IsSameId(&scrolling_client, kDocumentBackgroundType),
IsSameId(scroller, DisplayItem::kHitTest),
IsSameId(child, DisplayItem::kHitTest)));
HitTestData scroller_hit_test_data;
scroller_hit_test_data.touch_action_rects.emplace_back(
LayoutRect(0, 0, 100, 100));
HitTestData scrolled_hit_test_data;
scrolled_hit_test_data.touch_action_rects.emplace_back(
LayoutRect(0, 0, 200, 50));
const auto& paint_chunks = RootPaintController().PaintChunks();
EXPECT_THAT(
paint_chunks,
ElementsAre(
IsPaintChunk(
0, 1, PaintChunk::Id(scrolling_client, kDocumentBackgroundType),
GetLayoutView().FirstFragment().ContentsProperties()),
IsPaintChunk(1, 2,
PaintChunk::Id(*scroller->Layer(),
kNonScrollingBackgroundChunkType),
scroller->FirstFragment().LocalBorderBoxProperties(),
scroller_hit_test_data),
IsPaintChunk(
2, 3,
PaintChunk::Id(*scroller, kScrollingContentsBackgroundChunkType),
scroller->FirstFragment().ContentsProperties(),
scrolled_hit_test_data)));
const auto& scroller_paint_chunk = paint_chunks[1];
EXPECT_EQ(FloatRect(0, 0, 100, 100), scroller_paint_chunk.bounds);
// The hit test rect for the scroller itself should not be scrolled.
EXPECT_FALSE(scroller_paint_chunk.properties.Transform().ScrollNode());
const auto& scrolled_paint_chunk = paint_chunks[2];
EXPECT_EQ(FloatRect(0, 0, 200, 50), scrolled_paint_chunk.bounds);
// The hit test rect for the scrolled contents should be scrolled.
EXPECT_TRUE(scrolled_paint_chunk.properties.Transform().ScrollNode());
}
} // namespace blink