1 // Copyright 2013 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.
5 package org.chromium.android_webview.test;
7 import android.content.Context;
8 import android.graphics.Rect;
9 import android.test.InstrumentationTestCase;
10 import android.test.suitebuilder.annotation.SmallTest;
11 import android.view.View.MeasureSpec;
12 import android.view.View;
13 import android.widget.OverScroller;
15 import org.chromium.android_webview.AwScrollOffsetManager;
16 import org.chromium.base.test.util.Feature;
18 public class AwScrollOffsetManagerTest extends InstrumentationTestCase {
19 private static class TestScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate {
20 private int mOverScrollDeltaX;
21 private int mOverScrollDeltaY;
22 private int mOverScrollCallCount;
25 private int mNativeScrollX;
26 private int mNativeScrollY;
27 private int mInvalidateCount;
29 public int getOverScrollDeltaX() {
30 return mOverScrollDeltaX;
33 public int getOverScrollDeltaY() {
34 return mOverScrollDeltaY;
37 public int getOverScrollCallCount() {
38 return mOverScrollCallCount;
41 public int getScrollX() {
45 public int getScrollY() {
49 public int getNativeScrollX() {
50 return mNativeScrollX;
53 public int getNativeScrollY() {
54 return mNativeScrollY;
57 public int getInvalidateCount() {
58 return mInvalidateCount;
62 public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY,
63 int scrollRangeX, int scrollRangeY, boolean isTouchEvent) {
64 mOverScrollDeltaX = deltaX;
65 mOverScrollDeltaY = deltaY;
66 mOverScrollCallCount += 1;
70 public void scrollContainerViewTo(int x, int y) {
76 public void scrollNativeTo(int x, int y) {
82 public int getContainerViewScrollX() {
87 public int getContainerViewScrollY() {
92 public void invalidate() {
93 mInvalidateCount += 1;
97 private void simulateScrolling(AwScrollOffsetManager offsetManager,
98 TestScrollOffsetManagerDelegate delegate, int scrollX, int scrollY) {
99 // Scrolling is a two-phase action. First we ask the manager to scroll
100 int callCount = delegate.getOverScrollCallCount();
101 offsetManager.scrollContainerViewTo(scrollX, scrollY);
102 // The manager then asks the delegate to overscroll the view.
103 assertEquals(callCount + 1, delegate.getOverScrollCallCount());
104 assertEquals(scrollX, delegate.getOverScrollDeltaX() + delegate.getScrollX());
105 assertEquals(scrollY, delegate.getOverScrollDeltaY() + delegate.getScrollY());
106 // As a response to that the menager expects the view to call back with the new scroll.
107 offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false);
110 private void simlateOverScrollPropagation(AwScrollOffsetManager offsetManager,
111 TestScrollOffsetManagerDelegate delegate) {
112 assertTrue(delegate.getOverScrollCallCount() > 0);
114 offsetManager.onContainerViewOverScrolled(
115 delegate.getOverScrollDeltaX() + delegate.getScrollX(),
116 delegate.getOverScrollDeltaY() + delegate.getScrollY(), false, false);
120 @Feature({"AndroidWebView"})
121 public void testWhenContentSizeMatchesView() {
122 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
123 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
124 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
126 final int width = 132;
127 final int height = 212;
128 final int scrollX = 11;
129 final int scrollY = 13;
131 offsetManager.setMaxScrollOffset(0, 0);
132 offsetManager.setContainerViewSize(width, height);
134 assertEquals(width, offsetManager.computeHorizontalScrollRange());
135 assertEquals(height, offsetManager.computeVerticalScrollRange());
137 // Since the view size and contents size are equal no scrolling should be possible.
138 assertEquals(0, offsetManager.computeMaximumHorizontalScrollOffset());
139 assertEquals(0, offsetManager.computeMaximumVerticalScrollOffset());
141 // Scrolling should generate overscroll but not update the scroll offset.
142 simulateScrolling(offsetManager, delegate, scrollX, scrollY);
143 assertEquals(scrollX, delegate.getOverScrollDeltaX());
144 assertEquals(scrollY, delegate.getOverScrollDeltaY());
145 assertEquals(0, delegate.getScrollX());
146 assertEquals(0, delegate.getScrollY());
147 assertEquals(0, delegate.getNativeScrollX());
148 assertEquals(0, delegate.getNativeScrollY());
150 // Scrolling to 0,0 should result in no deltas.
151 simulateScrolling(offsetManager, delegate, 0, 0);
152 assertEquals(0, delegate.getOverScrollDeltaX());
153 assertEquals(0, delegate.getOverScrollDeltaY());
155 // Negative scrolling should result in negative deltas but no scroll offset update.
156 simulateScrolling(offsetManager, delegate, -scrollX, -scrollY);
157 assertEquals(-scrollX, delegate.getOverScrollDeltaX());
158 assertEquals(-scrollY, delegate.getOverScrollDeltaY());
159 assertEquals(0, delegate.getScrollX());
160 assertEquals(0, delegate.getScrollY());
161 assertEquals(0, delegate.getNativeScrollX());
162 assertEquals(0, delegate.getNativeScrollY());
165 private static final int VIEW_WIDTH = 211;
166 private static final int VIEW_HEIGHT = 312;
167 private static final int MAX_HORIZONTAL_OFFSET = 757;
168 private static final int MAX_VERTICAL_OFFSET = 127;
169 private static final int CONTENT_WIDTH = VIEW_WIDTH + MAX_HORIZONTAL_OFFSET;
170 private static final int CONTENT_HEIGHT = VIEW_HEIGHT + MAX_VERTICAL_OFFSET;
173 @Feature({"AndroidWebView"})
174 public void testScrollRangeAndMaxOffset() {
175 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
176 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
177 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
179 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
180 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
182 assertEquals(CONTENT_WIDTH, offsetManager.computeHorizontalScrollRange());
183 assertEquals(CONTENT_HEIGHT, offsetManager.computeVerticalScrollRange());
185 assertEquals(MAX_HORIZONTAL_OFFSET, offsetManager.computeMaximumHorizontalScrollOffset());
186 assertEquals(MAX_VERTICAL_OFFSET, offsetManager.computeMaximumVerticalScrollOffset());
188 // Scrolling beyond the maximum should be clamped.
189 final int scrollX = MAX_HORIZONTAL_OFFSET + 10;
190 final int scrollY = MAX_VERTICAL_OFFSET + 11;
192 simulateScrolling(offsetManager, delegate, scrollX, scrollY);
193 assertEquals(scrollX, delegate.getOverScrollDeltaX());
194 assertEquals(scrollY, delegate.getOverScrollDeltaY());
195 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX());
196 assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY());
197 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX());
198 assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY());
200 // Scrolling to negative coordinates should be clamped back to 0,0.
201 simulateScrolling(offsetManager, delegate, -scrollX, -scrollY);
202 assertEquals(0, delegate.getScrollX());
203 assertEquals(0, delegate.getScrollY());
204 assertEquals(0, delegate.getNativeScrollX());
205 assertEquals(0, delegate.getNativeScrollY());
207 // The onScrollChanged method is callable by third party code and should also be clamped
208 offsetManager.onContainerViewScrollChanged(scrollX, scrollY);
209 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX());
210 assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY());
212 offsetManager.onContainerViewScrollChanged(-scrollX, -scrollY);
213 assertEquals(0, delegate.getNativeScrollX());
214 assertEquals(0, delegate.getNativeScrollY());
218 @Feature({"AndroidWebView"})
219 public void testDelegateCanOverrideScroll() {
220 final int overrideScrollX = 10;
221 final int overrideScrollY = 10;
223 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate() {
225 public int getContainerViewScrollX() {
226 return overrideScrollX;
230 public int getContainerViewScrollY() {
231 return overrideScrollY;
234 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
235 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
237 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
238 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
240 offsetManager.onContainerViewOverScrolled(0, 0, false, false);
241 assertEquals(overrideScrollX, delegate.getNativeScrollX());
242 assertEquals(overrideScrollY, delegate.getNativeScrollY());
246 @Feature({"AndroidWebView"})
247 public void testDelegateOverridenScrollsDontExceedBounds() {
248 final int overrideScrollX = MAX_HORIZONTAL_OFFSET + 10;
249 final int overrideScrollY = MAX_VERTICAL_OFFSET + 20;
250 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate() {
252 public int getContainerViewScrollX() {
253 return overrideScrollX;
257 public int getContainerViewScrollY() {
258 return overrideScrollY;
261 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
262 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
264 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
265 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
267 offsetManager.onContainerViewOverScrolled(0, 0, false, false);
268 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX());
269 assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY());
273 @Feature({"AndroidWebView"})
274 public void testScrollContainerViewTo() {
275 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
276 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
277 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
279 final int scrollX = 31;
280 final int scrollY = 41;
282 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
283 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
285 assertEquals(0, delegate.getOverScrollDeltaX());
286 assertEquals(0, delegate.getOverScrollDeltaY());
287 int callCount = delegate.getOverScrollCallCount();
289 offsetManager.scrollContainerViewTo(scrollX, scrollY);
290 assertEquals(callCount + 1, delegate.getOverScrollCallCount());
291 assertEquals(scrollX, delegate.getOverScrollDeltaX());
292 assertEquals(scrollY, delegate.getOverScrollDeltaY());
296 @Feature({"AndroidWebView"})
297 public void testOnContainerViewOverScrolled() {
298 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
299 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
300 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
302 final int scrollX = 31;
303 final int scrollY = 41;
305 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
306 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
308 assertEquals(0, delegate.getScrollX());
309 assertEquals(0, delegate.getScrollY());
310 assertEquals(0, delegate.getNativeScrollX());
311 assertEquals(0, delegate.getNativeScrollY());
313 offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false);
314 assertEquals(scrollX, delegate.getScrollX());
315 assertEquals(scrollY, delegate.getScrollY());
316 assertEquals(scrollX, delegate.getNativeScrollX());
317 assertEquals(scrollY, delegate.getNativeScrollY());
321 @Feature({"AndroidWebView"})
322 public void testDefersScrollUntilTouchEnd() {
323 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
324 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
325 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
327 final int scrollX = 31;
328 final int scrollY = 41;
330 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
331 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
333 offsetManager.setProcessingTouchEvent(true);
334 offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false);
335 assertEquals(scrollX, delegate.getScrollX());
336 assertEquals(scrollY, delegate.getScrollY());
337 assertEquals(0, delegate.getNativeScrollX());
338 assertEquals(0, delegate.getNativeScrollY());
340 offsetManager.setProcessingTouchEvent(false);
341 assertEquals(scrollX, delegate.getScrollX());
342 assertEquals(scrollY, delegate.getScrollY());
343 assertEquals(scrollX, delegate.getNativeScrollX());
344 assertEquals(scrollY, delegate.getNativeScrollY());
348 @Feature({"AndroidWebView"})
349 public void testFlingScroll() {
350 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
351 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
352 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
354 offsetManager.flingScroll(0, 101);
355 assertTrue(!scroller.isFinished());
356 assertTrue(delegate.getInvalidateCount() == 1);
357 assertEquals(101, (int) scroller.getCurrVelocity());
359 offsetManager.flingScroll(111, 0);
360 assertTrue(!scroller.isFinished());
361 assertTrue(delegate.getInvalidateCount() == 2);
362 assertEquals(111, (int) scroller.getCurrVelocity());
366 @Feature({"AndroidWebView"})
367 public void testRequestChildRectangleOnScreenDontScrollIfAlreadyThere() {
368 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
369 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
370 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
372 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
373 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
375 offsetManager.requestChildRectangleOnScreen(0, 0,
376 new Rect(0, 0, VIEW_WIDTH / 4, VIEW_HEIGHT / 4), true);
377 assertEquals(0, delegate.getOverScrollDeltaX());
378 assertEquals(0, delegate.getOverScrollDeltaY());
379 assertEquals(0, delegate.getScrollX());
380 assertEquals(0, delegate.getScrollY());
382 offsetManager.requestChildRectangleOnScreen(3 * VIEW_WIDTH / 4, 3 * VIEW_HEIGHT / 4,
383 new Rect(0, 0, VIEW_WIDTH / 4, VIEW_HEIGHT / 4), true);
384 assertEquals(0, delegate.getOverScrollDeltaX());
385 assertEquals(0, delegate.getOverScrollDeltaY());
386 assertEquals(0, delegate.getScrollX());
387 assertEquals(0, delegate.getScrollY());
391 @Feature({"AndroidWebView"})
392 public void testRequestChildRectangleOnScreenScrollToBottom() {
393 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
394 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
395 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
397 final int rectWidth = 2;
398 final int rectHeight = 3;
400 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
401 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
403 offsetManager.requestChildRectangleOnScreen(CONTENT_WIDTH - rectWidth,
404 CONTENT_HEIGHT - rectHeight, new Rect(0, 0, rectWidth, rectHeight), true);
405 simlateOverScrollPropagation(offsetManager, delegate);
406 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getOverScrollDeltaX());
407 assertEquals(CONTENT_HEIGHT - rectHeight - VIEW_HEIGHT / 3, delegate.getOverScrollDeltaY());
408 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX());
409 assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY());
413 @Feature({"AndroidWebView"})
414 public void testRequestChildRectangleOnScreenScrollToBottomLargeRect() {
415 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
416 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
417 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
419 final int rectWidth = VIEW_WIDTH;
420 final int rectHeight = VIEW_HEIGHT;
422 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
423 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
425 offsetManager.requestChildRectangleOnScreen(CONTENT_WIDTH - rectWidth,
426 CONTENT_HEIGHT - rectHeight, new Rect(0, 0, rectWidth, rectHeight), true);
427 simlateOverScrollPropagation(offsetManager, delegate);
428 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getOverScrollDeltaX());
429 assertEquals(MAX_VERTICAL_OFFSET, delegate.getOverScrollDeltaY());
430 assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX());
431 assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY());
435 @Feature({"AndroidWebView"})
436 public void testRequestChildRectangleOnScreenScrollToTop() {
437 TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate();
438 OverScroller scroller = new OverScroller(getInstrumentation().getContext());
439 AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller);
441 final int rectWidth = 2;
442 final int rectHeight = 3;
444 offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET);
445 offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT);
446 simulateScrolling(offsetManager, delegate,
447 CONTENT_WIDTH - VIEW_WIDTH, CONTENT_HEIGHT - VIEW_HEIGHT);
449 offsetManager.requestChildRectangleOnScreen(0, 0,
450 new Rect(0, 0, rectWidth, rectHeight), true);
451 simlateOverScrollPropagation(offsetManager, delegate);
452 assertEquals(-CONTENT_WIDTH + VIEW_WIDTH, delegate.getOverScrollDeltaX());
453 assertEquals(-CONTENT_HEIGHT + VIEW_HEIGHT, delegate.getOverScrollDeltaY());
454 assertEquals(0, delegate.getScrollX());
455 assertEquals(0, delegate.getScrollX());