1 // Copyright 2017 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 "testing/gtest/include/gtest/gtest.h"
6 #include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
7 #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink.h"
8 #include "third_party/blink/public/web/web_script_source.h"
9 #include "third_party/blink/renderer/bindings/core/v8/scroll_into_view_options_or_boolean.h"
10 #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_into_view_options.h"
11 #include "third_party/blink/renderer/bindings/core/v8/v8_scroll_to_options.h"
12 #include "third_party/blink/renderer/core/dom/element.h"
13 #include "third_party/blink/renderer/core/frame/find_in_page.h"
14 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
15 #include "third_party/blink/renderer/core/frame/root_frame_viewport.h"
16 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
17 #include "third_party/blink/renderer/core/html/html_element.h"
18 #include "third_party/blink/renderer/core/layout/layout_box.h"
19 #include "third_party/blink/renderer/core/layout/layout_object.h"
20 #include "third_party/blink/renderer/core/page/scrolling/top_document_root_scroller_controller.h"
21 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
22 #include "third_party/blink/renderer/core/scroll/scroll_alignment.h"
23 #include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
24 #include "third_party/blink/renderer/core/testing/sim/sim_request.h"
25 #include "third_party/blink/renderer/core/testing/sim/sim_test.h"
26 
27 namespace blink {
28 
29 namespace {
30 
31 class ScrollIntoViewTest : public SimTest {};
32 
TEST_F(ScrollIntoViewTest,InstantScroll)33 TEST_F(ScrollIntoViewTest, InstantScroll) {
34   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
35   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
36   SimRequest request("https://example.com/test.html", "text/html");
37   LoadURL("https://example.com/test.html");
38   request.Complete(
39       "<div id='space' style='height: 1000px'></div>"
40       "<div id='content' style='height: 1000px'></div>");
41 
42   Compositor().BeginFrame();
43   ASSERT_EQ(Window().scrollY(), 0);
44   Element* content = GetDocument().getElementById("content");
45   ScrollIntoViewOptionsOrBoolean arg;
46   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
47   options->setBlock("start");
48   arg.SetScrollIntoViewOptions(options);
49   content->scrollIntoView(arg);
50 
51   ASSERT_EQ(Window().scrollY(), content->OffsetTop());
52 }
53 
TEST_F(ScrollIntoViewTest,ScrollPaddingOnDocumentElWhenBodyDefinesViewport)54 TEST_F(ScrollIntoViewTest, ScrollPaddingOnDocumentElWhenBodyDefinesViewport) {
55   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
56   WebView().MainFrameViewWidget()->Resize(gfx::Size(300, 300));
57   SimRequest request("https://example.com/test.html", "text/html");
58   LoadURL("https://example.com/test.html");
59   request.Complete(R"HTML(
60       <style>
61       html {
62         scroll-padding: 10px;
63       }
64       body {
65         margin: 0px;
66         height: 300px;
67         overflow: scroll;
68       }
69       </style>
70       <div id='space' style='height: 1000px'></div>
71       <div id='target' style='height: 200px;'></div>
72       <div id='space' style='height: 1000px'></div>
73     )HTML");
74 
75   Compositor().BeginFrame();
76   ASSERT_EQ(Window().scrollY(), 0);
77   Element* target = GetDocument().getElementById("target");
78   target->scrollIntoView();
79 
80   // Sanity check that document element is the viewport defining element
81   ASSERT_EQ(GetDocument().body(), GetDocument().ViewportDefiningElement());
82   ASSERT_EQ(Window().scrollY(), target->OffsetTop() - 10);
83 }
84 
TEST_F(ScrollIntoViewTest,ScrollPaddingOnDocumentElWhenDocumentElDefinesViewport)85 TEST_F(ScrollIntoViewTest,
86        ScrollPaddingOnDocumentElWhenDocumentElDefinesViewport) {
87   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
88   WebView().MainFrameViewWidget()->Resize(gfx::Size(300, 300));
89   SimRequest request("https://example.com/test.html", "text/html");
90   LoadURL("https://example.com/test.html");
91   request.Complete(R"HTML(
92       <style>
93       :root {
94         height: 300px;
95         overflow: scroll;
96         scroll-padding: 10px;
97       }
98       </style>
99       <div id='space' style='height: 1000px'></div>
100       <div id='target' style='height: 200px;'></div>
101       <div id='space' style='height: 1000px'></div>
102     )HTML");
103 
104   Compositor().BeginFrame();
105   ASSERT_EQ(Window().scrollY(), 0);
106   Element* target = GetDocument().getElementById("target");
107   target->scrollIntoView();
108 
109   // Sanity check that document element is the viewport defining element
110   ASSERT_EQ(GetDocument().documentElement(),
111             GetDocument().ViewportDefiningElement());
112   ASSERT_EQ(Window().scrollY(), target->OffsetTop() - 10);
113 }
114 
TEST_F(ScrollIntoViewTest,ScrollPaddingOnBodyWhenDocumentElDefinesViewport)115 TEST_F(ScrollIntoViewTest, ScrollPaddingOnBodyWhenDocumentElDefinesViewport) {
116   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
117   WebView().MainFrameViewWidget()->Resize(gfx::Size(300, 300));
118   SimRequest request("https://example.com/test.html", "text/html");
119   LoadURL("https://example.com/test.html");
120   request.Complete(R"HTML(
121       <style>
122       :root {
123         height: 300px;
124         overflow: scroll;
125         scroll-padding: 2px;
126       }
127       body {
128         margin: 0px;
129         height: 400px;
130         overflow: scroll;
131         scroll-padding: 10px;
132       }
133       </style>
134       <div id='space' style='height: 1000px'></div>
135       <div id='target' style='height: 200px;'></div>
136       <div id='space' style='height: 1000px'></div>
137     )HTML");
138 
139   Compositor().BeginFrame();
140   ASSERT_EQ(Window().scrollY(), 0);
141   Element* target = GetDocument().getElementById("target");
142   target->scrollIntoView();
143 
144   // Sanity check that document element is the viewport defining element
145   ASSERT_EQ(GetDocument().documentElement(),
146             GetDocument().ViewportDefiningElement());
147 
148   // When body and document elements are both scrollable then both the body and
149   // element should scroll and align with its padding.
150   Element* body = GetDocument().body();
151   ASSERT_EQ(body->scrollTop(), target->OffsetTop() - 10);
152   ASSERT_EQ(Window().scrollY(), 10 - 2);
153 }
154 
TEST_F(ScrollIntoViewTest,SmoothScroll)155 TEST_F(ScrollIntoViewTest, SmoothScroll) {
156   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
157   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
158   SimRequest request("https://example.com/test.html", "text/html");
159   LoadURL("https://example.com/test.html");
160   request.Complete(
161       "<div id='space' style='height: 1000px'></div>"
162       "<div id='content' style='height: 1000px'></div>");
163 
164   Element* content = GetDocument().getElementById("content");
165   ScrollIntoViewOptionsOrBoolean arg;
166   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
167   options->setBlock("start");
168   options->setBehavior("smooth");
169   arg.SetScrollIntoViewOptions(options);
170   Compositor().BeginFrame();
171   ASSERT_EQ(Window().scrollY(), 0);
172 
173   content->scrollIntoView(arg);
174   // Scrolling the container
175   Compositor().BeginFrame();  // update run_state_.
176   Compositor().BeginFrame();  // Set start_time = now.
177   Compositor().BeginFrame(0.2);
178   ASSERT_NEAR(Window().scrollY(), 299, 1);
179 
180   // Finish scrolling the container
181   Compositor().BeginFrame(1);
182   ASSERT_EQ(Window().scrollY(), content->OffsetTop());
183 }
184 
TEST_F(ScrollIntoViewTest,NestedContainer)185 TEST_F(ScrollIntoViewTest, NestedContainer) {
186   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
187   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
188   SimRequest request("https://example.com/test.html", "text/html");
189   LoadURL("https://example.com/test.html");
190   request.Complete(R"HTML(
191     <div id='space' style='height: 1000px'></div>
192     <div id='container' style='height: 600px; overflow: scroll'>
193       <div id='space1' style='height: 1000px'></div>
194       <div id='content' style='height: 1000px'></div>
195     </div>
196   )HTML");
197 
198   Element* container = GetDocument().getElementById("container");
199   Element* content = GetDocument().getElementById("content");
200   ScrollIntoViewOptionsOrBoolean arg;
201   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
202   options->setBlock("start");
203   options->setBehavior("smooth");
204   arg.SetScrollIntoViewOptions(options);
205   Compositor().BeginFrame();
206   ASSERT_EQ(Window().scrollY(), 0);
207   ASSERT_EQ(container->scrollTop(), 0);
208 
209   content->scrollIntoView(arg);
210   // Scrolling the outer container
211   Compositor().BeginFrame();  // update run_state_.
212   Compositor().BeginFrame();  // Set start_time = now.
213   Compositor().BeginFrame(0.2);
214   ASSERT_NEAR(Window().scrollY(), 299, 1);
215   ASSERT_EQ(container->scrollTop(), 0);
216 
217   // Finish scrolling the outer container
218   Compositor().BeginFrame(1);
219   ASSERT_EQ(Window().scrollY(), container->OffsetTop());
220   ASSERT_EQ(container->scrollTop(), 0);
221 
222   // Scrolling the inner container
223   Compositor().BeginFrame();  // Set start_time = now.
224   Compositor().BeginFrame(0.2);
225   ASSERT_NEAR(container->scrollTop(), 299, 1);
226 
227   // Finish scrolling the inner container
228   Compositor().BeginFrame(1);
229   ASSERT_EQ(container->scrollTop(),
230             content->OffsetTop() - container->OffsetTop());
231 }
232 
TEST_F(ScrollIntoViewTest,NewScrollIntoViewAbortsCurrentAnimation)233 TEST_F(ScrollIntoViewTest, NewScrollIntoViewAbortsCurrentAnimation) {
234   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
235   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
236   SimRequest request("https://example.com/test.html", "text/html");
237   LoadURL("https://example.com/test.html");
238   request.Complete(R"HTML(
239     <div id='container2' style='height: 1000px; overflow: scroll'>
240       <div id='space2' style='height: 1200px'></div>
241       <div id='content2' style='height: 1000px'></div>
242     </div>
243     <div id='container1' style='height: 600px; overflow: scroll'>
244       <div id='space1' style='height: 1000px'></div>
245       <div id='content1' style='height: 1000px'></div>
246     </div>
247   )HTML");
248 
249   Element* container1 = GetDocument().getElementById("container1");
250   Element* container2 = GetDocument().getElementById("container2");
251   Element* content1 = GetDocument().getElementById("content1");
252   Element* content2 = GetDocument().getElementById("content2");
253   ScrollIntoViewOptionsOrBoolean arg;
254   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
255   options->setBlock("start");
256   options->setBehavior("smooth");
257   arg.SetScrollIntoViewOptions(options);
258 
259   Compositor().BeginFrame();
260   ASSERT_EQ(Window().scrollY(), 0);
261   ASSERT_EQ(container1->scrollTop(), 0);
262   ASSERT_EQ(container2->scrollTop(), 0);
263 
264   content1->scrollIntoView(arg);
265   Compositor().BeginFrame();  // update run_state_.
266   Compositor().BeginFrame();  // Set start_time = now.
267   Compositor().BeginFrame(0.2);
268   ASSERT_NEAR(Window().scrollY(), 299, 1);
269   ASSERT_EQ(container1->scrollTop(), 0);
270 
271   content2->scrollIntoView(arg);
272   Compositor().BeginFrame();  // update run_state_.
273   Compositor().BeginFrame();  // Set start_time = now.
274   Compositor().BeginFrame(0.2);
275   ASSERT_NEAR(Window().scrollY(), 61, 1);
276   ASSERT_EQ(container1->scrollTop(), 0);  // container1 should not scroll.
277 
278   Compositor().BeginFrame(1);
279   ASSERT_EQ(Window().scrollY(), container2->OffsetTop());
280   ASSERT_EQ(container2->scrollTop(), 0);
281 
282   // Scrolling content2 in container2
283   Compositor().BeginFrame();  // Set start_time = now.
284   Compositor().BeginFrame(0.2);
285   ASSERT_NEAR(container2->scrollTop(), 300, 1);
286 
287   // Finish all the animation to make sure there is no another animation queued
288   // on container1.
289   while (Compositor().NeedsBeginFrame()) {
290     Compositor().BeginFrame();
291   }
292   ASSERT_EQ(Window().scrollY(), container2->OffsetTop());
293   ASSERT_EQ(container2->scrollTop(),
294             content2->OffsetTop() - container2->OffsetTop());
295   ASSERT_EQ(container1->scrollTop(), 0);
296 }
297 
TEST_F(ScrollIntoViewTest,ScrollWindowAbortsCurrentAnimation)298 TEST_F(ScrollIntoViewTest, ScrollWindowAbortsCurrentAnimation) {
299   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
300   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
301   SimRequest request("https://example.com/test.html", "text/html");
302   LoadURL("https://example.com/test.html");
303   request.Complete(R"HTML(
304     <div id='space' style='height: 1000px'></div>
305     <div id='container' style='height: 600px; overflow: scroll'>
306       <div id='space1' style='height: 1000px'></div>
307       <div id='content' style='height: 1000px'></div>
308     </div>
309   )HTML");
310 
311   Element* container = GetDocument().getElementById("container");
312   Element* content = GetDocument().getElementById("content");
313   ScrollIntoViewOptionsOrBoolean arg;
314   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
315   options->setBlock("start");
316   options->setBehavior("smooth");
317   arg.SetScrollIntoViewOptions(options);
318   Compositor().BeginFrame();
319   ASSERT_EQ(Window().scrollY(), 0);
320   ASSERT_EQ(container->scrollTop(), 0);
321 
322   content->scrollIntoView(arg);
323   // Scrolling the outer container
324   Compositor().BeginFrame();  // update run_state_.
325   Compositor().BeginFrame();  // Set start_time = now.
326   Compositor().BeginFrame(0.2);
327   ASSERT_NEAR(Window().scrollY(), 299, 1);
328   ASSERT_EQ(container->scrollTop(), 0);
329 
330   ScrollToOptions* window_option = ScrollToOptions::Create();
331   window_option->setLeft(0);
332   window_option->setTop(0);
333   window_option->setBehavior("smooth");
334   Window().scrollTo(window_option);
335   Compositor().BeginFrame();  // update run_state_.
336   Compositor().BeginFrame();  // Set start_time = now.
337   Compositor().BeginFrame(0.2);
338   ASSERT_NEAR(Window().scrollY(), 58, 1);
339 
340   Compositor().BeginFrame(1);
341   ASSERT_EQ(Window().scrollY(), 0);
342   ASSERT_EQ(container->scrollTop(), 0);
343 }
344 
TEST_F(ScrollIntoViewTest,BlockAndInlineSettings)345 TEST_F(ScrollIntoViewTest, BlockAndInlineSettings) {
346   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
347   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
348   SimRequest request("https://example.com/test.html", "text/html");
349   LoadURL("https://example.com/test.html");
350   request.Complete(R"HTML(
351     <div id='container' style='height: 2500px; width: 2500px;'>
352     <div id='content' style='height: 500px; width: 500px;
353     margin-left: 1000px; margin-right: 1000px; margin-top: 1000px;
354     margin-bottom: 1000px'></div></div>
355   )HTML");
356 
357   int content_height = 500;
358   int content_width = 500;
359   int window_height = 600;
360   int window_width = 800;
361 
362   Element* content = GetDocument().getElementById("content");
363   ScrollIntoViewOptionsOrBoolean arg1, arg2, arg3, arg4;
364   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
365   ASSERT_EQ(Window().scrollY(), 0);
366 
367   options->setBlock("nearest");
368   options->setInlinePosition("nearest");
369   arg1.SetScrollIntoViewOptions(options);
370   content->scrollIntoView(arg1);
371   ASSERT_EQ(Window().scrollX(),
372             content->OffsetLeft() + content_width - window_width);
373   ASSERT_EQ(Window().scrollY(),
374             content->OffsetTop() + content_height - window_height);
375 
376   options->setBlock("start");
377   options->setInlinePosition("start");
378   arg2.SetScrollIntoViewOptions(options);
379   content->scrollIntoView(arg2);
380   ASSERT_EQ(Window().scrollX(), content->OffsetLeft());
381   ASSERT_EQ(Window().scrollY(), content->OffsetTop());
382 
383   options->setBlock("center");
384   options->setInlinePosition("center");
385   arg3.SetScrollIntoViewOptions(options);
386   content->scrollIntoView(arg3);
387   ASSERT_EQ(Window().scrollX(),
388             content->OffsetLeft() + (content_width - window_width) / 2);
389   ASSERT_EQ(Window().scrollY(),
390             content->OffsetTop() + (content_height - window_height) / 2);
391 
392   options->setBlock("end");
393   options->setInlinePosition("end");
394   arg4.SetScrollIntoViewOptions(options);
395   content->scrollIntoView(arg4);
396   ASSERT_EQ(Window().scrollX(),
397             content->OffsetLeft() + content_width - window_width);
398   ASSERT_EQ(Window().scrollY(),
399             content->OffsetTop() + content_height - window_height);
400 }
401 
TEST_F(ScrollIntoViewTest,SmoothAndInstantInChain)402 TEST_F(ScrollIntoViewTest, SmoothAndInstantInChain) {
403   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
404   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
405   SimRequest request("https://example.com/test.html", "text/html");
406   LoadURL("https://example.com/test.html");
407   request.Complete(R"HTML(
408     <div id='space' style='height: 1000px'></div>
409     <div id='container' style='height: 600px; overflow: scroll;
410       scroll-behavior: smooth'>
411       <div id='space1' style='height: 1000px'></div>
412       <div id='inner_container' style='height: 1000px; overflow: scroll;'>
413         <div id='space2' style='height: 1000px'></div>
414         <div id='content' style='height: 1000px;'></div>
415       </div>
416     </div>
417   )HTML");
418 
419   Element* container = GetDocument().getElementById("container");
420   Element* inner_container = GetDocument().getElementById("inner_container");
421   Element* content = GetDocument().getElementById("content");
422   ScrollIntoViewOptionsOrBoolean arg;
423   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
424   options->setBlock("start");
425   arg.SetScrollIntoViewOptions(options);
426   Compositor().BeginFrame();
427   ASSERT_EQ(Window().scrollY(), 0);
428   ASSERT_EQ(container->scrollTop(), 0);
429 
430   content->scrollIntoView(arg);
431   // Instant scroll of the window should have finished.
432   ASSERT_EQ(Window().scrollY(), container->OffsetTop());
433   // Instant scroll of the inner container should not have started.
434   ASSERT_EQ(container->scrollTop(), 0);
435   // Smooth scroll should not have started.
436   ASSERT_EQ(container->scrollTop(), 0);
437 
438   // Scrolling the container
439   Compositor().BeginFrame();  // update run_state_.
440   Compositor().BeginFrame();  // Set start_time = now.
441   Compositor().BeginFrame(0.2);
442   ASSERT_NEAR(container->scrollTop(), 299, 1);
443 
444   // Finish scrolling the container
445   Compositor().BeginFrame(1);
446   ASSERT_EQ(container->scrollTop(),
447             inner_container->OffsetTop() - container->OffsetTop());
448   // Instant scroll of the inner container should have finished.
449   ASSERT_EQ(inner_container->scrollTop(),
450             content->OffsetTop() - inner_container->OffsetTop());
451 }
452 
TEST_F(ScrollIntoViewTest,SmoothScrollAnchor)453 TEST_F(ScrollIntoViewTest, SmoothScrollAnchor) {
454   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
455   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
456   SimRequest request("https://example.com/test.html#link", "text/html");
457   LoadURL("https://example.com/test.html#link");
458   request.Complete(R"HTML(
459     <div id='container' style='height: 600px; overflow: scroll;
460       scroll-behavior: smooth'>
461       <div id='space' style='height: 1000px'></div>
462       <div style='height: 1000px'><a name='link'
463     id='content'>hello</a></div>
464     </div>
465   )HTML");
466 
467   Element* content = GetDocument().getElementById("content");
468   Element* container = GetDocument().getElementById("container");
469   ASSERT_EQ(container->scrollTop(), 0);
470 
471   // Scrolling the container
472   Compositor().BeginFrame();  // update run_state_.
473   Compositor().BeginFrame();  // Set start_time = now.
474   Compositor().BeginFrame(0.2);
475   ASSERT_NEAR(container->scrollTop(), 299, 1);
476 
477   // Finish scrolling the container
478   Compositor().BeginFrame(1);
479   ASSERT_EQ(container->scrollTop(),
480             content->OffsetTop() - container->OffsetTop());
481 }
482 
TEST_F(ScrollIntoViewTest,FindDoesNotScrollOverflowHidden)483 TEST_F(ScrollIntoViewTest, FindDoesNotScrollOverflowHidden) {
484   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
485   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
486   SimRequest request("https://example.com/test.html", "text/html");
487   LoadURL("https://example.com/test.html");
488   request.Complete(R"HTML(
489     <div id='container' style='height: 400px; overflow: hidden;'>
490       <div id='space' style='height: 500px'></div>
491       <div style='height: 500px'>hello</div>
492     </div>
493   )HTML");
494   Element* container = GetDocument().getElementById("container");
495   Compositor().BeginFrame();
496   ASSERT_EQ(container->scrollTop(), 0);
497   const int kFindIdentifier = 12345;
498   auto options = mojom::blink::FindOptions::New();
499   options->run_synchronously_for_testing = true;
500   MainFrame().GetFindInPage()->FindInternal(
501       kFindIdentifier, WebString::FromUTF8("hello"), *options, false);
502   ASSERT_EQ(container->scrollTop(), 0);
503 }
504 
TEST_F(ScrollIntoViewTest,ApplyRootElementScrollBehaviorToViewport)505 TEST_F(ScrollIntoViewTest, ApplyRootElementScrollBehaviorToViewport) {
506   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
507   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
508   SimRequest request("https://example.com/test.html", "text/html");
509   LoadURL("https://example.com/test.html");
510   request.Complete(
511       "<html style='scroll-behavior: smooth'>"
512       "<div id='space' style='height: 1000px'></div>"
513       "<div id='content' style='height: 1000px'></div></html>");
514 
515   Element* content = GetDocument().getElementById("content");
516   ScrollIntoViewOptionsOrBoolean arg;
517   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
518   options->setBlock("start");
519   arg.SetScrollIntoViewOptions(options);
520   Compositor().BeginFrame();
521   ASSERT_EQ(Window().scrollY(), 0);
522 
523   content->scrollIntoView(arg);
524   // Scrolling the container
525   Compositor().BeginFrame();  // update run_state_.
526   Compositor().BeginFrame();  // Set start_time = now.
527   Compositor().BeginFrame(0.2);
528   ASSERT_NEAR(Window().scrollY(), 299, 1);
529 
530   // Finish scrolling the container
531   Compositor().BeginFrame(1);
532   ASSERT_EQ(Window().scrollY(), content->OffsetTop());
533 }
534 
535 // This test ensures the stop-at-layout viewport option works correctly when a
536 // non-default root scroller is set as the layout viewport.
TEST_F(ScrollIntoViewTest,StopAtLayoutViewportOption)537 TEST_F(ScrollIntoViewTest, StopAtLayoutViewportOption) {
538   ScopedImplicitRootScrollerForTest implicit_root_scroller(true);
539 
540   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
541   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
542   SimRequest request("https://example.com/test.html", "text/html");
543   LoadURL("https://example.com/test.html");
544   request.Complete(R"HTML(
545     <!DOCTYPE html>
546     <style>
547       body,html {
548         margin: 0;
549         width: 100%;
550         height: 100%;
551       }
552       #root {
553         width: 100%;
554         height: 100%;
555         overflow: auto;
556       }
557       #inner {
558         width: 100%;
559         height: 100%;
560         overflow: auto;
561         margin-top: 1000px;
562       }
563       #target {
564         margin-top: 1000px;
565         margin-bottom: 1000px;
566         width: 100px;
567         height: 100px;
568       }
569     </style>
570     <div id='root'>
571       <div id='inner'>
572         <div id='target'></div>
573       <div>
574     </div>
575   )HTML");
576 
577   Compositor().BeginFrame();
578 
579   Element* root = GetDocument().getElementById("root");
580   Element* inner = GetDocument().getElementById("inner");
581 
582   // Make sure the root scroller is set since that's what we're trying to test
583   // here.
584   {
585     TopDocumentRootScrollerController& rs_controller =
586         GetDocument().GetPage()->GlobalRootScrollerController();
587     ASSERT_EQ(root, rs_controller.GlobalRootScroller());
588   }
589 
590   // Use ScrollRectToVisible on the #target element, specifying
591   // stop_at_main_frame_layout_viewport.
592   LayoutObject* target =
593       GetDocument().getElementById("target")->GetLayoutObject();
594   auto params = ScrollAlignment::CreateScrollIntoViewParams(
595       ScrollAlignment::LeftAlways(), ScrollAlignment::TopAlways(),
596       mojom::blink::ScrollType::kProgrammatic, false,
597       mojom::blink::ScrollBehavior::kInstant);
598   params->stop_at_main_frame_layout_viewport = true;
599   target->ScrollRectToVisible(PhysicalRect(target->AbsoluteBoundingBoxRect()),
600                               std::move(params));
601 
602   ScrollableArea* root_scroller =
603       To<LayoutBox>(root->GetLayoutObject())->GetScrollableArea();
604   ScrollableArea* inner_scroller =
605       To<LayoutBox>(inner->GetLayoutObject())->GetScrollableArea();
606 
607   // Only the inner scroller should have scrolled. The root_scroller shouldn't
608   // scroll because it is the layout viewport.
609   ASSERT_EQ(root_scroller,
610             &GetDocument().View()->GetRootFrameViewport()->LayoutViewport());
611   EXPECT_EQ(ScrollOffset(), root_scroller->GetScrollOffset());
612   EXPECT_EQ(ScrollOffset(0, 1000), inner_scroller->GetScrollOffset());
613 }
614 
615 // This test passes if it doesn't crash/hit an ASAN check.
TEST_F(ScrollIntoViewTest,RemoveSequencedScrollableArea)616 TEST_F(ScrollIntoViewTest, RemoveSequencedScrollableArea) {
617   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
618   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
619   SimRequest request("https://example.com/test.html", "text/html");
620   LoadURL("https://example.com/test.html");
621   request.Complete(R"HTML(
622     <!DOCTYPE html>
623     <style>
624     .scroller {
625       scroll-behavior: smooth;
626       overflow: scroll;
627       position: absolute;
628       z-index: 0;
629       border: 10px solid #cce;
630     }
631     #outer {
632       width: 350px;
633       height: 200px;
634       left: 50px;
635       top: 50px;
636     }
637     #inner {
638       width: 200px;
639       height: 100px;
640       left: 50px;
641       top: 200px;
642     }
643     #target {
644       margin: 200px 0 20px 200px;
645       width: 50px;
646       height: 30px;
647       background-color: #c88;
648     }
649     </style>
650     <body>
651     <div class='scroller' id='outer'>
652       <div class='scroller' id='inner'>
653         <div id='target'></div>
654       </div>
655     </div>
656   )HTML");
657 
658   Compositor().BeginFrame();
659 
660   Element* target = GetDocument().getElementById("target");
661   target->scrollIntoView();
662 
663   Compositor().BeginFrame();  // update run_state_.
664   Compositor().BeginFrame();  // Set start_time = now.
665 
666   Element* inner = GetDocument().getElementById("inner");
667   Element* outer = GetDocument().getElementById("outer");
668   outer->removeChild(inner);
669 
670   // Make sure that we don't try to animate the removed scroller.
671   Compositor().BeginFrame(1);
672 }
673 
TEST_F(ScrollIntoViewTest,SmoothUserScrollNotAbortedByProgrammaticScrolls)674 TEST_F(ScrollIntoViewTest, SmoothUserScrollNotAbortedByProgrammaticScrolls) {
675   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
676   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
677   SimRequest request("https://example.com/test.html", "text/html");
678   LoadURL("https://example.com/test.html");
679   request.Complete(
680       "<div id='space' style='height: 1000px'></div>"
681       "<div id='content' style='height: 1000px'></div>");
682 
683   Compositor().BeginFrame();
684   ASSERT_EQ(Window().scrollY(), 0);
685 
686   // A smooth UserScroll.
687   Element* content = GetDocument().getElementById("content");
688   content->GetLayoutObject()->ScrollRectToVisible(
689       content->BoundingBoxForScrollIntoView(),
690       ScrollAlignment::CreateScrollIntoViewParams(
691           ScrollAlignment::ToEdgeIfNeeded(), ScrollAlignment::TopAlways(),
692           mojom::blink::ScrollType::kUser, false,
693           mojom::blink::ScrollBehavior::kSmooth, true));
694 
695   // Animating the container
696   Compositor().BeginFrame();  // update run_state_.
697   Compositor().BeginFrame();  // Set start_time = now.
698   Compositor().BeginFrame(0.2);
699   ASSERT_NEAR(Window().scrollY(), 299, 1);
700 
701   // ProgrammaticScroll that could interrupt the current smooth scroll.
702   Window().scrollTo(0, 0);
703 
704   // Finish scrolling the container
705   Compositor().BeginFrame(1);
706   // The programmatic scroll of Window shouldn't abort the user scroll.
707   ASSERT_EQ(Window().scrollY(), content->OffsetTop());
708 }
709 
TEST_F(ScrollIntoViewTest,LongDistanceSmoothScrollFinishedInThreeSeconds)710 TEST_F(ScrollIntoViewTest, LongDistanceSmoothScrollFinishedInThreeSeconds) {
711   v8::HandleScope HandleScope(v8::Isolate::GetCurrent());
712   WebView().MainFrameViewWidget()->Resize(gfx::Size(800, 600));
713   SimRequest request("https://example.com/test.html", "text/html");
714   LoadURL("https://example.com/test.html");
715   request.Complete(
716       "<div id='space' style='height: 100000px'></div>"
717       "<div id='target' style='height: 1000px'></div>");
718 
719   Compositor().BeginFrame();
720   ASSERT_EQ(Window().scrollY(), 0);
721 
722   Element* target = GetDocument().getElementById("target");
723   ScrollIntoViewOptionsOrBoolean arg;
724   ScrollIntoViewOptions* options = ScrollIntoViewOptions::Create();
725   options->setBlock("start");
726   options->setBehavior("smooth");
727   arg.SetScrollIntoViewOptions(options);
728   target->scrollIntoView(arg);
729 
730   // Scrolling the window
731   Compositor().BeginFrame();  // update run_state_.
732   Compositor().BeginFrame();  // Set start_time = now.
733   Compositor().BeginFrame(0.2);
734   ASSERT_NEAR(Window().scrollY(), 16971, 1);
735 
736   // Finish scrolling the container
737   Compositor().BeginFrame(0.5);
738   ASSERT_EQ(Window().scrollY(), target->OffsetTop());
739 }
740 
741 }  // namespace
742 
743 }  // namespace blink
744