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