Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: third_party/WebKit/Source/web/tests/RootScrollerTest.cpp

Issue 1913843004: Implementing document.setRootScroller API for main thread scrolling. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@overscrollController
Patch Set: Rebase and remove whitespace in Document.idl Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "core/page/scrolling/RootScroller.h"
6
7 #include "core/frame/FrameHost.h"
8 #include "core/frame/FrameView.h"
9 #include "core/frame/TopControls.h"
10 #include "core/html/HTMLFrameOwnerElement.h"
11 #include "core/page/Page.h"
12 #include "platform/testing/URLTestHelpers.h"
13 #include "platform/testing/UnitTestHelpers.h"
14 #include "public/platform/Platform.h"
15 #include "public/platform/WebURLLoaderMockFactory.h"
16 #include "public/web/WebCache.h"
17 #include "public/web/WebConsoleMessage.h"
18 #include "public/web/WebScriptSource.h"
19 #include "public/web/WebSettings.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "web/WebLocalFrameImpl.h"
22 #include "web/tests/FrameTestHelpers.h"
23 #include "wtf/Vector.h"
24
25 using blink::testing::runPendingTasks;
26 using testing::Mock;
27
28 namespace blink {
29
30 namespace {
31
32 class RootScrollerTestWebViewClient : public FrameTestHelpers::TestWebViewClient {
33 public:
34 MOCK_METHOD4(didOverscroll, void(const WebFloatSize&, const WebFloatSize&, c onst WebFloatPoint&, const WebFloatSize&));
35 };
36
37 class RootScrollerTest : public ::testing::Test {
38 public:
39 RootScrollerTest()
40 : m_baseURL("http://www.test.com/")
41 {
42 registerMockedHttpURLLoad("overflow-scrolling.html");
43 registerMockedHttpURLLoad("root-scroller.html");
44 registerMockedHttpURLLoad("root-scroller-iframe.html");
45 registerMockedHttpURLLoad("root-scroller-child.html");
46 }
47
48 ~RootScrollerTest() override
49 {
50 m_featuresBackup.restore();
51 Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs();
52 WebCache::clear();
53 }
54
55 WebViewImpl* initialize(const std::string& pageName)
56 {
57 RuntimeEnabledFeatures::setSetRootScrollerEnabled(true);
58
59 // Load a page with large body and set viewport size to 400x400 to
60 // ensure main frame is scrollable.
61 m_helper.initializeAndLoad(
62 m_baseURL + pageName, true, 0, &m_client, &configureSettings);
63
64 // Initialize top controls to be shown.
65 webViewImpl()->resizeWithTopControls(IntSize(400, 400), 50, true);
66 webViewImpl()->topControls().setShownRatio(1);
67
68 mainFrameView()->updateAllLifecyclePhases();
69
70 return webViewImpl();
71 }
72
73 static void configureSettings(WebSettings* settings)
74 {
75 settings->setJavaScriptEnabled(true);
76 settings->setAcceleratedCompositingEnabled(true);
77 settings->setPreferCompositingToLCDTextEnabled(true);
78 // Android settings.
79 settings->setViewportEnabled(true);
80 settings->setViewportMetaEnabled(true);
81 settings->setShrinksViewportContentToFit(true);
82 settings->setMainFrameResizesAreOrientationChanges(true);
83 }
84
85 void registerMockedHttpURLLoad(const std::string& fileName)
86 {
87 URLTestHelpers::registerMockedURLFromBaseURL(
88 WebString::fromUTF8(m_baseURL.c_str()),
89 WebString::fromUTF8(fileName.c_str()));
90 }
91
92 void executeScript(const WebString& code)
93 {
94 mainWebFrame()->executeScript(WebScriptSource(code));
95 mainWebFrame()->view()->updateAllLifecyclePhases();
96 runPendingTasks();
97 }
98
99 WebGestureEvent generateEvent(
100 WebInputEvent::Type type, int deltaX = 0, int deltaY = 0)
101 {
102 WebGestureEvent event;
103 event.type = type;
104 event.sourceDevice = WebGestureDeviceTouchscreen;
105 event.x = 100;
106 event.y = 100;
107 if (type == WebInputEvent::GestureScrollUpdate) {
108 event.data.scrollUpdate.deltaX = deltaX;
109 event.data.scrollUpdate.deltaY = deltaY;
110 }
111 return event;
112 }
113
114 void verticalScroll(float deltaY)
115 {
116 webViewImpl()->handleInputEvent(
117 generateEvent(WebInputEvent::GestureScrollBegin));
118 webViewImpl()->handleInputEvent(
119 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -deltaY));
120 webViewImpl()->handleInputEvent(
121 generateEvent(WebInputEvent::GestureScrollEnd));
122 }
123
124 WebViewImpl* webViewImpl() const
125 {
126 return m_helper.webViewImpl();
127 }
128
129 FrameHost& frameHost() const
130 {
131 return m_helper.webViewImpl()->page()->frameHost();
132 }
133
134 LocalFrame* mainFrame() const
135 {
136 return toWebLocalFrameImpl(webViewImpl()->mainFrame())->frame();
137 }
138
139 WebLocalFrame* mainWebFrame() const
140 {
141 return toWebLocalFrameImpl(webViewImpl()->mainFrame());
142 }
143
144 FrameView* mainFrameView() const
145 {
146 return webViewImpl()->mainFrameImpl()->frame()->view();
147 }
148
149 VisualViewport& visualViewport() const
150 {
151 return frameHost().visualViewport();
152 }
153
154 RootScroller& rootScroller() const
155 {
156 return *frameHost().rootScroller();
157 }
158
159 TopControls& topControls() const
160 {
161 return frameHost().topControls();
162 }
163
164 protected:
165 std::string m_baseURL;
166 RootScrollerTestWebViewClient m_client;
167 FrameTestHelpers::WebViewHelper m_helper;
168 RuntimeEnabledFeatures::Backup m_featuresBackup;
169 };
170
171 // Test that a default root scroller element is set if setRootScroller isn't
172 // called on any elements.
173 TEST_F(RootScrollerTest, TestDefaultRootScroller)
174 {
175 initialize("overflow-scrolling.html");
176
177 EXPECT_EQ(
178 mainFrame()->document()->documentElement(),
179 rootScroller().get());
180 }
181
182 // Tests that setting an element as the root scroller causes it to control url
183 // bar hiding and overscroll.
184 TEST_F(RootScrollerTest, TestSetRootScroller)
185 {
186 initialize("root-scroller.html");
187
188 Element* container = mainFrame()->document()->getElementById("container");
189 TrackExceptionState exceptionState;
190 mainFrame()->document()->setRootScroller(container, exceptionState);
191 ASSERT_EQ(container, mainFrame()->document()->rootScroller());
192
193 // Content is 1000x1000, WebView size is 400x400 so max scroll is 600px.
194 double maximumScroll = 600;
195
196 webViewImpl()->handleInputEvent(
197 generateEvent(WebInputEvent::GestureScrollBegin));
198
199 {
200 // Scrolling over the #container DIV should cause the top controls to
201 // hide.
202 ASSERT_FLOAT_EQ(1, topControls().shownRatio());
203 webViewImpl()->handleInputEvent(generateEvent(
204 WebInputEvent::GestureScrollUpdate, 0, -topControls().height()));
205 ASSERT_FLOAT_EQ(0, topControls().shownRatio());
206 }
207
208 {
209 // Make sure we're actually scrolling the DIV and not the FrameView.
210 webViewImpl()->handleInputEvent(
211 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -100));
212 ASSERT_FLOAT_EQ(100, container->scrollTop());
213 ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
214 }
215
216 {
217 // Scroll 50 pixels past the end. Ensure we report the 50 pixels as
218 // overscroll.
219 EXPECT_CALL(m_client,
220 didOverscroll(
221 WebFloatSize(0, 50),
222 WebFloatSize(0, 50),
223 WebFloatPoint(100, 100),
224 WebFloatSize()));
225 webViewImpl()->handleInputEvent(
226 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -550));
227 ASSERT_FLOAT_EQ(maximumScroll, container->scrollTop());
228 ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
229 Mock::VerifyAndClearExpectations(&m_client);
230 }
231
232 {
233 // Continue the gesture overscroll.
234 EXPECT_CALL(m_client,
235 didOverscroll(
236 WebFloatSize(0, 20),
237 WebFloatSize(0, 70),
238 WebFloatPoint(100, 100),
239 WebFloatSize()));
240 webViewImpl()->handleInputEvent(
241 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -20));
242 ASSERT_FLOAT_EQ(maximumScroll, container->scrollTop());
243 ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
244 Mock::VerifyAndClearExpectations(&m_client);
245 }
246
247 webViewImpl()->handleInputEvent(
248 generateEvent(WebInputEvent::GestureScrollEnd));
249
250 {
251 // Make sure a new gesture scroll still won't scroll the frameview and
252 // overscrolls.
253 webViewImpl()->handleInputEvent(
254 generateEvent(WebInputEvent::GestureScrollBegin));
255
256 EXPECT_CALL(m_client,
257 didOverscroll(
258 WebFloatSize(0, 30),
259 WebFloatSize(0, 30),
260 WebFloatPoint(100, 100),
261 WebFloatSize()));
262 webViewImpl()->handleInputEvent(
263 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -30));
264 ASSERT_FLOAT_EQ(maximumScroll, container->scrollTop());
265 ASSERT_FLOAT_EQ(0, mainFrameView()->scrollPositionDouble().y());
266 Mock::VerifyAndClearExpectations(&m_client);
267
268 webViewImpl()->handleInputEvent(
269 generateEvent(WebInputEvent::GestureScrollEnd));
270 }
271
272 {
273 // Scrolling up should show the top controls.
274 webViewImpl()->handleInputEvent(
275 generateEvent(WebInputEvent::GestureScrollBegin));
276
277 ASSERT_FLOAT_EQ(0, topControls().shownRatio());
278 webViewImpl()->handleInputEvent(
279 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 30));
280 ASSERT_FLOAT_EQ(0.6, topControls().shownRatio());
281
282 webViewImpl()->handleInputEvent(
283 generateEvent(WebInputEvent::GestureScrollEnd));
284 }
285 }
286
287 // Tests that removing the element that is the root scroller from the DOM tree
288 // resets the default element to be the root scroller.
289 TEST_F(RootScrollerTest, TestRemoveRootScrollerFromDom)
290 {
291 initialize("root-scroller.html");
292
293 ASSERT_EQ(
294 mainFrame()->document()->documentElement(),
295 rootScroller().get());
296
297 Element* container = mainFrame()->document()->getElementById("container");
298 TrackExceptionState exceptionState;
299 mainFrame()->document()->setRootScroller(container, exceptionState);
300
301 ASSERT_EQ(container, mainFrame()->document()->rootScroller());
302
303 mainFrame()->document()->body()->removeChild(container);
304
305 ASSERT_EQ(
306 mainFrame()->document()->documentElement(),
307 mainFrame()->document()->rootScroller());
308 }
309
310 // Tests that setting an element that isn't a valid scroller as the root
311 // scroller fails and doesn't change the current root scroller.
312 TEST_F(RootScrollerTest, TestSetRootScrollerOnInvalidElement)
313 {
314 initialize("root-scroller.html");
315
316 {
317 // Set to a non-block element. Should be rejected and a console message
318 // logged.
319 Element* element = mainFrame()->document()->getElementById("nonBlock");
320 TrackExceptionState exceptionState;
321 mainFrame()->document()->setRootScroller(element, exceptionState);
322 ASSERT_EQ(
323 mainFrame()->document()->documentElement(),
324 mainFrame()->document()->rootScroller());
325 EXPECT_TRUE(exceptionState.hadException());
326 }
327
328 {
329 // Set to an element with no size.
330 Element* element = mainFrame()->document()->getElementById("empty");
331 TrackExceptionState exceptionState;
332 mainFrame()->document()->setRootScroller(element, exceptionState);
333 ASSERT_EQ(
334 mainFrame()->document()->documentElement(),
335 mainFrame()->document()->rootScroller());
336 EXPECT_TRUE(exceptionState.hadException());
337 }
338 }
339
340 // Test that the root scroller resets to the default element when the current
341 // root scroller element becomes invalid as a scroller.
342 TEST_F(RootScrollerTest, TestRootScrollerBecomesInvalid)
343 {
344 initialize("root-scroller.html");
345
346 ASSERT_EQ(
347 mainFrame()->document()->documentElement(),
348 rootScroller().get());
349
350 Element* container = mainFrame()->document()->getElementById("container");
351 TrackExceptionState exceptionState;
352 mainFrame()->document()->setRootScroller(container, exceptionState);
353
354 ASSERT_EQ(container, mainFrame()->document()->rootScroller());
355
356 executeScript(
357 "document.querySelector('#container').style.display = 'inline'");
358
359 ASSERT_EQ(
360 mainFrame()->document()->documentElement(),
361 mainFrame()->document()->rootScroller());
362 }
363
364 // Tests that setting the root scroller of the top codument to an element that
365 // belongs to a nested document fails.
366 TEST_F(RootScrollerTest, TestSetRootScrollerOnElementInIframe)
367 {
368 initialize("root-scroller-iframe.html");
369
370 ASSERT_EQ(
371 mainFrame()->document()->documentElement(),
372 rootScroller().get());
373
374 {
375 // Trying to set an element from a nested document should fail.
376 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
377 mainFrame()->document()->getElementById("iframe"));
378 Element* innerContainer =
379 iframe->contentDocument()->getElementById("container");
380
381 TrackExceptionState exceptionState;
382 mainFrame()->document()->setRootScroller(
383 innerContainer,
384 exceptionState);
385 EXPECT_TRUE(exceptionState.hadException());
386
387 ASSERT_EQ(
388 mainFrame()->document()->documentElement(),
389 rootScroller().get());
390 }
391
392 {
393 // Setting the iframe itself, however, should work.
394 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
395 mainFrame()->document()->getElementById("iframe"));
396
397 TrackExceptionState exceptionState;
398 mainFrame()->document()->setRootScroller(iframe, exceptionState);
399 EXPECT_FALSE(exceptionState.hadException());
400
401 ASSERT_EQ(iframe, rootScroller().get());
402 }
403 }
404
405 // Tests that setting an otherwise valid element as the root scroller on a
406 // document within an iframe fails and getting the root scroller in the nested
407 // document returns the default element.
408 TEST_F(RootScrollerTest, TestRootScrollerWithinIframe)
409 {
410 initialize("root-scroller-iframe.html");
411
412 ASSERT_EQ(
413 mainFrame()->document()->documentElement(),
414 rootScroller().get());
415
416 {
417 // Trying to set an element within nested document should fail.
418 // rootScroller() should always return its documentElement.
419 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
420 mainFrame()->document()->getElementById("iframe"));
421
422 ASSERT_EQ(
423 iframe->contentDocument()->documentElement(),
424 iframe->contentDocument()->rootScroller());
425
426 Element* innerContainer =
427 iframe->contentDocument()->getElementById("container");
428 TrackExceptionState exceptionState;
429 iframe->contentDocument()->setRootScroller(
430 innerContainer,
431 exceptionState);
432 EXPECT_TRUE(exceptionState.hadException());
433
434 ASSERT_EQ(
435 iframe->contentDocument()->documentElement(),
436 iframe->contentDocument()->rootScroller());
437 }
438 }
439
440 // Tests that trying to set an element as the root scroller of a document inside
441 // an iframe fails when that element belongs to the parent document.
442 TEST_F(RootScrollerTest, TestSetRootScrollerOnElementFromOutsideIframe)
443 {
444 initialize("root-scroller-iframe.html");
445
446 ASSERT_EQ(
447 mainFrame()->document()->documentElement(),
448 rootScroller().get());
449 {
450 // Try to set the the root scroller of the child document to be the
451 // <iframe> element in the parent document.
452 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
453 mainFrame()->document()->getElementById("iframe"));
454 NonThrowableExceptionState nonThrow;
455 Element* body =
456 mainFrame()->document()->querySelector("body", nonThrow);
457
458 ASSERT_EQ(
459 iframe->contentDocument()->documentElement(),
460 iframe->contentDocument()->rootScroller());
461
462 TrackExceptionState exceptionState;
463 iframe->contentDocument()->setRootScroller(
464 iframe,
465 exceptionState);
466 EXPECT_TRUE(exceptionState.hadException());
467
468 ASSERT_EQ(
469 iframe->contentDocument()->documentElement(),
470 iframe->contentDocument()->rootScroller());
471
472 exceptionState.clearException();
473
474 // Try to set the root scroller of the child document to be the
475 // <body> element of the parent document.
476 iframe->contentDocument()->setRootScroller(
477 body,
478 exceptionState);
479 EXPECT_TRUE(exceptionState.hadException());
480
481 ASSERT_EQ(
482 iframe->contentDocument()->documentElement(),
483 iframe->contentDocument()->rootScroller());
484 }
485 }
486
487 } // namespace
488
489 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698