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