| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.android_webview.test; |
| |
| import android.graphics.Rect; |
| import android.test.InstrumentationTestCase; |
| import android.test.suitebuilder.annotation.SmallTest; |
| import android.widget.OverScroller; |
| |
| import org.chromium.android_webview.AwScrollOffsetManager; |
| import org.chromium.base.test.util.Feature; |
| |
| public class AwScrollOffsetManagerTest extends InstrumentationTestCase { |
| private static class TestScrollOffsetManagerDelegate implements AwScrollOffsetManager.Delegate { |
| private int mOverScrollDeltaX; |
| private int mOverScrollDeltaY; |
| private int mOverScrollCallCount; |
| private int mScrollX; |
| private int mScrollY; |
| private int mNativeScrollX; |
| private int mNativeScrollY; |
| private int mInvalidateCount; |
| |
| public int getOverScrollDeltaX() { |
| return mOverScrollDeltaX; |
| } |
| |
| public int getOverScrollDeltaY() { |
| return mOverScrollDeltaY; |
| } |
| |
| public int getOverScrollCallCount() { |
| return mOverScrollCallCount; |
| } |
| |
| public int getScrollX() { |
| return mScrollX; |
| } |
| |
| public int getScrollY() { |
| return mScrollY; |
| } |
| |
| public int getNativeScrollX() { |
| return mNativeScrollX; |
| } |
| |
| public int getNativeScrollY() { |
| return mNativeScrollY; |
| } |
| |
| public int getInvalidateCount() { |
| return mInvalidateCount; |
| } |
| |
| @Override |
| public void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY, |
| int scrollRangeX, int scrollRangeY, boolean isTouchEvent) { |
| mOverScrollDeltaX = deltaX; |
| mOverScrollDeltaY = deltaY; |
| mOverScrollCallCount += 1; |
| } |
| |
| @Override |
| public void scrollContainerViewTo(int x, int y) { |
| mScrollX = x; |
| mScrollY = y; |
| } |
| |
| @Override |
| public void scrollNativeTo(int x, int y) { |
| mNativeScrollX = x; |
| mNativeScrollY = y; |
| } |
| |
| @Override |
| public int getContainerViewScrollX() { |
| return mScrollX; |
| } |
| |
| @Override |
| public int getContainerViewScrollY() { |
| return mScrollY; |
| } |
| |
| @Override |
| public void invalidate() { |
| mInvalidateCount += 1; |
| } |
| } |
| |
| private void simulateScrolling(AwScrollOffsetManager offsetManager, |
| TestScrollOffsetManagerDelegate delegate, int scrollX, int scrollY) { |
| // Scrolling is a two-phase action. First we ask the manager to scroll |
| int callCount = delegate.getOverScrollCallCount(); |
| offsetManager.scrollContainerViewTo(scrollX, scrollY); |
| // The manager then asks the delegate to overscroll the view. |
| assertEquals(callCount + 1, delegate.getOverScrollCallCount()); |
| assertEquals(scrollX, delegate.getOverScrollDeltaX() + delegate.getScrollX()); |
| assertEquals(scrollY, delegate.getOverScrollDeltaY() + delegate.getScrollY()); |
| // As a response to that the menager expects the view to call back with the new scroll. |
| offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false); |
| } |
| |
| private void simlateOverScrollPropagation(AwScrollOffsetManager offsetManager, |
| TestScrollOffsetManagerDelegate delegate) { |
| assertTrue(delegate.getOverScrollCallCount() > 0); |
| |
| offsetManager.onContainerViewOverScrolled( |
| delegate.getOverScrollDeltaX() + delegate.getScrollX(), |
| delegate.getOverScrollDeltaY() + delegate.getScrollY(), false, false); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testWhenContentSizeMatchesView() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int width = 132; |
| final int height = 212; |
| final int scrollX = 11; |
| final int scrollY = 13; |
| |
| offsetManager.setMaxScrollOffset(0, 0); |
| offsetManager.setContainerViewSize(width, height); |
| |
| assertEquals(width, offsetManager.computeHorizontalScrollRange()); |
| assertEquals(height, offsetManager.computeVerticalScrollRange()); |
| |
| // Since the view size and contents size are equal no scrolling should be possible. |
| assertEquals(0, offsetManager.computeMaximumHorizontalScrollOffset()); |
| assertEquals(0, offsetManager.computeMaximumVerticalScrollOffset()); |
| |
| // Scrolling should generate overscroll but not update the scroll offset. |
| simulateScrolling(offsetManager, delegate, scrollX, scrollY); |
| assertEquals(scrollX, delegate.getOverScrollDeltaX()); |
| assertEquals(scrollY, delegate.getOverScrollDeltaY()); |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollY()); |
| assertEquals(0, delegate.getNativeScrollX()); |
| assertEquals(0, delegate.getNativeScrollY()); |
| |
| // Scrolling to 0,0 should result in no deltas. |
| simulateScrolling(offsetManager, delegate, 0, 0); |
| assertEquals(0, delegate.getOverScrollDeltaX()); |
| assertEquals(0, delegate.getOverScrollDeltaY()); |
| |
| // Negative scrolling should result in negative deltas but no scroll offset update. |
| simulateScrolling(offsetManager, delegate, -scrollX, -scrollY); |
| assertEquals(-scrollX, delegate.getOverScrollDeltaX()); |
| assertEquals(-scrollY, delegate.getOverScrollDeltaY()); |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollY()); |
| assertEquals(0, delegate.getNativeScrollX()); |
| assertEquals(0, delegate.getNativeScrollY()); |
| } |
| |
| private static final int VIEW_WIDTH = 211; |
| private static final int VIEW_HEIGHT = 312; |
| private static final int MAX_HORIZONTAL_OFFSET = 757; |
| private static final int MAX_VERTICAL_OFFSET = 127; |
| private static final int CONTENT_WIDTH = VIEW_WIDTH + MAX_HORIZONTAL_OFFSET; |
| private static final int CONTENT_HEIGHT = VIEW_HEIGHT + MAX_VERTICAL_OFFSET; |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testScrollRangeAndMaxOffset() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| assertEquals(CONTENT_WIDTH, offsetManager.computeHorizontalScrollRange()); |
| assertEquals(CONTENT_HEIGHT, offsetManager.computeVerticalScrollRange()); |
| |
| assertEquals(MAX_HORIZONTAL_OFFSET, offsetManager.computeMaximumHorizontalScrollOffset()); |
| assertEquals(MAX_VERTICAL_OFFSET, offsetManager.computeMaximumVerticalScrollOffset()); |
| |
| // Scrolling beyond the maximum should be clamped. |
| final int scrollX = MAX_HORIZONTAL_OFFSET + 10; |
| final int scrollY = MAX_VERTICAL_OFFSET + 11; |
| |
| simulateScrolling(offsetManager, delegate, scrollX, scrollY); |
| assertEquals(scrollX, delegate.getOverScrollDeltaX()); |
| assertEquals(scrollY, delegate.getOverScrollDeltaY()); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY()); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY()); |
| |
| // Scrolling to negative coordinates should be clamped back to 0,0. |
| simulateScrolling(offsetManager, delegate, -scrollX, -scrollY); |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollY()); |
| assertEquals(0, delegate.getNativeScrollX()); |
| assertEquals(0, delegate.getNativeScrollY()); |
| |
| // The onScrollChanged method is callable by third party code and should also be clamped |
| offsetManager.onContainerViewScrollChanged(scrollX, scrollY); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY()); |
| |
| offsetManager.onContainerViewScrollChanged(-scrollX, -scrollY); |
| assertEquals(0, delegate.getNativeScrollX()); |
| assertEquals(0, delegate.getNativeScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testDelegateCanOverrideScroll() { |
| final int overrideScrollX = 10; |
| final int overrideScrollY = 10; |
| |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate() { |
| @Override |
| public int getContainerViewScrollX() { |
| return overrideScrollX; |
| } |
| |
| @Override |
| public int getContainerViewScrollY() { |
| return overrideScrollY; |
| } |
| }; |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| offsetManager.onContainerViewOverScrolled(0, 0, false, false); |
| assertEquals(overrideScrollX, delegate.getNativeScrollX()); |
| assertEquals(overrideScrollY, delegate.getNativeScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testDelegateOverridenScrollsDontExceedBounds() { |
| final int overrideScrollX = MAX_HORIZONTAL_OFFSET + 10; |
| final int overrideScrollY = MAX_VERTICAL_OFFSET + 20; |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate() { |
| @Override |
| public int getContainerViewScrollX() { |
| return overrideScrollX; |
| } |
| |
| @Override |
| public int getContainerViewScrollY() { |
| return overrideScrollY; |
| } |
| }; |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| offsetManager.onContainerViewOverScrolled(0, 0, false, false); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getNativeScrollX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getNativeScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testScrollContainerViewTo() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int scrollX = 31; |
| final int scrollY = 41; |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| assertEquals(0, delegate.getOverScrollDeltaX()); |
| assertEquals(0, delegate.getOverScrollDeltaY()); |
| int callCount = delegate.getOverScrollCallCount(); |
| |
| offsetManager.scrollContainerViewTo(scrollX, scrollY); |
| assertEquals(callCount + 1, delegate.getOverScrollCallCount()); |
| assertEquals(scrollX, delegate.getOverScrollDeltaX()); |
| assertEquals(scrollY, delegate.getOverScrollDeltaY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testOnContainerViewOverScrolled() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int scrollX = 31; |
| final int scrollY = 41; |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollY()); |
| assertEquals(0, delegate.getNativeScrollX()); |
| assertEquals(0, delegate.getNativeScrollY()); |
| |
| offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false); |
| assertEquals(scrollX, delegate.getScrollX()); |
| assertEquals(scrollY, delegate.getScrollY()); |
| assertEquals(scrollX, delegate.getNativeScrollX()); |
| assertEquals(scrollY, delegate.getNativeScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testDefersScrollUntilTouchEnd() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int scrollX = 31; |
| final int scrollY = 41; |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| offsetManager.setProcessingTouchEvent(true); |
| offsetManager.onContainerViewOverScrolled(scrollX, scrollY, false, false); |
| assertEquals(scrollX, delegate.getScrollX()); |
| assertEquals(scrollY, delegate.getScrollY()); |
| assertEquals(0, delegate.getNativeScrollX()); |
| assertEquals(0, delegate.getNativeScrollY()); |
| |
| offsetManager.setProcessingTouchEvent(false); |
| assertEquals(scrollX, delegate.getScrollX()); |
| assertEquals(scrollY, delegate.getScrollY()); |
| assertEquals(scrollX, delegate.getNativeScrollX()); |
| assertEquals(scrollY, delegate.getNativeScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testFlingScroll() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| offsetManager.flingScroll(0, 101); |
| assertTrue(!scroller.isFinished()); |
| assertTrue(delegate.getInvalidateCount() == 1); |
| assertEquals(101, (int) scroller.getCurrVelocity()); |
| |
| offsetManager.flingScroll(111, 0); |
| assertTrue(!scroller.isFinished()); |
| assertTrue(delegate.getInvalidateCount() == 2); |
| assertEquals(111, (int) scroller.getCurrVelocity()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testRequestChildRectangleOnScreenDontScrollIfAlreadyThere() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| offsetManager.requestChildRectangleOnScreen(0, 0, |
| new Rect(0, 0, VIEW_WIDTH / 4, VIEW_HEIGHT / 4), true); |
| assertEquals(0, delegate.getOverScrollDeltaX()); |
| assertEquals(0, delegate.getOverScrollDeltaY()); |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollY()); |
| |
| offsetManager.requestChildRectangleOnScreen(3 * VIEW_WIDTH / 4, 3 * VIEW_HEIGHT / 4, |
| new Rect(0, 0, VIEW_WIDTH / 4, VIEW_HEIGHT / 4), true); |
| assertEquals(0, delegate.getOverScrollDeltaX()); |
| assertEquals(0, delegate.getOverScrollDeltaY()); |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testRequestChildRectangleOnScreenScrollToBottom() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int rectWidth = 2; |
| final int rectHeight = 3; |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| offsetManager.requestChildRectangleOnScreen(CONTENT_WIDTH - rectWidth, |
| CONTENT_HEIGHT - rectHeight, new Rect(0, 0, rectWidth, rectHeight), true); |
| simlateOverScrollPropagation(offsetManager, delegate); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getOverScrollDeltaX()); |
| assertEquals(CONTENT_HEIGHT - rectHeight - VIEW_HEIGHT / 3, delegate.getOverScrollDeltaY()); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testRequestChildRectangleOnScreenScrollToBottomLargeRect() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int rectWidth = VIEW_WIDTH; |
| final int rectHeight = VIEW_HEIGHT; |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| |
| offsetManager.requestChildRectangleOnScreen(CONTENT_WIDTH - rectWidth, |
| CONTENT_HEIGHT - rectHeight, new Rect(0, 0, rectWidth, rectHeight), true); |
| simlateOverScrollPropagation(offsetManager, delegate); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getOverScrollDeltaX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getOverScrollDeltaY()); |
| assertEquals(MAX_HORIZONTAL_OFFSET, delegate.getScrollX()); |
| assertEquals(MAX_VERTICAL_OFFSET, delegate.getScrollY()); |
| } |
| |
| @SmallTest |
| @Feature({"AndroidWebView"}) |
| public void testRequestChildRectangleOnScreenScrollToTop() { |
| TestScrollOffsetManagerDelegate delegate = new TestScrollOffsetManagerDelegate(); |
| OverScroller scroller = new OverScroller(getInstrumentation().getContext()); |
| AwScrollOffsetManager offsetManager = new AwScrollOffsetManager(delegate, scroller); |
| |
| final int rectWidth = 2; |
| final int rectHeight = 3; |
| |
| offsetManager.setMaxScrollOffset(MAX_HORIZONTAL_OFFSET, MAX_VERTICAL_OFFSET); |
| offsetManager.setContainerViewSize(VIEW_WIDTH, VIEW_HEIGHT); |
| simulateScrolling(offsetManager, delegate, |
| CONTENT_WIDTH - VIEW_WIDTH, CONTENT_HEIGHT - VIEW_HEIGHT); |
| |
| offsetManager.requestChildRectangleOnScreen(0, 0, |
| new Rect(0, 0, rectWidth, rectHeight), true); |
| simlateOverScrollPropagation(offsetManager, delegate); |
| assertEquals(-CONTENT_WIDTH + VIEW_WIDTH, delegate.getOverScrollDeltaX()); |
| assertEquals(-CONTENT_HEIGHT + VIEW_HEIGHT, delegate.getOverScrollDeltaY()); |
| assertEquals(0, delegate.getScrollX()); |
| assertEquals(0, delegate.getScrollX()); |
| } |
| } |