OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2015 Google Inc. All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are | |
6 * met: | |
7 * | |
8 * * Redistributions of source code must retain the above copyright | |
9 * notice, this list of conditions and the following disclaimer. | |
10 * * Redistributions in binary form must reproduce the above | |
11 * copyright notice, this list of conditions and the following disclaimer | |
12 * in the documentation and/or other materials provided with the | |
13 * distribution. | |
14 * * Neither the name of Google Inc. nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 #include "core/frame/TopControls.h" | |
31 | |
32 #include "core/dom/ClientRect.h" | |
33 #include "core/frame/FrameHost.h" | |
34 #include "core/frame/FrameView.h" | |
35 #include "core/frame/LocalFrame.h" | |
36 #include "core/frame/VisualViewport.h" | |
37 #include "core/page/Page.h" | |
38 #include "platform/testing/URLTestHelpers.h" | |
39 #include "public/platform/Platform.h" | |
40 #include "public/platform/WebURLLoaderMockFactory.h" | |
41 #include "public/web/WebCache.h" | |
42 #include "public/web/WebElement.h" | |
43 #include "public/web/WebSettings.h" | |
44 #include "testing/gmock/include/gmock/gmock.h" | |
45 #include "testing/gtest/include/gtest/gtest.h" | |
46 #include "web/WebLocalFrameImpl.h" | |
47 #include "web/tests/FrameTestHelpers.h" | |
48 | |
49 namespace blink { | |
50 | |
51 // These tests cover top controls scrolling on main-thread. | |
52 // The animation for completing a partial show/hide is done in compositor so | |
53 // it is not covered here. | |
54 class TopControlsTest : public testing::Test { | |
55 public: | |
56 TopControlsTest() : m_baseURL("http://www.test.com/") { | |
57 registerMockedHttpURLLoad("large-div.html"); | |
58 registerMockedHttpURLLoad("overflow-scrolling.html"); | |
59 registerMockedHttpURLLoad("iframe-scrolling.html"); | |
60 registerMockedHttpURLLoad("iframe-scrolling-inner.html"); | |
61 registerMockedHttpURLLoad("percent-height.html"); | |
62 registerMockedHttpURLLoad("vh-height.html"); | |
63 registerMockedHttpURLLoad("vh-height-width-800.html"); | |
64 registerMockedHttpURLLoad("vh-height-width-800-extra-wide.html"); | |
65 } | |
66 | |
67 ~TopControlsTest() override { | |
68 Platform::current()->getURLLoaderMockFactory()->unregisterAllURLs(); | |
69 WebCache::clear(); | |
70 } | |
71 | |
72 WebViewImpl* initialize(const std::string& pageName = "large-div.html") { | |
73 RuntimeEnabledFeatures::setInertTopControlsEnabled(true); | |
74 | |
75 // Load a page with large body and set viewport size to 400x400 to ensure | |
76 // main frame is scrollable. | |
77 m_helper.initializeAndLoad(m_baseURL + pageName, true, nullptr, nullptr, | |
78 nullptr, &configureSettings); | |
79 | |
80 webViewImpl()->resize(IntSize(400, 400)); | |
81 return webViewImpl(); | |
82 } | |
83 | |
84 static void configureSettings(WebSettings* settings) { | |
85 settings->setJavaScriptEnabled(true); | |
86 settings->setAcceleratedCompositingEnabled(true); | |
87 settings->setPreferCompositingToLCDTextEnabled(true); | |
88 // Android settings | |
89 settings->setViewportEnabled(true); | |
90 settings->setViewportMetaEnabled(true); | |
91 settings->setShrinksViewportContentToFit(true); | |
92 settings->setMainFrameResizesAreOrientationChanges(true); | |
93 } | |
94 | |
95 void registerMockedHttpURLLoad(const std::string& fileName) { | |
96 URLTestHelpers::registerMockedURLFromBaseURL( | |
97 WebString::fromUTF8(m_baseURL.c_str()), | |
98 WebString::fromUTF8(fileName.c_str())); | |
99 } | |
100 | |
101 WebGestureEvent generateEvent(WebInputEvent::Type type, | |
102 int deltaX = 0, | |
103 int deltaY = 0) { | |
104 WebGestureEvent event; | |
105 event.type = type; | |
106 event.sourceDevice = WebGestureDeviceTouchscreen; | |
107 event.x = 100; | |
108 event.y = 100; | |
109 if (type == WebInputEvent::GestureScrollUpdate) { | |
110 event.data.scrollUpdate.deltaX = deltaX; | |
111 event.data.scrollUpdate.deltaY = deltaY; | |
112 } | |
113 return event; | |
114 } | |
115 | |
116 void verticalScroll(float deltaY) { | |
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 Element* getElementById(const WebString& id) { | |
126 return static_cast<Element*>( | |
127 webViewImpl()->mainFrame()->document().getElementById(id)); | |
128 } | |
129 | |
130 WebViewImpl* webViewImpl() const { return m_helper.webView(); } | |
131 LocalFrame* frame() const { | |
132 return m_helper.webView()->mainFrameImpl()->frame(); | |
133 } | |
134 VisualViewport& visualViewport() const { | |
135 return m_helper.webView()->page()->frameHost().visualViewport(); | |
136 } | |
137 | |
138 private: | |
139 std::string m_baseURL; | |
140 FrameTestHelpers::WebViewHelper m_helper; | |
141 }; | |
142 | |
143 #define EXPECT_SIZE_EQ(expected, actual) \ | |
144 do { \ | |
145 EXPECT_FLOAT_EQ((expected).width(), (actual).width()); \ | |
146 EXPECT_FLOAT_EQ((expected).height(), (actual).height()); \ | |
147 } while (false) | |
148 | |
149 // Disable these tests on Mac OSX until further investigation. | |
150 // Local build on Mac is OK but the bot fails. This is not an issue as | |
151 // Top Controls are currently only used on Android. | |
152 #if OS(MACOSX) | |
153 #define MAYBE(test) DISABLED_##test | |
154 #else | |
155 #define MAYBE(test) test | |
156 #endif | |
157 | |
158 // Scrolling down should hide top controls. | |
159 TEST_F(TopControlsTest, MAYBE(HideOnScrollDown)) { | |
160 WebViewImpl* webView = initialize(); | |
161 // initialize top controls to be shown. | |
162 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
163 webView->topControls().setShownRatio(1); | |
164 | |
165 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
166 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
167 | |
168 // Top controls should be scrolled partially and page should not scroll. | |
169 webView->handleInputEvent( | |
170 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -25.f)); | |
171 EXPECT_FLOAT_EQ(25.f, webView->topControls().contentOffset()); | |
172 EXPECT_SIZE_EQ(ScrollOffset(0, 0), frame()->view()->scrollOffset()); | |
173 | |
174 // Top controls should consume 25px and become hidden. Excess scroll should be | |
175 // consumed by the page. | |
176 webView->handleInputEvent( | |
177 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -40.f)); | |
178 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
179 EXPECT_SIZE_EQ(ScrollOffset(0, 15), frame()->view()->scrollOffset()); | |
180 | |
181 // Only page should consume scroll | |
182 webView->handleInputEvent( | |
183 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -20.f)); | |
184 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
185 EXPECT_SIZE_EQ(ScrollOffset(0, 35), frame()->view()->scrollOffset()); | |
186 } | |
187 | |
188 // Scrolling up should show top controls. | |
189 TEST_F(TopControlsTest, MAYBE(ShowOnScrollUp)) { | |
190 WebViewImpl* webView = initialize(); | |
191 // initialize top controls to be hidden. | |
192 webView->resizeWithTopControls(webView->size(), 50.f, false); | |
193 webView->topControls().setShownRatio(0); | |
194 | |
195 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
196 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
197 | |
198 webView->handleInputEvent( | |
199 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 10.f)); | |
200 EXPECT_FLOAT_EQ(10.f, webView->topControls().contentOffset()); | |
201 EXPECT_SIZE_EQ(ScrollOffset(0, 0), frame()->view()->scrollOffset()); | |
202 | |
203 webView->handleInputEvent( | |
204 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 50.f)); | |
205 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
206 EXPECT_SIZE_EQ(ScrollOffset(0, 0), frame()->view()->scrollOffset()); | |
207 } | |
208 | |
209 // Scrolling up after previous scroll downs should cause top controls to be | |
210 // shown only after all previously scrolled down amount is compensated. | |
211 TEST_F(TopControlsTest, MAYBE(ScrollDownThenUp)) { | |
212 WebViewImpl* webView = initialize(); | |
213 // initialize top controls to be shown and position page at 100px. | |
214 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
215 webView->topControls().setShownRatio(1); | |
216 frame()->view()->getScrollableArea()->setScrollOffset(ScrollOffset(0, 100), | |
217 ProgrammaticScroll); | |
218 | |
219 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
220 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
221 | |
222 // Scroll down to completely hide top controls. Excess deltaY (100px) should | |
223 // be consumed by the page. | |
224 webView->handleInputEvent( | |
225 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -150.f)); | |
226 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
227 EXPECT_SIZE_EQ(ScrollOffset(0, 200), frame()->view()->scrollOffset()); | |
228 | |
229 // Scroll up and ensure the top controls does not move until we recover 100px | |
230 // previously scrolled. | |
231 webView->handleInputEvent( | |
232 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 40.f)); | |
233 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
234 EXPECT_SIZE_EQ(ScrollOffset(0, 160), frame()->view()->scrollOffset()); | |
235 | |
236 webView->handleInputEvent( | |
237 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 60.f)); | |
238 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
239 EXPECT_SIZE_EQ(ScrollOffset(0, 100), frame()->view()->scrollOffset()); | |
240 | |
241 // Now we have hit the threshold so further scroll up should be consumed by | |
242 // top controls. | |
243 webView->handleInputEvent( | |
244 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 30.f)); | |
245 EXPECT_FLOAT_EQ(30.f, webView->topControls().contentOffset()); | |
246 EXPECT_SIZE_EQ(ScrollOffset(0, 100), frame()->view()->scrollOffset()); | |
247 | |
248 // Once top control is fully shown then page should consume any excess scroll. | |
249 webView->handleInputEvent( | |
250 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 70.f)); | |
251 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
252 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
253 } | |
254 | |
255 // Scrolling down should always cause visible top controls to start hiding even | |
256 // if we have been scrolling up previously. | |
257 TEST_F(TopControlsTest, MAYBE(ScrollUpThenDown)) { | |
258 WebViewImpl* webView = initialize(); | |
259 // initialize top controls to be hidden and position page at 100px. | |
260 webView->resizeWithTopControls(webView->size(), 50.f, false); | |
261 webView->topControls().setShownRatio(0); | |
262 frame()->view()->getScrollableArea()->setScrollOffset(ScrollOffset(0, 100), | |
263 ProgrammaticScroll); | |
264 | |
265 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
266 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
267 | |
268 // Scroll up to completely show top controls. Excess deltaY (50px) should be | |
269 // consumed by the page. | |
270 webView->handleInputEvent( | |
271 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 100.f)); | |
272 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
273 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
274 | |
275 // Scroll down and ensure only top controls is scrolled | |
276 webView->handleInputEvent( | |
277 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -40.f)); | |
278 EXPECT_FLOAT_EQ(10.f, webView->topControls().contentOffset()); | |
279 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
280 | |
281 webView->handleInputEvent( | |
282 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -60.f)); | |
283 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
284 EXPECT_SIZE_EQ(ScrollOffset(0, 100), frame()->view()->scrollOffset()); | |
285 } | |
286 | |
287 // Top controls should not consume horizontal scroll. | |
288 TEST_F(TopControlsTest, MAYBE(HorizontalScroll)) { | |
289 WebViewImpl* webView = initialize(); | |
290 // initialize top controls to be shown. | |
291 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
292 webView->topControls().setShownRatio(1); | |
293 | |
294 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
295 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
296 | |
297 // Top controls should not consume horizontal scroll. | |
298 webView->handleInputEvent( | |
299 generateEvent(WebInputEvent::GestureScrollUpdate, -110.f, -100.f)); | |
300 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
301 EXPECT_SIZE_EQ(ScrollOffset(110, 50), frame()->view()->scrollOffset()); | |
302 | |
303 webView->handleInputEvent( | |
304 generateEvent(WebInputEvent::GestureScrollUpdate, -40.f, 0)); | |
305 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
306 EXPECT_SIZE_EQ(ScrollOffset(150, 50), frame()->view()->scrollOffset()); | |
307 } | |
308 | |
309 // Page scale should not impact top controls scrolling | |
310 TEST_F(TopControlsTest, MAYBE(PageScaleHasNoImpact)) { | |
311 WebViewImpl* webView = initialize(); | |
312 webViewImpl()->setDefaultPageScaleLimits(0.25f, 5); | |
313 webView->setPageScaleFactor(2.0); | |
314 | |
315 // Initialize top controls to be shown. | |
316 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
317 webView->topControls().setShownRatio(1); | |
318 | |
319 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
320 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
321 | |
322 // Top controls should be scrolled partially and page should not scroll. | |
323 webView->handleInputEvent( | |
324 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -20.f)); | |
325 EXPECT_FLOAT_EQ(30.f, webView->topControls().contentOffset()); | |
326 EXPECT_SIZE_EQ(ScrollOffset(0, 0), | |
327 frame()->view()->getScrollableArea()->scrollOffset()); | |
328 | |
329 // Top controls should consume 30px and become hidden. Excess scroll should be | |
330 // consumed by the page at 2x scale. | |
331 webView->handleInputEvent( | |
332 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -70.f)); | |
333 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
334 EXPECT_SIZE_EQ(ScrollOffset(0, 20), | |
335 frame()->view()->getScrollableArea()->scrollOffset()); | |
336 | |
337 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollEnd)); | |
338 | |
339 // Change page scale and test. | |
340 webView->setPageScaleFactor(0.5); | |
341 | |
342 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
343 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
344 EXPECT_SIZE_EQ(ScrollOffset(0, 20), | |
345 frame()->view()->getScrollableArea()->scrollOffset()); | |
346 | |
347 webView->handleInputEvent( | |
348 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 50.f)); | |
349 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
350 EXPECT_SIZE_EQ(ScrollOffset(0, 20), | |
351 frame()->view()->getScrollableArea()->scrollOffset()); | |
352 | |
353 // At 0.5x scale scrolling 10px should take us to the top of the page. | |
354 webView->handleInputEvent( | |
355 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 10.f)); | |
356 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
357 EXPECT_SIZE_EQ(ScrollOffset(0, 0), | |
358 frame()->view()->getScrollableArea()->scrollOffset()); | |
359 } | |
360 | |
361 // Some scroll deltas result in a shownRatio that can't be realized in a | |
362 // floating-point number. Make sure that if the top controls aren't fully | |
363 // scrolled, scrollBy doesn't return any excess delta. i.e. There should be no | |
364 // slippage between the content and top controls. | |
365 TEST_F(TopControlsTest, MAYBE(FloatingPointSlippage)) { | |
366 WebViewImpl* webView = initialize(); | |
367 webViewImpl()->setDefaultPageScaleLimits(0.25f, 5); | |
368 webView->setPageScaleFactor(2.0); | |
369 | |
370 // Initialize top controls to be shown. | |
371 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
372 webView->topControls().setShownRatio(1); | |
373 | |
374 webView->topControls().scrollBegin(); | |
375 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
376 | |
377 // This will result in a 20px scroll to the top controls so the show ratio | |
378 // will be 30/50 == 0.6 which is not representible in a float. Make sure | |
379 // that scroll still consumes the whole delta. | |
380 FloatSize remainingDelta = webView->topControls().scrollBy(FloatSize(0, 10)); | |
381 EXPECT_EQ(0, remainingDelta.height()); | |
382 } | |
383 | |
384 // Scrollable subregions should scroll before top controls | |
385 TEST_F(TopControlsTest, MAYBE(ScrollableSubregionScrollFirst)) { | |
386 WebViewImpl* webView = initialize("overflow-scrolling.html"); | |
387 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
388 webView->topControls().setShownRatio(1); | |
389 frame()->view()->getScrollableArea()->setScrollOffset(ScrollOffset(0, 50), | |
390 ProgrammaticScroll); | |
391 | |
392 // Test scroll down | |
393 // Scroll down should scroll the overflow div first but top controls and main | |
394 // frame should not scroll. | |
395 verticalScroll(-800.f); | |
396 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
397 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
398 | |
399 // Continued scroll down should start hiding top controls but main frame | |
400 // should not scroll. | |
401 verticalScroll(-40.f); | |
402 EXPECT_FLOAT_EQ(10.f, webView->topControls().contentOffset()); | |
403 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
404 | |
405 // Continued scroll down should scroll down the main frame | |
406 verticalScroll(-40.f); | |
407 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
408 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
409 | |
410 // Test scroll up | |
411 // scroll up should scroll overflow div first | |
412 verticalScroll(800.f); | |
413 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
414 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
415 | |
416 // Continued scroll up should start showing top controls but main frame should | |
417 // not scroll. | |
418 verticalScroll(40.f); | |
419 EXPECT_FLOAT_EQ(40.f, webView->topControls().contentOffset()); | |
420 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
421 | |
422 // Continued scroll down up scroll up the main frame | |
423 verticalScroll(40.f); | |
424 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
425 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
426 } | |
427 | |
428 // Scrollable iframes should scroll before top controls | |
429 TEST_F(TopControlsTest, MAYBE(ScrollableIframeScrollFirst)) { | |
430 WebViewImpl* webView = initialize("iframe-scrolling.html"); | |
431 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
432 webView->topControls().setShownRatio(1); | |
433 frame()->view()->getScrollableArea()->setScrollOffset(ScrollOffset(0, 50), | |
434 ProgrammaticScroll); | |
435 | |
436 // Test scroll down | |
437 // Scroll down should scroll the iframe first but top controls and main frame | |
438 // should not scroll. | |
439 verticalScroll(-800.f); | |
440 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
441 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
442 | |
443 // Continued scroll down should start hiding top controls but main frame | |
444 // should not scroll. | |
445 verticalScroll(-40.f); | |
446 EXPECT_FLOAT_EQ(10.f, webView->topControls().contentOffset()); | |
447 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
448 | |
449 // Continued scroll down should scroll down the main frame | |
450 verticalScroll(-40.f); | |
451 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
452 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
453 | |
454 // Test scroll up | |
455 // scroll up should scroll iframe first | |
456 verticalScroll(800.f); | |
457 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
458 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
459 | |
460 // Continued scroll up should start showing top controls but main frame should | |
461 // not scroll. | |
462 verticalScroll(40.f); | |
463 EXPECT_FLOAT_EQ(40.f, webView->topControls().contentOffset()); | |
464 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
465 | |
466 // Continued scroll down up scroll up the main frame | |
467 verticalScroll(40.f); | |
468 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
469 EXPECT_SIZE_EQ(ScrollOffset(0, 50), frame()->view()->scrollOffset()); | |
470 } | |
471 | |
472 // Top controls visibility should remain consistent when height is changed. | |
473 TEST_F(TopControlsTest, MAYBE(HeightChangeMaintainsVisibility)) { | |
474 WebViewImpl* webView = initialize(); | |
475 webView->resizeWithTopControls(webView->size(), 20.f, false); | |
476 webView->topControls().setShownRatio(0); | |
477 | |
478 webView->resizeWithTopControls(webView->size(), 20.f, false); | |
479 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
480 | |
481 webView->resizeWithTopControls(webView->size(), 40.f, false); | |
482 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
483 | |
484 // Scroll up to show top controls. | |
485 verticalScroll(40.f); | |
486 EXPECT_FLOAT_EQ(40.f, webView->topControls().contentOffset()); | |
487 | |
488 // Changing height of a fully shown top controls should correctly adjust | |
489 // content offset | |
490 webView->resizeWithTopControls(webView->size(), 30.f, false); | |
491 EXPECT_FLOAT_EQ(30.f, webView->topControls().contentOffset()); | |
492 } | |
493 | |
494 // Zero delta should not have any effect on top controls. | |
495 TEST_F(TopControlsTest, MAYBE(ZeroHeightMeansNoEffect)) { | |
496 WebViewImpl* webView = initialize(); | |
497 webView->resizeWithTopControls(webView->size(), 0, false); | |
498 webView->topControls().setShownRatio(0); | |
499 frame()->view()->getScrollableArea()->setScrollOffset(ScrollOffset(0, 100), | |
500 ProgrammaticScroll); | |
501 | |
502 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
503 | |
504 verticalScroll(20.f); | |
505 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
506 EXPECT_SIZE_EQ(ScrollOffset(0, 80), frame()->view()->scrollOffset()); | |
507 | |
508 verticalScroll(-30.f); | |
509 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
510 EXPECT_SIZE_EQ(ScrollOffset(0, 110), frame()->view()->scrollOffset()); | |
511 | |
512 webView->topControls().setShownRatio(1); | |
513 EXPECT_FLOAT_EQ(0.f, webView->topControls().contentOffset()); | |
514 } | |
515 | |
516 // Top controls should not hide when scrolling up past limit | |
517 TEST_F(TopControlsTest, MAYBE(ScrollUpPastLimitDoesNotHide)) { | |
518 WebViewImpl* webView = initialize(); | |
519 // Initialize top controls to be shown | |
520 webView->resizeWithTopControls(webView->size(), 50.f, true); | |
521 webView->topControls().setShownRatio(1); | |
522 // Use 2x scale so that both visual viewport and frameview are scrollable | |
523 webView->setPageScaleFactor(2.0); | |
524 | |
525 // Fully scroll frameview but visualviewport remains scrollable | |
526 webView->mainFrame()->setScrollOffset(WebSize(0, 10000)); | |
527 visualViewport().setLocation(FloatPoint(0, 0)); | |
528 verticalScroll(-10.f); | |
529 EXPECT_FLOAT_EQ(40, webView->topControls().contentOffset()); | |
530 | |
531 webView->topControls().setShownRatio(1); | |
532 // Fully scroll visual veiwport but frameview remains scrollable | |
533 webView->mainFrame()->setScrollOffset(WebSize(0, 0)); | |
534 visualViewport().setLocation(FloatPoint(0, 10000)); | |
535 verticalScroll(-20.f); | |
536 EXPECT_FLOAT_EQ(30, webView->topControls().contentOffset()); | |
537 | |
538 webView->topControls().setShownRatio(1); | |
539 // Fully scroll both frameview and visual viewport | |
540 webView->mainFrame()->setScrollOffset(WebSize(0, 10000)); | |
541 visualViewport().setLocation(FloatPoint(0, 10000)); | |
542 verticalScroll(-30.f); | |
543 // Top controls should not move because neither frameview nor visual viewport | |
544 // are scrollable | |
545 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
546 } | |
547 | |
548 // Top controls should honor its constraints | |
549 TEST_F(TopControlsTest, MAYBE(StateConstraints)) { | |
550 WebViewImpl* webView = initialize(); | |
551 webView->resizeWithTopControls(webView->size(), 50.f, false); | |
552 frame()->view()->getScrollableArea()->setScrollOffset(ScrollOffset(0, 100), | |
553 ProgrammaticScroll); | |
554 | |
555 // Setting permitted state should change the content offset to match the | |
556 // constraint. | |
557 webView->updateTopControlsState(WebTopControlsShown, WebTopControlsShown, | |
558 false); | |
559 EXPECT_FLOAT_EQ(50.f, webView->topControls().contentOffset()); | |
560 | |
561 // Only shown state is permitted so controls cannot hide | |
562 verticalScroll(-20.f); | |
563 EXPECT_FLOAT_EQ(50, webView->topControls().contentOffset()); | |
564 EXPECT_SIZE_EQ(ScrollOffset(0, 120), frame()->view()->scrollOffset()); | |
565 | |
566 // Setting permitted state should change content offset to match the | |
567 // constraint. | |
568 webView->updateTopControlsState(WebTopControlsHidden, WebTopControlsHidden, | |
569 false); | |
570 EXPECT_FLOAT_EQ(0, webView->topControls().contentOffset()); | |
571 | |
572 // Only hidden state is permitted so controls cannot show | |
573 verticalScroll(30.f); | |
574 EXPECT_FLOAT_EQ(0, webView->topControls().contentOffset()); | |
575 EXPECT_SIZE_EQ(ScrollOffset(0, 90), frame()->view()->scrollOffset()); | |
576 | |
577 // Setting permitted state to "both" should not change content offset. | |
578 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsBoth, | |
579 false); | |
580 EXPECT_FLOAT_EQ(0, webView->topControls().contentOffset()); | |
581 | |
582 // Both states are permitted so controls can either show or hide | |
583 verticalScroll(50.f); | |
584 EXPECT_FLOAT_EQ(50, webView->topControls().contentOffset()); | |
585 EXPECT_SIZE_EQ(ScrollOffset(0, 90), frame()->view()->scrollOffset()); | |
586 | |
587 verticalScroll(-50.f); | |
588 EXPECT_FLOAT_EQ(0, webView->topControls().contentOffset()); | |
589 EXPECT_SIZE_EQ(ScrollOffset(0, 90), frame()->view()->scrollOffset()); | |
590 | |
591 // Setting permitted state to "both" should not change an in-flight offset. | |
592 verticalScroll(20.f); | |
593 EXPECT_FLOAT_EQ(20, webView->topControls().contentOffset()); | |
594 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsBoth, | |
595 false); | |
596 EXPECT_FLOAT_EQ(20, webView->topControls().contentOffset()); | |
597 | |
598 // An animated state change shouldn't cause a change to the content offset | |
599 // since it'll be driven from the compositor. | |
600 webView->updateTopControlsState(WebTopControlsHidden, WebTopControlsHidden, | |
601 true); | |
602 EXPECT_FLOAT_EQ(20, webView->topControls().contentOffset()); | |
603 | |
604 webView->updateTopControlsState(WebTopControlsShown, WebTopControlsShown, | |
605 true); | |
606 EXPECT_FLOAT_EQ(20, webView->topControls().contentOffset()); | |
607 | |
608 // Setting just the constraint should affect the content offset. | |
609 webView->updateTopControlsState(WebTopControlsHidden, WebTopControlsBoth, | |
610 false); | |
611 EXPECT_FLOAT_EQ(0, webView->topControls().contentOffset()); | |
612 | |
613 webView->updateTopControlsState(WebTopControlsShown, WebTopControlsBoth, | |
614 false); | |
615 EXPECT_FLOAT_EQ(50, webView->topControls().contentOffset()); | |
616 } | |
617 | |
618 // Ensure that top controls do not affect the layout by showing and hiding | |
619 // except for position: fixed elements. | |
620 TEST_F(TopControlsTest, MAYBE(DontAffectLayoutHeight)) { | |
621 // Initialize with the top controls showing. | |
622 WebViewImpl* webView = initialize("percent-height.html"); | |
623 webView->resizeWithTopControls(WebSize(400, 300), 100.f, true); | |
624 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsShown, | |
625 false); | |
626 webView->topControls().setShownRatio(1); | |
627 webView->updateAllLifecyclePhases(); | |
628 | |
629 ASSERT_EQ(100.f, webView->topControls().contentOffset()); | |
630 | |
631 // When the top controls are showing, there's 300px for the layout height so | |
632 // 50% should result in both the position:fixed and position: absolute divs | |
633 // having 150px of height. | |
634 Element* absPos = getElementById(WebString::fromUTF8("abs")); | |
635 Element* fixedPos = getElementById(WebString::fromUTF8("fixed")); | |
636 EXPECT_FLOAT_EQ(150.f, absPos->getBoundingClientRect()->height()); | |
637 EXPECT_FLOAT_EQ(150.f, fixedPos->getBoundingClientRect()->height()); | |
638 | |
639 // The layout size on the FrameView should not include the top controls. | |
640 EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height()); | |
641 | |
642 // Hide the top controls. | |
643 verticalScroll(-100.f); | |
644 webView->resizeWithTopControls(WebSize(400, 400), 100.f, false); | |
645 webView->updateAllLifecyclePhases(); | |
646 | |
647 ASSERT_EQ(0.f, webView->topControls().contentOffset()); | |
648 | |
649 // Hiding the top controls shouldn't change the height of the initial | |
650 // containing block for non-position: fixed. Position: fixed however should | |
651 // use the entire height of the viewport however. | |
652 EXPECT_FLOAT_EQ(150.f, absPos->getBoundingClientRect()->height()); | |
653 EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height()); | |
654 | |
655 // The layout size should not change as a result of top controls hiding. | |
656 EXPECT_EQ(300, frame()->view()->layoutSize(IncludeScrollbars).height()); | |
657 } | |
658 | |
659 // Ensure that top controls do not affect vh units. | |
660 TEST_F(TopControlsTest, MAYBE(DontAffectVHUnits)) { | |
661 // Initialize with the top controls showing. | |
662 WebViewImpl* webView = initialize("vh-height.html"); | |
663 webView->resizeWithTopControls(WebSize(400, 300), 100.f, true); | |
664 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsShown, | |
665 false); | |
666 webView->topControls().setShownRatio(1); | |
667 webView->updateAllLifecyclePhases(); | |
668 | |
669 ASSERT_EQ(100.f, webView->topControls().contentOffset()); | |
670 | |
671 // 'vh' units should be based on the viewport when the top controls are | |
672 // hidden. | |
673 Element* absPos = getElementById(WebString::fromUTF8("abs")); | |
674 Element* fixedPos = getElementById(WebString::fromUTF8("fixed")); | |
675 EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height()); | |
676 EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height()); | |
677 | |
678 // The size used for viewport units should not be reduced by the top | |
679 // controls. | |
680 EXPECT_EQ(400, frame()->view()->viewportSizeForViewportUnits().height()); | |
681 | |
682 // Hide the top controls. | |
683 verticalScroll(-100.f); | |
684 webView->resizeWithTopControls(WebSize(400, 400), 100.f, false); | |
685 webView->updateAllLifecyclePhases(); | |
686 | |
687 ASSERT_EQ(0.f, webView->topControls().contentOffset()); | |
688 | |
689 // vh units should be static with respect to the top controls so neighter | |
690 // <div> should change size are a result of the top controls hiding. | |
691 EXPECT_FLOAT_EQ(200.f, absPos->getBoundingClientRect()->height()); | |
692 EXPECT_FLOAT_EQ(200.f, fixedPos->getBoundingClientRect()->height()); | |
693 | |
694 // The viewport size used for vh units should not change as a result of top | |
695 // controls hiding. | |
696 EXPECT_EQ(400, frame()->view()->viewportSizeForViewportUnits().height()); | |
697 } | |
698 | |
699 // Ensure that on a legacy page (there's a non-1 minimum scale) 100vh units fill | |
700 // the viewport, with top controls hidden, when the viewport encompasses the | |
701 // layout width. | |
702 TEST_F(TopControlsTest, MAYBE(DontAffectVHUnitsWithScale)) { | |
703 // Initialize with the top controls showing. | |
704 WebViewImpl* webView = initialize("vh-height-width-800.html"); | |
705 webView->resizeWithTopControls(WebSize(400, 300), 100.f, true); | |
706 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsShown, | |
707 false); | |
708 webView->topControls().setShownRatio(1); | |
709 webView->updateAllLifecyclePhases(); | |
710 | |
711 ASSERT_EQ(100.f, webView->topControls().contentOffset()); | |
712 | |
713 // Device viewport is 400px but the page is width=800 so minimum-scale | |
714 // should be 0.5. This is also the scale at which the viewport fills the | |
715 // layout width. | |
716 ASSERT_EQ(0.5f, webView->minimumPageScaleFactor()); | |
717 | |
718 // We should size vh units so that 100vh fills the viewport at min-scale so | |
719 // we have to account for the minimum page scale factor. Since both boxes | |
720 // are 50vh, and layout scale = 0.5, we have a vh viewport of 400 / 0.5 = 800 | |
721 // so we expect 50vh to be 400px. | |
722 Element* absPos = getElementById(WebString::fromUTF8("abs")); | |
723 Element* fixedPos = getElementById(WebString::fromUTF8("fixed")); | |
724 EXPECT_FLOAT_EQ(400.f, absPos->getBoundingClientRect()->height()); | |
725 EXPECT_FLOAT_EQ(400.f, fixedPos->getBoundingClientRect()->height()); | |
726 | |
727 // The size used for viewport units should not be reduced by the top | |
728 // controls. | |
729 EXPECT_EQ(800, frame()->view()->viewportSizeForViewportUnits().height()); | |
730 | |
731 // Hide the top controls. | |
732 verticalScroll(-100.f); | |
733 webView->resizeWithTopControls(WebSize(400, 400), 100.f, false); | |
734 webView->updateAllLifecyclePhases(); | |
735 | |
736 ASSERT_EQ(0.f, webView->topControls().contentOffset()); | |
737 | |
738 // vh units should be static with respect to the top controls so neighter | |
739 // <div> should change size are a result of the top controls hiding. | |
740 EXPECT_FLOAT_EQ(400.f, absPos->getBoundingClientRect()->height()); | |
741 EXPECT_FLOAT_EQ(400.f, fixedPos->getBoundingClientRect()->height()); | |
742 | |
743 // The viewport size used for vh units should not change as a result of top | |
744 // controls hiding. | |
745 EXPECT_EQ(800, frame()->view()->viewportSizeForViewportUnits().height()); | |
746 } | |
747 | |
748 // Ensure that on a legacy page (there's a non-1 minimum scale) whose viewport | |
749 // at minimum-scale is larger than the layout size, 100vh units fill the | |
750 // viewport, with top controls hidden, when the viewport is scaled such that | |
751 // its width equals the layout width. | |
752 TEST_F(TopControlsTest, MAYBE(DontAffectVHUnitsUseLayoutSize)) { | |
753 // Initialize with the top controls showing. | |
754 WebViewImpl* webView = initialize("vh-height-width-800-extra-wide.html"); | |
755 webView->resizeWithTopControls(WebSize(400, 300), 100.f, true); | |
756 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsShown, | |
757 false); | |
758 webView->topControls().setShownRatio(1); | |
759 webView->updateAllLifecyclePhases(); | |
760 | |
761 ASSERT_EQ(100.f, webView->topControls().contentOffset()); | |
762 | |
763 // Device viewport is 400px and page is width=800 but there's an element | |
764 // that's 1600px wide so the minimum scale is 0.25 to encompass that. | |
765 ASSERT_EQ(0.25f, webView->minimumPageScaleFactor()); | |
766 | |
767 // The viewport will match the layout width at scale=0.5 so the height used | |
768 // for vh should be (300 / 0.5) for the layout height + (100 / 0.5) for top | |
769 // controls = 800. | |
770 EXPECT_EQ(800, frame()->view()->viewportSizeForViewportUnits().height()); | |
771 } | |
772 | |
773 // This tests that the viewport remains anchored when top controls are brought | |
774 // in while the document is fully scrolled. This normally causes clamping of the | |
775 // visual viewport to keep it bounded by the layout viewport so we're testing | |
776 // that the viewport anchoring logic is working to keep the view unchanged. | |
777 TEST_F(TopControlsTest, MAYBE(AnchorViewportDuringTopControlsAdjustment)) { | |
778 int contentHeight = 1016; | |
779 int layoutViewportHeight = 500; | |
780 int visualViewportHeight = 500; | |
781 int topControlsHeight = 100; | |
782 int pageScale = 2; | |
783 int minScale = 1; | |
784 | |
785 // Initialize with the top controls showing. | |
786 WebViewImpl* webView = initialize("large-div.html"); | |
787 webViewImpl()->setDefaultPageScaleLimits(minScale, 5); | |
788 webView->resizeWithTopControls(WebSize(800, layoutViewportHeight), | |
789 topControlsHeight, true); | |
790 webView->updateTopControlsState(WebTopControlsBoth, WebTopControlsShown, | |
791 false); | |
792 webView->topControls().setShownRatio(1); | |
793 webView->updateAllLifecyclePhases(); | |
794 | |
795 FrameView* view = frame()->view(); | |
796 ScrollableArea* rootViewport = frame()->view()->getScrollableArea(); | |
797 | |
798 int expectedVisualOffset = | |
799 ((layoutViewportHeight + topControlsHeight / minScale) * pageScale - | |
800 (visualViewportHeight + topControlsHeight)) / | |
801 pageScale; | |
802 int expectedLayoutOffset = | |
803 contentHeight - (layoutViewportHeight + topControlsHeight / minScale); | |
804 int expectedRootOffset = expectedVisualOffset + expectedLayoutOffset; | |
805 | |
806 // Zoom in to 2X and fully scroll both viewports. | |
807 webView->setPageScaleFactor(pageScale); | |
808 { | |
809 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
810 webView->handleInputEvent( | |
811 generateEvent(WebInputEvent::GestureScrollUpdate, 0, -10000)); | |
812 | |
813 ASSERT_EQ(0.f, webView->topControls().contentOffset()); | |
814 | |
815 EXPECT_EQ(expectedVisualOffset, visualViewport().scrollOffset().height()); | |
816 EXPECT_EQ(expectedLayoutOffset, | |
817 view->layoutViewportScrollableArea()->scrollOffset().height()); | |
818 EXPECT_EQ(expectedRootOffset, rootViewport->scrollOffset().height()); | |
819 | |
820 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollEnd)); | |
821 } | |
822 | |
823 // Commit the top controls resize so that the top controls do not shrink the | |
824 // layout size. This should not have moved any of the viewports. | |
825 webView->resizeWithTopControls( | |
826 WebSize(800, layoutViewportHeight + topControlsHeight), topControlsHeight, | |
827 false); | |
828 webView->updateAllLifecyclePhases(); | |
829 ASSERT_EQ(expectedVisualOffset, visualViewport().scrollOffset().height()); | |
830 ASSERT_EQ(expectedLayoutOffset, | |
831 view->layoutViewportScrollableArea()->scrollOffset().height()); | |
832 ASSERT_EQ(expectedRootOffset, rootViewport->scrollOffset().height()); | |
833 | |
834 // Now scroll back up just enough to show the top controls. The top controls | |
835 // should shrink both viewports but the layout viewport by a greater amount. | |
836 // This means the visual viewport's offset must be clamped to keep it within | |
837 // the layout viewport. Make sure we adjust the scroll position to account | |
838 // for this and keep the visual viewport at the same location relative to | |
839 // the document (i.e. the user shouldn't see a movement). | |
840 { | |
841 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollBegin)); | |
842 webView->handleInputEvent( | |
843 generateEvent(WebInputEvent::GestureScrollUpdate, 0, 80)); | |
844 | |
845 visualViewport().clampToBoundaries(); | |
846 view->setScrollOffset(view->scrollOffset(), ProgrammaticScroll); | |
847 | |
848 ASSERT_EQ(80.f, webView->topControls().contentOffset()); | |
849 EXPECT_EQ(expectedRootOffset, rootViewport->scrollOffset().height()); | |
850 | |
851 webView->handleInputEvent(generateEvent(WebInputEvent::GestureScrollEnd)); | |
852 } | |
853 } | |
854 | |
855 } // namespace blink | |
OLD | NEW |