AwScrollOffsetManager.java revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
15b955920c1d8f2cd35aae3c85b656578286a8bc1Anders Carlsson// Copyright 2013 The Chromium Authors. All rights reserved.
25d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson// Use of this source code is governed by a BSD-style license that can be
35d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson// found in the LICENSE file.
45d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson
55d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlssonpackage org.chromium.android_webview;
65d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson
75d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlssonimport android.graphics.Rect;
85d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlssonimport android.widget.OverScroller;
95d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson
105d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlssonimport com.google.common.annotations.VisibleForTesting;
115d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson
125d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson/**
135d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson * Takes care of syncing the scroll offset between the Android View system and the
1464bee65a3436e3f0c352fcfe2130676f3502cffeEli Friedman * InProcessViewRenderer.
15d67ef0eed463b43980f04a444155f423114be34bDevang Patel *
1656c00c4868831c9a137ca7b0e16d063cf986d110Lang Hames * Unless otherwise values (sizes, scroll offsets) are in physical pixels.
175d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson */
182f1986b557fa671c4f8c9dd0d071398edfc073d5Anders Carlsson@VisibleForTesting
197e1dff7a68a4d00e71debafa7f5c259473091746John McCallpublic class AwScrollOffsetManager {
205d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson    // Values taken from WebViewClassic.
219fc6a7774643a810c8501dae2323e863fefb623eJohn McCall
2256c00c4868831c9a137ca7b0e16d063cf986d110Lang Hames    // The amount of content to overlap between two screens when using pageUp/pageDown methiods.
233ee36af5bbb8c2cd203a140c3785215539cd56b4Devang Patel    private static final int PAGE_SCROLL_OVERLAP = 24;
242f1986b557fa671c4f8c9dd0d071398edfc073d5Anders Carlsson    // Standard animated scroll speed.
255d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson    private static final int STD_SCROLL_ANIMATION_SPEED_PIX_PER_SEC = 480;
265d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson    // Time for the longest scroll animation.
275d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson    private static final int MAX_SCROLL_ANIMATION_DURATION_MILLISEC = 750;
2855c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck
2934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    /**
3034a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson     * The interface that all users of AwScrollOffsetManager should implement.
31f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall     *
32f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall     * The unit of all the values in this delegate are physical pixels.
3355c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck     */
3434a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    public interface Delegate {
3534a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // Call View#overScrollBy on the containerView.
3634a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        void overScrollContainerViewBy(int deltaX, int deltaY, int scrollX, int scrollY,
37f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall                int scrollRangeX, int scrollRangeY, boolean isTouchEvent);
3834a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // Call View#scrollTo on the containerView.
3934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        void scrollContainerViewTo(int x, int y);
4034a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // Store the scroll offset in the native side. This should really be a simple store
4134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // operation, the native side shouldn't synchronously alter the scroll offset from within
4234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // this call.
4334a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        void scrollNativeTo(int x, int y);
4434a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson
4534a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        int getContainerViewScrollX();
4634a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        int getContainerViewScrollY();
4734a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson
4855c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck        void invalidate();
4934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    }
5034a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson
5134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    private final Delegate mDelegate;
5234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson
5355c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck    // Scroll offset as seen by the native side.
5434a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    private int mNativeScrollX;
555d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson    private int mNativeScrollY;
5684080ec16ede6a6fe85a1d991690c6bda82a59eeAnders Carlsson
57a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson    // How many pixels can we scroll in a given direction.
58f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall    private int mMaxHorizontalScrollOffset;
59f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall    private int mMaxVerticalScrollOffset;
60f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall
61a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson    // Size of the container view.
6255c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck    private int mContainerViewWidth;
63f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall    private int mContainerViewHeight;
64f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall
6555c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck    // Whether we're in the middle of processing a touch event.
66a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson    private boolean mProcessingTouchEvent;
67a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson
682acc6e3feda5e4f7d9009bdcf8b1cd777fecfe2dChris Lattner    private boolean mFlinging;
69a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson
70a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson    // Whether (and to what value) to update the native side scroll offset after we've finished
7155c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck    // processing a touch event.
7284080ec16ede6a6fe85a1d991690c6bda82a59eeAnders Carlsson    private boolean mApplyDeferredNativeScroll;
7384080ec16ede6a6fe85a1d991690c6bda82a59eeAnders Carlsson    private int mDeferredNativeScrollX;
748561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson    private int mDeferredNativeScrollY;
75bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
76bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    private OverScroller mScroller;
77bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
78bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    public AwScrollOffsetManager(Delegate delegate, OverScroller overScroller) {
79bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        mDelegate = delegate;
808561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson        mScroller = overScroller;
818561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson    }
828561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson
838561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson    //----- Scroll range and extent calculation methods -------------------------------------------
84bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
85bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    public int computeHorizontalScrollRange() {
86bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        return mContainerViewWidth + mMaxHorizontalScrollOffset;
87bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    }
88bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
89bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    public int computeMaximumHorizontalScrollOffset() {
905fff46b65389f7e7eb576e47c7bc3ca67326a206Ken Dyck        return mMaxHorizontalScrollOffset;
91bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    }
928561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson
935fff46b65389f7e7eb576e47c7bc3ca67326a206Ken Dyck    public int computeHorizontalScrollOffset() {
94bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        return mDelegate.getContainerViewScrollX();
955fff46b65389f7e7eb576e47c7bc3ca67326a206Ken Dyck    }
96bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
97bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    public int computeVerticalScrollRange() {
98bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        return mContainerViewHeight + mMaxVerticalScrollOffset;
99bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    }
1005fff46b65389f7e7eb576e47c7bc3ca67326a206Ken Dyck
101bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    public int computeMaximumVerticalScrollOffset() {
1025fff46b65389f7e7eb576e47c7bc3ca67326a206Ken Dyck        return mMaxVerticalScrollOffset;
103bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    }
104bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
105bff225ecf77fb891596ecb1b27196310d268365eJohn McCall    public int computeVerticalScrollOffset() {
106bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        return mDelegate.getContainerViewScrollY();
107d103f9f9b401b419e756f8c1849683cd77586067Anders Carlsson    }
108bff225ecf77fb891596ecb1b27196310d268365eJohn McCall
1099dc228a1b971aa884766a9bdfdf5fa8ee4730b5bAnders Carlsson    public int computeVerticalScrollExtent() {
1107916c997127fe616ba255ba4cade10e5de0c8812John McCall        return mContainerViewHeight;
1117916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
1127916c997127fe616ba255ba4cade10e5de0c8812John McCall
1137916c997127fe616ba255ba4cade10e5de0c8812John McCall    //---------------------------------------------------------------------------------------------
1147916c997127fe616ba255ba4cade10e5de0c8812John McCall    /**
1157916c997127fe616ba255ba4cade10e5de0c8812John McCall     * Called when the scroll range changes. This needs to be the size of the on-screen content.
1167916c997127fe616ba255ba4cade10e5de0c8812John McCall     */
1177916c997127fe616ba255ba4cade10e5de0c8812John McCall    public void setMaxScrollOffset(int width, int height) {
1187916c997127fe616ba255ba4cade10e5de0c8812John McCall        mMaxHorizontalScrollOffset = width;
1197916c997127fe616ba255ba4cade10e5de0c8812John McCall        mMaxVerticalScrollOffset = height;
1207916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
1217916c997127fe616ba255ba4cade10e5de0c8812John McCall
1227916c997127fe616ba255ba4cade10e5de0c8812John McCall    /**
1237916c997127fe616ba255ba4cade10e5de0c8812John McCall     * Called when the physical size of the view changes.
1247916c997127fe616ba255ba4cade10e5de0c8812John McCall     */
1257916c997127fe616ba255ba4cade10e5de0c8812John McCall    public void setContainerViewSize(int width, int height) {
1267916c997127fe616ba255ba4cade10e5de0c8812John McCall        mContainerViewWidth = width;
1279dc228a1b971aa884766a9bdfdf5fa8ee4730b5bAnders Carlsson        mContainerViewHeight = height;
1289dc228a1b971aa884766a9bdfdf5fa8ee4730b5bAnders Carlsson    }
1297916c997127fe616ba255ba4cade10e5de0c8812John McCall
1307916c997127fe616ba255ba4cade10e5de0c8812John McCall    public void syncScrollOffsetFromOnDraw() {
1317916c997127fe616ba255ba4cade10e5de0c8812John McCall        // Unfortunately apps override onScrollChanged without calling super which is why we need
1329dc228a1b971aa884766a9bdfdf5fa8ee4730b5bAnders Carlsson        // to sync the scroll offset on every onDraw.
1339dc228a1b971aa884766a9bdfdf5fa8ee4730b5bAnders Carlsson        onContainerViewScrollChanged(mDelegate.getContainerViewScrollX(),
1345d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson                mDelegate.getContainerViewScrollY());
13534a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    }
1368561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson
137f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall    public void setProcessingTouchEvent(boolean processingTouchEvent) {
138f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        assert mProcessingTouchEvent != processingTouchEvent;
13934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        mProcessingTouchEvent = processingTouchEvent;
140f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall
14134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        if (!mProcessingTouchEvent && mApplyDeferredNativeScroll) {
142f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall            mApplyDeferredNativeScroll = false;
14334a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson            scrollNativeTo(mDeferredNativeScrollX, mDeferredNativeScrollY);
14434a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        }
1457916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
1467916c997127fe616ba255ba4cade10e5de0c8812John McCall
1477916c997127fe616ba255ba4cade10e5de0c8812John McCall    // Called by the native side to scroll the container view.
1487916c997127fe616ba255ba4cade10e5de0c8812John McCall    public void scrollContainerViewTo(int x, int y) {
14934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        mNativeScrollX = x;
15034a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        mNativeScrollY = y;
15134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson
15234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        final int scrollX = mDelegate.getContainerViewScrollX();
15334a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        final int scrollY = mDelegate.getContainerViewScrollY();
1547916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int deltaX = x - scrollX;
1557916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int deltaY = y - scrollY;
1567916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int scrollRangeX = computeMaximumHorizontalScrollOffset();
1577916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int scrollRangeY = computeMaximumVerticalScrollOffset();
15855c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck
1598561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson        // We use overScrollContainerViewBy to be compatible with WebViewClassic which used this
160f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        // method for handling both over-scroll as well as in-bounds scroll.
16134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        mDelegate.overScrollContainerViewBy(deltaX, deltaY, scrollX, scrollY,
1627916c997127fe616ba255ba4cade10e5de0c8812John McCall                scrollRangeX, scrollRangeY, mProcessingTouchEvent);
1637916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
1647916c997127fe616ba255ba4cade10e5de0c8812John McCall
1657916c997127fe616ba255ba4cade10e5de0c8812John McCall    public boolean isFlingActive() {
1667916c997127fe616ba255ba4cade10e5de0c8812John McCall        return mFlinging;
1677916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
1687916c997127fe616ba255ba4cade10e5de0c8812John McCall
1697916c997127fe616ba255ba4cade10e5de0c8812John McCall    // Called by the native side to over-scroll the container view.
1707916c997127fe616ba255ba4cade10e5de0c8812John McCall    public void overScrollBy(int deltaX, int deltaY) {
1717916c997127fe616ba255ba4cade10e5de0c8812John McCall        // TODO(mkosiba): Once http://crbug.com/260663 and http://crbug.com/261239 are fixed it
17234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // should be possible to uncomment the following asserts:
1732acc6e3feda5e4f7d9009bdcf8b1cd777fecfe2dChris Lattner        // if (deltaX < 0) assert mDelegate.getContainerViewScrollX() == 0;
174f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        // if (deltaX > 0) assert mDelegate.getContainerViewScrollX() ==
1757916c997127fe616ba255ba4cade10e5de0c8812John McCall        //          computeMaximumHorizontalScrollOffset();
1767916c997127fe616ba255ba4cade10e5de0c8812John McCall        scrollBy(deltaX, deltaY);
1777916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
17855c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck
17934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    private void scrollBy(int deltaX, int deltaY) {
18034a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        if (deltaX == 0 && deltaY == 0) return;
1817916c997127fe616ba255ba4cade10e5de0c8812John McCall
1827916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int scrollX = mDelegate.getContainerViewScrollX();
1837916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int scrollY = mDelegate.getContainerViewScrollY();
18434a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        final int scrollRangeX = computeMaximumHorizontalScrollOffset();
1857916c997127fe616ba255ba4cade10e5de0c8812John McCall        final int scrollRangeY = computeMaximumVerticalScrollOffset();
1867916c997127fe616ba255ba4cade10e5de0c8812John McCall
18734a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // The android.view.View.overScrollBy method is used for both scrolling and over-scrolling
1887916c997127fe616ba255ba4cade10e5de0c8812John McCall        // which is why we use it here.
1897916c997127fe616ba255ba4cade10e5de0c8812John McCall        mDelegate.overScrollContainerViewBy(deltaX, deltaY, scrollX, scrollY,
1907916c997127fe616ba255ba4cade10e5de0c8812John McCall                scrollRangeX, scrollRangeY, mProcessingTouchEvent);
19134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    }
1927916c997127fe616ba255ba4cade10e5de0c8812John McCall
1937916c997127fe616ba255ba4cade10e5de0c8812John McCall    private int clampHorizontalScroll(int scrollX) {
1947916c997127fe616ba255ba4cade10e5de0c8812John McCall        scrollX = Math.max(0, scrollX);
19534a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        scrollX = Math.min(computeMaximumHorizontalScrollOffset(), scrollX);
19634a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        return scrollX;
1977916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
19834a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson
199336a7dc56871ccfeceecc296c9624f66f7ac01ecAnders Carlsson    private int clampVerticalScroll(int scrollY) {
2007916c997127fe616ba255ba4cade10e5de0c8812John McCall        scrollY = Math.max(0, scrollY);
201336a7dc56871ccfeceecc296c9624f66f7ac01ecAnders Carlsson        scrollY = Math.min(computeMaximumVerticalScrollOffset(), scrollY);
20234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        return scrollY;
2037916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
20455c02585de7b02bcb72352f731d9bc342c8282f3Ken Dyck
2059a8ad9b28d54a3adc4cb8061d564f99f80144e30Ken Dyck    // Called by the View system as a response to the mDelegate.overScrollContainerViewBy call.
20634a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    public void onContainerViewOverScrolled(int scrollX, int scrollY, boolean clampedX,
20734a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson            boolean clampedY) {
2087916c997127fe616ba255ba4cade10e5de0c8812John McCall        // Clamp the scroll offset at (0, max).
20934a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        scrollX = clampHorizontalScroll(scrollX);
2107916c997127fe616ba255ba4cade10e5de0c8812John McCall        scrollY = clampVerticalScroll(scrollY);
2117916c997127fe616ba255ba4cade10e5de0c8812John McCall
21234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        mDelegate.scrollContainerViewTo(scrollX, scrollY);
2137916c997127fe616ba255ba4cade10e5de0c8812John McCall
2147916c997127fe616ba255ba4cade10e5de0c8812John McCall        // This is only necessary if the containerView scroll offset ends up being different
2157916c997127fe616ba255ba4cade10e5de0c8812John McCall        // than the one set from native in which case we want the value stored on the native side
21634a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        // to reflect the value stored in the containerView (and not the other way around).
2177916c997127fe616ba255ba4cade10e5de0c8812John McCall        scrollNativeTo(mDelegate.getContainerViewScrollX(), mDelegate.getContainerViewScrollY());
2187916c997127fe616ba255ba4cade10e5de0c8812John McCall    }
2197916c997127fe616ba255ba4cade10e5de0c8812John McCall
22034a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    // Called by the View system when the scroll offset had changed. This might not get called if
22134a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    // the embedder overrides WebView#onScrollChanged without calling super.onScrollChanged. If
22234a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    // this method does get called it is called both as a response to the embedder scrolling the
22334a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    // view as well as a response to mDelegate.scrollContainerViewTo.
22434a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    public void onContainerViewScrollChanged(int x, int y) {
22534a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson        scrollNativeTo(x, y);
22634a2d384c745ebc39cae45dc1c0c4a6a7012e09bAnders Carlsson    }
227a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
2288561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson    private void scrollNativeTo(int x, int y) {
229f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        x = clampHorizontalScroll(x);
230f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        y = clampVerticalScroll(y);
231a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
232f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        // We shouldn't do the store to native while processing a touch event since that confuses
233a04efdf635d35d88e65041fad007225d8c8d64a5Anders Carlsson        // the gesture processing logic.
234a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        if (mProcessingTouchEvent) {
2358561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson            mDeferredNativeScrollX = x;
2362acc6e3feda5e4f7d9009bdcf8b1cd777fecfe2dChris Lattner            mDeferredNativeScrollY = y;
237c764830bdb6de82baed068889096bd3e52d4cbdaRichard Smith            mApplyDeferredNativeScroll = true;
238a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson            return;
239f871d0cc377a1367b519a6cce26be74607566ebaJohn McCall        }
240a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson
241a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson        if (x == mNativeScrollX && y == mNativeScrollY)
242a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson            return;
243a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson
244a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson        // The scrollNativeTo call should be a simple store, so it's OK to assume it always
245a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson        // succeeds.
246a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        mNativeScrollX = x;
247a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        mNativeScrollY = y;
248a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
249a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        mDelegate.scrollNativeTo(x, y);
250a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    }
251a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
252a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    // Called whenever some other touch interaction requires the fling gesture to be canceled.
253a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    public void onFlingCancelGesture() {
254a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        // TODO(mkosiba): Support speeding up a fling by flinging again.
255b924124316becf2968a37dab36d0c48ea167666fAnders Carlsson        // http://crbug.com/265841
256a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        mScroller.forceFinished(true);
257a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    }
258a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
259a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    // Called when a fling gesture is not handled by the renderer.
260a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson    // We explicitly ask the renderer not to handle fling gestures targeted at the root
261c5685438df6105052b02c9e02f01c34489606308Eli Friedman    // scroll layer.
262c5685438df6105052b02c9e02f01c34489606308Eli Friedman    public void onUnhandledFlingStartEvent(int velocityX, int velocityY) {
263c5685438df6105052b02c9e02f01c34489606308Eli Friedman        flingScroll(-velocityX, -velocityY);
264a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson    }
265a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson
266a552ea76b0e4ce4ccdbb845667f0a12da6608c52Anders Carlsson    // Starts the fling animation. Called both as a response to a fling gesture and as via the
267a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    // public WebView#flingScroll(int, int) API.
268a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson    public void flingScroll(int velocityX, int velocityY) {
269a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        final int scrollX = mDelegate.getContainerViewScrollX();
270a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        final int scrollY = mDelegate.getContainerViewScrollY();
271a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        final int scrollRangeX = computeMaximumHorizontalScrollOffset();
272a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        final int scrollRangeY = computeMaximumVerticalScrollOffset();
273a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
274bbf3bacb3e0c1ebb3e8a4a8b1330404a7e379315Jay Foad        mScroller.fling(scrollX, scrollY, velocityX, velocityY,
275a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson                0, scrollRangeX, 0, scrollRangeY);
276a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        mDelegate.invalidate();
27732baf62b9a3aea3b63be6925b64aa182b0a2278eAnders Carlsson    }
278a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson
27932baf62b9a3aea3b63be6925b64aa182b0a2278eAnders Carlsson    // Called immediately before the draw to update the scroll offset.
2805d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson    public void computeScrollAndAbsorbGlow(OverScrollGlow overScrollGlow) {
281a3697c9c155bda93fd2802f37084b620f4738822Anders Carlsson        mFlinging = mScroller.computeScrollOffset();
2825d58a1d50e2644668122b8efb6b603a706ecfd6bAnders Carlsson        if (!mFlinging) {
28321c9ad9d29d08a287292c670e7c52bc522c7f8bbAnders Carlsson            return;
284c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        }
285c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
286314e622d20e67ad2f9bd3e3d4473fb23bec93132Anders Carlsson        final int oldX = mDelegate.getContainerViewScrollX();
287378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor        final int oldY = mDelegate.getContainerViewScrollY();
288378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor        int x = mScroller.getCurrX();
289af4403545a50a60d208e6fcae057308d576a92e0Anders Carlsson        int y = mScroller.getCurrY();
290c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
291c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        final int scrollRangeX = computeMaximumHorizontalScrollOffset();
292c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        final int scrollRangeY = computeMaximumVerticalScrollOffset();
293c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
294c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        if (overScrollGlow != null) {
295c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson            overScrollGlow.absorbGlow(x, y, oldX, oldY, scrollRangeX, scrollRangeY,
2963b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall                    mScroller.getCurrVelocity());
297c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        }
298c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
2993b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall        // The mScroller is configured not to go outside of the scrollable range, so this call
3003b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall        // should never result in attempting to scroll outside of the scrollable region.
301378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor        scrollBy(x - oldX, y - oldY);
302378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor
303378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor        mDelegate.invalidate();
304378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor    }
305378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor
306378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor    private static int computeDurationInMilliSec(int dx, int dy) {
307af4403545a50a60d208e6fcae057308d576a92e0Anders Carlsson        int distance = Math.max(Math.abs(dx), Math.abs(dy));
3083b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall        int duration = distance * 1000 / STD_SCROLL_ANIMATION_SPEED_PIX_PER_SEC;
309314e622d20e67ad2f9bd3e3d4473fb23bec93132Anders Carlsson        return Math.min(duration, MAX_SCROLL_ANIMATION_DURATION_MILLISEC);
3103b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall    }
3113b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall
312c11bb2191088b9e74bec5007a4e05c78b41a8f64Anders Carlsson    private boolean animateScrollTo(int x, int y) {
313c11bb2191088b9e74bec5007a4e05c78b41a8f64Anders Carlsson        final int scrollX = mDelegate.getContainerViewScrollX();
3144230d529a8797bbeef2328b60abeae333f7e143fKen Dyck        final int scrollY = mDelegate.getContainerViewScrollY();
3154230d529a8797bbeef2328b60abeae333f7e143fKen Dyck
3164230d529a8797bbeef2328b60abeae333f7e143fKen Dyck        x = clampHorizontalScroll(x);
317c11bb2191088b9e74bec5007a4e05c78b41a8f64Anders Carlsson        y = clampVerticalScroll(y);
318c11bb2191088b9e74bec5007a4e05c78b41a8f64Anders Carlsson
319c11bb2191088b9e74bec5007a4e05c78b41a8f64Anders Carlsson        int dx = x - scrollX;
3203b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall        int dy = y - scrollY;
3213b47733ceac33306bd54ce9d6c7d8eeeae52c7caJohn McCall
322c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        if (dx == 0 && dy == 0)
323af4403545a50a60d208e6fcae057308d576a92e0Anders Carlsson            return false;
324c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
325c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        mScroller.startScroll(scrollX, scrollY, dx, dy, computeDurationInMilliSec(dx, dy));
326c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        mDelegate.invalidate();
327c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
328c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        return true;
3291cbce125b91cad81c8be3f8bbae8df917211176cAnders Carlsson    }
330c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson
331c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson    /**
332c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson     * See {@link android.webkit.WebView#pageUp(boolean)}
333c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson     */
334c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson    public boolean pageUp(boolean top) {
335c997d4278d329e18891aac9698fb991b2d4622ebAnders Carlsson        final int scrollX = mDelegate.getContainerViewScrollX();
336182ab5112650d3228291c4dadd64a9f77f5aeb51John McCall        final int scrollY = mDelegate.getContainerViewScrollY();
33750da2cadcc6da86abff6772de65280ace2cabc94John McCall
3381f0fca54676cfa8616e7f3cd7a26788ab937e3cdJohn McCall        if (top) {
33950da2cadcc6da86abff6772de65280ace2cabc94John McCall            // go to the top of the document
34050da2cadcc6da86abff6772de65280ace2cabc94John McCall            return animateScrollTo(scrollX, 0);
34150da2cadcc6da86abff6772de65280ace2cabc94John McCall        }
34250da2cadcc6da86abff6772de65280ace2cabc94John McCall        int dy = -mContainerViewHeight / 2;
343182ab5112650d3228291c4dadd64a9f77f5aeb51John McCall        if (mContainerViewHeight > 2 * PAGE_SCROLL_OVERLAP) {
344ad346f4f678ab1c3222425641d851dc63e9dfa1aJohn McCall            dy = -mContainerViewHeight + PAGE_SCROLL_OVERLAP;
34550da2cadcc6da86abff6772de65280ace2cabc94John McCall        }
34650da2cadcc6da86abff6772de65280ace2cabc94John McCall        // animateScrollTo clamps the argument to the scrollable range so using (scrollY + dy) is
34750da2cadcc6da86abff6772de65280ace2cabc94John McCall        // fine.
34850da2cadcc6da86abff6772de65280ace2cabc94John McCall        return animateScrollTo(scrollX, scrollY + dy);
34950da2cadcc6da86abff6772de65280ace2cabc94John McCall    }
35050da2cadcc6da86abff6772de65280ace2cabc94John McCall
35150da2cadcc6da86abff6772de65280ace2cabc94John McCall    /**
35250da2cadcc6da86abff6772de65280ace2cabc94John McCall     * See {@link android.webkit.WebView#pageDown(boolean)}
353378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor     */
354378e1e739aed97e9b278beeb20e9f5bbe34c0232Douglas Gregor    public boolean pageDown(boolean bottom) {
355182ab5112650d3228291c4dadd64a9f77f5aeb51John McCall        final int scrollX = mDelegate.getContainerViewScrollX();
356182ab5112650d3228291c4dadd64a9f77f5aeb51John McCall        final int scrollY = mDelegate.getContainerViewScrollY();
3577e1dff7a68a4d00e71debafa7f5c259473091746John McCall
3587e1dff7a68a4d00e71debafa7f5c259473091746John McCall        if (bottom) {
3597e1dff7a68a4d00e71debafa7f5c259473091746John McCall            return animateScrollTo(scrollX, computeVerticalScrollRange());
3607e1dff7a68a4d00e71debafa7f5c259473091746John McCall        }
3617e1dff7a68a4d00e71debafa7f5c259473091746John McCall        int dy = mContainerViewHeight / 2;
3627e1dff7a68a4d00e71debafa7f5c259473091746John McCall        if (mContainerViewHeight > 2 * PAGE_SCROLL_OVERLAP) {
3637e1dff7a68a4d00e71debafa7f5c259473091746John McCall            dy = mContainerViewHeight - PAGE_SCROLL_OVERLAP;
3647e1dff7a68a4d00e71debafa7f5c259473091746John McCall        }
3657e1dff7a68a4d00e71debafa7f5c259473091746John McCall        // animateScrollTo clamps the argument to the scrollable range so using (scrollY + dy) is
3667e1dff7a68a4d00e71debafa7f5c259473091746John McCall        // fine.
3677e1dff7a68a4d00e71debafa7f5c259473091746John McCall        return animateScrollTo(scrollX, scrollY + dy);
3687e1dff7a68a4d00e71debafa7f5c259473091746John McCall    }
3697e1dff7a68a4d00e71debafa7f5c259473091746John McCall
3707e1dff7a68a4d00e71debafa7f5c259473091746John McCall    /**
3717e1dff7a68a4d00e71debafa7f5c259473091746John McCall     * See {@link android.webkit.WebView#requestChildRectangleOnScreen(View, Rect, boolean)}
3727e1dff7a68a4d00e71debafa7f5c259473091746John McCall     */
3737e1dff7a68a4d00e71debafa7f5c259473091746John McCall    public boolean requestChildRectangleOnScreen(int childOffsetX, int childOffsetY, Rect rect,
3747e1dff7a68a4d00e71debafa7f5c259473091746John McCall            boolean immediate) {
3757e1dff7a68a4d00e71debafa7f5c259473091746John McCall        // TODO(mkosiba): WebViewClassic immediately returns false if a zoom animation is
3767e1dff7a68a4d00e71debafa7f5c259473091746John McCall        // in progress. We currently can't tell if one is happening.. should we instead cancel any
3777e1dff7a68a4d00e71debafa7f5c259473091746John McCall        // scroll animation when the size/pageScaleFactor changes?
3787e1dff7a68a4d00e71debafa7f5c259473091746John McCall
3797e1dff7a68a4d00e71debafa7f5c259473091746John McCall        // TODO(mkosiba): Take scrollbar width into account in the screenRight/screenBotton
380182ab5112650d3228291c4dadd64a9f77f5aeb51John McCall        // calculations. http://crbug.com/269032
381182ab5112650d3228291c4dadd64a9f77f5aeb51John McCall
382607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        final int scrollX = mDelegate.getContainerViewScrollX();
383607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        final int scrollY = mDelegate.getContainerViewScrollY();
384cbb67480094b3bcb5b715acd827cbad55e2a204cSean Hunt
385607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        rect.offset(childOffsetX, childOffsetY);
386607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson
387607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        int screenTop = scrollY;
388607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        int screenBottom = scrollY + mContainerViewHeight;
389607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        int scrollYDelta = 0;
390607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson
391607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        if (rect.bottom > screenBottom) {
392607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson            int oneThirdOfScreenHeight = mContainerViewHeight / 3;
393607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson            if (rect.width() > 2 * oneThirdOfScreenHeight) {
394607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson                // If the rectangle is too tall to fit in the bottom two thirds
39580638c5e6395344c1e6096542b0ff3b8bfb2139eAnders Carlsson                // of the screen, place it at the top.
396607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson                scrollYDelta = rect.top - screenTop;
397607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson            } else {
398607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson                // If the rectangle will still fit on screen, we want its
399607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson                // top to be in the top third of the screen.
400607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson                scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
4017e1dff7a68a4d00e71debafa7f5c259473091746John McCall            }
4027e1dff7a68a4d00e71debafa7f5c259473091746John McCall        } else if (rect.top < screenTop) {
4037e1dff7a68a4d00e71debafa7f5c259473091746John McCall            scrollYDelta = rect.top - screenTop;
4047e1dff7a68a4d00e71debafa7f5c259473091746John McCall        }
4057e1dff7a68a4d00e71debafa7f5c259473091746John McCall
4067e1dff7a68a4d00e71debafa7f5c259473091746John McCall        int screenLeft = scrollX;
407bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        int screenRight = scrollX + mContainerViewWidth;
408bff225ecf77fb891596ecb1b27196310d268365eJohn McCall        int scrollXDelta = 0;
4098561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson
4108561a8666c70f924c8f0209c41b9b77bbbf90607Anders Carlsson        if (rect.right > screenRight && rect.left > screenLeft) {
41150da2cadcc6da86abff6772de65280ace2cabc94John McCall            if (rect.width() > mContainerViewWidth) {
41250da2cadcc6da86abff6772de65280ace2cabc94John McCall                scrollXDelta += (rect.left - screenLeft);
413d7722d9d76a851e7897f4127626616d3b1b8e530Eli Friedman            } else {
4147c2349be2d11143a2e59a167fd43362a3bf4585eJohn McCall                scrollXDelta += (rect.right - screenRight);
415f394078fde147dcf27e9b6a7965517388d64dcb6Eli Friedman            }
4167c2349be2d11143a2e59a167fd43362a3bf4585eJohn McCall        } else if (rect.left < screenLeft) {
417410ffb2bc5f072d58a73c14560345bcf77dec1ccJohn McCall            scrollXDelta -= (screenLeft - rect.left);
418649b4a1a9b5e6f768ca0cb84bd97b00f51083e15Chad Rosier        }
419558d2abc7f9fd6801cc7677200992313ae90b5d8John McCall
420558d2abc7f9fd6801cc7677200992313ae90b5d8John McCall        if (scrollYDelta == 0 && scrollXDelta == 0) {
421594d5e8bd9870080aad6a761538e272bc2dfcc13Anders Carlsson            return false;
4224e4d08403ca5cfd4d558fa2936215d3a4e5a528dDavid Blaikie        }
423c1cfdf8647a499b6b3024f4bd14a236cddb23988Anders Carlsson
4241f0fca54676cfa8616e7f3cd7a26788ab937e3cdJohn McCall        if (immediate) {
4251f0fca54676cfa8616e7f3cd7a26788ab937e3cdJohn McCall            scrollBy(scrollXDelta, scrollYDelta);
426607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson            return true;
427607d037c3f4376cbc8979d0eb9cd2f49a58ea033Anders Carlsson        } else {
428fb8cc253420e93cee33d29df5a2bdae6aaf16e39Douglas Gregor            return animateScrollTo(scrollX + scrollXDelta, scrollY + scrollYDelta);
429fb8cc253420e93cee33d29df5a2bdae6aaf16e39Douglas Gregor        }
4300bdb5aa1a3384d194b0e14a9ecbe3309ff33daa3Eli Friedman    }
431fb8cc253420e93cee33d29df5a2bdae6aaf16e39Douglas Gregor}
432fb8cc253420e93cee33d29df5a2bdae6aaf16e39Douglas Gregor