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

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 Created 4 years, 8 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 Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs();
51 WebCache::clear();
52 }
53
54 WebViewImpl* initialize(const std::string& pageName)
55 {
56 RuntimeEnabledFeatures::setSetRootScrollerEnabled(true);
57
58 // Load a page with large body and set viewport size to 400x400 to
59 // ensure main frame is scrollable.
60 m_helper.initializeAndLoad(
61 m_baseURL + pageName, true, 0, &m_client, &configureSettings);
62
63 webViewImpl()->resize(IntSize(400, 400));
64
65 // Initialize top controls to be shown.
66 webViewImpl()->setTopControlsHeight(50.f, true);
67 webViewImpl()->topControls().setShownRatio(1);
68
69 mainFrameView()->updateAllLifecyclePhases();
70
71 return webViewImpl();
72 }
73
74 static void configureSettings(WebSettings* settings)
75 {
76 settings->setJavaScriptEnabled(true);
77 settings->setAcceleratedCompositingEnabled(true);
78 settings->setPreferCompositingToLCDTextEnabled(true);
79 // Android settings.
80 settings->setViewportEnabled(true);
81 settings->setViewportMetaEnabled(true);
82 settings->setShrinksViewportContentToFit(true);
83 settings->setMainFrameResizesAreOrientationChanges(true);
84 }
85
86 void registerMockedHttpURLLoad(const std::string& fileName)
87 {
88 URLTestHelpers::registerMockedURLFromBaseURL(
89 WebString::fromUTF8(m_baseURL.c_str()),
90 WebString::fromUTF8(fileName.c_str()));
91 }
92
93 void executeScript(const WebString& code)
94 {
95 mainWebFrame()->executeScript(WebScriptSource(code));
96 mainWebFrame()->view()->updateAllLifecyclePhases();
97 runPendingTasks();
98 }
99
100 WebGestureEvent generateEvent(
101 WebInputEvent::Type type, int deltaX = 0, int deltaY = 0)
102 {
103 WebGestureEvent event;
104 event.type = type;
105 event.sourceDevice = WebGestureDeviceTouchscreen;
106 event.x = 100;
107 event.y = 100;
108 if (type == WebInputEvent::GestureScrollUpdate) {
109 event.data.scrollUpdate.deltaX = deltaX;
110 event.data.scrollUpdate.deltaY = deltaY;
111 }
112 return event;
113 }
114
115 void verticalScroll(float deltaY)
116 {
117 webViewImpl()->handleInputEvent(
118 generateEvent(WebInputEvent::GestureScrollBegin));
119 webViewImpl()->handleInputEvent(
120 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -deltaY));
121 webViewImpl()->handleInputEvent(
122 generateEvent(WebInputEvent::GestureScrollEnd));
123 }
124
125 WebViewImpl* webViewImpl() const
126 {
127 return m_helper.webViewImpl();
128 }
129
130 FrameHost& frameHost() const
131 {
132 return m_helper.webViewImpl()->page()->frameHost();
133 }
134
135 LocalFrame* mainFrame() const
136 {
137 return toWebLocalFrameImpl(webViewImpl()->mainFrame())->frame();
138 }
139
140 WebLocalFrame* mainWebFrame() const
141 {
142 return toWebLocalFrameImpl(webViewImpl()->mainFrame());
143 }
144
145 FrameView* mainFrameView() const
146 {
147 return webViewImpl()->mainFrameImpl()->frame()->view();
148 }
149
150 VisualViewport& visualViewport() const
151 {
152 return frameHost().visualViewport();
153 }
154
155 RootScroller& rootScroller() const
156 {
157 return *frameHost().rootScroller();
158 }
159
160 TopControls& topControls() const
161 {
162 return frameHost().topControls();
163 }
164
165 protected:
166 std::string m_baseURL;
167 RootScrollerTestWebViewClient m_client;
168 FrameTestHelpers::WebViewHelper m_helper;
169 };
170
171 // Test that a root scroller is set if setRootScroller isn't called on any
172 // 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 overscroll rather
218 // than scrolling the FrameView.
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());
tdresser 2016/04/27 15:37:08 Because of scroll latching, we wouldn't scroll the
bokan 2016/04/27 17:53:56 Yah, this comment is misleading so I changed it. W
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 root scroller from the DOM tree resets the default
288 // 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 the root scroller on an invalid element doesn't change the
311 // 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 root scroller resets to the default element when the current root
341 // scroller becomes invalid.
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 on an element within a iframe fails.
tdresser 2016/04/27 15:37:08 Do you set the root scroller on an element, or do
bokan 2016/04/27 17:53:56 Good point. The way to think about it is that the
365 TEST_F(RootScrollerTest, TestSetRootScrollerOnElementInIframe)
366 {
367 initialize("root-scroller-iframe.html");
368
369 ASSERT_EQ(
370 mainFrame()->document()->documentElement(),
371 rootScroller().get());
372
373 {
374 // Trying to set an element from a nested document should fail.
375 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
376 mainFrame()->document()->getElementById("iframe"));
377 Element* innerContainer =
378 iframe->contentDocument()->getElementById("container");
379
380 TrackExceptionState exceptionState;
381 mainFrame()->document()->setRootScroller(
382 innerContainer,
383 exceptionState);
384 EXPECT_TRUE(exceptionState.hadException());
385
386 ASSERT_EQ(
387 mainFrame()->document()->documentElement(),
388 rootScroller().get());
389 }
390
391 {
392 // Setting the iframe itself, however, should work.
393 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
394 mainFrame()->document()->getElementById("iframe"));
395
396 TrackExceptionState exceptionState;
397 mainFrame()->document()->setRootScroller(iframe, exceptionState);
398 EXPECT_FALSE(exceptionState.hadException());
399
400 ASSERT_EQ(iframe, rootScroller().get());
401 }
402 }
403
404 // Tests that setting the root scroller on a document within an iframe fails
405 // and getting the root scroller returns the default element.
406 TEST_F(RootScrollerTest, TestRootScrollerWithinIframe)
407 {
408 initialize("root-scroller-iframe.html");
409
410 ASSERT_EQ(
411 mainFrame()->document()->documentElement(),
412 rootScroller().get());
413
414 {
tdresser 2016/04/27 15:37:08 It might be worth splitting up these two cases int
bokan 2016/04/27 17:53:56 Done.
415 // Trying to set an element within nested document should fail.
416 // rootScroller() should always return its documentElement.
417 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
418 mainFrame()->document()->getElementById("iframe"));
419
420 ASSERT_EQ(
421 iframe->contentDocument()->documentElement(),
422 iframe->contentDocument()->rootScroller());
423
424 Element* innerContainer =
425 iframe->contentDocument()->getElementById("container");
426 TrackExceptionState exceptionState;
427 iframe->contentDocument()->setRootScroller(
428 innerContainer,
429 exceptionState);
430 EXPECT_TRUE(exceptionState.hadException());
431
432 ASSERT_EQ(
433 iframe->contentDocument()->documentElement(),
434 iframe->contentDocument()->rootScroller());
435 }
436
437 {
438 // Trying to set an element from outside the iframe should fail.
tdresser 2016/04/27 15:37:08 I initially misinterpreted this comment, thinking
bokan 2016/04/27 17:53:56 Done.
439 HTMLFrameOwnerElement* iframe = toHTMLFrameOwnerElement(
440 mainFrame()->document()->getElementById("iframe"));
441 NonThrowableExceptionState nonThrow;
442 Element* body =
443 mainFrame()->document()->querySelector("body", nonThrow);
444
445 ASSERT_EQ(
446 iframe->contentDocument()->documentElement(),
447 iframe->contentDocument()->rootScroller());
448
449 TrackExceptionState exceptionState1;
tdresser 2016/04/27 15:37:08 Is there any reason to not reuse the exceptionStat
bokan 2016/04/27 17:53:56 Didn't think to just clear the exception state. Do
450 iframe->contentDocument()->setRootScroller(
451 iframe,
452 exceptionState1);
453 EXPECT_TRUE(exceptionState1.hadException());
454
455 ASSERT_EQ(
456 iframe->contentDocument()->documentElement(),
457 iframe->contentDocument()->rootScroller());
458
459 TrackExceptionState exceptionState2;
460 iframe->contentDocument()->setRootScroller(
461 body,
462 exceptionState2);
463 EXPECT_TRUE(exceptionState2.hadException());
464
465 ASSERT_EQ(
466 iframe->contentDocument()->documentElement(),
467 iframe->contentDocument()->rootScroller());
468 }
469 }
470
471 } // namespace
472
473 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698