1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "build/build_config.h"
6 #include "testing/gtest/include/gtest/gtest.h"
7 #include "third_party/blink/public/common/features.h"
8 #include "third_party/blink/public/common/input/web_coalesced_input_event.h"
9 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
10 #include "third_party/blink/public/platform/web_theme_engine.h"
11 #include "third_party/blink/public/web/web_script_source.h"
12 #include "third_party/blink/renderer/core/frame/frame_test_helpers.h"
13 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
14 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
15 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
16 #include "third_party/blink/renderer/core/html/html_iframe_element.h"
17 #include "third_party/blink/renderer/core/input/event_handler.h"
18 #include "third_party/blink/renderer/core/inspector/dev_tools_emulator.h"
19 #include "third_party/blink/renderer/core/layout/layout_custom_scrollbar_part.h"
20 #include "third_party/blink/renderer/core/layout/layout_view.h"
21 #include "third_party/blink/renderer/core/page/page.h"
22 #include "third_party/blink/renderer/core/paint/paint_layer.h"
23 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
24 #include "third_party/blink/renderer/core/scroll/scrollbar_theme_overlay_mock.h"
25 #include "third_party/blink/renderer/core/testing/color_scheme_helper.h"
26 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
27 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
28 #include "third_party/blink/renderer/platform/graphics/graphics_layer.h"
29 #include "third_party/blink/renderer/platform/testing/testing_platform_support.h"
30 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
31 #include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
32 #include "ui/base/cursor/cursor.h"
33 #include "ui/base/cursor/mojom/cursor_type.mojom-blink.h"
34
35 namespace blink {
36
37 namespace {
38
39 class StubWebThemeEngine : public WebThemeEngine {
40 public:
StubWebThemeEngine()41 StubWebThemeEngine() {
42 painted_color_scheme_.fill(mojom::blink::ColorScheme::kLight);
43 }
44
GetSize(Part part)45 gfx::Size GetSize(Part part) override {
46 switch (part) {
47 case kPartScrollbarHorizontalThumb:
48 return gfx::Size(kMinimumHorizontalLength, 15);
49 case kPartScrollbarVerticalThumb:
50 return gfx::Size(15, kMinimumVerticalLength);
51 default:
52 return gfx::Size();
53 }
54 }
GetOverlayScrollbarStyle(ScrollbarStyle * style)55 void GetOverlayScrollbarStyle(ScrollbarStyle* style) override {
56 style->fade_out_delay = base::TimeDelta();
57 style->fade_out_duration = base::TimeDelta();
58 style->thumb_thickness = 3;
59 style->scrollbar_margin = 0;
60 style->color = SkColorSetARGB(128, 64, 64, 64);
61 }
62 static constexpr int kMinimumHorizontalLength = 51;
63 static constexpr int kMinimumVerticalLength = 52;
64
Paint(cc::PaintCanvas *,Part part,State,const gfx::Rect &,const ExtraParams *,mojom::blink::ColorScheme color_scheme)65 void Paint(cc::PaintCanvas*,
66 Part part,
67 State,
68 const gfx::Rect&,
69 const ExtraParams*,
70 mojom::blink::ColorScheme color_scheme) override {
71 // Make sure we don't overflow the array.
72 DCHECK(part <= kPartProgressBar);
73 painted_color_scheme_[part] = color_scheme;
74 }
75
GetPaintedPartColorScheme(Part part) const76 mojom::blink::ColorScheme GetPaintedPartColorScheme(Part part) const {
77 return painted_color_scheme_[part];
78 }
79
80 private:
81 std::array<mojom::blink::ColorScheme, kPartProgressBar + 1>
82 painted_color_scheme_;
83 };
84
85 constexpr int StubWebThemeEngine::kMinimumHorizontalLength;
86 constexpr int StubWebThemeEngine::kMinimumVerticalLength;
87
88 class ScrollbarTestingPlatformSupport : public TestingPlatformSupport {
89 public:
ThemeEngine()90 WebThemeEngine* ThemeEngine() override { return &mock_theme_engine_; }
91
92 private:
93 StubWebThemeEngine mock_theme_engine_;
94 };
95
96 } // namespace
97
98 class ScrollbarsTest : public SimTest {
99 public:
SetUp()100 void SetUp() override {
101 SimTest::SetUp();
102 // We don't use the mock scrollbar theme in this file, but use the normal
103 // scrollbar theme with mock WebThemeEngine, for better control of testing
104 // environment. This is after SimTest::SetUp() to override the mock overlay
105 // scrollbar settings initialized there.
106 mock_overlay_scrollbars_ =
107 std::make_unique<ScopedMockOverlayScrollbars>(false);
108 original_overlay_scrollbars_enabled_ =
109 ScrollbarThemeSettings::OverlayScrollbarsEnabled();
110 }
111
TearDown()112 void TearDown() override {
113 ScrollbarThemeSettings::SetOverlayScrollbarsEnabled(
114 original_overlay_scrollbars_enabled_);
115 mock_overlay_scrollbars_.reset();
116 SimTest::TearDown();
117 }
118
SetOverlayScrollbarsEnabled(bool b)119 void SetOverlayScrollbarsEnabled(bool b) {
120 ScrollbarThemeSettings::SetOverlayScrollbarsEnabled(b);
121 }
122
HitTest(int x,int y)123 HitTestResult HitTest(int x, int y) {
124 return WebView().MainFrameViewWidget()->CoreHitTestResultAt(
125 gfx::PointF(x, y));
126 }
127
GetEventHandler()128 EventHandler& GetEventHandler() {
129 return GetDocument().GetFrame()->GetEventHandler();
130 }
131
HandleMouseMoveEvent(int x,int y)132 void HandleMouseMoveEvent(int x, int y) {
133 WebMouseEvent event(WebInputEvent::Type::kMouseMove, gfx::PointF(x, y),
134 gfx::PointF(x, y),
135 WebPointerProperties::Button::kNoButton, 0,
136 WebInputEvent::kNoModifiers, base::TimeTicks::Now());
137 event.SetFrameScale(1);
138 GetEventHandler().HandleMouseMoveEvent(event, Vector<WebMouseEvent>(),
139 Vector<WebMouseEvent>());
140 }
141
HandleMousePressEvent(int x,int y)142 void HandleMousePressEvent(int x, int y) {
143 WebMouseEvent event(WebInputEvent::Type::kMouseDown, gfx::PointF(x, y),
144 gfx::PointF(x, y), WebPointerProperties::Button::kLeft,
145 0, WebInputEvent::Modifiers::kLeftButtonDown,
146 base::TimeTicks::Now());
147 event.SetFrameScale(1);
148 GetEventHandler().HandleMousePressEvent(event);
149 }
150
HandleContextMenuEvent(int x,int y)151 void HandleContextMenuEvent(int x, int y) {
152 WebMouseEvent event(
153 WebInputEvent::Type::kMouseDown, gfx::PointF(x, y), gfx::PointF(x, y),
154 WebPointerProperties::Button::kNoButton, 0,
155 WebInputEvent::Modifiers::kNoModifiers, base::TimeTicks::Now());
156 event.SetFrameScale(1);
157 GetEventHandler().SendContextMenuEvent(event);
158 }
159
HandleMouseReleaseEvent(int x,int y)160 void HandleMouseReleaseEvent(int x, int y) {
161 WebMouseEvent event(WebInputEvent::Type::kMouseUp, gfx::PointF(x, y),
162 gfx::PointF(x, y), WebPointerProperties::Button::kLeft,
163 0, WebInputEvent::Modifiers::kNoModifiers,
164 base::TimeTicks::Now());
165 event.SetFrameScale(1);
166 GetEventHandler().HandleMouseReleaseEvent(event);
167 }
168
HandleMouseMiddlePressEvent(int x,int y)169 void HandleMouseMiddlePressEvent(int x, int y) {
170 WebMouseEvent event(
171 WebInputEvent::Type::kMouseDown, gfx::PointF(x, y), gfx::PointF(x, y),
172 WebPointerProperties::Button::kMiddle, 0,
173 WebInputEvent::Modifiers::kMiddleButtonDown, base::TimeTicks::Now());
174 event.SetFrameScale(1);
175 GetEventHandler().HandleMousePressEvent(event);
176 }
177
HandleMouseMiddleReleaseEvent(int x,int y)178 void HandleMouseMiddleReleaseEvent(int x, int y) {
179 WebMouseEvent event(
180 WebInputEvent::Type::kMouseUp, gfx::PointF(x, y), gfx::PointF(x, y),
181 WebPointerProperties::Button::kMiddle, 0,
182 WebInputEvent::Modifiers::kMiddleButtonDown, base::TimeTicks::Now());
183 event.SetFrameScale(1);
184 GetEventHandler().HandleMouseReleaseEvent(event);
185 }
186
HandleMouseLeaveEvent()187 void HandleMouseLeaveEvent() {
188 WebMouseEvent event(WebInputEvent::Type::kMouseLeave, gfx::PointF(1, 1),
189 gfx::PointF(1, 1), WebPointerProperties::Button::kLeft,
190 0, WebInputEvent::Modifiers::kLeftButtonDown,
191 base::TimeTicks::Now());
192 event.SetFrameScale(1);
193 GetEventHandler().HandleMouseLeaveEvent(event);
194 }
195
GenerateWheelGestureEvent(WebInputEvent::Type type,const IntPoint & position,ScrollOffset offset=ScrollOffset ())196 WebCoalescedInputEvent GenerateWheelGestureEvent(
197 WebInputEvent::Type type,
198 const IntPoint& position,
199 ScrollOffset offset = ScrollOffset()) {
200 return GenerateGestureEvent(type, WebGestureDevice::kTouchpad, position,
201 offset);
202 }
203
GenerateTouchGestureEvent(WebInputEvent::Type type,const IntPoint & position,ScrollOffset offset=ScrollOffset ())204 WebCoalescedInputEvent GenerateTouchGestureEvent(
205 WebInputEvent::Type type,
206 const IntPoint& position,
207 ScrollOffset offset = ScrollOffset()) {
208 return GenerateGestureEvent(type, WebGestureDevice::kTouchscreen, position,
209 offset);
210 }
211
CursorType()212 ui::mojom::blink::CursorType CursorType() {
213 return GetDocument()
214 .GetFrame()
215 ->GetChromeClient()
216 .LastSetCursorForTesting()
217 .type();
218 }
219
GetScrollbarTheme()220 ScrollbarTheme& GetScrollbarTheme() {
221 return GetDocument().GetPage()->GetScrollbarTheme();
222 }
223
GetScrollableArea(const Element & element) const224 PaintLayerScrollableArea* GetScrollableArea(const Element& element) const {
225 return element.GetLayoutBox()->GetScrollableArea();
226 }
227
228 protected:
GenerateGestureEvent(WebInputEvent::Type type,WebGestureDevice device,const IntPoint & position,ScrollOffset offset)229 WebCoalescedInputEvent GenerateGestureEvent(WebInputEvent::Type type,
230 WebGestureDevice device,
231 const IntPoint& position,
232 ScrollOffset offset) {
233 WebGestureEvent event(type, WebInputEvent::kNoModifiers,
234 base::TimeTicks::Now(), device);
235
236 event.SetPositionInWidget(gfx::PointF(position.X(), position.Y()));
237
238 if (type == WebInputEvent::Type::kGestureScrollUpdate) {
239 event.data.scroll_update.delta_x = offset.Width();
240 event.data.scroll_update.delta_y = offset.Height();
241 } else if (type == WebInputEvent::Type::kGestureScrollBegin) {
242 event.data.scroll_begin.delta_x_hint = offset.Width();
243 event.data.scroll_begin.delta_y_hint = offset.Height();
244 }
245 return WebCoalescedInputEvent(event, ui::LatencyInfo());
246 }
247
248 private:
249 ScopedTestingPlatformSupport<ScrollbarTestingPlatformSupport> platform;
250 std::unique_ptr<ScopedMockOverlayScrollbars> mock_overlay_scrollbars_;
251 bool original_overlay_scrollbars_enabled_;
252 };
253
254 class ScrollbarsTestWithVirtualTimer : public ScrollbarsTest {
255 public:
SetUp()256 void SetUp() override {
257 ScrollbarsTest::SetUp();
258 WebView().Scheduler()->EnableVirtualTime();
259 }
260
TearDown()261 void TearDown() override {
262 WebView().Scheduler()->DisableVirtualTimeForTesting();
263 ScrollbarsTest::TearDown();
264 }
265
TimeAdvance()266 void TimeAdvance() {
267 WebView().Scheduler()->SetVirtualTimePolicy(
268 PageScheduler::VirtualTimePolicy::kAdvance);
269 }
270
StopVirtualTimeAndExitRunLoop()271 void StopVirtualTimeAndExitRunLoop() {
272 WebView().Scheduler()->SetVirtualTimePolicy(
273 PageScheduler::VirtualTimePolicy::kPause);
274 test::ExitRunLoop();
275 }
276
277 // Some task queues may have repeating v8 tasks that run forever so we impose
278 // a hard (virtual) time limit.
RunTasksForPeriod(base::TimeDelta delay)279 void RunTasksForPeriod(base::TimeDelta delay) {
280 TimeAdvance();
281 scheduler::GetSingleThreadTaskRunnerForTesting()->PostDelayedTask(
282 FROM_HERE,
283 WTF::Bind(
284 &ScrollbarsTestWithVirtualTimer::StopVirtualTimeAndExitRunLoop,
285 WTF::Unretained(this)),
286 delay);
287 test::EnterRunLoop();
288 }
289 };
290
291 // Try to force enable/disable overlay. Skip the test if the desired setting
292 // is not supported by the platform.
293 #define ENABLE_OVERLAY_SCROLLBARS(b) \
294 do { \
295 SetOverlayScrollbarsEnabled(b); \
296 if (WebView().GetPage()->GetScrollbarTheme().UsesOverlayScrollbars() != b) \
297 return; \
298 } while (false)
299
TEST_F(ScrollbarsTest,DocumentStyleRecalcPreservesScrollbars)300 TEST_F(ScrollbarsTest, DocumentStyleRecalcPreservesScrollbars) {
301 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
302 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
303 SimRequest request("https://example.com/test.html", "text/html");
304 LoadURL("https://example.com/test.html");
305 request.Complete(R"HTML(
306 <!DOCTYPE html>
307 <style> body { width: 1600px; height: 1200px; } </style>)HTML");
308 auto* layout_viewport = GetDocument().View()->LayoutViewport();
309
310 Compositor().BeginFrame();
311 ASSERT_TRUE(layout_viewport->VerticalScrollbar() &&
312 layout_viewport->HorizontalScrollbar());
313
314 // Forces recalc of LayoutView's computed style in Document::updateStyle,
315 // without invalidating layout.
316 MainFrame().ExecuteScriptAndReturnValue(WebScriptSource(
317 "document.querySelector('style').sheet.insertRule('body {}', 1);"));
318
319 Compositor().BeginFrame();
320 ASSERT_TRUE(layout_viewport->VerticalScrollbar() &&
321 layout_viewport->HorizontalScrollbar());
322 }
323
TEST(ScrollbarsTestWithOwnWebViewHelper,ScrollbarSizeForUseZoomDSF)324 TEST(ScrollbarsTestWithOwnWebViewHelper, ScrollbarSizeForUseZoomDSF) {
325 ScopedTestingPlatformSupport<TestingPlatformSupport> platform;
326 platform->SetUseZoomForDSF(true);
327 frame_test_helpers::WebViewHelper web_view_helper;
328 // Needed so visual viewport supplies its own scrollbars. We don't support
329 // this setting changing after initialization, so we must set it through
330 // WebViewHelper.
331 web_view_helper.set_viewport_enabled(true);
332
333 WebViewImpl* web_view_impl = web_view_helper.Initialize();
334
335 web_view_impl->MainFrameViewWidget()->SetDeviceScaleFactorForTesting(1.f);
336 web_view_impl->MainFrameViewWidget()->Resize(gfx::Size(800, 600));
337
338 WebURL base_url = url_test_helpers::ToKURL("http://example.com/");
339 frame_test_helpers::LoadHTMLString(web_view_impl->MainFrameImpl(),
340 "<!DOCTYPE html>"
341 "<style>"
342 " body {"
343 " width: 1600px;"
344 " height: 1200px;"
345 " }"
346 "</style>"
347 "<body>"
348 "</body>",
349 base_url);
350 web_view_impl->MainFrameViewWidget()->UpdateAllLifecyclePhases(
351 DocumentUpdateReason::kTest);
352
353 Document* document =
354 To<LocalFrame>(web_view_impl->GetPage()->MainFrame())->GetDocument();
355
356 VisualViewport& visual_viewport = document->GetPage()->GetVisualViewport();
357 int horizontal_scrollbar =
358 visual_viewport.LayerForHorizontalScrollbar()->bounds().height();
359 int vertical_scrollbar =
360 visual_viewport.LayerForVerticalScrollbar()->bounds().width();
361
362 const float device_scale = 3.5f;
363 web_view_impl->MainFrameViewWidget()->SetDeviceScaleFactorForTesting(
364 device_scale);
365 web_view_impl->MainFrameViewWidget()->Resize(gfx::Size(400, 300));
366
367 EXPECT_EQ(clampTo<int>(std::floor(horizontal_scrollbar * device_scale)),
368 visual_viewport.LayerForHorizontalScrollbar()->bounds().height());
369 EXPECT_EQ(clampTo<int>(std::floor(vertical_scrollbar * device_scale)),
370 visual_viewport.LayerForVerticalScrollbar()->bounds().width());
371
372 web_view_impl->MainFrameViewWidget()->SetDeviceScaleFactorForTesting(1.f);
373 web_view_impl->MainFrameViewWidget()->Resize(gfx::Size(800, 600));
374
375 EXPECT_EQ(horizontal_scrollbar,
376 visual_viewport.LayerForHorizontalScrollbar()->bounds().height());
377 EXPECT_EQ(vertical_scrollbar,
378 visual_viewport.LayerForVerticalScrollbar()->bounds().width());
379 }
380
381 // Ensure that causing a change in scrollbar existence causes a nested layout
382 // to recalculate the existence of the opposite scrollbar. The bug here was
383 // caused by trying to avoid the layout when overlays are enabled but not
384 // checking whether the scrollbars should be custom - which do take up layout
385 // space. https://crbug.com/668387.
TEST_F(ScrollbarsTest,CustomScrollbarsCauseLayoutOnExistenceChange)386 TEST_F(ScrollbarsTest, CustomScrollbarsCauseLayoutOnExistenceChange) {
387 // This test is specifically checking the behavior when overlay scrollbars
388 // are enabled.
389 ENABLE_OVERLAY_SCROLLBARS(true);
390
391 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
392 SimRequest request("https://example.com/test.html", "text/html");
393 LoadURL("https://example.com/test.html");
394 request.Complete(R"HTML(
395 <!DOCTYPE html>
396 <style>
397 ::-webkit-scrollbar {
398 height: 16px;
399 width: 16px
400 }
401 ::-webkit-scrollbar-thumb {
402 background-color: rgba(0,0,0,.2);
403 }
404 html, body{
405 margin: 0;
406 height: 100%;
407 }
408 .box {
409 width: 100%;
410 height: 100%;
411 }
412 .transformed {
413 transform: translateY(100px);
414 }
415 </style>
416 <div id='box' class='box'></div>
417 )HTML");
418
419 ScrollableArea* layout_viewport = GetDocument().View()->LayoutViewport();
420
421 Compositor().BeginFrame();
422
423 ASSERT_FALSE(layout_viewport->VerticalScrollbar());
424 ASSERT_FALSE(layout_viewport->HorizontalScrollbar());
425
426 // Adding translation will cause a vertical scrollbar to appear but not dirty
427 // layout otherwise. Ensure the change of scrollbar causes a layout to
428 // recalculate the page width with the vertical scrollbar added.
429 MainFrame().ExecuteScript(WebScriptSource(
430 "document.getElementById('box').className = 'box transformed';"));
431 Compositor().BeginFrame();
432
433 ASSERT_TRUE(layout_viewport->VerticalScrollbar());
434 ASSERT_FALSE(layout_viewport->HorizontalScrollbar());
435 }
436
TEST_F(ScrollbarsTest,TransparentBackgroundUsesDarkOverlayColorTheme)437 TEST_F(ScrollbarsTest, TransparentBackgroundUsesDarkOverlayColorTheme) {
438 // This test is specifically checking the behavior when overlay scrollbars
439 // are enabled.
440 ENABLE_OVERLAY_SCROLLBARS(true);
441
442 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
443 WebView().SetBaseBackgroundColorOverride(SK_ColorTRANSPARENT);
444 SimRequest request("https://example.com/test.html", "text/html");
445 LoadURL("https://example.com/test.html");
446 request.Complete(R"HTML(
447 <!DOCTYPE html>
448 <style>
449 body{
450 height: 300%;
451 }
452 </style>
453 )HTML");
454 Compositor().BeginFrame();
455
456 ScrollableArea* layout_viewport = GetDocument().View()->LayoutViewport();
457
458 EXPECT_EQ(kScrollbarOverlayColorThemeDark,
459 layout_viewport->GetScrollbarOverlayColorTheme());
460 }
461
TEST_F(ScrollbarsTest,BodyBackgroundChangesOverlayColorTheme)462 TEST_F(ScrollbarsTest, BodyBackgroundChangesOverlayColorTheme) {
463 // This test is specifically checking the behavior when overlay scrollbars
464 // are enabled.
465 ENABLE_OVERLAY_SCROLLBARS(true);
466
467 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
468 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
469 SimRequest request("https://example.com/test.html", "text/html");
470 LoadURL("https://example.com/test.html");
471 request.Complete(R"HTML(
472 <!DOCTYPE html>
473 <body style='background:white'></body>
474 )HTML");
475 Compositor().BeginFrame();
476
477 ScrollableArea* layout_viewport = GetDocument().View()->LayoutViewport();
478
479 EXPECT_EQ(kScrollbarOverlayColorThemeDark,
480 layout_viewport->GetScrollbarOverlayColorTheme());
481
482 MainFrame().ExecuteScriptAndReturnValue(
483 WebScriptSource("document.body.style.backgroundColor = 'black';"));
484
485 Compositor().BeginFrame();
486 EXPECT_EQ(kScrollbarOverlayColorThemeLight,
487 layout_viewport->GetScrollbarOverlayColorTheme());
488 }
489
490 // Ensure overlay scrollbar change to display:none correctly.
TEST_F(ScrollbarsTest,OverlayScrollbarChangeToDisplayNoneDynamically)491 TEST_F(ScrollbarsTest, OverlayScrollbarChangeToDisplayNoneDynamically) {
492 // This test is specifically checking the behavior when overlay scrollbars
493 // are enabled.
494 ENABLE_OVERLAY_SCROLLBARS(true);
495
496 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
497 SimRequest request("https://example.com/test.html", "text/html");
498 LoadURL("https://example.com/test.html");
499 request.Complete(R"HTML(
500 <!DOCTYPE html>
501 <style>
502 .noscrollbars::-webkit-scrollbar { display: none; }
503 #div{ height: 100px; width:100px; overflow:scroll; }
504 .big{ height: 2000px; }
505 body { overflow:scroll; }
506 </style>
507 <div id='div'>
508 <div class='big'>
509 </div>
510 </div>
511 <div class='big'>
512 </div>
513 )HTML");
514 Compositor().BeginFrame();
515
516 Document& document = GetDocument();
517 Element* div = document.getElementById("div");
518
519 // Ensure we have overlay scrollbar for div and root.
520 auto* scrollable_div = GetScrollableArea(*div);
521
522 ScrollableArea* scrollable_root = GetDocument().View()->LayoutViewport();
523
524 DCHECK(scrollable_div->VerticalScrollbar());
525 DCHECK(scrollable_div->VerticalScrollbar()->IsOverlayScrollbar());
526
527 DCHECK(!scrollable_div->HorizontalScrollbar());
528
529 DCHECK(scrollable_root->VerticalScrollbar());
530 DCHECK(scrollable_root->VerticalScrollbar()->IsOverlayScrollbar());
531
532 // For PaintLayer Overlay Scrollbar we will remove the scrollbar when it is
533 // not necessary even with overflow:scroll.
534 DCHECK(!scrollable_root->HorizontalScrollbar());
535
536 // Set display:none.
537 div->setAttribute(html_names::kClassAttr, "noscrollbars");
538 document.body()->setAttribute(html_names::kClassAttr, "noscrollbars");
539 Compositor().BeginFrame();
540
541 EXPECT_TRUE(scrollable_div->VerticalScrollbar());
542 EXPECT_TRUE(scrollable_div->VerticalScrollbar()->IsCustomScrollbar());
543 EXPECT_TRUE(scrollable_div->VerticalScrollbar()->FrameRect().IsEmpty());
544
545 EXPECT_TRUE(scrollable_div->HorizontalScrollbar());
546 EXPECT_TRUE(scrollable_div->HorizontalScrollbar()->IsCustomScrollbar());
547 EXPECT_TRUE(scrollable_div->HorizontalScrollbar()->FrameRect().IsEmpty());
548
549 EXPECT_TRUE(scrollable_root->VerticalScrollbar());
550 EXPECT_TRUE(scrollable_root->VerticalScrollbar()->IsCustomScrollbar());
551 EXPECT_TRUE(scrollable_root->VerticalScrollbar()->FrameRect().IsEmpty());
552
553 EXPECT_TRUE(scrollable_root->HorizontalScrollbar());
554 EXPECT_TRUE(scrollable_root->HorizontalScrollbar()->IsCustomScrollbar());
555 EXPECT_TRUE(scrollable_root->HorizontalScrollbar()->FrameRect().IsEmpty());
556 }
557
558 // Ensure that overlay scrollbars are not created, even in overflow:scroll,
559 // situations when there's no overflow. Specifically, after style-only changes.
TEST_F(ScrollbarsTest,OverlayScrolblarNotCreatedInUnscrollableAxis)560 TEST_F(ScrollbarsTest, OverlayScrolblarNotCreatedInUnscrollableAxis) {
561 // This test is specifically checking the behavior when overlay scrollbars
562 // are enabled.
563 ENABLE_OVERLAY_SCROLLBARS(true);
564
565 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
566 SimRequest request("https://example.com/test.html", "text/html");
567 LoadURL("https://example.com/test.html");
568 request.Complete(R"HTML(
569 <!DOCTYPE html>
570 <style>
571 #target {
572 width: 100px;
573 height: 100px;
574 overflow-y: scroll;
575 opacity: 0.5;
576 }
577 </style>
578 <div id="target"></div>
579 )HTML");
580
581 Compositor().BeginFrame();
582
583 auto* target = GetDocument().getElementById("target");
584 auto* scrollable_area = target->GetLayoutBox()->GetScrollableArea();
585
586 ASSERT_FALSE(scrollable_area->VerticalScrollbar());
587 ASSERT_FALSE(scrollable_area->HorizontalScrollbar());
588
589 // Mutate the opacity so that we cause a style-only change.
590 target->setAttribute(html_names::kStyleAttr, "opacity: 0.9");
591 Compositor().BeginFrame();
592
593 ASSERT_FALSE(scrollable_area->VerticalScrollbar());
594 ASSERT_FALSE(scrollable_area->HorizontalScrollbar());
595 }
596
TEST_F(ScrollbarsTest,scrollbarIsNotHandlingTouchpadScroll)597 TEST_F(ScrollbarsTest, scrollbarIsNotHandlingTouchpadScroll) {
598 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
599 SimRequest request("https://example.com/test.html", "text/html");
600 LoadURL("https://example.com/test.html");
601 request.Complete(R"HTML(
602 <!DOCTYPE html>
603 <style>
604 #scrollable { height: 100px; width: 100px; overflow: scroll; }
605 #content { height: 200px; width: 200px;}
606 </style>
607 <div id='scrollable'>
608 <div id='content'></div>
609 </div>
610 )HTML");
611 Compositor().BeginFrame();
612
613 Document& document = GetDocument();
614 Element* scrollable = document.getElementById("scrollable");
615
616 auto* scrollable_area = GetScrollableArea(*scrollable);
617 DCHECK(scrollable_area->VerticalScrollbar());
618 WebGestureEvent scroll_begin(
619 WebInputEvent::Type::kGestureScrollBegin, WebInputEvent::kNoModifiers,
620 base::TimeTicks::Now(), WebGestureDevice::kTouchpad);
621 scroll_begin.SetPositionInWidget(
622 gfx::PointF(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2,
623 scrollable->OffsetTop()));
624 scroll_begin.SetPositionInScreen(
625 gfx::PointF(scrollable->OffsetLeft() + scrollable->OffsetWidth() - 2,
626 scrollable->OffsetTop()));
627 scroll_begin.data.scroll_begin.delta_x_hint = 0;
628 scroll_begin.data.scroll_begin.delta_y_hint = 10;
629 scroll_begin.SetFrameScale(1);
630 GetEventHandler().HandleGestureScrollEvent(scroll_begin);
631 DCHECK(!GetEventHandler().IsScrollbarHandlingGestures());
632 bool should_update_capture = false;
633 DCHECK(!scrollable_area->VerticalScrollbar()->GestureEvent(
634 scroll_begin, &should_update_capture));
635 }
636
TEST_F(ScrollbarsTest,HidingScrollbarsOnScrollableAreaDisablesScrollbars)637 TEST_F(ScrollbarsTest, HidingScrollbarsOnScrollableAreaDisablesScrollbars) {
638 // This test is specifically checking the behavior when overlay scrollbars
639 // are enabled.
640 ENABLE_OVERLAY_SCROLLBARS(true);
641
642 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
643
644 SimRequest request("https://example.com/test.html", "text/html");
645 LoadURL("https://example.com/test.html");
646 request.Complete(R"HTML(
647 <!DOCTYPE html>
648 <style>
649 #scroller { overflow: scroll; width: 1000px; height: 1000px }
650 #spacer { width: 2000px; height: 2000px }
651 </style>
652 <div id='scroller'>
653 <div id='spacer'></div>
654 </div>
655 )HTML");
656 Compositor().BeginFrame();
657
658 Document& document = GetDocument();
659 LocalFrameView* frame_view = WebView().MainFrameImpl()->GetFrameView();
660 Element* scroller = document.getElementById("scroller");
661 auto* scroller_area = GetScrollableArea(*scroller);
662 ScrollableArea* frame_scroller_area = frame_view->LayoutViewport();
663
664 // Scrollbars are hidden at start.
665 scroller_area->SetScrollbarsHiddenForTesting(true);
666 frame_scroller_area->SetScrollbarsHiddenForTesting(true);
667 ASSERT_TRUE(scroller_area->HorizontalScrollbar());
668 ASSERT_TRUE(scroller_area->VerticalScrollbar());
669 ASSERT_TRUE(frame_scroller_area->HorizontalScrollbar());
670 ASSERT_TRUE(frame_scroller_area->VerticalScrollbar());
671
672 EXPECT_TRUE(frame_scroller_area->ScrollbarsHiddenIfOverlay());
673 EXPECT_FALSE(frame_scroller_area->HorizontalScrollbar()
674 ->ShouldParticipateInHitTesting());
675 EXPECT_FALSE(frame_scroller_area->VerticalScrollbar()
676 ->ShouldParticipateInHitTesting());
677
678 EXPECT_TRUE(scroller_area->ScrollbarsHiddenIfOverlay());
679 EXPECT_FALSE(
680 scroller_area->HorizontalScrollbar()->ShouldParticipateInHitTesting());
681 EXPECT_FALSE(
682 scroller_area->VerticalScrollbar()->ShouldParticipateInHitTesting());
683
684 frame_scroller_area->SetScrollbarsHiddenForTesting(false);
685 EXPECT_TRUE(frame_scroller_area->HorizontalScrollbar()
686 ->ShouldParticipateInHitTesting());
687 EXPECT_TRUE(frame_scroller_area->VerticalScrollbar()
688 ->ShouldParticipateInHitTesting());
689 frame_scroller_area->SetScrollbarsHiddenForTesting(true);
690 EXPECT_FALSE(frame_scroller_area->HorizontalScrollbar()
691 ->ShouldParticipateInHitTesting());
692 EXPECT_FALSE(frame_scroller_area->VerticalScrollbar()
693 ->ShouldParticipateInHitTesting());
694
695 scroller_area->SetScrollbarsHiddenForTesting(false);
696 EXPECT_TRUE(
697 scroller_area->HorizontalScrollbar()->ShouldParticipateInHitTesting());
698 EXPECT_TRUE(
699 scroller_area->VerticalScrollbar()->ShouldParticipateInHitTesting());
700 scroller_area->SetScrollbarsHiddenForTesting(true);
701 EXPECT_FALSE(
702 scroller_area->HorizontalScrollbar()->ShouldParticipateInHitTesting());
703 EXPECT_FALSE(
704 scroller_area->VerticalScrollbar()->ShouldParticipateInHitTesting());
705 }
706
707 // Ensure mouse cursor should be pointer when hovering over the scrollbar.
TEST_F(ScrollbarsTest,MouseOverScrollbarInCustomCursorElement)708 TEST_F(ScrollbarsTest, MouseOverScrollbarInCustomCursorElement) {
709 // Skip this test if scrollbars don't allow hit testing on the platform.
710 if (!WebView().GetPage()->GetScrollbarTheme().AllowsHitTest())
711 return;
712
713 WebView().MainFrameViewWidget()->Resize(gfx::Size(250, 250));
714
715 SimRequest request("https://example.com/test.html", "text/html");
716 LoadURL("https://example.com/test.html");
717 request.Complete(R"HTML(
718 <!DOCTYPE html>
719 <style>
720 body {
721 margin: 0;
722 }
723 #d1 {
724 width: 200px;
725 height: 200px;
726 overflow: auto;
727 cursor: move;
728 }
729 #d2 {
730 height: 400px;
731 }
732 </style>
733 <div id='d1'>
734 <div id='d2'></div>
735 </div>
736 )HTML");
737 Compositor().BeginFrame();
738
739 Document& document = GetDocument();
740
741 Element* div = document.getElementById("d1");
742
743 // Ensure hittest has DIV and scrollbar.
744 HitTestResult hit_test_result = HitTest(195, 5);
745
746 EXPECT_EQ(hit_test_result.InnerElement(), div);
747 EXPECT_TRUE(hit_test_result.GetScrollbar());
748
749 HandleMouseMoveEvent(195, 5);
750
751 EXPECT_EQ(ui::mojom::blink::CursorType::kPointer, CursorType());
752 }
753
754 // Ensure mouse cursor should be override when hovering over the custom
755 // scrollbar.
TEST_F(ScrollbarsTest,MouseOverCustomScrollbarInCustomCursorElement)756 TEST_F(ScrollbarsTest, MouseOverCustomScrollbarInCustomCursorElement) {
757 // Skip this test if scrollbars don't allow hit testing on the platform.
758 if (!WebView().GetPage()->GetScrollbarTheme().AllowsHitTest())
759 return;
760
761 WebView().MainFrameViewWidget()->Resize(gfx::Size(250, 250));
762
763 SimRequest request("https://example.com/test.html", "text/html");
764 LoadURL("https://example.com/test.html");
765 request.Complete(R"HTML(
766 <!DOCTYPE html>
767 <style>
768 body {
769 margin: 0;
770 }
771 #d1 {
772 width: 200px;
773 height: 200px;
774 overflow: auto;
775 cursor: move;
776 }
777 #d2 {
778 height: 400px;
779 }
780 ::-webkit-scrollbar {
781 background: none;
782 height: 5px;
783 width: 5px;
784 }
785 ::-webkit-scrollbar-thumb {
786 background-color: black;
787 }
788 </style>
789 <div id='d1'>
790 <div id='d2'></div>
791 </div>
792 )HTML");
793 Compositor().BeginFrame();
794
795 Document& document = GetDocument();
796
797 Element* div = document.getElementById("d1");
798
799 // Ensure hittest has DIV and scrollbar.
800 HitTestResult hit_test_result = HitTest(195, 5);
801
802 EXPECT_EQ(hit_test_result.InnerElement(), div);
803 EXPECT_TRUE(hit_test_result.GetScrollbar());
804
805 HandleMouseMoveEvent(195, 5);
806
807 EXPECT_EQ(ui::mojom::blink::CursorType::kMove, CursorType());
808 }
809
810 // Makes sure that mouse hover over an overlay scrollbar doesn't activate
811 // elements below (except the Element that owns the scrollbar) unless the
812 // scrollbar is faded out.
TEST_F(ScrollbarsTest,MouseOverLinkAndOverlayScrollbar)813 TEST_F(ScrollbarsTest, MouseOverLinkAndOverlayScrollbar) {
814 // This test is specifically checking the behavior when overlay scrollbars
815 // are enabled.
816 ENABLE_OVERLAY_SCROLLBARS(true);
817 // Skip this test if scrollbars don't allow hit testing on the platform.
818 if (!WebView().GetPage()->GetScrollbarTheme().AllowsHitTest())
819 return;
820
821 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
822
823 SimRequest request("https://example.com/test.html", "text/html");
824 LoadURL("https://example.com/test.html");
825 request.Complete(R"HTML(
826 <!DOCTYPE html>
827 <a id='a' href='javascript:void(0);' style='font-size: 20px'>
828 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
829 </a>
830 <div style='position: absolute; top: 1000px'>
831 end
832 </div>
833 )HTML");
834
835 Compositor().BeginFrame();
836
837 // Enable the Scrollbar.
838 WebView()
839 .MainFrameImpl()
840 ->GetFrameView()
841 ->LayoutViewport()
842 ->SetScrollbarsHiddenForTesting(false);
843
844 Document& document = GetDocument();
845 Element* a_tag = document.getElementById("a");
846
847 // This position is on scrollbar if it's enabled, or on the <a> element.
848 int x = 190;
849 int y = a_tag->OffsetTop();
850
851 // Ensure hittest only has scrollbar.
852 HitTestResult hit_test_result = HitTest(x, y);
853
854 EXPECT_FALSE(hit_test_result.URLElement());
855 EXPECT_TRUE(hit_test_result.InnerElement());
856 ASSERT_TRUE(hit_test_result.GetScrollbar());
857 EXPECT_FALSE(hit_test_result.GetScrollbar()->IsCustomScrollbar());
858
859 // Mouse over link. Mouse cursor should be hand.
860 HandleMouseMoveEvent(a_tag->OffsetLeft(), a_tag->OffsetTop());
861
862 EXPECT_EQ(ui::mojom::blink::CursorType::kHand, CursorType());
863
864 // Mouse over enabled overlay scrollbar. Mouse cursor should be pointer and no
865 // active hover element.
866 HandleMouseMoveEvent(x, y);
867
868 EXPECT_EQ(ui::mojom::blink::CursorType::kPointer, CursorType());
869
870 HandleMousePressEvent(x, y);
871
872 EXPECT_TRUE(document.GetActiveElement());
873 EXPECT_TRUE(document.HoverElement());
874
875 HandleMouseReleaseEvent(x, y);
876
877 // Mouse over disabled overlay scrollbar. Mouse cursor should be hand and has
878 // active hover element.
879 WebView()
880 .MainFrameImpl()
881 ->GetFrameView()
882 ->LayoutViewport()
883 ->SetScrollbarsHiddenForTesting(true);
884
885 // Ensure hittest only has link
886 hit_test_result = HitTest(x, y);
887
888 EXPECT_TRUE(hit_test_result.URLElement());
889 EXPECT_TRUE(hit_test_result.InnerElement());
890 EXPECT_FALSE(hit_test_result.GetScrollbar());
891
892 HandleMouseMoveEvent(x, y);
893
894 EXPECT_EQ(ui::mojom::blink::CursorType::kHand, CursorType());
895
896 HandleMousePressEvent(x, y);
897
898 EXPECT_TRUE(document.GetActiveElement());
899 EXPECT_TRUE(document.HoverElement());
900 }
901
902 // Makes sure that mouse hover over an custom scrollbar doesn't change the
903 // activate elements.
TEST_F(ScrollbarsTest,MouseOverCustomScrollbar)904 TEST_F(ScrollbarsTest, MouseOverCustomScrollbar) {
905 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
906
907 SimRequest request("https://example.com/test.html", "text/html");
908 LoadURL("https://example.com/test.html");
909 request.Complete(R"HTML(
910 <!DOCTYPE html>
911 <style>
912 #scrollbar {
913 position: absolute;
914 top: 0;
915 left: 0;
916 height: 180px;
917 width: 180px;
918 overflow-x: auto;
919 }
920 ::-webkit-scrollbar {
921 width: 8px;
922 }
923 ::-webkit-scrollbar-thumb {
924 background-color: hsla(0, 0%, 56%, 0.6);
925 }
926 </style>
927 <div id='scrollbar'>
928 <div style='position: absolute; top: 1000px;'>
929 make scrollbar show
930 </div>
931 </div>
932 )HTML");
933
934 Compositor().BeginFrame();
935
936 Document& document = GetDocument();
937
938 Element* scrollbar_div = document.getElementById("scrollbar");
939 EXPECT_TRUE(scrollbar_div);
940
941 // Ensure hittest only has DIV
942 HitTestResult hit_test_result = HitTest(1, 1);
943
944 EXPECT_TRUE(hit_test_result.InnerElement());
945 EXPECT_FALSE(hit_test_result.GetScrollbar());
946
947 // Mouse over DIV
948 HandleMouseMoveEvent(1, 1);
949
950 // DIV :hover
951 EXPECT_EQ(document.HoverElement(), scrollbar_div);
952
953 // Ensure hittest has DIV and scrollbar
954 hit_test_result = HitTest(175, 1);
955
956 EXPECT_TRUE(hit_test_result.InnerElement());
957 EXPECT_TRUE(hit_test_result.GetScrollbar());
958 EXPECT_TRUE(hit_test_result.GetScrollbar()->IsCustomScrollbar());
959
960 // Mouse over scrollbar
961 HandleMouseMoveEvent(175, 1);
962
963 // Custom not change the DIV :hover
964 EXPECT_EQ(document.HoverElement(), scrollbar_div);
965 EXPECT_EQ(hit_test_result.GetScrollbar()->HoveredPart(),
966 ScrollbarPart::kThumbPart);
967 }
968
969 // Makes sure that mouse hover over an overlay scrollbar doesn't hover iframe
970 // below.
TEST_F(ScrollbarsTest,MouseOverScrollbarAndIFrame)971 TEST_F(ScrollbarsTest, MouseOverScrollbarAndIFrame) {
972 // This test is specifically checking the behavior when overlay scrollbars
973 // are enabled.
974 ENABLE_OVERLAY_SCROLLBARS(true);
975 // Skip this test if scrollbars don't allow hit testing on the platform.
976 if (!WebView().GetPage()->GetScrollbarTheme().AllowsHitTest())
977 return;
978
979 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
980
981 SimRequest main_resource("https://example.com/", "text/html");
982 SimRequest frame_resource("https://example.com/iframe.html", "text/html");
983 LoadURL("https://example.com/");
984 main_resource.Complete(R"HTML(
985 <!DOCTYPE html>
986 <style>
987 body {
988 margin: 0;
989 height: 2000px;
990 }
991 iframe {
992 height: 200px;
993 width: 200px;
994 }
995 </style>
996 <iframe id='iframe' src='iframe.html'>
997 </iframe>
998 )HTML");
999 Compositor().BeginFrame();
1000
1001 // Enable the Scrollbar.
1002 WebView()
1003 .MainFrameImpl()
1004 ->GetFrameView()
1005 ->LayoutViewport()
1006 ->SetScrollbarsHiddenForTesting(false);
1007
1008 frame_resource.Complete("<!DOCTYPE html>");
1009 Compositor().BeginFrame();
1010
1011 Document& document = GetDocument();
1012 Element* iframe = document.getElementById("iframe");
1013 DCHECK(iframe);
1014
1015 // Ensure hittest only has IFRAME.
1016 HitTestResult hit_test_result = HitTest(5, 5);
1017
1018 EXPECT_TRUE(hit_test_result.InnerElement());
1019 EXPECT_FALSE(hit_test_result.GetScrollbar());
1020
1021 // Mouse over IFRAME.
1022 HandleMouseMoveEvent(5, 5);
1023
1024 // IFRAME hover.
1025 EXPECT_EQ(document.HoverElement(), iframe);
1026
1027 // Ensure hittest has scrollbar.
1028 hit_test_result = HitTest(195, 5);
1029 EXPECT_TRUE(hit_test_result.InnerElement());
1030 EXPECT_TRUE(hit_test_result.GetScrollbar());
1031 EXPECT_TRUE(hit_test_result.GetScrollbar()->Enabled());
1032
1033 // Mouse over scrollbar.
1034 HandleMouseMoveEvent(195, 5);
1035
1036 // IFRAME not hover.
1037 EXPECT_NE(document.HoverElement(), iframe);
1038
1039 // Disable the Scrollbar.
1040 WebView()
1041 .MainFrameImpl()
1042 ->GetFrameView()
1043 ->LayoutViewport()
1044 ->SetScrollbarsHiddenForTesting(true);
1045
1046 // Ensure hittest has IFRAME and no scrollbar.
1047 hit_test_result = HitTest(196, 5);
1048
1049 EXPECT_TRUE(hit_test_result.InnerElement());
1050 EXPECT_FALSE(hit_test_result.GetScrollbar());
1051
1052 // Mouse over disabled scrollbar.
1053 HandleMouseMoveEvent(196, 5);
1054
1055 // IFRAME hover.
1056 EXPECT_EQ(document.HoverElement(), iframe);
1057 }
1058
1059 // Makes sure that mouse hover over a scrollbar also hover the element owns the
1060 // scrollbar.
TEST_F(ScrollbarsTest,MouseOverScrollbarAndParentElement)1061 TEST_F(ScrollbarsTest, MouseOverScrollbarAndParentElement) {
1062 // This test requires that scrollbars take up space.
1063 ENABLE_OVERLAY_SCROLLBARS(false);
1064
1065 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1066
1067 SimRequest request("https://example.com/test.html", "text/html");
1068 LoadURL("https://example.com/test.html");
1069 request.Complete(R"HTML(
1070 <!DOCTYPE html>
1071 <style>
1072 #parent {
1073 position: absolute;
1074 top: 0;
1075 left: 0;
1076 height: 180px;
1077 width: 180px;
1078 overflow-y: scroll;
1079 }
1080 </style>
1081 <div id='parent'>
1082 <div id='child' style='position: absolute; top: 1000px;'>
1083 make scrollbar enabled
1084 </div>
1085 </div>
1086 )HTML");
1087
1088 Compositor().BeginFrame();
1089
1090 Document& document = GetDocument();
1091
1092 Element* parent_div = document.getElementById("parent");
1093 Element* child_div = document.getElementById("child");
1094 EXPECT_TRUE(parent_div);
1095 EXPECT_TRUE(child_div);
1096
1097 auto* scrollable_area = GetScrollableArea(*parent_div);
1098
1099 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
1100 EXPECT_FALSE(scrollable_area->VerticalScrollbar()->IsOverlayScrollbar());
1101
1102 // Ensure hittest only has DIV.
1103 HitTestResult hit_test_result = HitTest(1, 1);
1104
1105 EXPECT_TRUE(hit_test_result.InnerElement());
1106 EXPECT_FALSE(hit_test_result.GetScrollbar());
1107
1108 // Mouse over DIV.
1109 HandleMouseMoveEvent(1, 1);
1110
1111 // DIV :hover.
1112 EXPECT_EQ(document.HoverElement(), parent_div);
1113
1114 // Ensure hittest has DIV and scrollbar.
1115 hit_test_result = HitTest(175, 5);
1116
1117 EXPECT_TRUE(hit_test_result.InnerElement());
1118 EXPECT_TRUE(hit_test_result.GetScrollbar());
1119 EXPECT_FALSE(hit_test_result.GetScrollbar()->IsCustomScrollbar());
1120 EXPECT_TRUE(hit_test_result.GetScrollbar()->Enabled());
1121
1122 // Mouse over scrollbar.
1123 HandleMouseMoveEvent(175, 5);
1124
1125 // Not change the DIV :hover.
1126 EXPECT_EQ(document.HoverElement(), parent_div);
1127
1128 // Disable the Scrollbar by remove the childDiv.
1129 child_div->remove();
1130 Compositor().BeginFrame();
1131
1132 // Ensure hittest has DIV and no scrollbar.
1133 hit_test_result = HitTest(175, 5);
1134
1135 EXPECT_TRUE(hit_test_result.InnerElement());
1136 EXPECT_TRUE(hit_test_result.GetScrollbar());
1137 EXPECT_FALSE(hit_test_result.GetScrollbar()->Enabled());
1138 EXPECT_LT(hit_test_result.InnerElement()->clientWidth(), 180);
1139
1140 // Mouse over disabled scrollbar.
1141 HandleMouseMoveEvent(175, 5);
1142
1143 // Not change the DIV :hover.
1144 EXPECT_EQ(document.HoverElement(), parent_div);
1145 }
1146
1147 // Makes sure that mouse over a root scrollbar also hover the html element.
TEST_F(ScrollbarsTest,MouseOverRootScrollbar)1148 TEST_F(ScrollbarsTest, MouseOverRootScrollbar) {
1149 // Skip this test if scrollbars don't allow hit testing on the platform.
1150 if (!WebView().GetPage()->GetScrollbarTheme().AllowsHitTest())
1151 return;
1152
1153 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1154
1155 SimRequest request("https://example.com/test.html", "text/html");
1156 LoadURL("https://example.com/test.html");
1157 request.Complete(R"HTML(
1158 <!DOCTYPE html>
1159 <style>
1160 body {
1161 overflow: scroll;
1162 }
1163 </style>
1164 )HTML");
1165
1166 Compositor().BeginFrame();
1167
1168 Document& document = GetDocument();
1169
1170 // Ensure hittest has <html> element and scrollbar.
1171 HitTestResult hit_test_result = HitTest(195, 5);
1172
1173 EXPECT_TRUE(hit_test_result.InnerElement());
1174 EXPECT_EQ(hit_test_result.InnerElement(), document.documentElement());
1175 EXPECT_TRUE(hit_test_result.GetScrollbar());
1176
1177 // Mouse over scrollbar.
1178 HandleMouseMoveEvent(195, 5);
1179
1180 // Hover <html element.
1181 EXPECT_EQ(document.HoverElement(), document.documentElement());
1182 }
1183
TEST_F(ScrollbarsTest,MouseReleaseUpdatesScrollbarHoveredPart)1184 TEST_F(ScrollbarsTest, MouseReleaseUpdatesScrollbarHoveredPart) {
1185 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1186
1187 SimRequest request("https://example.com/test.html", "text/html");
1188 LoadURL("https://example.com/test.html");
1189 request.Complete(R"HTML(
1190 <!DOCTYPE html>
1191 <style>
1192 #scrollbar {
1193 position: absolute;
1194 top: 0;
1195 left: 0;
1196 height: 180px;
1197 width: 180px;
1198 overflow-x: auto;
1199 }
1200 ::-webkit-scrollbar {
1201 width: 8px;
1202 }
1203 ::-webkit-scrollbar-thumb {
1204 background-color: hsla(0, 0%, 56%, 0.6);
1205 }
1206 </style>
1207 <div id='scrollbar'>
1208 <div style='position: absolute; top: 1000px;'>make scrollbar
1209 shows</div>
1210 </div>
1211 )HTML");
1212
1213 Compositor().BeginFrame();
1214
1215 Document& document = GetDocument();
1216
1217 Element* scrollbar_div = document.getElementById("scrollbar");
1218 EXPECT_TRUE(scrollbar_div);
1219
1220 auto* scrollable_area = GetScrollableArea(*scrollbar_div);
1221
1222 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
1223 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
1224 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1225 EXPECT_EQ(scrollbar->HoveredPart(), ScrollbarPart::kNoPart);
1226
1227 // Mouse moved over the scrollbar.
1228 HandleMouseMoveEvent(175, 1);
1229 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1230 EXPECT_EQ(scrollbar->HoveredPart(), ScrollbarPart::kThumbPart);
1231
1232 // Mouse pressed.
1233 HandleMousePressEvent(175, 1);
1234 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
1235 EXPECT_EQ(scrollbar->HoveredPart(), ScrollbarPart::kThumbPart);
1236
1237 // Mouse moved off the scrollbar while still pressed.
1238 HandleMouseLeaveEvent();
1239 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
1240 EXPECT_EQ(scrollbar->HoveredPart(), ScrollbarPart::kThumbPart);
1241
1242 // Mouse released.
1243 HandleMouseReleaseEvent(1, 1);
1244 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1245 EXPECT_EQ(scrollbar->HoveredPart(), ScrollbarPart::kNoPart);
1246 }
1247
TEST_F(ScrollbarsTest,ContextMenuUpdatesScrollbarPressedPart)1248 TEST_F(ScrollbarsTest, ContextMenuUpdatesScrollbarPressedPart) {
1249 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1250
1251 SimRequest request("https://example.com/test.html", "text/html");
1252 LoadURL("https://example.com/test.html");
1253 request.Complete(R"HTML(
1254 <!DOCTYPE html>
1255 <style>
1256 body { margin: 0px }
1257 #scroller { overflow-x: auto; width: 180px; height: 100px }
1258 #spacer { height: 300px }
1259 ::-webkit-scrollbar { width: 8px }
1260 ::-webkit-scrollbar-thumb {
1261 background-color: hsla(0, 0%, 56%, 0.6)
1262 }
1263 </style>
1264 <div id='scroller'>
1265 <div id='spacer'></div>
1266 </div>
1267 )HTML");
1268
1269 Compositor().BeginFrame();
1270
1271 Document& document = GetDocument();
1272
1273 Element* scrollbar_div = document.getElementById("scroller");
1274 EXPECT_TRUE(scrollbar_div);
1275
1276 auto* scrollable_area = GetScrollableArea(*scrollbar_div);
1277
1278 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
1279 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
1280 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1281
1282 // Mouse moved over the scrollbar.
1283 HandleMouseMoveEvent(175, 5);
1284 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1285
1286 // Press the scrollbar.
1287 HandleMousePressEvent(175, 5);
1288 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
1289
1290 // ContextMenu while still pressed.
1291 HandleContextMenuEvent(175, 5);
1292 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1293
1294 // Mouse moved off the scrollbar.
1295 HandleMousePressEvent(50, 5);
1296 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
1297 }
1298
TEST_F(ScrollbarsTest,CustomScrollbarInOverlayScrollbarThemeWillNotCauseDCHECKFails)1299 TEST_F(ScrollbarsTest,
1300 CustomScrollbarInOverlayScrollbarThemeWillNotCauseDCHECKFails) {
1301 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1302
1303 SimRequest request("https://example.com/test.html", "text/html");
1304 LoadURL("https://example.com/test.html");
1305 request.Complete(R"HTML(
1306 <!DOCTYPE html>
1307 <style type='text/css'>
1308 ::-webkit-scrollbar {
1309 width: 16px;
1310 height: 16px;
1311 overflow: visible;
1312 }
1313 div {
1314 width: 1000px;
1315 }
1316 </style>
1317 <div style='position: absolute; top: 1000px;'>
1318 end
1319 </div>
1320 )HTML");
1321
1322 // No DCHECK Fails. Issue 676678.
1323 Compositor().BeginFrame();
1324 }
1325
1326 // Make sure root custom scrollbar can change by Emulator but div custom
1327 // scrollbar not.
TEST_F(ScrollbarsTest,CustomScrollbarChangeToMobileByEmulator)1328 TEST_F(ScrollbarsTest, CustomScrollbarChangeToMobileByEmulator) {
1329 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1330
1331 SimRequest request("https://example.com/test.html", "text/html");
1332 LoadURL("https://example.com/test.html");
1333 request.Complete(R"HTML(
1334 <!DOCTYPE html>
1335 <style type='text/css'>
1336 body {
1337 height: 10000px;
1338 margin: 0;
1339 }
1340 #d1 {
1341 height: 200px;
1342 width: 200px;
1343 overflow: auto;
1344 }
1345 #d2 {
1346 height: 2000px;
1347 }
1348 ::-webkit-scrollbar {
1349 width: 10px;
1350 }
1351 </style>
1352 <div id='d1'>
1353 <div id='d2'/>
1354 </div>
1355 )HTML");
1356
1357 Compositor().BeginFrame();
1358
1359 Document& document = GetDocument();
1360
1361 ScrollableArea* root_scrollable = document.View()->LayoutViewport();
1362
1363 Element* div = document.getElementById("d1");
1364
1365 auto* div_scrollable = GetScrollableArea(*div);
1366
1367 VisualViewport& viewport = WebView().GetPage()->GetVisualViewport();
1368
1369 DCHECK(root_scrollable->VerticalScrollbar());
1370 DCHECK(root_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1371 DCHECK(!root_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1372 DCHECK(!root_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1373
1374 DCHECK(!viewport.LayerForHorizontalScrollbar());
1375
1376 DCHECK(div_scrollable->VerticalScrollbar());
1377 DCHECK(div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1378 DCHECK(!div_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1379 DCHECK(!div_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1380
1381 // Turn on mobile emulator.
1382 DeviceEmulationParams params;
1383 params.screen_type = mojom::EmulatedScreenType::kMobile;
1384 WebView().EnableDeviceEmulation(params);
1385
1386 // For root Scrollbar, mobile emulator will change them to page VisualViewport
1387 // scrollbar layer.
1388 EXPECT_TRUE(viewport.LayerForVerticalScrollbar());
1389 EXPECT_FALSE(root_scrollable->VerticalScrollbar());
1390
1391 EXPECT_TRUE(div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1392
1393 // Turn off mobile emulator.
1394 WebView().DisableDeviceEmulation();
1395
1396 EXPECT_TRUE(root_scrollable->VerticalScrollbar());
1397 EXPECT_TRUE(root_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1398 EXPECT_FALSE(root_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1399 EXPECT_FALSE(root_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1400
1401 DCHECK(!viewport.LayerForHorizontalScrollbar());
1402
1403 EXPECT_TRUE(div_scrollable->VerticalScrollbar());
1404 EXPECT_TRUE(div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1405 EXPECT_FALSE(div_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1406 EXPECT_FALSE(div_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1407 }
1408
1409 // Ensure custom scrollbar recreate when style owner change,
TEST_F(ScrollbarsTest,CustomScrollbarWhenStyleOwnerChange)1410 TEST_F(ScrollbarsTest, CustomScrollbarWhenStyleOwnerChange) {
1411 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1412
1413 SimRequest request("https://example.com/test.html", "text/html");
1414 LoadURL("https://example.com/test.html");
1415 request.Complete(R"HTML(
1416 <!DOCTYPE html>
1417 <style type='text/css'>
1418 #d1 {
1419 height: 200px;
1420 width: 200px;
1421 overflow: auto;
1422 }
1423 #d2 {
1424 height: 2000px;
1425 }
1426 ::-webkit-scrollbar {
1427 width: 10px;
1428 }
1429 .custom::-webkit-scrollbar {
1430 width: 5px;
1431 }
1432 </style>
1433 <div id='d1'>
1434 <div id='d2'></div>
1435 </div>
1436 )HTML");
1437
1438 Compositor().BeginFrame();
1439
1440 Document& document = GetDocument();
1441
1442 Element* div = document.getElementById("d1");
1443
1444 auto* div_scrollable = GetScrollableArea(*div);
1445
1446 DCHECK(div_scrollable->VerticalScrollbar());
1447 DCHECK(div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1448 DCHECK_EQ(div_scrollable->VerticalScrollbar()->Width(), 10);
1449 DCHECK(!div_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1450 DCHECK(!div_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1451
1452 div->setAttribute(html_names::kClassAttr, "custom");
1453 Compositor().BeginFrame();
1454
1455 EXPECT_TRUE(div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1456 EXPECT_EQ(div_scrollable->VerticalScrollbar()->Width(), 5);
1457 }
1458
1459 // Make sure overlay scrollbars on non-composited scrollers fade out and set
1460 // the hidden bit as needed.
1461 // To avoid TSAN/ASAN race issue, this test use Virtual Time and give scrollbar
1462 // a huge fadeout delay.
1463 // Disable on Android since VirtualTime not work for Android.
1464 // http://crbug.com/633321
1465 #if defined(OS_ANDROID)
TEST_F(ScrollbarsTestWithVirtualTimer,DISABLED_TestNonCompositedOverlayScrollbarsFade)1466 TEST_F(ScrollbarsTestWithVirtualTimer,
1467 DISABLED_TestNonCompositedOverlayScrollbarsFade) {
1468 #else
1469 TEST_F(ScrollbarsTestWithVirtualTimer, TestNonCompositedOverlayScrollbarsFade) {
1470 #endif
1471 // This test relies on mock overlay scrollbars.
1472 ScopedMockOverlayScrollbars mock_overlay_scrollbars(true);
1473
1474 TimeAdvance();
1475 constexpr base::TimeDelta kMockOverlayFadeOutDelay =
1476 base::TimeDelta::FromSeconds(5);
1477
1478 ScrollbarTheme& theme = GetScrollbarTheme();
1479 ASSERT_TRUE(theme.IsMockTheme());
1480 ASSERT_TRUE(theme.UsesOverlayScrollbars());
1481 ScrollbarThemeOverlayMock& mock_overlay_theme =
1482 static_cast<ScrollbarThemeOverlayMock&>(theme);
1483 mock_overlay_theme.SetOverlayScrollbarFadeOutDelay(kMockOverlayFadeOutDelay);
1484
1485 WebView().MainFrameViewWidget()->Resize(gfx::Size(640, 480));
1486 SimRequest request("https://example.com/test.html", "text/html");
1487 LoadURL("https://example.com/test.html");
1488 RunTasksForPeriod(kMockOverlayFadeOutDelay);
1489 request.Complete(R"HTML(
1490 <!DOCTYPE html>
1491 <style>
1492 #space {
1493 width: 1000px;
1494 height: 1000px;
1495 }
1496 #container {
1497 width: 200px;
1498 height: 200px;
1499 overflow: scroll;
1500 /* Ensure the scroller is non-composited. */
1501 border: border: 2px solid;
1502 border-radius: 25px;
1503 }
1504 div { height:1000px; width: 200px; }
1505 </style>
1506 <div id='container'>
1507 <div id='space'></div>
1508 </div>
1509 )HTML");
1510 Compositor().BeginFrame();
1511
1512 Document& document = GetDocument();
1513 Element* container = document.getElementById("container");
1514 auto* scrollable_area = GetScrollableArea(*container);
1515
1516 DCHECK(!scrollable_area->UsesCompositedScrolling());
1517
1518 EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay());
1519 RunTasksForPeriod(kMockOverlayFadeOutDelay);
1520 EXPECT_TRUE(scrollable_area->ScrollbarsHiddenIfOverlay());
1521
1522 scrollable_area->SetScrollOffset(ScrollOffset(10, 10),
1523 mojom::blink::ScrollType::kProgrammatic,
1524 mojom::blink::ScrollBehavior::kInstant);
1525
1526 EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay());
1527 RunTasksForPeriod(kMockOverlayFadeOutDelay);
1528 EXPECT_TRUE(scrollable_area->ScrollbarsHiddenIfOverlay());
1529
1530 MainFrame().ExecuteScript(WebScriptSource(
1531 "document.getElementById('space').style.height = '500px';"));
1532 Compositor().BeginFrame();
1533
1534 EXPECT_TRUE(scrollable_area->ScrollbarsHiddenIfOverlay());
1535
1536 MainFrame().ExecuteScript(WebScriptSource(
1537 "document.getElementById('container').style.height = '300px';"));
1538 Compositor().BeginFrame();
1539
1540 EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay());
1541 RunTasksForPeriod(kMockOverlayFadeOutDelay);
1542 EXPECT_TRUE(scrollable_area->ScrollbarsHiddenIfOverlay());
1543
1544 // Non-composited scrollbars don't fade out while mouse is over.
1545 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
1546 scrollable_area->SetScrollOffset(ScrollOffset(20, 20),
1547 mojom::blink::ScrollType::kProgrammatic,
1548 mojom::blink::ScrollBehavior::kInstant);
1549 EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay());
1550 scrollable_area->MouseEnteredScrollbar(*scrollable_area->VerticalScrollbar());
1551 RunTasksForPeriod(kMockOverlayFadeOutDelay);
1552 EXPECT_FALSE(scrollable_area->ScrollbarsHiddenIfOverlay());
1553 scrollable_area->MouseExitedScrollbar(*scrollable_area->VerticalScrollbar());
1554 RunTasksForPeriod(kMockOverlayFadeOutDelay);
1555 EXPECT_TRUE(scrollable_area->ScrollbarsHiddenIfOverlay());
1556
1557 mock_overlay_theme.SetOverlayScrollbarFadeOutDelay(base::TimeDelta());
1558 }
1559
1560 TEST_F(ScrollbarsTestWithVirtualTimer, TestCompositedOverlayScrollbarsNoFade) {
1561 ENABLE_OVERLAY_SCROLLBARS(true);
1562
1563 WebView().MainFrameViewWidget()->Resize(gfx::Size(640, 480));
1564 SimRequest request("https://example.com/test.html", "text/html");
1565 LoadURL("https://example.com/test.html");
1566
1567 request.Complete(R"HTML(
1568 <!DOCTYPE html>
1569 <style>
1570 #space {
1571 width: 1000px;
1572 height: 1000px;
1573 }
1574 #container {
1575 /* Force composited scrolling */
1576 will-change: transform;
1577 width: 200px;
1578 height: 200px;
1579 overflow: scroll;
1580 }
1581 div { height:1000px; width: 200px; }
1582 </style>
1583 <div id='container'>
1584 <div id='space'></div>
1585 </div>
1586 )HTML");
1587 Compositor().BeginFrame();
1588
1589 Document& document = GetDocument();
1590 Element* container = document.getElementById("container");
1591 auto* scrollable_area = GetScrollableArea(*container);
1592
1593 DCHECK(scrollable_area->UsesCompositedScrolling());
1594 EXPECT_TRUE(scrollable_area->HasOverlayScrollbars());
1595
1596 EXPECT_TRUE(scrollable_area->HasLayerForVerticalScrollbar());
1597 Scrollbar* vertical_scrollbar = scrollable_area->VerticalScrollbar();
1598
1599 scrollable_area->MouseEnteredScrollbar(*vertical_scrollbar);
1600 EXPECT_FALSE(scrollable_area->NeedsShowScrollbarLayers());
1601
1602 scrollable_area->MouseExitedScrollbar(*vertical_scrollbar);
1603 EXPECT_FALSE(scrollable_area->NeedsShowScrollbarLayers());
1604
1605 scrollable_area->MouseCapturedScrollbar();
1606 EXPECT_FALSE(scrollable_area->NeedsShowScrollbarLayers());
1607
1608 scrollable_area->MouseReleasedScrollbar();
1609 EXPECT_FALSE(scrollable_area->NeedsShowScrollbarLayers());
1610 }
1611
1612 class ScrollbarAppearanceTest
1613 : public ScrollbarsTest,
1614 public testing::WithParamInterface</*use_overlay_scrollbars=*/bool> {};
1615
1616 // Test both overlay and non-overlay scrollbars.
1617 INSTANTIATE_TEST_SUITE_P(All, ScrollbarAppearanceTest, testing::Bool());
1618
1619 // Make sure native scrollbar can change by Emulator.
1620 // Disable on Android since Android always enable OverlayScrollbar.
1621 #if defined(OS_ANDROID)
1622 TEST_P(ScrollbarAppearanceTest,
1623 DISABLED_NativeScrollbarChangeToMobileByEmulator) {
1624 #else
1625 TEST_P(ScrollbarAppearanceTest, NativeScrollbarChangeToMobileByEmulator) {
1626 #endif
1627 bool use_overlay_scrollbar = GetParam();
1628 ENABLE_OVERLAY_SCROLLBARS(use_overlay_scrollbar);
1629
1630 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
1631
1632 SimRequest request("https://example.com/test.html", "text/html");
1633 LoadURL("https://example.com/test.html");
1634 request.Complete(R"HTML(
1635 <!DOCTYPE html>
1636 <style type='text/css'>
1637 body {
1638 height: 10000px;
1639 margin: 0;
1640 }
1641 #d1 {
1642 height: 200px;
1643 width: 200px;
1644 overflow: auto;
1645 }
1646 #d2 {
1647 height: 2000px;
1648 }
1649 </style>
1650 <!-- flex creates DelayScrollOffsetClampScope to increase test coverge -->
1651 <div style='display: flex'>
1652 <div id='d1'>
1653 <div id='d2'/>
1654 </div>
1655 </div>
1656 )HTML");
1657
1658 Compositor().BeginFrame();
1659
1660 Document& document = GetDocument();
1661
1662 ScrollableArea* root_scrollable = document.View()->LayoutViewport();
1663
1664 Element* div = document.getElementById("d1");
1665
1666 auto* div_scrollable = GetScrollableArea(*div);
1667
1668 VisualViewport& viewport = WebView().GetPage()->GetVisualViewport();
1669
1670 DCHECK(root_scrollable->VerticalScrollbar());
1671 DCHECK(!root_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1672 DCHECK_EQ(use_overlay_scrollbar,
1673 root_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1674 DCHECK(!root_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1675
1676 DCHECK(!viewport.LayerForHorizontalScrollbar());
1677
1678 DCHECK(div_scrollable->VerticalScrollbar());
1679 DCHECK(!div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1680 DCHECK_EQ(use_overlay_scrollbar,
1681 div_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1682 DCHECK(!div_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1683
1684 // Turn on mobile emulator.
1685 DeviceEmulationParams params;
1686 params.screen_type = mojom::EmulatedScreenType::kMobile;
1687 WebView().EnableDeviceEmulation(params);
1688
1689 // For root Scrollbar, mobile emulator will change them to page VisualViewport
1690 // scrollbar layer.
1691 EXPECT_TRUE(viewport.LayerForHorizontalScrollbar());
1692
1693 // Ensure div scrollbar also change to mobile overlay theme.
1694 EXPECT_TRUE(div_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1695 EXPECT_TRUE(div_scrollable->VerticalScrollbar()->IsSolidColor());
1696
1697 // Turn off mobile emulator.
1698 WebView().DisableDeviceEmulation();
1699
1700 EXPECT_TRUE(root_scrollable->VerticalScrollbar());
1701 EXPECT_FALSE(root_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1702 DCHECK_EQ(use_overlay_scrollbar,
1703 root_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1704 EXPECT_FALSE(root_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1705
1706 DCHECK(!viewport.LayerForHorizontalScrollbar());
1707
1708 EXPECT_TRUE(div_scrollable->VerticalScrollbar());
1709 EXPECT_FALSE(div_scrollable->VerticalScrollbar()->IsCustomScrollbar());
1710 DCHECK_EQ(use_overlay_scrollbar,
1711 div_scrollable->VerticalScrollbar()->IsOverlayScrollbar());
1712 EXPECT_FALSE(div_scrollable->VerticalScrollbar()->GetTheme().IsMockTheme());
1713 }
1714
1715 #if !defined(OS_MAC)
1716 // Ensure that the minimum length for a scrollbar thumb comes from the
1717 // WebThemeEngine. Note, Mac scrollbars differ from all other platforms so this
1718 // test doesn't apply there. https://crbug.com/682209.
1719 TEST_P(ScrollbarAppearanceTest, ThemeEngineDefinesMinimumThumbLength) {
1720 ScopedTestingPlatformSupport<ScrollbarTestingPlatformSupport> platform;
1721 ENABLE_OVERLAY_SCROLLBARS(GetParam());
1722
1723 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
1724 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1725 SimRequest request("https://example.com/test.html", "text/html");
1726 LoadURL("https://example.com/test.html");
1727 request.Complete(R"HTML(
1728 <!DOCTYPE html>
1729 <style> body { width: 1000000px; height: 1000000px; } </style>)HTML");
1730 ScrollableArea* scrollable_area = GetDocument().View()->LayoutViewport();
1731
1732 Compositor().BeginFrame();
1733 ASSERT_TRUE(scrollable_area->VerticalScrollbar());
1734 ASSERT_TRUE(scrollable_area->HorizontalScrollbar());
1735
1736 ScrollbarTheme& theme = scrollable_area->VerticalScrollbar()->GetTheme();
1737 EXPECT_EQ(StubWebThemeEngine::kMinimumHorizontalLength,
1738 theme.ThumbLength(*scrollable_area->HorizontalScrollbar()));
1739 EXPECT_EQ(StubWebThemeEngine::kMinimumVerticalLength,
1740 theme.ThumbLength(*scrollable_area->VerticalScrollbar()));
1741 }
1742
1743 // Ensure thumb position is correctly calculated even at ridiculously large
1744 // scales.
1745 TEST_P(ScrollbarAppearanceTest, HugeScrollingThumbPosition) {
1746 ScopedTestingPlatformSupport<ScrollbarTestingPlatformSupport> platform;
1747 ENABLE_OVERLAY_SCROLLBARS(GetParam());
1748
1749 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
1750 WebView().MainFrameViewWidget()->Resize(gfx::Size(1000, 1000));
1751 SimRequest request("https://example.com/test.html", "text/html");
1752 LoadURL("https://example.com/test.html");
1753 request.Complete(R"HTML(
1754 <!DOCTYPE html>
1755 <style> body { margin: 0px; height: 10000000px; } </style>)HTML");
1756 ScrollableArea* scrollable_area = GetDocument().View()->LayoutViewport();
1757
1758 Compositor().BeginFrame();
1759
1760 scrollable_area->SetScrollOffset(ScrollOffset(0, 10000000),
1761 mojom::blink::ScrollType::kProgrammatic);
1762
1763 Compositor().BeginFrame();
1764
1765 int scroll_y = scrollable_area->GetScrollOffset().Height();
1766 ASSERT_EQ(9999000, scroll_y);
1767
1768 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
1769 ASSERT_TRUE(scrollbar);
1770
1771 int max_thumb_position = WebView().MainFrameViewWidget()->Size().height() -
1772 StubWebThemeEngine::kMinimumVerticalLength;
1773 max_thumb_position -=
1774 scrollbar->GetTheme().ScrollbarMargin(scrollbar->ScaleFromDIP()) * 2;
1775
1776 EXPECT_EQ(max_thumb_position,
1777 scrollbar->GetTheme().ThumbPosition(*scrollbar));
1778 }
1779 #endif
1780
1781 // A body with width just under the window width should not have scrollbars.
1782 TEST_F(ScrollbarsTest, WideBodyShouldNotHaveScrollbars) {
1783 // This test requires that scrollbars take up space.
1784 ENABLE_OVERLAY_SCROLLBARS(false);
1785
1786 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1787 SimRequest request("https://example.com/test.html", "text/html");
1788 LoadURL("https://example.com/test.html");
1789 request.Complete(R"HTML(
1790 <!DOCTYPE html>
1791 <style>
1792 body {
1793 margin: 0;
1794 background: blue;
1795 height: 10px;
1796 width: 799px;
1797 }
1798 )HTML");
1799 Compositor().BeginFrame();
1800 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1801 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
1802 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
1803 }
1804
1805 // A body with height just under the window height should not have scrollbars.
1806 TEST_F(ScrollbarsTest, TallBodyShouldNotHaveScrollbars) {
1807 // This test requires that scrollbars take up space.
1808 ENABLE_OVERLAY_SCROLLBARS(false);
1809
1810 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1811 SimRequest request("https://example.com/test.html", "text/html");
1812 LoadURL("https://example.com/test.html");
1813 request.Complete(R"HTML(
1814 <!DOCTYPE html>
1815 <style>
1816 body {
1817 margin: 0;
1818 background: blue;
1819 height: 599px;
1820 width: 10px;
1821 }
1822 )HTML");
1823 Compositor().BeginFrame();
1824 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1825 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
1826 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
1827 }
1828
1829 // A body with dimensions just barely inside the window dimensions should not
1830 // have scrollbars.
1831 TEST_F(ScrollbarsTest, TallAndWideBodyShouldNotHaveScrollbars) {
1832 // This test requires that scrollbars take up space.
1833 ENABLE_OVERLAY_SCROLLBARS(false);
1834
1835 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1836 SimRequest request("https://example.com/test.html", "text/html");
1837 LoadURL("https://example.com/test.html");
1838 request.Complete(R"HTML(
1839 <!DOCTYPE html>
1840 <style>
1841 body {
1842 margin: 0;
1843 background: blue;
1844 height: 599px;
1845 width: 799px;
1846 }
1847 )HTML");
1848 Compositor().BeginFrame();
1849 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1850 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
1851 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
1852 }
1853
1854 // A body with dimensions equal to the window dimensions should not have
1855 // scrollbars.
1856 TEST_F(ScrollbarsTest, BodySizeEqualWindowSizeShouldNotHaveScrollbars) {
1857 // This test requires that scrollbars take up space.
1858 ENABLE_OVERLAY_SCROLLBARS(false);
1859
1860 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1861 SimRequest request("https://example.com/test.html", "text/html");
1862 LoadURL("https://example.com/test.html");
1863 request.Complete(R"HTML(
1864 <!DOCTYPE html>
1865 <style>
1866 body {
1867 margin: 0;
1868 background: blue;
1869 height: 600px;
1870 width: 800px;
1871 }
1872 )HTML");
1873 Compositor().BeginFrame();
1874 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1875 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
1876 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
1877 }
1878
1879 // A body with percentage width extending beyond the window width should cause a
1880 // horizontal scrollbar.
1881 TEST_F(ScrollbarsTest, WidePercentageBodyShouldHaveScrollbar) {
1882 // This test requires that scrollbars take up space.
1883 ENABLE_OVERLAY_SCROLLBARS(false);
1884
1885 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1886 SimRequest request("https://example.com/test.html", "text/html");
1887 LoadURL("https://example.com/test.html");
1888 request.Complete(R"HTML(
1889 <!DOCTYPE html>
1890 <style>
1891 html { height: 100%; }
1892 body {
1893 margin: 0;
1894 width: 101%;
1895 height: 10px;
1896 }
1897 </style>
1898 )HTML");
1899 Compositor().BeginFrame();
1900 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1901 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
1902 EXPECT_TRUE(layout_viewport->HorizontalScrollbar());
1903 }
1904
1905 // Similar to |WidePercentageBodyShouldHaveScrollbar| but with a body height
1906 // equal to the window height.
1907 TEST_F(ScrollbarsTest, WidePercentageAndTallBodyShouldHaveScrollbar) {
1908 // This test requires that scrollbars take up space.
1909 ENABLE_OVERLAY_SCROLLBARS(false);
1910
1911 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1912 SimRequest request("https://example.com/test.html", "text/html");
1913 LoadURL("https://example.com/test.html");
1914 request.Complete(R"HTML(
1915 <!DOCTYPE html>
1916 <style>
1917 html { height: 100%; }
1918 body {
1919 margin: 0;
1920 width: 101%;
1921 height: 100%;
1922 }
1923 </style>
1924 )HTML");
1925 Compositor().BeginFrame();
1926 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1927 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
1928 EXPECT_TRUE(layout_viewport->HorizontalScrollbar());
1929 }
1930
1931 // A body with percentage height extending beyond the window height should cause
1932 // a vertical scrollbar.
1933 TEST_F(ScrollbarsTest, TallPercentageBodyShouldHaveScrollbar) {
1934 // This test requires that scrollbars take up space.
1935 ENABLE_OVERLAY_SCROLLBARS(false);
1936
1937 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1938 SimRequest request("https://example.com/test.html", "text/html");
1939 LoadURL("https://example.com/test.html");
1940 request.Complete(R"HTML(
1941 <!DOCTYPE html>
1942 <style>
1943 html { height: 100%; }
1944 body {
1945 margin: 0;
1946 width: 10px;
1947 height: 101%;
1948 }
1949 </style>
1950 )HTML");
1951 Compositor().BeginFrame();
1952 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1953 EXPECT_TRUE(layout_viewport->VerticalScrollbar());
1954 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
1955 }
1956
1957 // Similar to |TallPercentageBodyShouldHaveScrollbar| but with a body width
1958 // equal to the window width.
1959 TEST_F(ScrollbarsTest, TallPercentageAndWideBodyShouldHaveScrollbar) {
1960 // This test requires that scrollbars take up space.
1961 ENABLE_OVERLAY_SCROLLBARS(false);
1962
1963 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1964 SimRequest request("https://example.com/test.html", "text/html");
1965 LoadURL("https://example.com/test.html");
1966 request.Complete(R"HTML(
1967 <!DOCTYPE html>
1968 <style>
1969 html { height: 100%; }
1970 body {
1971 margin: 0;
1972 width: 100%;
1973 height: 101%;
1974 }
1975 </style>
1976 )HTML");
1977 Compositor().BeginFrame();
1978 auto* layout_viewport = GetDocument().View()->LayoutViewport();
1979 EXPECT_TRUE(layout_viewport->VerticalScrollbar());
1980 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
1981 }
1982
1983 // A body with percentage dimensions extending beyond the window dimensions
1984 // should cause scrollbars.
1985 TEST_F(ScrollbarsTest, TallAndWidePercentageBodyShouldHaveScrollbars) {
1986 // This test requires that scrollbars take up space.
1987 ENABLE_OVERLAY_SCROLLBARS(false);
1988
1989 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
1990 SimRequest request("https://example.com/test.html", "text/html");
1991 LoadURL("https://example.com/test.html");
1992 request.Complete(R"HTML(
1993 <!DOCTYPE html>
1994 <style>
1995 html { height: 100%; }
1996 body {
1997 margin: 0;
1998 width: 101%;
1999 height: 101%;
2000 }
2001 </style>
2002 )HTML");
2003 Compositor().BeginFrame();
2004 auto* layout_viewport = GetDocument().View()->LayoutViewport();
2005 EXPECT_TRUE(layout_viewport->VerticalScrollbar());
2006 EXPECT_TRUE(layout_viewport->HorizontalScrollbar());
2007 }
2008
2009 TEST_F(ScrollbarsTest, MouseOverIFrameScrollbar) {
2010 // This test requires that scrollbars take up space.
2011 ENABLE_OVERLAY_SCROLLBARS(false);
2012
2013 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
2014
2015 SimRequest main_resource("https://example.com/test.html", "text/html");
2016 SimRequest frame_resource("https://example.com/iframe.html", "text/html");
2017 LoadURL("https://example.com/test.html");
2018 main_resource.Complete(R"HTML(
2019 <!DOCTYPE html>
2020 <style>
2021 body {
2022 margin: 0;
2023 }
2024 iframe {
2025 width: 200px;
2026 height: 200px;
2027 }
2028 </style>
2029 <iframe id='iframe' src='iframe.html'>
2030 </iframe>
2031 )HTML");
2032
2033 frame_resource.Complete(R"HTML(
2034 <!DOCTYPE html>
2035 <style>
2036 body {
2037 margin: 0;
2038 height :500px;
2039 }
2040 </style>
2041 )HTML");
2042 Compositor().BeginFrame();
2043
2044 Document& document = GetDocument();
2045 Element* iframe = document.getElementById("iframe");
2046 DCHECK(iframe);
2047
2048 // Ensure hittest has scrollbar.
2049 HitTestResult hit_test_result = HitTest(196, 10);
2050 EXPECT_TRUE(hit_test_result.InnerElement());
2051 EXPECT_TRUE(hit_test_result.GetScrollbar());
2052 EXPECT_TRUE(hit_test_result.GetScrollbar()->Enabled());
2053
2054 // Mouse over scrollbar.
2055 HandleMouseMoveEvent(196, 5);
2056
2057 // IFRAME hover.
2058 EXPECT_EQ(document.HoverElement(), iframe);
2059 }
2060
2061 TEST_F(ScrollbarsTest, AutosizeTest) {
2062 // This test requires that scrollbars take up space.
2063 ENABLE_OVERLAY_SCROLLBARS(false);
2064
2065 WebView().MainFrameViewWidget()->Resize(gfx::Size(0, 0));
2066 SimRequest resource("https://example.com/test.html", "text/html");
2067 LoadURL("https://example.com/test.html");
2068 resource.Complete(R"HTML(
2069 <!DOCTYPE html>
2070 <style>
2071 body, html {
2072 width: 100%;
2073 margin: 0;
2074 }
2075 #container {
2076 width: 100.7px;
2077 height: 150px;
2078 }
2079 </style>
2080 <div id="container"></div>
2081 )HTML");
2082
2083 DCHECK(!GetScrollbarTheme().UsesOverlayScrollbars());
2084
2085 // Needs to dispatch the load event so FramViewAutoSizeInfo doesn't prevent
2086 // down-sizing.
2087 test::RunPendingTasks();
2088
2089 LocalFrameView* frame_view = WebView().MainFrameImpl()->GetFrameView();
2090 ScrollableArea* layout_viewport = frame_view->LayoutViewport();
2091
2092 // Enable auto size mode where the frame is resized such that the content
2093 // doesn't need scrollbars (up to a maximum).
2094 WebView().EnableAutoResizeMode(gfx::Size(100, 100), gfx::Size(100, 200));
2095
2096 // Note, the frame autosizer doesn't work correctly with subpixel sizes so
2097 // even though the container is a fraction larger than the frame, we don't
2098 // consider that for overflow.
2099 {
2100 Compositor().BeginFrame();
2101 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
2102 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
2103 EXPECT_EQ(100, frame_view->FrameRect().Width());
2104 EXPECT_EQ(150, frame_view->FrameRect().Height());
2105 }
2106
2107 // Subsequent autosizes should be stable. Specifically checking the condition
2108 // from https://crbug.com/811478.
2109 {
2110 frame_view->SetNeedsLayout();
2111 Compositor().BeginFrame();
2112 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
2113 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
2114 EXPECT_EQ(100, frame_view->FrameRect().Width());
2115 EXPECT_EQ(150, frame_view->FrameRect().Height());
2116 }
2117
2118 // Try again.
2119 {
2120 frame_view->SetNeedsLayout();
2121 Compositor().BeginFrame();
2122 EXPECT_FALSE(layout_viewport->VerticalScrollbar());
2123 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
2124 EXPECT_EQ(100, frame_view->FrameRect().Width());
2125 EXPECT_EQ(150, frame_view->FrameRect().Height());
2126 }
2127 }
2128
2129 TEST_F(ScrollbarsTest, AutosizeAlmostRemovableScrollbar) {
2130 // This test requires that scrollbars take up space.
2131 ENABLE_OVERLAY_SCROLLBARS(false);
2132 WebView().EnableAutoResizeMode(gfx::Size(25, 25), gfx::Size(800, 600));
2133
2134 SimRequest resource("https://example.com/test.html", "text/html");
2135 LoadURL("https://example.com/test.html");
2136 resource.Complete(R"HTML(
2137 <style>
2138 body { margin: 0; padding: 15px }
2139 #b1, #b2 { display: inline-block; width: 205px; height: 45px; }
2140 #b1 { background: #888; }
2141 #b2 { background: #bbb; }
2142 #spacer { width: 400px; height: 490px; background: #eee; }
2143 </style>
2144 <div id="b1"></div><div id="b2"></div>
2145 <div id="spacer"></div>
2146 )HTML");
2147
2148 // Finish loading.
2149 test::RunPendingTasks();
2150
2151 LocalFrameView* frame_view = WebView().MainFrameImpl()->GetFrameView();
2152 ScrollableArea* layout_viewport = frame_view->LayoutViewport();
2153
2154 // Check three times to verify stability.
2155 for (int i = 0; i < 3; i++) {
2156 frame_view->SetNeedsLayout();
2157 Compositor().BeginFrame();
2158 EXPECT_TRUE(layout_viewport->VerticalScrollbar());
2159 EXPECT_FALSE(layout_viewport->HorizontalScrollbar());
2160 EXPECT_EQ(445, frame_view->Width());
2161 EXPECT_EQ(600, frame_view->Height());
2162 }
2163 }
2164
2165 TEST_F(ScrollbarsTest,
2166 HideTheOverlayScrollbarNotCrashAfterPLSADisposedPaintLayer) {
2167 // This test is specifically checking the behavior when overlay scrollbars
2168 // are enabled.
2169 ENABLE_OVERLAY_SCROLLBARS(true);
2170
2171 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2172 SimRequest request("https://example.com/test.html", "text/html");
2173 LoadURL("https://example.com/test.html");
2174 request.Complete(R"HTML(
2175 <!DOCTYPE html>
2176 <style>
2177 #div{ height: 100px; overflow-y:scroll; }
2178 .big{ height: 2000px; }
2179 .hide { display: none; }
2180 </style>
2181 <div id='div'>
2182 <div class='big'>
2183 </div>
2184 </div>
2185 )HTML");
2186 Compositor().BeginFrame();
2187
2188 Document& document = GetDocument();
2189 Element* div = document.getElementById("div");
2190 auto* scrollable_div = GetScrollableArea(*div);
2191
2192 scrollable_div->SetScrollbarsHiddenForTesting(false);
2193 ASSERT_TRUE(scrollable_div);
2194 ASSERT_TRUE(scrollable_div->GetPageScrollbarTheme().UsesOverlayScrollbars());
2195 ASSERT_TRUE(scrollable_div->VerticalScrollbar());
2196
2197 EXPECT_FALSE(scrollable_div->ScrollbarsHiddenIfOverlay());
2198
2199 // Set display:none calls Dispose().
2200 div->setAttribute(html_names::kClassAttr, "hide");
2201 Compositor().BeginFrame();
2202
2203 // After paint layer in scrollable dispose, we can still call scrollbar hidden
2204 // just not change scrollbar.
2205 scrollable_div->SetScrollbarsHiddenForTesting(true);
2206
2207 EXPECT_FALSE(scrollable_div->ScrollbarsHiddenIfOverlay());
2208 }
2209
2210 TEST_F(ScrollbarsTest, PLSADisposeShouldClearPointerInLayers) {
2211 GetDocument().GetFrame()->GetSettings()->SetPreferCompositingToLCDTextEnabled(
2212 true);
2213 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2214 SimRequest request("https://example.com/test.html", "text/html");
2215 LoadURL("https://example.com/test.html");
2216 request.Complete(R"HTML(
2217 <!DOCTYPE html>
2218 <style>
2219 /* transform keeps the graphics layer */
2220 #div { width: 100px; height: 100px; will-change: transform; }
2221 .scroller{ overflow: scroll; }
2222 .big{ height: 2000px; }
2223 /* positioned so we still keep the PaintLayer */
2224 .hide { overflow: visible; position: absolute; }
2225 </style>
2226 <div id='div' class='scroller' style='z-index:1'>
2227 <div class='big'>
2228 </div>
2229 </div>
2230 )HTML");
2231 Compositor().BeginFrame();
2232
2233 Document& document = GetDocument();
2234 Element* div = document.getElementById("div");
2235 auto* scrollable_div = GetScrollableArea(*div);
2236
2237 ASSERT_TRUE(scrollable_div);
2238
2239 PaintLayer* paint_layer = scrollable_div->Layer();
2240 ASSERT_TRUE(paint_layer);
2241
2242 cc::Layer* graphics_layer = scrollable_div->LayerForScrolling();
2243 ASSERT_TRUE(graphics_layer);
2244
2245 div->setAttribute(html_names::kClassAttr, "hide");
2246 document.UpdateStyleAndLayout(DocumentUpdateReason::kTest);
2247
2248 EXPECT_FALSE(paint_layer->GetScrollableArea());
2249 }
2250
2251 TEST_F(ScrollbarsTest, OverlayScrollbarHitTest) {
2252 // This test is specifically checking the behavior when overlay scrollbars
2253 // are enabled.
2254 ENABLE_OVERLAY_SCROLLBARS(true);
2255 // Skip this test if scrollbars don't allow hit testing on the platform.
2256 if (!WebView().GetPage()->GetScrollbarTheme().AllowsHitTest())
2257 return;
2258
2259 WebView().MainFrameViewWidget()->Resize(gfx::Size(300, 300));
2260
2261 SimRequest main_resource("https://example.com/", "text/html");
2262 SimRequest frame_resource("https://example.com/iframe.html", "text/html");
2263 LoadURL("https://example.com/");
2264 main_resource.Complete(R"HTML(
2265 <!DOCTYPE html>
2266 <style>
2267 body {
2268 margin: 0;
2269 height: 2000px;
2270 }
2271 iframe {
2272 height: 200px;
2273 width: 200px;
2274 }
2275 </style>
2276 <iframe id='iframe' src='iframe.html'>
2277 </iframe>
2278 )HTML");
2279 Compositor().BeginFrame();
2280
2281 // Enable the main frame scrollbar.
2282 WebView()
2283 .MainFrameImpl()
2284 ->GetFrameView()
2285 ->LayoutViewport()
2286 ->SetScrollbarsHiddenForTesting(false);
2287
2288 frame_resource.Complete("<!DOCTYPE html><body style='height: 999px'></body>");
2289 Compositor().BeginFrame();
2290
2291 // Enable the iframe scrollbar.
2292 auto* iframe_element =
2293 To<HTMLIFrameElement>(GetDocument().getElementById("iframe"));
2294 iframe_element->contentDocument()
2295 ->View()
2296 ->LayoutViewport()
2297 ->SetScrollbarsHiddenForTesting(false);
2298
2299 // Hit test on and off the main frame scrollbar.
2300 HitTestResult hit_test_result = HitTest(295, 5);
2301 EXPECT_TRUE(hit_test_result.GetScrollbar());
2302 hit_test_result = HitTest(250, 5);
2303 EXPECT_FALSE(hit_test_result.GetScrollbar());
2304
2305 // Hit test on and off the iframe scrollbar.
2306 hit_test_result = HitTest(195, 5);
2307 EXPECT_TRUE(hit_test_result.GetScrollbar());
2308 hit_test_result = HitTest(150, 5);
2309 EXPECT_FALSE(hit_test_result.GetScrollbar());
2310 }
2311
2312 TEST_F(ScrollbarsTest, AllowMiddleButtonPressOnScrollbar) {
2313 // This test requires that scrollbars take up space.
2314 ENABLE_OVERLAY_SCROLLBARS(false);
2315
2316 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2317 SimRequest request("https://example.com/test.html", "text/html");
2318 LoadURL("https://example.com/test.html");
2319 request.Complete(R"HTML(
2320 <!DOCTYPE html>
2321 <style>
2322 #big {
2323 height: 800px;
2324 }
2325 </style>
2326 <div id='big'>
2327 </div>
2328 )HTML");
2329 Compositor().BeginFrame();
2330
2331 ScrollableArea* scrollable_area =
2332 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2333
2334 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
2335 ASSERT_TRUE(scrollbar);
2336 ASSERT_TRUE(scrollbar->Enabled());
2337
2338 // Not allow press scrollbar with middle button.
2339 HandleMouseMoveEvent(195, 5);
2340 HandleMouseMiddlePressEvent(195, 5);
2341 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2342 HandleMouseMiddleReleaseEvent(195, 5);
2343 }
2344
2345 // Ensure Scrollbar not release press by middle button down.
2346 TEST_F(ScrollbarsTest, MiddleDownShouldNotAffectScrollbarPress) {
2347 // This test requires that scrollbars take up space.
2348 ENABLE_OVERLAY_SCROLLBARS(false);
2349
2350 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2351 SimRequest request("https://example.com/test.html", "text/html");
2352 LoadURL("https://example.com/test.html");
2353 request.Complete(R"HTML(
2354 <!DOCTYPE html>
2355 <style>
2356 #big {
2357 height: 800px;
2358 }
2359 </style>
2360 <div id='big'>
2361 </div>
2362 )HTML");
2363 Compositor().BeginFrame();
2364
2365 ScrollableArea* scrollable_area =
2366 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2367
2368 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
2369 ASSERT_TRUE(scrollbar);
2370 ASSERT_TRUE(scrollbar->Enabled());
2371
2372 // Press on scrollbar then move mouse out of scrollbar and middle click
2373 // should not release the press state. Then relase mouse left button should
2374 // release the scrollbar press state.
2375
2376 // Move mouse to thumb.
2377 HandleMouseMoveEvent(195, 5);
2378 HandleMousePressEvent(195, 5);
2379 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2380
2381 // Move mouse out of scrollbar with press.
2382 WebMouseEvent event(WebInputEvent::Type::kMouseMove, gfx::PointF(5, 5),
2383 gfx::PointF(5, 5), WebPointerProperties::Button::kLeft, 0,
2384 WebInputEvent::Modifiers::kLeftButtonDown,
2385 base::TimeTicks::Now());
2386 event.SetFrameScale(1);
2387 GetEventHandler().HandleMouseMoveEvent(event, Vector<WebMouseEvent>(),
2388 Vector<WebMouseEvent>());
2389 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2390
2391 // Middle click should not release scrollbar press state.
2392 HandleMouseMiddlePressEvent(5, 5);
2393 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2394
2395 // Middle button release should release scrollbar press state.
2396 HandleMouseMiddleReleaseEvent(5, 5);
2397 EXPECT_EQ(scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2398 }
2399
2400 TEST_F(ScrollbarsTest, UseCounterNegativeWhenThumbIsNotScrolledWithMouse) {
2401 // This test requires that scrollbars take up space.
2402 ENABLE_OVERLAY_SCROLLBARS(false);
2403
2404 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2405 SimRequest request("https://example.com/test.html", "text/html");
2406 LoadURL("https://example.com/test.html");
2407 request.Complete(R"HTML(
2408 <!DOCTYPE html>
2409 <style>
2410 #content { height: 350px; width: 350px; }
2411 </style>
2412 <div id='scrollable'>
2413 <div id='content'></div>
2414 </div>
2415 )HTML");
2416 Compositor().BeginFrame();
2417
2418 ScrollableArea* scrollable_area =
2419 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2420 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
2421 EXPECT_TRUE(scrollable_area->HorizontalScrollbar());
2422 Scrollbar* vertical_scrollbar = scrollable_area->VerticalScrollbar();
2423 Scrollbar* horizontal_scrollbar = scrollable_area->HorizontalScrollbar();
2424 EXPECT_EQ(vertical_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2425 EXPECT_EQ(horizontal_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2426
2427 // Scrolling the page with a mouse wheel won't trigger the UseCounter.
2428 WebView().MainFrameViewWidget()->HandleInputEvent(
2429 GenerateWheelGestureEvent(WebInputEvent::Type::kGestureScrollBegin,
2430 IntPoint(100, 100), ScrollOffset(0, -100)));
2431 WebView().MainFrameViewWidget()->HandleInputEvent(
2432 GenerateWheelGestureEvent(WebInputEvent::Type::kGestureScrollUpdate,
2433 IntPoint(100, 100), ScrollOffset(0, -100)));
2434 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateWheelGestureEvent(
2435 WebInputEvent::Type::kGestureScrollEnd, IntPoint(100, 100)));
2436 EXPECT_FALSE(GetDocument().IsUseCounted(
2437 WebFeature::kVerticalScrollbarThumbScrollingWithMouse));
2438
2439 // Hovering over the vertical scrollbar won't trigger the UseCounter.
2440 HandleMouseMoveEvent(195, 5);
2441 EXPECT_FALSE(GetDocument().IsUseCounted(
2442 WebFeature::kVerticalScrollbarThumbScrollingWithMouse));
2443
2444 // Hovering over the horizontal scrollbar won't trigger the UseCounter.
2445 HandleMouseMoveEvent(5, 195);
2446 EXPECT_FALSE(GetDocument().IsUseCounted(
2447 WebFeature::kHorizontalScrollbarThumbScrollingWithMouse));
2448
2449 // Clicking on the vertical scrollbar won't trigger the UseCounter.
2450 HandleMousePressEvent(195, 175);
2451 EXPECT_EQ(vertical_scrollbar->PressedPart(),
2452 ScrollbarPart::kForwardTrackPart);
2453 HandleMouseReleaseEvent(195, 175);
2454 EXPECT_FALSE(GetDocument().IsUseCounted(
2455 WebFeature::kVerticalScrollbarThumbScrollingWithMouse));
2456
2457 // Clicking on the horizontal scrollbar won't trigger the UseCounter.
2458 HandleMousePressEvent(175, 195);
2459 EXPECT_EQ(horizontal_scrollbar->PressedPart(),
2460 ScrollbarPart::kForwardTrackPart);
2461 HandleMouseReleaseEvent(175, 195);
2462 EXPECT_FALSE(GetDocument().IsUseCounted(
2463 WebFeature::kHorizontalScrollbarThumbScrollingWithMouse));
2464
2465 // Clicking outside the scrollbar and then releasing over the thumb of the
2466 // vertical scrollbar won't trigger the UseCounter.
2467 HandleMousePressEvent(50, 50);
2468 HandleMouseMoveEvent(195, 5);
2469 HandleMouseReleaseEvent(195, 5);
2470 EXPECT_FALSE(GetDocument().IsUseCounted(
2471 WebFeature::kVerticalScrollbarThumbScrollingWithMouse));
2472
2473 // Clicking outside the scrollbar and then releasing over the thumb of the
2474 // horizontal scrollbar won't trigger the UseCounter.
2475 HandleMousePressEvent(50, 50);
2476 HandleMouseMoveEvent(5, 195);
2477 HandleMouseReleaseEvent(5, 195);
2478 EXPECT_FALSE(GetDocument().IsUseCounted(
2479 WebFeature::kHorizontalScrollbarThumbScrollingWithMouse));
2480 }
2481
2482 TEST_F(ScrollbarsTest, UseCounterPositiveWhenThumbIsScrolledWithMouse) {
2483 // This test requires that scrollbars take up space.
2484 ENABLE_OVERLAY_SCROLLBARS(false);
2485
2486 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2487 SimRequest request("https://example.com/test.html", "text/html");
2488 LoadURL("https://example.com/test.html");
2489 request.Complete(R"HTML(
2490 <!DOCTYPE html>
2491 <style>
2492 #content { height: 350px; width: 350px; }
2493 </style>
2494 <div id='scrollable'>
2495 <div id='content'></div>
2496 </div>
2497 )HTML");
2498 Compositor().BeginFrame();
2499
2500 ScrollableArea* scrollable_area =
2501 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2502 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
2503 EXPECT_TRUE(scrollable_area->HorizontalScrollbar());
2504 Scrollbar* vertical_scrollbar = scrollable_area->VerticalScrollbar();
2505 Scrollbar* horizontal_scrollbar = scrollable_area->HorizontalScrollbar();
2506 EXPECT_EQ(vertical_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2507 EXPECT_EQ(horizontal_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2508
2509 // Clicking the thumb on the vertical scrollbar will trigger the UseCounter.
2510 HandleMousePressEvent(195, 5);
2511 EXPECT_EQ(vertical_scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2512 HandleMouseReleaseEvent(195, 5);
2513 EXPECT_TRUE(GetDocument().IsUseCounted(
2514 WebFeature::kVerticalScrollbarThumbScrollingWithMouse));
2515
2516 // Clicking the thumb on the horizontal scrollbar will trigger the UseCounter.
2517 HandleMousePressEvent(5, 195);
2518 EXPECT_EQ(horizontal_scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2519 HandleMouseReleaseEvent(5, 195);
2520 EXPECT_TRUE(GetDocument().IsUseCounted(
2521 WebFeature::kHorizontalScrollbarThumbScrollingWithMouse));
2522 }
2523
2524 TEST_F(ScrollbarsTest, UseCounterNegativeWhenThumbIsNotScrolledWithTouch) {
2525 // This test requires that scrollbars take up space.
2526 ENABLE_OVERLAY_SCROLLBARS(false);
2527
2528 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2529 SimRequest request("https://example.com/test.html", "text/html");
2530 LoadURL("https://example.com/test.html");
2531 request.Complete(R"HTML(
2532 <!DOCTYPE html>
2533 <style>
2534 #content { height: 350px; width: 350px; }
2535 </style>
2536 <div id='scrollable'>
2537 <div id='content'></div>
2538 </div>
2539 )HTML");
2540 Compositor().BeginFrame();
2541
2542 ScrollableArea* scrollable_area =
2543 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2544 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
2545 EXPECT_TRUE(scrollable_area->HorizontalScrollbar());
2546 Scrollbar* vertical_scrollbar = scrollable_area->VerticalScrollbar();
2547 Scrollbar* horizontal_scrollbar = scrollable_area->HorizontalScrollbar();
2548 EXPECT_EQ(vertical_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2549 EXPECT_EQ(horizontal_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2550
2551 // Tapping on the vertical scrollbar won't trigger the UseCounter.
2552 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2553 WebInputEvent::Type::kGestureTapDown, IntPoint(195, 175)));
2554 EXPECT_EQ(vertical_scrollbar->PressedPart(),
2555 ScrollbarPart::kForwardTrackPart);
2556 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2557 WebInputEvent::Type::kGestureTapCancel, IntPoint(195, 175)));
2558 EXPECT_FALSE(GetDocument().IsUseCounted(
2559 WebFeature::kVerticalScrollbarThumbScrollingWithTouch));
2560
2561 // Tapping on the horizontal scrollbar won't trigger the UseCounter.
2562 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2563 WebInputEvent::Type::kGestureTapDown, IntPoint(175, 195)));
2564 EXPECT_EQ(horizontal_scrollbar->PressedPart(),
2565 ScrollbarPart::kForwardTrackPart);
2566 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2567 WebInputEvent::Type::kGestureTapCancel, IntPoint(175, 195)));
2568 EXPECT_FALSE(GetDocument().IsUseCounted(
2569 WebFeature::kHorizontalScrollbarThumbScrollingWithTouch));
2570
2571 // Tapping outside the scrollbar and then releasing over the thumb of the
2572 // vertical scrollbar won't trigger the UseCounter.
2573 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2574 WebInputEvent::Type::kGestureTapDown, IntPoint(50, 50)));
2575 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2576 WebInputEvent::Type::kGestureTapCancel, IntPoint(195, 5)));
2577 EXPECT_FALSE(GetDocument().IsUseCounted(
2578 WebFeature::kVerticalScrollbarThumbScrollingWithTouch));
2579
2580 // Tapping outside the scrollbar and then releasing over the thumb of the
2581 // horizontal scrollbar won't trigger the UseCounter.
2582 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2583 WebInputEvent::Type::kGestureTapDown, IntPoint(50, 50)));
2584 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2585 WebInputEvent::Type::kGestureTapCancel, IntPoint(5, 195)));
2586 EXPECT_FALSE(GetDocument().IsUseCounted(
2587 WebFeature::kHorizontalScrollbarThumbScrollingWithTouch));
2588 }
2589
2590 TEST_F(ScrollbarsTest, UseCounterPositiveWhenThumbIsScrolledWithTouch) {
2591 // This test requires that scrollbars take up space.
2592 ENABLE_OVERLAY_SCROLLBARS(false);
2593
2594 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2595 SimRequest request("https://example.com/test.html", "text/html");
2596 LoadURL("https://example.com/test.html");
2597 request.Complete(R"HTML(
2598 <!DOCTYPE html>
2599 <style>
2600 #content { height: 350px; width: 350px; }
2601 </style>
2602 <div id='scrollable'>
2603 <div id='content'></div>
2604 </div>
2605 )HTML");
2606 Compositor().BeginFrame();
2607
2608 ScrollableArea* scrollable_area =
2609 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2610 EXPECT_TRUE(scrollable_area->VerticalScrollbar());
2611 EXPECT_TRUE(scrollable_area->HorizontalScrollbar());
2612 Scrollbar* vertical_scrollbar = scrollable_area->VerticalScrollbar();
2613 Scrollbar* horizontal_scrollbar = scrollable_area->HorizontalScrollbar();
2614 EXPECT_EQ(vertical_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2615 EXPECT_EQ(horizontal_scrollbar->PressedPart(), ScrollbarPart::kNoPart);
2616
2617 // Clicking the thumb on the vertical scrollbar will trigger the UseCounter.
2618 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2619 WebInputEvent::Type::kGestureTapDown, IntPoint(195, 5)));
2620 EXPECT_EQ(vertical_scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2621 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2622 WebInputEvent::Type::kGestureTapCancel, IntPoint(195, 5)));
2623 EXPECT_TRUE(GetDocument().IsUseCounted(
2624 WebFeature::kVerticalScrollbarThumbScrollingWithTouch));
2625
2626 // Clicking the thumb on the horizontal scrollbar will trigger the UseCounter.
2627 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2628 WebInputEvent::Type::kGestureTapDown, IntPoint(5, 195)));
2629 EXPECT_EQ(horizontal_scrollbar->PressedPart(), ScrollbarPart::kThumbPart);
2630 WebView().MainFrameViewWidget()->HandleInputEvent(GenerateTouchGestureEvent(
2631 WebInputEvent::Type::kGestureTapCancel, IntPoint(5, 195)));
2632 EXPECT_TRUE(GetDocument().IsUseCounted(
2633 WebFeature::kHorizontalScrollbarThumbScrollingWithTouch));
2634 }
2635
2636 TEST_F(ScrollbarsTest, UseCounterCustomScrollbarPercentSize) {
2637 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2638 SimRequest request("https://example.com/test.html", "text/html");
2639 LoadURL("https://example.com/test.html");
2640 request.Complete(R"HTML(
2641 <!DOCTYPE html>
2642 <style>
2643 ::-webkit-scrollbar { width: 10px; height: 10%; }
2644 ::-webkit-scrollbar-thumb { min-width: 10%; min-height: 10px; }
2645 </style>
2646 <div id="target" style="width: 100px; height: 100px; overflow: auto">
2647 <div id="child" style="width: 50px; height: 50px"></div>
2648 </div>
2649 )HTML");
2650 Compositor().BeginFrame();
2651
2652 // No scrollbars initially.
2653 EXPECT_FALSE(
2654 GetDocument().IsUseCounted(WebFeature::kCustomScrollbarPercentThickness));
2655 EXPECT_FALSE(GetDocument().IsUseCounted(
2656 WebFeature::kCustomScrollbarPartPercentLength));
2657
2658 // Show vertical scrollbar which uses fixed lengths for thickness
2659 // (width: 10px) and thumb minimum length (min-height: 10px).
2660 auto* child = GetDocument().getElementById("child");
2661 child->setAttribute(html_names::kStyleAttr, "width: 50px; height: 200px");
2662 Compositor().BeginFrame();
2663 EXPECT_FALSE(
2664 GetDocument().IsUseCounted(WebFeature::kCustomScrollbarPercentThickness));
2665 EXPECT_FALSE(GetDocument().IsUseCounted(
2666 WebFeature::kCustomScrollbarPartPercentLength));
2667
2668 // Show horizontal scrollbar which uses percent lengths for thickness
2669 // (height: 10%) and thumb minimum length (min-width: 10%).
2670 child->setAttribute(html_names::kStyleAttr, "width: 200px; height: 50px");
2671 Compositor().BeginFrame();
2672 EXPECT_TRUE(
2673 GetDocument().IsUseCounted(WebFeature::kCustomScrollbarPercentThickness));
2674 EXPECT_TRUE(GetDocument().IsUseCounted(
2675 WebFeature::kCustomScrollbarPartPercentLength));
2676 }
2677
2678 TEST_F(ScrollbarsTest, CheckScrollCornerIfThereIsNoScrollbar) {
2679 // This test is specifically checking the behavior when overlay scrollbars
2680 // are enabled.
2681 ENABLE_OVERLAY_SCROLLBARS(true);
2682
2683 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2684 SimRequest request("https://example.com/test.html", "text/html");
2685 LoadURL("https://example.com/test.html");
2686 request.Complete(R"HTML(
2687 <!DOCTYPE html>
2688 <style>
2689 #container {
2690 width: 50px;
2691 height: 100px;
2692 overflow-x: auto;
2693 }
2694 #content {
2695 width: 75px;
2696 height: 50px;
2697 background-color: green;
2698 }
2699 #container::-webkit-scrollbar {
2700 height: 8px;
2701 width: 8px;
2702 }
2703 #container::-webkit-scrollbar-corner {
2704 background: transparent;
2705 }
2706 </style>
2707 <div id='container'>
2708 <div id='content'></div>
2709 </div>
2710 )HTML");
2711
2712 Compositor().BeginFrame();
2713
2714 auto* element = GetDocument().getElementById("container");
2715 auto* scrollable_container = GetScrollableArea(*element);
2716
2717 // There should initially be a scrollbar and a scroll corner.
2718 EXPECT_TRUE(scrollable_container->HasScrollbar());
2719 EXPECT_TRUE(scrollable_container->ScrollCorner());
2720
2721 // Make the container non-scrollable so the scrollbar and corner disappear.
2722 element->setAttribute(html_names::kStyleAttr, "width: 100px;");
2723 GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kTest);
2724
2725 EXPECT_FALSE(scrollable_container->HasScrollbar());
2726 EXPECT_FALSE(scrollable_container->ScrollCorner());
2727 }
2728
2729 TEST_F(ScrollbarsTest, NoNeedsBeginFrameForCustomScrollbarAfterBeginFrame) {
2730 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2731
2732 SimRequest request("https://example.com/test.html", "text/html");
2733 LoadURL("https://example.com/test.html");
2734 request.Complete(R"HTML(
2735 <!DOCTYPE html>
2736 <style>
2737 ::-webkit-scrollbar { height: 20px; }
2738 ::-webkit-scrollbar-thumb { background-color: blue; }
2739 #target { width: 200px; height: 200px; overflow: scroll; }
2740 </style>
2741 <div id="target">
2742 <div style="width: 500px; height: 500px"></div>
2743 </div>
2744 )HTML");
2745
2746 while (Compositor().NeedsBeginFrame())
2747 Compositor().BeginFrame();
2748
2749 auto* target = GetDocument().getElementById("target");
2750 auto* scrollbar = To<CustomScrollbar>(
2751 target->GetLayoutBox()->GetScrollableArea()->HorizontalScrollbar());
2752 LayoutCustomScrollbarPart* thumb = scrollbar->GetPart(kThumbPart);
2753 auto thumb_size = thumb->Size();
2754 EXPECT_FALSE(thumb->ShouldCheckForPaintInvalidation());
2755 EXPECT_FALSE(Compositor().NeedsBeginFrame());
2756
2757 WebView().MainFrameViewWidget()->UpdateAllLifecyclePhases(
2758 DocumentUpdateReason::kTest);
2759 EXPECT_FALSE(thumb->ShouldCheckForPaintInvalidation());
2760 EXPECT_FALSE(Compositor().NeedsBeginFrame());
2761
2762 target->setAttribute(html_names::kStyleAttr, "width: 400px");
2763 EXPECT_TRUE(Compositor().NeedsBeginFrame());
2764 Compositor().BeginFrame();
2765 EXPECT_FALSE(thumb->ShouldCheckForPaintInvalidation());
2766 EXPECT_FALSE(Compositor().NeedsBeginFrame());
2767 EXPECT_NE(thumb_size, thumb->Size());
2768 }
2769
2770 TEST_F(ScrollbarsTest, CustomScrollbarHypotheticalThickness) {
2771 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2772
2773 SimRequest request("https://example.com/test.html", "text/html");
2774 LoadURL("https://example.com/test.html");
2775 request.Complete(R"HTML(
2776 <!DOCTYPE html>
2777 <style>
2778 #target1::-webkit-scrollbar { width: 22px; height: 33px; }
2779 #target2::-webkit-scrollbar:horizontal { height: 13px; }
2780 ::-webkit-scrollbar:vertical { width: 21px; }
2781 </style>
2782 <div id="target1" style="width: 60px; height: 70px; overflow: scroll"></div>
2783 <div id="target2" style="width: 80px; height: 90px; overflow: scroll"></div>
2784 )HTML");
2785
2786 Compositor().BeginFrame();
2787
2788 auto* target1 = GetDocument().getElementById("target1");
2789 auto* scrollable_area1 = target1->GetLayoutBox()->GetScrollableArea();
2790 EXPECT_EQ(33, CustomScrollbar::HypotheticalScrollbarThickness(
2791 scrollable_area1, kHorizontalScrollbar, target1));
2792 EXPECT_EQ(22, CustomScrollbar::HypotheticalScrollbarThickness(
2793 scrollable_area1, kVerticalScrollbar, target1));
2794
2795 auto* target2 = GetDocument().getElementById("target2");
2796 auto* scrollable_area2 = target2->GetLayoutBox()->GetScrollableArea();
2797 EXPECT_EQ(13, CustomScrollbar::HypotheticalScrollbarThickness(
2798 scrollable_area2, kHorizontalScrollbar, target2));
2799 EXPECT_EQ(21, CustomScrollbar::HypotheticalScrollbarThickness(
2800 scrollable_area2, kVerticalScrollbar, target2));
2801 }
2802
2803 // For infinite scrolling page (load more content when scroll to bottom), user
2804 // press on scrollbar button should keep scrolling after content loaded.
2805 // Disable on Android since VirtualTime not work for Android.
2806 // http://crbug.com/633321
2807 #if defined(OS_ANDROID)
2808 TEST_F(ScrollbarsTestWithVirtualTimer,
2809 DISABLED_PressScrollbarButtonOnInfiniteScrolling) {
2810 #else
2811 TEST_F(ScrollbarsTestWithVirtualTimer,
2812 PressScrollbarButtonOnInfiniteScrolling) {
2813 #endif
2814 TimeAdvance();
2815 GetDocument().GetFrame()->GetSettings()->SetScrollAnimatorEnabled(false);
2816 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2817
2818 SimRequest request("https://example.com/test.html", "text/html");
2819 LoadURL("https://example.com/test.html");
2820 RunTasksForPeriod(base::TimeDelta::FromMilliseconds(1000));
2821 request.Complete(R"HTML(
2822 <!DOCTYPE html>
2823 <style>
2824 html, body{
2825 margin: 0;
2826 }
2827 ::-webkit-scrollbar {
2828 width: 30px;
2829 height: 30px;
2830 }
2831 ::-webkit-scrollbar-button {
2832 width: 30px;
2833 height: 30px;
2834 background: #00FF00;
2835 display: block;
2836 }
2837 ::-webkit-scrollbar-thumb {
2838 background: #0000FF;
2839 }
2840 ::-webkit-scrollbar-track {
2841 background: #aaaaaa;
2842 }
2843 #big {
2844 height: 400px;
2845 }
2846 </style>
2847 <div id='big'>
2848 </div>
2849 )HTML");
2850
2851 Compositor().BeginFrame();
2852
2853 ScrollableArea* scrollable_area =
2854 WebView().MainFrameImpl()->GetFrameView()->LayoutViewport();
2855 Scrollbar* scrollbar = scrollable_area->VerticalScrollbar();
2856
2857 // Scroll to bottom.
2858 scrollable_area->SetScrollOffset(ScrollOffset(0, 400),
2859 mojom::blink::ScrollType::kProgrammatic,
2860 mojom::blink::ScrollBehavior::kInstant);
2861 EXPECT_EQ(scrollable_area->ScrollOffsetInt(), IntSize(0, 200));
2862
2863 HandleMouseMoveEvent(195, 195);
2864 HandleMousePressEvent(195, 195);
2865 ASSERT_EQ(scrollbar->PressedPart(), ScrollbarPart::kForwardButtonEndPart);
2866
2867 // Wait for 2 delay.
2868 RunTasksForPeriod(base::TimeDelta::FromMilliseconds(1000));
2869 RunTasksForPeriod(base::TimeDelta::FromMilliseconds(1000));
2870 // Change #big size.
2871 MainFrame().ExecuteScript(WebScriptSource(
2872 "document.getElementById('big').style.height = '1000px';"));
2873 Compositor().BeginFrame();
2874
2875 RunTasksForPeriod(base::TimeDelta::FromMilliseconds(1000));
2876 RunTasksForPeriod(base::TimeDelta::FromMilliseconds(1000));
2877
2878 // Verify that the scrollbar autopress timer requested some scrolls via
2879 // gestures. The button was pressed for 2 seconds and the timer fires
2880 // every 250ms - we should have at least 7 injected gesture updates.
2881 EXPECT_GT(WebWidgetClient().GetInjectedScrollEvents().size(), 6u);
2882 }
2883
2884 class ScrollbarTrackMarginsTest : public ScrollbarsTest {
2885 public:
2886 void PrepareTest(const String& track_style) {
2887 WebView().MainFrameViewWidget()->Resize(gfx::Size(200, 200));
2888
2889 SimRequest request("https://example.com/test.html", "text/html");
2890 LoadURL("https://example.com/test.html");
2891 request.Complete(R"HTML(
2892 <!DOCTYPE html>
2893 <style>
2894 ::-webkit-scrollbar {
2895 width: 10px;
2896 })HTML" + track_style +
2897 R"HTML(
2898 #d1 {
2899 position: absolute;
2900 left: 0;
2901 right: 0;
2902 top: 0;
2903 bottom: 0;
2904 overflow-x:scroll;
2905 overflow-y:scroll;
2906 }
2907 </style>
2908 <div id='d1'/>
2909 )HTML");
2910
2911 // No DCHECK failure. Issue 801123.
2912 Compositor().BeginFrame();
2913
2914 Element* div = GetDocument().getElementById("d1");
2915 ASSERT_TRUE(div);
2916
2917 auto* div_scrollable = GetScrollableArea(*div);
2918
2919 ASSERT_TRUE(div_scrollable->HorizontalScrollbar());
2920 CustomScrollbar* horizontal_scrollbar =
2921 To<CustomScrollbar>(div_scrollable->HorizontalScrollbar());
2922 horizontal_track_ = horizontal_scrollbar->GetPart(kTrackBGPart);
2923 ASSERT_TRUE(horizontal_track_);
2924
2925 ASSERT_TRUE(div_scrollable->VerticalScrollbar());
2926 CustomScrollbar* vertical_scrollbar =
2927 To<CustomScrollbar>(div_scrollable->VerticalScrollbar());
2928 vertical_track_ = vertical_scrollbar->GetPart(kTrackBGPart);
2929 ASSERT_TRUE(vertical_track_);
2930 }
2931
2932 LayoutCustomScrollbarPart* horizontal_track_ = nullptr;
2933 LayoutCustomScrollbarPart* vertical_track_ = nullptr;
2934 };
2935
2936 TEST_F(ScrollbarTrackMarginsTest,
2937 CustomScrollbarFractionalMarginsWillNotCauseDCHECKFailure) {
2938 PrepareTest(R"CSS(
2939 ::-webkit-scrollbar-track {
2940 margin-left: 10.2px;
2941 margin-top: 20.4px;
2942 margin-right: 30.6px;
2943 margin-bottom: 40.8px;
2944 })CSS");
2945
2946 EXPECT_EQ(10, horizontal_track_->MarginLeft());
2947 EXPECT_EQ(31, horizontal_track_->MarginRight());
2948 EXPECT_EQ(20, vertical_track_->MarginTop());
2949 EXPECT_EQ(41, vertical_track_->MarginBottom());
2950 }
2951
2952 TEST_F(ScrollbarTrackMarginsTest,
2953 CustomScrollbarScaledMarginsWillNotCauseDCHECKFailure) {
2954 WebView().SetZoomFactorForDeviceScaleFactor(1.25f);
2955
2956 PrepareTest(R"CSS(
2957 ::-webkit-scrollbar-track {
2958 margin-left: 11px;
2959 margin-top: 21px;
2960 margin-right: 31px;
2961 margin-bottom: 41px;
2962 })CSS");
2963
2964 EXPECT_EQ(14, horizontal_track_->MarginLeft());
2965 EXPECT_EQ(39, horizontal_track_->MarginRight());
2966 EXPECT_EQ(26, vertical_track_->MarginTop());
2967 EXPECT_EQ(51, vertical_track_->MarginBottom());
2968 }
2969
2970 class ScrollbarColorSchemeTest : public ScrollbarAppearanceTest {};
2971
2972 INSTANTIATE_TEST_SUITE_P(NonOverlay,
2973 ScrollbarColorSchemeTest,
2974 testing::Values(false));
2975
2976 #if defined(OS_ANDROID) || defined(OS_MAC)
2977 // Not able to paint non-overlay scrollbars through ThemeEngine on Android or
2978 // Mac.
2979 #define MAYBE_ThemeEnginePaint DISABLED_ThemeEnginePaint
2980 #else
2981 #define MAYBE_ThemeEnginePaint ThemeEnginePaint
2982 #endif
2983
2984 TEST_P(ScrollbarColorSchemeTest, MAYBE_ThemeEnginePaint) {
2985 ScopedTestingPlatformSupport<ScrollbarTestingPlatformSupport> platform;
2986 ScopedCSSColorSchemeUARenderingForTest color_scheme_ua_scope(true);
2987
2988 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
2989 SimRequest request("https://example.com/test.html", "text/html");
2990 LoadURL("https://example.com/test.html");
2991 request.Complete(R"HTML(
2992 <!DOCTYPE html>
2993 <style>
2994 #scrollable {
2995 width: 100px;
2996 height: 100px;
2997 overflow: scroll;
2998 color-scheme: dark;
2999 }
3000 #filler {
3001 width: 200px;
3002 height: 200px;
3003 }
3004 </style>
3005 <div id="scrollable">
3006 <div id="filler"></div>
3007 </div>
3008 )HTML");
3009
3010 ColorSchemeHelper color_scheme_helper(GetDocument());
3011 color_scheme_helper.SetPreferredColorScheme(
3012 mojom::blink::PreferredColorScheme::kDark);
3013
3014 Compositor().BeginFrame();
3015
3016 auto* theme_engine =
3017 static_cast<StubWebThemeEngine*>(Platform::Current()->ThemeEngine());
3018 EXPECT_EQ(mojom::blink::ColorScheme::kDark,
3019 theme_engine->GetPaintedPartColorScheme(
3020 WebThemeEngine::kPartScrollbarHorizontalThumb));
3021 EXPECT_EQ(mojom::blink::ColorScheme::kDark,
3022 theme_engine->GetPaintedPartColorScheme(
3023 WebThemeEngine::kPartScrollbarVerticalThumb));
3024 EXPECT_EQ(mojom::blink::ColorScheme::kDark,
3025 theme_engine->GetPaintedPartColorScheme(
3026 WebThemeEngine::kPartScrollbarCorner));
3027 }
3028
3029 // Test scrollbar-gutter values with classic scrollbars and horizontal-tb text.
3030 TEST_F(ScrollbarsTest, ScrollbarGutterWithHorizontalTextAndClassicScrollbars) {
3031 // This test requires that scrollbars take up space.
3032 ENABLE_OVERLAY_SCROLLBARS(false);
3033
3034 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
3035 SimRequest request("https://example.com/test.html", "text/html");
3036 LoadURL("https://example.com/test.html");
3037 request.Complete(R"HTML(
3038 <!DOCTYPE html>
3039 <style>
3040 div {
3041 width: 100px;
3042 height: 100px;
3043 overflow: auto;
3044 writing-mode: horizontal-tb;
3045 }
3046 #auto {
3047 scrollbar-gutter: auto;
3048 }
3049 #stable {
3050 scrollbar-gutter: stable;
3051 }
3052 #stable_both {
3053 scrollbar-gutter: stable both;
3054 }
3055 #always {
3056 scrollbar-gutter: always;
3057 }
3058 #always_both {
3059 scrollbar-gutter: always both;
3060 }
3061 #stable_force {
3062 overflow: visible;
3063 scrollbar-gutter: stable force;
3064 }
3065 #stable_both_force {
3066 overflow: hidden;
3067 scrollbar-gutter: stable both force;
3068 }
3069 #always_force {
3070 overflow: visible;
3071 scrollbar-gutter: always force;
3072 }
3073 #always_both_force {
3074 overflow: hidden;
3075 scrollbar-gutter: always both force;
3076 }
3077 </style>
3078 <div id="auto"></div>
3079 <div id="stable"></div>
3080 <div id="stable_both"></div>
3081 <div id="always"></div>
3082 <div id="always_both"></div>
3083 <div id="stable_force"></div>
3084 <div id="stable_both_force"></div>
3085 <div id="always_force"></div>
3086 <div id="always_both_force"></div>
3087 )HTML");
3088 Compositor().BeginFrame();
3089 auto* auto_ = GetDocument().getElementById("auto");
3090 auto* box_auto = auto_->GetLayoutBox();
3091 EXPECT_EQ(box_auto->OffsetWidth(), 100);
3092 EXPECT_EQ(box_auto->ClientWidth(), 100);
3093 NGPhysicalBoxStrut box_auto_scrollbars = box_auto->ComputeScrollbars();
3094 EXPECT_EQ(box_auto_scrollbars.top, 0);
3095 EXPECT_EQ(box_auto_scrollbars.bottom, 0);
3096 EXPECT_EQ(box_auto_scrollbars.left, 0);
3097 EXPECT_EQ(box_auto_scrollbars.right, 0);
3098
3099 auto* stable = GetDocument().getElementById("stable");
3100 auto* box_stable = stable->GetLayoutBox();
3101 EXPECT_EQ(box_stable->OffsetWidth(), 100);
3102 EXPECT_EQ(box_stable->ClientWidth(), 85);
3103 NGPhysicalBoxStrut box_stable_scrollbars = box_stable->ComputeScrollbars();
3104 EXPECT_EQ(box_stable_scrollbars.top, 0);
3105 EXPECT_EQ(box_stable_scrollbars.bottom, 0);
3106 EXPECT_EQ(box_stable_scrollbars.left, 0);
3107 EXPECT_EQ(box_stable_scrollbars.right, 15);
3108
3109 auto* stable_both = GetDocument().getElementById("stable_both");
3110 auto* box_stable_both = stable_both->GetLayoutBox();
3111 EXPECT_EQ(box_stable_both->OffsetWidth(), 100);
3112 EXPECT_EQ(box_stable_both->ClientWidth(), 70);
3113 NGPhysicalBoxStrut box_stable_both_scrollbars =
3114 box_stable_both->ComputeScrollbars();
3115 EXPECT_EQ(box_stable_both_scrollbars.top, 0);
3116 EXPECT_EQ(box_stable_both_scrollbars.bottom, 0);
3117 EXPECT_EQ(box_stable_both_scrollbars.left, 15);
3118 EXPECT_EQ(box_stable_both_scrollbars.right, 15);
3119
3120 auto* always = GetDocument().getElementById("always");
3121 auto* box_always = always->GetLayoutBox();
3122 EXPECT_EQ(box_always->OffsetWidth(), 100);
3123 EXPECT_EQ(box_always->ClientWidth(), 85);
3124 NGPhysicalBoxStrut box_always_scrollbars = box_always->ComputeScrollbars();
3125 EXPECT_EQ(box_always_scrollbars.top, 0);
3126 EXPECT_EQ(box_always_scrollbars.bottom, 0);
3127 EXPECT_EQ(box_always_scrollbars.left, 0);
3128 EXPECT_EQ(box_always_scrollbars.right, 15);
3129
3130 auto* always_both = GetDocument().getElementById("always_both");
3131 auto* box_always_both = always_both->GetLayoutBox();
3132 EXPECT_EQ(box_always_both->OffsetWidth(), 100);
3133 EXPECT_EQ(box_always_both->ClientWidth(), 70);
3134 NGPhysicalBoxStrut box_always_both_scrollbars =
3135 box_always_both->ComputeScrollbars();
3136 EXPECT_EQ(box_always_both_scrollbars.top, 0);
3137 EXPECT_EQ(box_always_both_scrollbars.bottom, 0);
3138 EXPECT_EQ(box_always_both_scrollbars.left, 15);
3139 EXPECT_EQ(box_always_both_scrollbars.right, 15);
3140
3141 auto* stable_force = GetDocument().getElementById("stable_force");
3142 auto* box_stable_force = stable_force->GetLayoutBox();
3143 EXPECT_EQ(box_stable_force->OffsetWidth(), 100);
3144 EXPECT_EQ(box_stable_force->ClientWidth(), 85);
3145 EXPECT_EQ(box_stable_force->ComputeScrollbars(), box_stable_scrollbars);
3146
3147 auto* stable_both_force = GetDocument().getElementById("stable_both_force");
3148 auto* box_stable_both_force = stable_both_force->GetLayoutBox();
3149 EXPECT_EQ(box_stable_both_force->OffsetWidth(), 100);
3150 EXPECT_EQ(box_stable_both_force->ClientWidth(), 70);
3151 EXPECT_EQ(box_stable_both_force->ComputeScrollbars(),
3152 box_stable_both_scrollbars);
3153
3154 auto* always_force = GetDocument().getElementById("always_force");
3155 auto* box_always_force = always_force->GetLayoutBox();
3156 EXPECT_EQ(box_always_force->OffsetWidth(), 100);
3157 EXPECT_EQ(box_always_force->ClientWidth(), 85);
3158 EXPECT_EQ(box_always_force->ComputeScrollbars(), box_always_scrollbars);
3159
3160 auto* always_both_force = GetDocument().getElementById("always_both_force");
3161 auto* box_always_both_force = always_both_force->GetLayoutBox();
3162 EXPECT_EQ(box_always_both_force->OffsetWidth(), 100);
3163 EXPECT_EQ(box_always_both_force->ClientWidth(), 70);
3164 EXPECT_EQ(box_always_both_force->ComputeScrollbars(),
3165 box_always_both_scrollbars);
3166 }
3167
3168 // Test scrollbar-gutter values with classic scrollbars and vertical-rl text.
3169 TEST_F(ScrollbarsTest, ScrollbarGutterWithVerticalTextAndClassicScrollbars) {
3170 // This test requires that scrollbars take up space.
3171 ENABLE_OVERLAY_SCROLLBARS(false);
3172
3173 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
3174 SimRequest request("https://example.com/test.html", "text/html");
3175 LoadURL("https://example.com/test.html");
3176 request.Complete(R"HTML(
3177 <!DOCTYPE html>
3178 <style>
3179 div {
3180 width: 100px;
3181 height: 100px;
3182 overflow: auto;
3183 writing-mode: vertical-rl;
3184 }
3185 #auto {
3186 scrollbar-gutter: auto;
3187 }
3188 #stable {
3189 scrollbar-gutter: stable;
3190 }
3191 #stable_both {
3192 scrollbar-gutter: stable both;
3193 }
3194 #always {
3195 scrollbar-gutter: always;
3196 }
3197 #always_both {
3198 scrollbar-gutter: always both;
3199 }
3200 #stable_force {
3201 overflow: hidden;
3202 scrollbar-gutter: stable force;
3203 }
3204 #stable_both_force {
3205 overflow: visible;
3206 scrollbar-gutter: stable both force;
3207 }
3208 #always_force {
3209 overflow: hidden;
3210 scrollbar-gutter: always force;
3211 }
3212 #always_both_force {
3213 overflow: visible;
3214 scrollbar-gutter: always both force;
3215 }
3216 </style>
3217 <div id="auto"></div>
3218 <div id="stable"></div>
3219 <div id="stable_both"></div>
3220 <div id="always"></div>
3221 <div id="always_both"></div>
3222 <div id="stable_force"></div>
3223 <div id="stable_both_force"></div>
3224 <div id="always_force"></div>
3225 <div id="always_both_force"></div>
3226 )HTML");
3227 Compositor().BeginFrame();
3228 auto* auto_ = GetDocument().getElementById("auto");
3229 auto* box_auto = auto_->GetLayoutBox();
3230 EXPECT_EQ(box_auto->OffsetHeight(), 100);
3231 EXPECT_EQ(box_auto->ClientHeight(), 100);
3232 NGPhysicalBoxStrut box_auto_scrollbars = box_auto->ComputeScrollbars();
3233 EXPECT_EQ(box_auto_scrollbars.top, 0);
3234 EXPECT_EQ(box_auto_scrollbars.bottom, 0);
3235 EXPECT_EQ(box_auto_scrollbars.left, 0);
3236 EXPECT_EQ(box_auto_scrollbars.right, 0);
3237
3238 auto* stable = GetDocument().getElementById("stable");
3239 auto* box_stable = stable->GetLayoutBox();
3240 EXPECT_EQ(box_stable->OffsetHeight(), 100);
3241 EXPECT_EQ(box_stable->ClientHeight(), 85);
3242 NGPhysicalBoxStrut box_stable_scrollbars = box_stable->ComputeScrollbars();
3243 EXPECT_EQ(box_stable_scrollbars.top, 0);
3244 EXPECT_EQ(box_stable_scrollbars.bottom, 15);
3245 EXPECT_EQ(box_stable_scrollbars.left, 0);
3246 EXPECT_EQ(box_stable_scrollbars.right, 0);
3247
3248 auto* stable_both = GetDocument().getElementById("stable_both");
3249 auto* box_stable_both = stable_both->GetLayoutBox();
3250 EXPECT_EQ(box_stable_both->OffsetHeight(), 100);
3251 EXPECT_EQ(box_stable_both->ClientHeight(), 70);
3252 NGPhysicalBoxStrut box_stable_both_scrollbars =
3253 box_stable_both->ComputeScrollbars();
3254 EXPECT_EQ(box_stable_both_scrollbars.top, 15);
3255 EXPECT_EQ(box_stable_both_scrollbars.bottom, 15);
3256 EXPECT_EQ(box_stable_both_scrollbars.left, 0);
3257 EXPECT_EQ(box_stable_both_scrollbars.right, 0);
3258
3259 auto* always = GetDocument().getElementById("always");
3260 auto* box_always = always->GetLayoutBox();
3261 EXPECT_EQ(box_always->OffsetHeight(), 100);
3262 EXPECT_EQ(box_always->ClientHeight(), 85);
3263 NGPhysicalBoxStrut box_always_scrollbars = box_always->ComputeScrollbars();
3264 EXPECT_EQ(box_always_scrollbars.top, 0);
3265 EXPECT_EQ(box_always_scrollbars.bottom, 15);
3266 EXPECT_EQ(box_always_scrollbars.left, 0);
3267 EXPECT_EQ(box_always_scrollbars.right, 0);
3268
3269 auto* always_both = GetDocument().getElementById("always_both");
3270 auto* box_always_both = always_both->GetLayoutBox();
3271 EXPECT_EQ(box_always_both->OffsetHeight(), 100);
3272 EXPECT_EQ(box_always_both->ClientHeight(), 70);
3273 NGPhysicalBoxStrut box_always_both_scrollbars =
3274 box_always_both->ComputeScrollbars();
3275 EXPECT_EQ(box_always_both_scrollbars.top, 15);
3276 EXPECT_EQ(box_always_both_scrollbars.bottom, 15);
3277 EXPECT_EQ(box_always_both_scrollbars.left, 0);
3278 EXPECT_EQ(box_always_both_scrollbars.right, 0);
3279
3280 auto* stable_force = GetDocument().getElementById("stable_force");
3281 auto* box_stable_force = stable_force->GetLayoutBox();
3282 EXPECT_EQ(box_stable_force->OffsetHeight(), 100);
3283 EXPECT_EQ(box_stable_force->ClientHeight(), 85);
3284 EXPECT_EQ(box_stable_force->ComputeScrollbars(), box_stable_scrollbars);
3285
3286 auto* stable_both_force = GetDocument().getElementById("stable_both_force");
3287 auto* box_stable_both_force = stable_both_force->GetLayoutBox();
3288 EXPECT_EQ(box_stable_both_force->OffsetHeight(), 100);
3289 EXPECT_EQ(box_stable_both_force->ClientHeight(), 70);
3290 EXPECT_EQ(box_stable_both_force->ComputeScrollbars(),
3291 box_stable_both_scrollbars);
3292
3293 auto* always_force = GetDocument().getElementById("always_force");
3294 auto* box_always_force = always_force->GetLayoutBox();
3295 EXPECT_EQ(box_always_force->OffsetHeight(), 100);
3296 EXPECT_EQ(box_always_force->ClientHeight(), 85);
3297 EXPECT_EQ(box_always_force->ComputeScrollbars(), box_always_scrollbars);
3298
3299 auto* always_both_force = GetDocument().getElementById("always_both_force");
3300 auto* box_always_both_force = always_both_force->GetLayoutBox();
3301 EXPECT_EQ(box_always_both_force->OffsetHeight(), 100);
3302 EXPECT_EQ(box_always_both_force->ClientHeight(), 70);
3303 EXPECT_EQ(box_always_both_force->ComputeScrollbars(),
3304 box_always_both_scrollbars);
3305 }
3306
3307 // Test scrollbar-gutter values with overlay scrollbars and horizontal-tb text.
3308 TEST_F(ScrollbarsTest, ScrollbarGutterWithHorizontalTextAndOverlayScrollbars) {
3309 // This test is specifically checking the behavior when overlay scrollbars
3310 // are enabled.
3311 ENABLE_OVERLAY_SCROLLBARS(true);
3312
3313 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
3314 SimRequest request("https://example.com/test.html", "text/html");
3315 LoadURL("https://example.com/test.html");
3316 request.Complete(R"HTML(
3317 <!DOCTYPE html>
3318 <style>
3319 div {
3320 width: 100px;
3321 height: 100px;
3322 overflow: auto;
3323 writing-mode: horizontal-tb;
3324 }
3325 #auto {
3326 scrollbar-gutter: auto;
3327 }
3328 #stable {
3329 scrollbar-gutter: stable;
3330 }
3331 #stable_both {
3332 scrollbar-gutter: stable both;
3333 }
3334 #always {
3335 scrollbar-gutter: always;
3336 }
3337 #always_both {
3338 scrollbar-gutter: always both;
3339 }
3340 #stable_force {
3341 overflow: hidden;
3342 scrollbar-gutter: stable force;
3343 }
3344 #stable_both_force {
3345 overflow: visible;
3346 scrollbar-gutter: stable both force;
3347 }
3348 #always_force {
3349 overflow: hidden;
3350 scrollbar-gutter: always force;
3351 }
3352 #always_both_force {
3353 overflow: visible;
3354 scrollbar-gutter: always both force;
3355 }
3356 </style>
3357 <div id="auto"></div>
3358 <div id="stable"></div>
3359 <div id="stable_both"></div>
3360 <div id="always"></div>
3361 <div id="always_both"></div>
3362 <div id="stable_force"></div>
3363 <div id="stable_both_force"></div>
3364 <div id="always_force"></div>
3365 <div id="always_both_force"></div>
3366 )HTML");
3367 Compositor().BeginFrame();
3368 auto* auto_ = GetDocument().getElementById("auto");
3369 auto* box_auto = auto_->GetLayoutBox();
3370 EXPECT_EQ(box_auto->OffsetWidth(), 100);
3371 EXPECT_EQ(box_auto->ClientWidth(), 100);
3372 NGPhysicalBoxStrut box_auto_scrollbars = box_auto->ComputeScrollbars();
3373 EXPECT_EQ(box_auto_scrollbars.top, 0);
3374 EXPECT_EQ(box_auto_scrollbars.bottom, 0);
3375 EXPECT_EQ(box_auto_scrollbars.left, 0);
3376 EXPECT_EQ(box_auto_scrollbars.right, 0);
3377
3378 auto* stable = GetDocument().getElementById("stable");
3379 auto* box_stable = stable->GetLayoutBox();
3380 EXPECT_EQ(box_stable->OffsetWidth(), 100);
3381 EXPECT_EQ(box_stable->ClientWidth(), 100);
3382 NGPhysicalBoxStrut box_stable_scrollbars = box_stable->ComputeScrollbars();
3383 EXPECT_EQ(box_stable_scrollbars.top, 0);
3384 EXPECT_EQ(box_stable_scrollbars.bottom, 0);
3385 EXPECT_EQ(box_stable_scrollbars.left, 0);
3386 EXPECT_EQ(box_stable_scrollbars.right, 0);
3387
3388 auto* stable_both = GetDocument().getElementById("stable_both");
3389 auto* box_stable_both = stable_both->GetLayoutBox();
3390 EXPECT_EQ(box_stable_both->OffsetWidth(), 100);
3391 EXPECT_EQ(box_stable_both->ClientWidth(), 100);
3392 NGPhysicalBoxStrut box_stable_both_scrollbars =
3393 box_stable_both->ComputeScrollbars();
3394 EXPECT_EQ(box_stable_both_scrollbars.top, 0);
3395 EXPECT_EQ(box_stable_both_scrollbars.bottom, 0);
3396 EXPECT_EQ(box_stable_both_scrollbars.left, 0);
3397 EXPECT_EQ(box_stable_both_scrollbars.right, 0);
3398
3399 // The size of overlay scrollbars is different between operating systems,
3400 // which is why we use these relative comparisons.
3401
3402 auto* always = GetDocument().getElementById("always");
3403 auto* box_always = always->GetLayoutBox();
3404 EXPECT_EQ(box_always->OffsetWidth(), 100);
3405 EXPECT_LT(box_always->ClientWidth(), box_auto->ClientWidth());
3406 NGPhysicalBoxStrut box_always_scrollbars = box_always->ComputeScrollbars();
3407 EXPECT_EQ(box_always_scrollbars.top, 0);
3408 EXPECT_EQ(box_always_scrollbars.bottom, 0);
3409 EXPECT_EQ(box_always_scrollbars.left, 0);
3410 // scrollbar gutter
3411 EXPECT_GT(box_always_scrollbars.right, 0);
3412
3413 auto* always_both = GetDocument().getElementById("always_both");
3414 auto* box_always_both = always_both->GetLayoutBox();
3415 EXPECT_EQ(box_always_both->OffsetWidth(), 100);
3416 EXPECT_LT(box_always_both->ClientWidth(), box_always->ClientWidth());
3417 NGPhysicalBoxStrut box_always_both_scrollbars =
3418 box_always_both->ComputeScrollbars();
3419 EXPECT_EQ(box_always_both_scrollbars.top, 0);
3420 EXPECT_EQ(box_always_both_scrollbars.bottom, 0);
3421 // scrollbar gutters
3422 EXPECT_GT(box_always_both_scrollbars.left, 0);
3423 EXPECT_GT(box_always_both_scrollbars.right, 0);
3424
3425 auto* stable_force = GetDocument().getElementById("stable_force");
3426 auto* box_stable_force = stable_force->GetLayoutBox();
3427 EXPECT_EQ(box_stable_force->OffsetWidth(), 100);
3428 EXPECT_EQ(box_stable_force->ClientWidth(), 100);
3429 EXPECT_EQ(box_stable_force->ComputeScrollbars(), box_stable_scrollbars);
3430
3431 auto* stable_both_force = GetDocument().getElementById("stable_both_force");
3432 auto* box_stable_both_force = stable_both_force->GetLayoutBox();
3433 EXPECT_EQ(box_stable_both_force->OffsetWidth(), 100);
3434 EXPECT_EQ(box_stable_both_force->ClientWidth(), 100);
3435 EXPECT_EQ(box_stable_both_force->ComputeScrollbars(),
3436 box_stable_both_scrollbars);
3437
3438 auto* always_force = GetDocument().getElementById("always_force");
3439 auto* box_always_force = always_force->GetLayoutBox();
3440 EXPECT_EQ(box_always_force->OffsetWidth(), 100);
3441 EXPECT_LT(box_always_force->ClientWidth(), box_auto->ClientWidth());
3442 EXPECT_EQ(box_always_force->ComputeScrollbars(), box_always_scrollbars);
3443
3444 auto* always_both_force = GetDocument().getElementById("always_both_force");
3445 auto* box_always_both_force = always_both_force->GetLayoutBox();
3446 EXPECT_EQ(box_always_both_force->OffsetWidth(), 100);
3447 EXPECT_LT(box_always_both_force->ClientWidth(),
3448 box_always_force->ClientWidth());
3449 EXPECT_EQ(box_always_both_force->ComputeScrollbars(),
3450 box_always_both_scrollbars);
3451 }
3452
3453 // Test scrollbar-gutter values with overlay scrollbars and vertical-rl text.
3454 TEST_F(ScrollbarsTest, ScrollbarGutterWithVerticalTextAndOverlayScrollbars) {
3455 // This test is specifically checking the behavior when overlay scrollbars
3456 // are enabled.
3457 ENABLE_OVERLAY_SCROLLBARS(true);
3458
3459 WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
3460 SimRequest request("https://example.com/test.html", "text/html");
3461 LoadURL("https://example.com/test.html");
3462 request.Complete(R"HTML(
3463 <!DOCTYPE html>
3464 <style>
3465 div {
3466 width: 100px;
3467 height: 100px;
3468 overflow: auto;
3469 writing-mode: vertical-rl;
3470 }
3471 #auto {
3472 scrollbar-gutter: auto;
3473 }
3474 #stable {
3475 scrollbar-gutter: stable;
3476 }
3477 #stable_both {
3478 scrollbar-gutter: stable both;
3479 }
3480 #always {
3481 scrollbar-gutter: always;
3482 }
3483 #always_both {
3484 scrollbar-gutter: always both;
3485 }
3486 #stable_force {
3487 overflow: visible;
3488 scrollbar-gutter: stable force;
3489 }
3490 #stable_both_force {
3491 overflow: hidden;
3492 scrollbar-gutter: stable both force;
3493 }
3494 #always_force {
3495 overflow: visible;
3496 scrollbar-gutter: always force;
3497 }
3498 #always_both_force {
3499 overflow: hidden;
3500 scrollbar-gutter: always both force;
3501 }
3502 </style>
3503 <div id="auto"></div>
3504 <div id="stable"></div>
3505 <div id="stable_both"></div>
3506 <div id="always"></div>
3507 <div id="always_both"></div>
3508 <div id="stable_force"></div>
3509 <div id="stable_both_force"></div>
3510 <div id="always_force"></div>
3511 <div id="always_both_force"></div>
3512 )HTML");
3513 Compositor().BeginFrame();
3514 auto* auto_ = GetDocument().getElementById("auto");
3515 auto* box_auto = auto_->GetLayoutBox();
3516 EXPECT_EQ(box_auto->OffsetHeight(), 100);
3517 EXPECT_EQ(box_auto->ClientHeight(), 100);
3518 NGPhysicalBoxStrut box_auto_scrollbars = box_auto->ComputeScrollbars();
3519 EXPECT_EQ(box_auto_scrollbars.top, 0);
3520 EXPECT_EQ(box_auto_scrollbars.bottom, 0);
3521 EXPECT_EQ(box_auto_scrollbars.left, 0);
3522 EXPECT_EQ(box_auto_scrollbars.right, 0);
3523
3524 auto* stable = GetDocument().getElementById("stable");
3525 auto* box_stable = stable->GetLayoutBox();
3526 EXPECT_EQ(box_stable->OffsetHeight(), 100);
3527 EXPECT_EQ(box_stable->ClientHeight(), 100);
3528 NGPhysicalBoxStrut box_stable_scrollbars = box_stable->ComputeScrollbars();
3529 EXPECT_EQ(box_stable_scrollbars.top, 0);
3530 EXPECT_EQ(box_stable_scrollbars.bottom, 0);
3531 EXPECT_EQ(box_stable_scrollbars.left, 0);
3532 EXPECT_EQ(box_stable_scrollbars.right, 0);
3533
3534 auto* stable_both = GetDocument().getElementById("stable_both");
3535 auto* box_stable_both = stable_both->GetLayoutBox();
3536 EXPECT_EQ(box_stable_both->OffsetHeight(), 100);
3537 EXPECT_EQ(box_stable_both->ClientHeight(), 100);
3538 NGPhysicalBoxStrut box_stable_both_scrollbars =
3539 box_stable_both->ComputeScrollbars();
3540 EXPECT_EQ(box_stable_both_scrollbars.top, 0);
3541 EXPECT_EQ(box_stable_both_scrollbars.bottom, 0);
3542 EXPECT_EQ(box_stable_both_scrollbars.left, 0);
3543 EXPECT_EQ(box_stable_both_scrollbars.right, 0);
3544
3545 auto* always = GetDocument().getElementById("always");
3546 auto* box_always = always->GetLayoutBox();
3547 EXPECT_EQ(box_always->OffsetHeight(), 100);
3548 EXPECT_LT(box_always->ClientHeight(), box_auto->ClientHeight());
3549 NGPhysicalBoxStrut box_always_scrollbars = box_always->ComputeScrollbars();
3550 EXPECT_EQ(box_always_scrollbars.top, 0);
3551 // scrollbar gutter
3552 EXPECT_GT(box_always_scrollbars.bottom, 0);
3553 EXPECT_EQ(box_always_scrollbars.left, 0);
3554 EXPECT_EQ(box_always_scrollbars.right, 0);
3555
3556 auto* always_both = GetDocument().getElementById("always_both");
3557 auto* box_always_both = always_both->GetLayoutBox();
3558 EXPECT_EQ(box_always_both->OffsetHeight(), 100);
3559 EXPECT_LT(box_always_both->ClientHeight(), box_always->ClientHeight());
3560 NGPhysicalBoxStrut box_always_both_scrollbars =
3561 box_always_both->ComputeScrollbars();
3562 // scrollbar gutters
3563 EXPECT_GT(box_always_both_scrollbars.top, 0);
3564 EXPECT_GT(box_always_both_scrollbars.bottom, 0);
3565 EXPECT_EQ(box_always_both_scrollbars.left, 0);
3566 EXPECT_EQ(box_always_both_scrollbars.right, 0);
3567
3568 auto* stable_force = GetDocument().getElementById("stable_force");
3569 auto* box_stable_force = stable_force->GetLayoutBox();
3570 EXPECT_EQ(box_stable_force->OffsetHeight(), 100);
3571 EXPECT_EQ(box_stable_force->ClientHeight(), 100);
3572 EXPECT_EQ(box_stable_force->ComputeScrollbars(), box_stable_scrollbars);
3573
3574 auto* stable_both_force = GetDocument().getElementById("stable_both_force");
3575 auto* box_stable_both_force = stable_both_force->GetLayoutBox();
3576 EXPECT_EQ(box_stable_both_force->OffsetHeight(), 100);
3577 EXPECT_EQ(box_stable_both_force->ClientHeight(), 100);
3578 EXPECT_EQ(box_stable_both_force->ComputeScrollbars(),
3579 box_stable_both_scrollbars);
3580
3581 // TODO this fails because overflow is "visible"
3582 auto* always_force = GetDocument().getElementById("always_force");
3583 auto* box_always_force = always_force->GetLayoutBox();
3584 EXPECT_EQ(box_always_force->OffsetHeight(), 100);
3585 EXPECT_LT(box_always_force->ClientHeight(), box_auto->ClientHeight());
3586 EXPECT_EQ(box_always_force->ComputeScrollbars(), box_always_scrollbars);
3587
3588 auto* always_both_force = GetDocument().getElementById("always_both_force");
3589 auto* box_always_both_force = always_both_force->GetLayoutBox();
3590 EXPECT_EQ(box_always_both_force->OffsetHeight(), 100);
3591 EXPECT_LT(box_always_both_force->ClientHeight(),
3592 box_always_force->ClientHeight());
3593 EXPECT_EQ(box_always_both_force->ComputeScrollbars(),
3594 box_always_both_scrollbars);
3595 }
3596
3597 } // namespace blink
3598