blob: eca554b6a54c45f57ece40a36250a562e866e280 [file] [log] [blame]
// Copyright 2016 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_layer_scrollable_area.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_box_model_object.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/scroll/scroll_types.h"
#include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
#include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
#include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
using testing::_;
namespace blink {
namespace {
class ScrollableAreaMockChromeClient : public RenderingTestChromeClient {
public:
MOCK_METHOD3(MockSetToolTip, void(LocalFrame*, const String&, TextDirection));
void SetToolTip(LocalFrame& frame,
const String& tooltip_text,
TextDirection dir) override {
MockSetToolTip(&frame, tooltip_text, dir);
}
};
} // namespace
class PaintLayerScrollableAreaTestBase : public RenderingTest {
public:
PaintLayerScrollableAreaTestBase()
: RenderingTest(MakeGarbageCollected<EmptyLocalFrameClient>()),
chrome_client_(MakeGarbageCollected<ScrollableAreaMockChromeClient>()) {
}
~PaintLayerScrollableAreaTestBase() override {
testing::Mock::VerifyAndClearExpectations(&GetChromeClient());
}
ScrollableAreaMockChromeClient& GetChromeClient() const override {
return *chrome_client_;
}
BackgroundPaintLocation GetBackgroundPaintLocation(const char* element_id) {
return ToLayoutBoxModelObject(GetLayoutObjectByElementId(element_id))
->GetBackgroundPaintLocation();
}
private:
void SetUp() override {
EnableCompositing();
RenderingTest::SetUp();
}
Persistent<ScrollableAreaMockChromeClient> chrome_client_;
};
class PaintLayerScrollableAreaTest : public PaintLayerScrollableAreaTestBase,
public PaintTestConfigurations {};
INSTANTIATE_PAINT_TEST_SUITE_P(PaintLayerScrollableAreaTest);
using PaintLayerScrollableAreaTestSPv1 = PaintLayerScrollableAreaTestBase;
TEST_P(PaintLayerScrollableAreaTest,
CanPaintBackgroundOntoScrollingContentsLayer) {
GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
true);
SetBodyInnerHTML(R"HTML(
<style>
.scroller {
overflow: scroll;
will-change: transform;
width: 300px;
height: 300px;
}
.spacer { height: 1000px; }
#scroller13::-webkit-scrollbar {
width: 13px;
height: 13px;
}
</style>
<div id='scroller1' class='scroller' style='background: white local;'>
<div id='negative-composited-child' style='background-color: red;
width: 1px; height: 1px; position: absolute;
backface-visibility: hidden; z-index: -1'></div>
<div class='spacer'></div>
</div>
<div id='scroller2' class='scroller' style='background: white content-box;
padding: 10px;'>
<div class='spacer'></div>
</div>
<div id='scroller3' class='scroller'
style='background: white local content-box; padding: 10px;'>
<div class='spacer'></div>
</div>
<div id='scroller4' class='scroller'
style='background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUg),
white local;'>
<div class='spacer'></div>
</div>
<div id='scroller5' class='scroller'
style='background:
url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUg) local, white local;'>
<div class='spacer'></div>
</div>
<div id='scroller6' class='scroller'
style='background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUg)
local, white padding-box; padding: 10px;'>
<div class='spacer'></div>
</div>
<div id='scroller7' class='scroller'
style='background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUg)
local, white content-box; padding: 10px;'>
<div class='spacer'></div>
</div>
<div id='scroller8' class='scroller' style='background: white border-box;'>
<div class='spacer'></div>
</div>
<div id='scroller9' class='scroller' style='background: white border-box;
border: 10px solid black;'>
<div class='spacer'></div>
</div>
<div id='scroller10' class='scroller' style='background: white border-box;
border: 10px solid rgba(0, 0, 0, 0.5);'>
<div class='spacer'></div>
</div>
<div id='scroller11' class='scroller'
style='background: white content-box;'>
<div class='spacer'></div>
</div>
<div id='scroller12' class='scroller' style='background: white content-box;
padding: 10px;'>
<div class='spacer'></div>
</div>
<div id='scroller13' class='scroller' style='background: white border-box;'>
<div class='spacer'></div>
</div>
<div id='scroller14' class='scroller' style='background: white;
border: 1px solid black; outline: 1px solid blue;
outline-offset: -1px;'>
<div class='spacer'></div>
</div>
<div id='scroller15' class='scroller' style='background: white;
border: 1px solid black; outline: 1px solid blue;
outline-offset: -2px;'>
<div class='spacer'></div>
</div>
<div id='scroller16' class='scroller' style='position: absolute;
background: white; clip: rect(0px,10px,10px,0px);'>
<div class='spacer'></div>
</div>
<div id='scroller17' class='scroller'
style='background: rgba(255, 255, 255, 0.5) border-box;
border: 5px solid rgba(0, 0, 0, 0.5);'>
<div class='spacer'></div>
</div>
<div id='scroller18' class='scroller'
style='background: white;
border: 5px dashed black;'>
<div class='spacer'></div>
</div>
)HTML");
// #scroller1 can paint background into scrolling contents layer even with a
// negative z-index child.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller1"));
// #scroller2 cannot paint background into scrolling contents layer because it
// has a content-box clip without local attachment.
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetBackgroundPaintLocation("scroller2"));
// #scroller3 can paint background into scrolling contents layer.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller3"));
// #scroller4 cannot paint background into scrolling contents layer because
// the background image is not locally attached.
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetBackgroundPaintLocation("scroller4"));
// #scroller5 can paint background into scrolling contents layer because both
// the image and color are locally attached.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller5"));
// #scroller6 can paint background into scrolling contents layer because the
// image is locally attached and even though the color is not, it is filled to
// the padding box so it will be drawn the same as a locally attached
// background.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller6"));
// #scroller7 cannot paint background into scrolling contents layer because
// the color is filled to the content box and we have padding so it is not
// equivalent to a locally attached background.
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetBackgroundPaintLocation("scroller7"));
// #scroller8 can paint background into scrolling contents layer because its
// border-box is equivalent to its padding box since it has no border.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller8"));
// #scroller9 can paint background into scrolling contents layer because its
// border is opaque so it completely covers the background outside of the
// padding-box.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller9"));
// #scroller10 paints the background into both layers because its border is
// partially transparent so the background must be drawn to the
// border-box edges.
EXPECT_EQ(
kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller10"));
// #scroller11 can paint background into scrolling contents layer because its
// content-box is equivalent to its padding box since it has no padding.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller11"));
// #scroller12 cannot paint background into scrolling contents layer because
// it has padding so its content-box is not equivalent to its padding-box.
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetBackgroundPaintLocation("scroller12"));
// #scroller13 paints the background into both layers because it has a custom
// scrollbar which the background may need to draw under.
EXPECT_EQ(
kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller13"));
// #scroller14 can paint background into scrolling contents layer because the
// outline is drawn outside the padding box.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller14"));
// #scroller15 can paint background into scrolling contents layer because
// the outline is drawn into the decoration layer which will not be covered
// up.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller15"));
// #scroller16 cannot paint background into scrolling contents layer because
// the scroller has a clip which would not be respected by the scrolling
// contents layer.
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetBackgroundPaintLocation("scroller16"));
// #scroller17 can only be painted once as it is translucent, and it must
// be painted in the graphics layer to be under the translucent border.
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetBackgroundPaintLocation("scroller17"));
// #scroller18 can be painted in both layers because the background is a
// solid color, it must be because the dashed border reveals the background
// underneath it.
EXPECT_EQ(
kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents,
GetBackgroundPaintLocation("scroller18"));
}
TEST_F(PaintLayerScrollableAreaTestSPv1, OpaqueContainedLayersPromoted) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px;
contain: paint; background: white local content-box;
border: 10px solid rgba(0, 255, 0, 0.5); }
#scrolled { height: 300px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
ASSERT_TRUE(paint_layer->GraphicsLayerBacking());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque());
}
// Tests that we don't promote scrolling content which would not be contained.
// Promoting the scroller would also require promoting the positioned div
// which would lose subpixel anti-aliasing due to its transparent background.
TEST_F(PaintLayerScrollableAreaTestSPv1, NonContainedLayersNotPromoted) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px;
background: white local content-box;
border: 10px solid rgba(0, 255, 0, 0.5); }
#scrolled { height: 300px; }
#positioned { position: relative; }
</style>
<div id="scroller">
<div id="positioned">Not contained by scroller.</div>
<div id="scrolled"></div>
</div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
}
TEST_F(PaintLayerScrollableAreaTestSPv1, TransparentLayersNotPromoted) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px; background:
rgba(0, 255, 0, 0.5) local content-box; border: 10px solid rgba(0, 255,
0, 0.5); contain: paint; }
#scrolled { height: 300px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
}
TEST_F(PaintLayerScrollableAreaTestSPv1, OpaqueLayersDepromotedOnStyleChange) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px; background:
white local content-box; contain: paint; }
#scrolled { height: 300px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
// Change the background to transparent
scroller->setAttribute(
html_names::kStyleAttr,
"background: rgba(255,255,255,0.5) local content-box;");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
}
TEST_F(PaintLayerScrollableAreaTestSPv1, OpaqueLayersPromotedOnStyleChange) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px; background:
rgba(255,255,255,0.5) local content-box; contain: paint; }
#scrolled { height: 300px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
// Change the background to opaque
scroller->setAttribute(html_names::kStyleAttr,
"background: white local content-box;");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking());
ASSERT_TRUE(paint_layer->GraphicsLayerBacking());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque());
}
// Tests that a transform on the scroller or an ancestor will prevent promotion
// TODO(flackr): Allow integer transforms as long as all of the ancestor
// transforms are also integer.
TEST_F(PaintLayerScrollableAreaTestSPv1,
OnlyNonTransformedOpaqueLayersPromoted) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px; background:
white local content-box; contain: paint; }
#scrolled { height: 300px; }
</style>
<div id="parent">
<div id="scroller"><div id="scrolled"></div></div>
</div>
)HTML");
Element* parent = GetDocument().getElementById("parent");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking());
ASSERT_TRUE(paint_layer->GraphicsLayerBacking());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque());
// Change the parent to have a transform.
parent->setAttribute(html_names::kStyleAttr, "transform: translate(1px, 0);");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
// Change the parent to have no transform again.
parent->removeAttribute(html_names::kStyleAttr);
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking());
ASSERT_TRUE(paint_layer->GraphicsLayerBacking());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque());
// Apply a transform to the scroller directly.
scroller->setAttribute(html_names::kStyleAttr,
"transform: translate(1px, 0);");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
}
// Test that opacity applied to the scroller or an ancestor will cause the
// scrolling contents layer to not be promoted.
TEST_F(PaintLayerScrollableAreaTestSPv1, OnlyOpaqueLayersPromoted) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: scroll; height: 200px; width: 200px; background:
white local content-box; contain: paint; }
#scrolled { height: 300px; }
</style>
<div id="parent">
<div id="scroller"><div id="scrolled"></div></div>
</div>
)HTML");
Element* parent = GetDocument().getElementById("parent");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking());
ASSERT_TRUE(paint_layer->GraphicsLayerBacking());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque());
// Change the parent to be partially translucent.
parent->setAttribute(html_names::kStyleAttr, "opacity: 0.5;");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
// Change the parent to be opaque again.
parent->setAttribute(html_names::kStyleAttr, "opacity: 1;");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking());
ASSERT_TRUE(paint_layer->GraphicsLayerBacking());
EXPECT_TRUE(paint_layer->GraphicsLayerBacking()->ContentsOpaque());
// Make the scroller translucent.
scroller->setAttribute(html_names::kStyleAttr, "opacity: 0.5");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
EXPECT_FALSE(paint_layer->GraphicsLayerBacking());
}
// Test that <input> elements get promoted with "will-change:transform".
TEST_F(PaintLayerScrollableAreaTestSPv1, InputElementPromotionTest) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
.composited { will-change: transform; }
</style>
<input id='input' width=10 style='font-size:40pt;'/>
)HTML");
Element* element = GetDocument().getElementById("input");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(element->GetLayoutObject())->Layer();
ASSERT_FALSE(paint_layer);
element->setAttribute("class", "composited");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
ASSERT_TRUE(paint_layer->HasCompositedLayerMapping());
}
// Test that <select> elements get promoted with "will-change:transform".
TEST_F(PaintLayerScrollableAreaTestSPv1, SelectElementPromotionTest) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
.composited { will-change: transform; }
</style>
<select id='select' size='2'>
<option> value 1</option>
<option> value 2</option>
<option> value 3</option>
<option> value 4</option>
</select>
)HTML");
Element* element = GetDocument().getElementById("select");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(element->GetLayoutObject())->Layer();
// Paint layer is created on most platforms but not on all of them, e.g.
// Android Nexus 4. It's better not to check paint_layer separately.
ASSERT_TRUE(!paint_layer || !paint_layer->HasCompositedLayerMapping());
element->setAttribute("class", "composited");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(element->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
ASSERT_TRUE(paint_layer->HasCompositedLayerMapping());
}
// Ensure OverlayScrollbarColorTheme get updated when page load
TEST_P(PaintLayerScrollableAreaTest, OverlayScrollbarColorThemeUpdated) {
SetBodyInnerHTML(R"HTML(
<style>
div { overflow: scroll; }
#white { background-color: white; }
#black { background-color: black; }
</style>
<div id="none">a</div>
<div id="white">b</div>
<div id="black">c</div>
)HTML");
Element* none = GetDocument().getElementById("none");
Element* white = GetDocument().getElementById("white");
Element* black = GetDocument().getElementById("black");
PaintLayer* none_layer =
ToLayoutBoxModelObject(none->GetLayoutObject())->Layer();
PaintLayer* white_layer =
ToLayoutBoxModelObject(white->GetLayoutObject())->Layer();
PaintLayer* black_layer =
ToLayoutBoxModelObject(black->GetLayoutObject())->Layer();
ASSERT_TRUE(none_layer);
ASSERT_TRUE(white_layer);
ASSERT_TRUE(black_layer);
ASSERT_EQ(ScrollbarOverlayColorTheme::kScrollbarOverlayColorThemeDark,
none_layer->GetScrollableArea()->GetScrollbarOverlayColorTheme());
ASSERT_EQ(ScrollbarOverlayColorTheme::kScrollbarOverlayColorThemeDark,
white_layer->GetScrollableArea()->GetScrollbarOverlayColorTheme());
ASSERT_EQ(ScrollbarOverlayColorTheme::kScrollbarOverlayColorThemeLight,
black_layer->GetScrollableArea()->GetScrollbarOverlayColorTheme());
}
// Test that css clip applied to the scroller will cause the
// scrolling contents layer to not be promoted.
TEST_F(PaintLayerScrollableAreaTestSPv1,
OnlyAutoClippedScrollingContentsLayerPromoted) {
SetBodyInnerHTML(R"HTML(
<style>
.clip { clip: rect(0px,60px,50px,0px); }
#scroller { position: absolute; overflow: auto;
height: 100px; width: 100px; background: grey;
will-change:transform; }
#scrolled { height: 300px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayer* paint_layer =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
// Add clip to scroller.
scroller->setAttribute(html_names::kClassAttr, "clip");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_FALSE(paint_layer->NeedsCompositedScrolling());
// Change the scroller to be auto clipped again.
scroller->removeAttribute("class");
UpdateAllLifecyclePhasesForTest();
paint_layer = ToLayoutBoxModelObject(scroller->GetLayoutObject())->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_TRUE(paint_layer->NeedsCompositedScrolling());
}
TEST_P(PaintLayerScrollableAreaTest, HideTooltipWhenScrollPositionChanges) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller { width: 100px; height: 100px; overflow: scroll; }
#scrolled { height: 300px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
PaintLayerScrollableArea* scrollable_area =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->GetScrollableArea();
ASSERT_TRUE(scrollable_area);
EXPECT_CALL(GetChromeClient(),
MockSetToolTip(GetDocument().GetFrame(), String(), _))
.Times(1);
scrollable_area->SetScrollOffset(ScrollOffset(1, 1), kUserScroll);
// Programmatic scrolling should not dismiss the tooltip, so setToolTip
// should not be called for this invocation.
EXPECT_CALL(GetChromeClient(),
MockSetToolTip(GetDocument().GetFrame(), String(), _))
.Times(0);
scrollable_area->SetScrollOffset(ScrollOffset(2, 2), kProgrammaticScroll);
}
TEST_P(PaintLayerScrollableAreaTest, IncludeOverlayScrollbarsInVisibleWidth) {
USE_NON_OVERLAY_SCROLLBARS();
SetBodyInnerHTML(R"HTML(
<style>
#scroller { overflow: overlay; height: 100px; width: 100px; }
#scrolled { width: 100px; height: 200px; }
</style>
<div id="scroller"><div id="scrolled"></div></div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
ASSERT_TRUE(scroller);
PaintLayerScrollableArea* scrollable_area =
ToLayoutBoxModelObject(scroller->GetLayoutObject())->GetScrollableArea();
ASSERT_TRUE(scrollable_area);
scrollable_area->SetScrollOffset(ScrollOffset(100, 0), kClampingScroll);
EXPECT_EQ(scrollable_area->GetScrollOffset().Width(), 0);
}
TEST_P(PaintLayerScrollableAreaTest, ShowAutoScrollbarsForVisibleContent) {
USE_NON_OVERLAY_SCROLLBARS();
SetBodyInnerHTML(R"HTML(
<style>
#outerDiv {
width: 15px;
height: 100px;
overflow-y: auto;
overflow-x: hidden;
}
#innerDiv {
height:300px;
width: 1px;
}
</style>
<div id='outerDiv'>
<div id='innerDiv'></div>
</div>
)HTML");
Element* outer_div = GetDocument().getElementById("outerDiv");
ASSERT_TRUE(outer_div);
outer_div->GetLayoutObject()->SetNeedsLayout("test");
UpdateAllLifecyclePhasesForTest();
PaintLayerScrollableArea* scrollable_area =
ToLayoutBoxModelObject(outer_div->GetLayoutObject())->GetScrollableArea();
ASSERT_TRUE(scrollable_area);
EXPECT_TRUE(scrollable_area->HasVerticalScrollbar());
}
TEST_P(PaintLayerScrollableAreaTest, FloatOverflowInRtlContainer) {
USE_NON_OVERLAY_SCROLLBARS();
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
#container {
width: 200px;
overflow-x: auto;
overflow-y: scroll;
direction: rtl;
}
</style>
<div id='container'>
<div style='float:left'>
lorem ipsum
</div>
</div>
)HTML");
Element* container = GetDocument().getElementById("container");
ASSERT_TRUE(container);
PaintLayerScrollableArea* scrollable_area =
ToLayoutBoxModelObject(container->GetLayoutObject())->GetScrollableArea();
ASSERT_TRUE(scrollable_area);
EXPECT_FALSE(scrollable_area->HasHorizontalScrollbar());
}
TEST_P(PaintLayerScrollableAreaTest, ScrollOriginInRtlContainer) {
SetBodyInnerHTML(R"HTML(
<!DOCTYPE html>
<style>
#container {
width: 200px;
overflow: auto;
direction: rtl;
}
#content {
width: 300px;
}
</style>
<div id='container'>
<div id='content'>
lorem ipsum
<div>
</div>
)HTML");
Element* container = GetDocument().getElementById("container");
ASSERT_TRUE(container);
PaintLayerScrollableArea* scrollable_area =
ToLayoutBoxModelObject(container->GetLayoutObject())->GetScrollableArea();
ASSERT_TRUE(scrollable_area);
EXPECT_EQ(scrollable_area->ScrollOrigin().X(), 100);
}
TEST_P(PaintLayerScrollableAreaTest, OverflowHiddenScrollOffsetInvalidation) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller {
overflow: hidden;
height: 200px;
width: 200px;
}
</style>
<div id='scroller'>
<div id='forceScroll' style='height: 2000px;'></div>
</div>
)HTML");
auto* scroller = GetLayoutObjectByElementId("scroller");
auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea();
const auto* properties = scroller->FirstFragment().PaintProperties();
// No scroll offset translation is needed when scroll offset is zero.
EXPECT_EQ(nullptr, properties->ScrollTranslation());
EXPECT_EQ(FloatSize(0, 0), scrollable_area->GetScrollOffset());
// Going from zero scroll offset to non-zero may require a new paint property
// and should invalidate paint and paint properties.
scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
EXPECT_TRUE(scroller->PaintingLayer()->SelfNeedsRepaint());
EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
// A scroll offset translation is needed when scroll offset is non-zero.
EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset());
EXPECT_NE(nullptr, properties->ScrollTranslation());
// A property update is needed when scroll offset changes.
scrollable_area->SetScrollOffset(ScrollOffset(0, 2), kProgrammaticScroll);
EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
// A scroll offset translation is still needed when scroll offset is non-zero.
EXPECT_EQ(FloatSize(0, 2), scrollable_area->GetScrollOffset());
EXPECT_NE(nullptr, properties->ScrollTranslation());
// Going from non-zero scroll offset to zero may require destroying a paint
// property and should invalidate paint and paint properties.
scrollable_area->SetScrollOffset(ScrollOffset(0, 0), kProgrammaticScroll);
EXPECT_TRUE(scroller->PaintingLayer()->SelfNeedsRepaint());
EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
// No scroll offset translation is needed when scroll offset is zero.
EXPECT_EQ(nullptr, properties->ScrollTranslation());
EXPECT_EQ(FloatSize(0, 0), scrollable_area->GetScrollOffset());
}
TEST_P(PaintLayerScrollableAreaTest, ScrollDoesNotInvalidate) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller {
overflow: scroll;
height: 200px;
width: 200px;
background: linear-gradient(black, white);
}
</style>
<div id='scroller'>
<div id='forceScroll' style='height: 2000px;'></div>
</div>
)HTML");
auto* scroller = GetLayoutObjectByElementId("scroller");
auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea();
const auto* properties = scroller->FirstFragment().PaintProperties();
// Scroll offset translation is needed even when scroll offset is zero.
EXPECT_NE(nullptr, properties->ScrollTranslation());
EXPECT_EQ(FloatSize(0, 0), scrollable_area->GetScrollOffset());
// Changing the scroll offset should not require paint invalidation.
scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
EXPECT_FALSE(scroller->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset());
EXPECT_NE(nullptr, properties->ScrollTranslation());
}
TEST_P(PaintLayerScrollableAreaTest,
ScrollWithLocalAttachmentBackgroundInScrollingContents) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller {
overflow: scroll;
height: 200px;
width: 200px;
background: linear-gradient(black, white);
background-attachment: local;
}
</style>
<div id='scroller'>
<div id='forceScroll' style='height: 2000px;'></div>
</div>
)HTML");
auto* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
auto* scrollable_area = scroller->GetScrollableArea();
EXPECT_EQ(kBackgroundPaintInScrollingContents,
scroller->GetBackgroundPaintLocation());
// Programmatically changing the scroll offset.
scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
// No invalidation because the background paints into scrolling contents.
EXPECT_FALSE(scroller->ShouldDoFullPaintInvalidation());
EXPECT_FALSE(scroller->BackgroundNeedsFullPaintInvalidation());
} else {
// Full invalidation because there is no separate scrolling contents layer.
EXPECT_TRUE(scroller->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(scroller->BackgroundNeedsFullPaintInvalidation());
}
EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset());
const auto* properties = scroller->FirstFragment().PaintProperties();
EXPECT_NE(nullptr, properties->ScrollTranslation());
}
TEST_P(PaintLayerScrollableAreaTest,
ScrollWithLocalAttachmentBackgroundInMainLayer) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller {
overflow: scroll;
height: 200px;
width: 200px;
border: 10px dashed black;
background: linear-gradient(black, white) local, yellow;
}
</style>
<div id='scroller'>
<div id='forceScroll' style='height: 2000px;'></div>
</div>
)HTML");
auto* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
auto* scrollable_area = scroller->GetScrollableArea();
EXPECT_EQ(
kBackgroundPaintInGraphicsLayer | kBackgroundPaintInScrollingContents,
scroller->GetBackgroundPaintLocation());
// Programmatically changing the scroll offset.
scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
// No invalidation because the background paints into the main layer.
EXPECT_TRUE(scroller->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(scroller->BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(scroller->NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(FloatSize(0, 1), scrollable_area->GetScrollOffset());
const auto* properties = scroller->FirstFragment().PaintProperties();
EXPECT_NE(nullptr, properties->ScrollTranslation());
}
TEST_P(PaintLayerScrollableAreaTest, ViewScrollWithFixedAttachmentBackground) {
SetBodyInnerHTML(R"HTML(
<style>
html, #fixed-background {
background: linear-gradient(black, white) fixed;
}
#fixed-background {
width: 200px;
height: 200px;
overflow: scroll;
}
</style>
<div id="fixed-background">
<div style="height: 3000px"></div>
</div>
<div style="height: 3000px"></div>
)HTML");
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetLayoutView().GetBackgroundPaintLocation());
auto* fixed_background_div =
ToLayoutBox(GetLayoutObjectByElementId("fixed-background"));
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
fixed_background_div->GetBackgroundPaintLocation());
auto* div_scrollable_area = fixed_background_div->GetScrollableArea();
auto* view_scrollable_area = GetLayoutView().GetScrollableArea();
// Programmatically changing the view's scroll offset. Should invalidate all
// objects with fixed attachment background.
view_scrollable_area->SetScrollOffset(ScrollOffset(0, 1),
kProgrammaticScroll);
EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate());
EXPECT_TRUE(GetLayoutView().ShouldDoFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
// Programmatically changing the div's scroll offset. Should invalidate the
// scrolled div with fixed attachment background.
div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->NeedsPaintPropertyUpdate());
EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation());
EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
}
TEST_P(PaintLayerScrollableAreaTest,
ViewScrollWithSolidColorFixedAttachmentBackground) {
SetBodyInnerHTML(R"HTML(
<style>
html, #fixed-background {
background: green fixed;
}
#fixed-background {
width: 200px;
height: 200px;
overflow: scroll;
}
</style>
<div id="fixed-background">
<div style="height: 3000px"></div>
</div>
<div style="height: 3000px"></div>
)HTML");
// Fixed-attachment solid-color background should be treated as default
// attachment.
EXPECT_EQ(kBackgroundPaintInScrollingContents,
GetLayoutView().GetBackgroundPaintLocation());
auto* fixed_background_div =
ToLayoutBox(GetLayoutObjectByElementId("fixed-background"));
EXPECT_EQ(kBackgroundPaintInScrollingContents,
fixed_background_div->GetBackgroundPaintLocation());
auto* div_scrollable_area = fixed_background_div->GetScrollableArea();
auto* view_scrollable_area = GetLayoutView().GetScrollableArea();
// Programmatically changing the view's scroll offset. Should invalidate all
// objects with fixed attachment background.
view_scrollable_area->SetScrollOffset(ScrollOffset(0, 1),
kProgrammaticScroll);
EXPECT_FALSE(fixed_background_div->ShouldDoFullPaintInvalidation());
EXPECT_FALSE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate());
EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation());
EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
// Programmatically changing the div's scroll offset. Should invalidate the
// scrolled div with fixed attachment background.
div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
EXPECT_FALSE(fixed_background_div->ShouldDoFullPaintInvalidation());
EXPECT_FALSE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->NeedsPaintPropertyUpdate());
EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation());
EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
}
TEST_P(PaintLayerScrollableAreaTest,
ViewScrollWithFixedAttachmentBackgroundPreferCompositingToLCDText) {
GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
true);
SetBodyInnerHTML(R"HTML(
<style>
html, #fixed-background {
background: linear-gradient(black, white) fixed;
}
#fixed-background {
width: 200px;
height: 200px;
overflow: scroll;
}
</style>
<div id="fixed-background">
<div style="height: 3000px"></div>
</div>
<div style="height: 3000px"></div>
)HTML");
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
GetLayoutView().GetBackgroundPaintLocation());
auto* fixed_background_div =
ToLayoutBox(GetLayoutObjectByElementId("fixed-background"));
EXPECT_EQ(kBackgroundPaintInGraphicsLayer,
fixed_background_div->GetBackgroundPaintLocation());
auto* div_scrollable_area = fixed_background_div->GetScrollableArea();
auto* view_scrollable_area = GetLayoutView().GetScrollableArea();
// Programmatically changing the view's scroll offset. Should invalidate all
// objects with fixed attachment background except the layout view.
view_scrollable_area->SetScrollOffset(ScrollOffset(0, 1),
kProgrammaticScroll);
EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
EXPECT_FALSE(fixed_background_div->NeedsPaintPropertyUpdate());
EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation());
EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
UpdateAllLifecyclePhasesForTest();
// Programmatically changing the div's scroll offset. Should invalidate the
// scrolled div with fixed attachment background.
div_scrollable_area->SetScrollOffset(ScrollOffset(0, 1), kProgrammaticScroll);
EXPECT_TRUE(fixed_background_div->ShouldDoFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(fixed_background_div->NeedsPaintPropertyUpdate());
EXPECT_FALSE(GetLayoutView().ShouldDoFullPaintInvalidation());
EXPECT_FALSE(GetLayoutView().BackgroundNeedsFullPaintInvalidation());
EXPECT_TRUE(GetLayoutView().NeedsPaintPropertyUpdate());
}
TEST_P(PaintLayerScrollableAreaTest, HitTestOverlayScrollbars) {
SetBodyInnerHTML(R"HTML(
<style>
html, body {
margin: 0;
}
#scroller {
overflow: scroll;
height: 100px;
width: 100px;
}
#scrolled {
width: 1000px;
height: 1000px;
}
</style>
<div id='scroller'><div id='scrolled'></div></div>
)HTML");
auto* scroller = GetLayoutObjectByElementId("scroller");
auto* scrollable_area = ToLayoutBoxModelObject(scroller)->GetScrollableArea();
scrollable_area->SetScrollbarsHiddenIfOverlay(true);
HitTestRequest hit_request(HitTestRequest::kMove | HitTestRequest::kReadOnly);
HitTestLocation location(PhysicalOffset(95, 5));
HitTestResult hit_result(hit_request, location);
GetDocument().GetLayoutView()->HitTest(location, hit_result);
EXPECT_EQ(hit_result.GetScrollbar(), nullptr);
location = HitTestLocation(PhysicalOffset(5, 95));
hit_result = HitTestResult(hit_request, location);
GetDocument().GetLayoutView()->HitTest(location, hit_result);
EXPECT_EQ(hit_result.GetScrollbar(), nullptr);
scrollable_area->SetScrollbarsHiddenIfOverlay(false);
location = HitTestLocation(PhysicalOffset(95, 5));
hit_result = HitTestResult(hit_request, location);
GetDocument().GetLayoutView()->HitTest(location, hit_result);
EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->VerticalScrollbar());
location = HitTestLocation(PhysicalOffset(5, 95));
hit_result = HitTestResult(hit_request, location);
GetDocument().GetLayoutView()->HitTest(location, hit_result);
EXPECT_EQ(hit_result.GetScrollbar(), scrollable_area->HorizontalScrollbar());
}
TEST_P(PaintLayerScrollableAreaTest, CompositedStickyDescendant) {
SetBodyInnerHTML(R"HTML(
<div id=scroller style="overflow: scroll; width: 500px; height: 300px;
will-change: transform">
<div id=sticky style="top: 0px; position: sticky; background: green">
</div>
<div style="width: 10px; height: 700px; background: lightblue"></div>
</div>
)HTML");
auto* scroller =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("scroller"));
auto* scrollable_area = scroller->GetScrollableArea();
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
EXPECT_EQ(kPaintsIntoOwnBacking, scroller->Layer()->GetCompositingState());
auto* sticky = ToLayoutBoxModelObject(GetLayoutObjectByElementId("sticky"));
EXPECT_TRUE(sticky->FirstFragment()
.LocalBorderBoxProperties()
.Transform()
.IsIdentity());
scrollable_area->SetScrollOffset(ScrollOffset(0, 50), kUserScroll);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(FloatSize(0, 50), sticky->FirstFragment()
.LocalBorderBoxProperties()
.Transform()
.Translation2D());
}
// Delayed scroll offset clamping should not crash. https://crbug.com/842495
TEST_P(PaintLayerScrollableAreaTest, IgnoreDelayedScrollOnDestroyedLayer) {
SetBodyInnerHTML(R"HTML(
<div id=scroller style="overflow: scroll; width: 200px; height: 200px;">
<div style="height: 1000px;"></div>
</div>
)HTML");
Element* scroller = GetDocument().getElementById("scroller");
{
PaintLayerScrollableArea::DelayScrollOffsetClampScope scope;
PaintLayerScrollableArea::DelayScrollOffsetClampScope::SetNeedsClamp(
scroller->GetLayoutBox()->GetScrollableArea());
scroller->SetInlineStyleProperty(CSSPropertyID::kDisplay,
CSSValueID::kNone);
UpdateAllLifecyclePhasesForTest();
}
}
TEST_P(PaintLayerScrollableAreaTest, ScrollbarMaximum) {
SetBodyInnerHTML(R"HTML(
<style>
#spacer {
height: 17.984375px;
}
#scroller {
border-top: 0.328125px solid gray;
border-bottom: 0.328125px solid gray;
height:149.34375px;
width: 100px;
overflow-y:auto;
}
#content {
height: 156.578125px;
}
</style>
<div id='spacer'></div>
<div id='scroller'>
<div id='content'></div>
</div>
)HTML");
LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
scrollable_area->ScrollBy(ScrollOffset(0, 1000), kProgrammaticScroll);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(scrollbar->CurrentPos(), scrollbar->Maximum());
}
TEST_P(PaintLayerScrollableAreaTest, ScrollingBackgroundDisplayItemClient) {
SetBodyInnerHTML(R"HTML(
<style>
::-webkit-scrollbar { display: none; }
#scroller {
width: 100.7px;
height: 100.4px;
overflow: scroll;
border-top: 2.6px solid blue;
border-left: 2.4px solid blue;
will-change: transform;
}
#content {
width: 50.7px;
height: 200.4px;
}
</style>
<div id="scroller">
<div id="content"></div>
</div>
)HTML");
EXPECT_EQ(IntRect(2, 3, 101, 200),
ToLayoutBox(GetLayoutObjectByElementId("scroller"))
->GetScrollableArea()
->GetScrollingBackgroundDisplayItemClient()
.VisualRect());
}
TEST_P(PaintLayerScrollableAreaTest, RtlScrollOriginSnapping) {
SetBodyInnerHTML(R"HTML(
<style>
#container {
direction: rtl;
display: flex;
}
#scroller {
width: 100%;
height: 100px;
overflow: hidden;
}
#scroller-content {
width: 200%;
height: 200px;
}
</style>
<div id="container">
<div id="first-child" style="flex:1; display:none"></div>
<div style="flex:2.2">
<div id="scroller">
<div id ="scroller-content"></div>
</div>
</div>
</div>
)HTML");
// Test that scroll origin is snapped such that maximum scroll offset is
// always zero for an rtl block.
GetFrame().View()->Resize(795, 600);
UpdateAllLifecyclePhasesForTest();
LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
EXPECT_EQ(scrollable_area->MaximumScrollOffsetInt(), IntSize(0, 100));
Element* first_child = GetElementById("first-child");
first_child->RemoveInlineStyleProperty(CSSPropertyID::kDisplay);
UpdateAllLifecyclePhasesForTest();
EXPECT_EQ(scrollable_area->MaximumScrollOffsetInt(), IntSize(0, 100));
}
TEST_P(PaintLayerScrollableAreaTest, ShowCustomResizerInTextarea) {
GetPage().GetSettings().SetTextAreasAreResizable(true);
SetBodyInnerHTML(R"HTML(
<!doctype HTML>
<style>
textarea {
width: 200px;
height: 100px;
}
::-webkit-resizer {
background-color: red;
}
</style>
<textarea id="target"></textarea>
)HTML");
const auto* paint_layer =
ToLayoutBoxModelObject(GetLayoutObjectByElementId("target"))->Layer();
ASSERT_TRUE(paint_layer);
EXPECT_NE(paint_layer->GetScrollableArea()->Resizer(), nullptr);
}
class PaintLayerScrollableAreaCompositingTest
: public PaintLayerScrollableAreaTestBase,
public testing::WithParamInterface<unsigned>,
private ScopedCompositeAfterPaintForTest {
public:
PaintLayerScrollableAreaCompositingTest()
: ScopedCompositeAfterPaintForTest(GetParam() & kCompositeAfterPaint) {
if (GetParam() & kDoNotCompositeTrivial3D) {
scoped_feature_list_.InitAndEnableFeature(
blink::features::kDoNotCompositeTrivial3D);
} else {
scoped_feature_list_.InitAndDisableFeature(
blink::features::kDoNotCompositeTrivial3D);
}
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_DO_NOT_COMPOSITE_TRIVIAL_3D_P(
PaintLayerScrollableAreaCompositingTest);
// Test that a trivial 3D transform results in composited scrolling.
TEST_P(PaintLayerScrollableAreaCompositingTest, CompositeWithTrivial3D) {
SetBodyInnerHTML(R"HTML(
<style>
#scroller {
width: 100px;
height: 100px;
overflow: scroll;
transform: translateZ(0);
}
#scrolled {
width: 200px;
height: 200px;
}
</style>
<div id="scroller">
<div id="scrolled"></div>
</div>
)HTML");
LayoutBox* scroller = ToLayoutBox(GetLayoutObjectByElementId("scroller"));
PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea();
if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
EXPECT_TRUE(scrollable_area->NeedsCompositedScrolling());
const auto* properties = scroller->FirstFragment().PaintProperties();
EXPECT_TRUE(properties->ScrollTranslation()->HasDirectCompositingReasons());
}
} // namespace blink