[chromium] Transfer wheel fling via WebCompositorInputHandlerClient
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Mar 2012 06:08:16 +0000 (06:08 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Mar 2012 06:08:16 +0000 (06:08 +0000)
https://bugs.webkit.org/show_bug.cgi?id=81740

Patch by James Robinson <jamesr@chromium.org> on 2012-03-27
Reviewed by Adrienne Walker.

Source/WebCore:

Adds the ability to construct an in-progress PlatformGestureAnimation.

* platform/ActivePlatformGestureAnimation.cpp:
(WebCore::ActivePlatformGestureAnimation::create):
(WebCore):
(WebCore::ActivePlatformGestureAnimation::ActivePlatformGestureAnimation):
* platform/ActivePlatformGestureAnimation.h:
(ActivePlatformGestureAnimation):
* platform/TouchpadFlingPlatformGestureCurve.cpp:
(WebCore::TouchpadFlingPlatformGestureCurve::create):
(WebCore::TouchpadFlingPlatformGestureCurve::TouchpadFlingPlatformGestureCurve):
* platform/TouchpadFlingPlatformGestureCurve.h:
(TouchpadFlingPlatformGestureCurve):

Source/WebKit/chromium:

Adds a path for transfering an active wheel fling animation out to the embedder from the compositor and back in
to a WebViewImpl via the embedder. This is used when we start a wheel fling animation on the compositor thread
but then hit a condition that we can't handle from the compositor, such as registered wheel event listeners or a
scrollable area we can't handle.

New tests added to WebCompositorInputHandlerTest for the transfering logic.

* public/WebActiveWheelFlingParameters.h: Copied from Source/WebKit/chromium/public/WebCompositorInputHandlerClient.h.
(WebKit):
(WebActiveWheelFlingParameters):
(WebKit::WebActiveWheelFlingParameters::WebActiveWheelFlingParameters):
* public/WebCompositorInputHandlerClient.h:
(WebKit):
(WebCompositorInputHandlerClient):
(WebKit::WebCompositorInputHandlerClient::transferActiveWheelFlingAnimation):
* public/WebView.h:
(WebKit):
(WebView):
* src/WebCompositorInputHandlerImpl.cpp:
(WebKit::WebCompositorInputHandlerImpl::handleGestureFling):
(WebKit::WebCompositorInputHandlerImpl::animate):
(WebKit::WebCompositorInputHandlerImpl::cancelCurrentFling):
(WebKit::WebCompositorInputHandlerImpl::scrollBy):
* src/WebCompositorInputHandlerImpl.h:
(WebCore):
* src/WebViewImpl.cpp:
(WebKit::WebViewImpl::transferActiveWheelFlingAnimation):
(WebKit):
* src/WebViewImpl.h:
(WebViewImpl):
* tests/WebCompositorInputHandlerImplTest.cpp:
(WebKit::MockWebCompositorInputHandlerClient::MockWebCompositorInputHandlerClient):
(MockWebCompositorInputHandlerClient):
(WebKit::TEST):
(WebKit::WebCompositorInputHandlerImplTest::WebCompositorInputHandlerImplTest):
(WebKit::WebCompositorInputHandlerImplTest::~WebCompositorInputHandlerImplTest):
(WebCompositorInputHandlerImplTest):
(WebKit::TEST_F):
(WebKit):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@112364 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/ActivePlatformGestureAnimation.cpp
Source/WebCore/platform/ActivePlatformGestureAnimation.h
Source/WebCore/platform/TouchpadFlingPlatformGestureCurve.cpp
Source/WebCore/platform/TouchpadFlingPlatformGestureCurve.h
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/public/WebActiveWheelFlingParameters.h [new file with mode: 0644]
Source/WebKit/chromium/public/WebCompositorInputHandlerClient.h
Source/WebKit/chromium/public/WebView.h
Source/WebKit/chromium/src/WebCompositorInputHandlerImpl.cpp
Source/WebKit/chromium/src/WebCompositorInputHandlerImpl.h
Source/WebKit/chromium/src/WebViewImpl.cpp
Source/WebKit/chromium/src/WebViewImpl.h
Source/WebKit/chromium/tests/WebCompositorInputHandlerImplTest.cpp

index b0ec54e..8cc0dc4 100644 (file)
@@ -1,3 +1,24 @@
+2012-03-27  James Robinson  <jamesr@chromium.org>
+
+        [chromium] Transfer wheel fling via WebCompositorInputHandlerClient
+        https://bugs.webkit.org/show_bug.cgi?id=81740
+
+        Reviewed by Adrienne Walker.
+
+        Adds the ability to construct an in-progress PlatformGestureAnimation.
+
+        * platform/ActivePlatformGestureAnimation.cpp:
+        (WebCore::ActivePlatformGestureAnimation::create):
+        (WebCore):
+        (WebCore::ActivePlatformGestureAnimation::ActivePlatformGestureAnimation):
+        * platform/ActivePlatformGestureAnimation.h:
+        (ActivePlatformGestureAnimation):
+        * platform/TouchpadFlingPlatformGestureCurve.cpp:
+        (WebCore::TouchpadFlingPlatformGestureCurve::create):
+        (WebCore::TouchpadFlingPlatformGestureCurve::TouchpadFlingPlatformGestureCurve):
+        * platform/TouchpadFlingPlatformGestureCurve.h:
+        (TouchpadFlingPlatformGestureCurve):
+
 2012-03-27  Nat Duca  <nduca@chromium.org>
 
         [chromium] Route monotonic clock up from compositor
index b8cf07a..01600bd 100644 (file)
@@ -41,6 +41,11 @@ PassOwnPtr<ActivePlatformGestureAnimation> ActivePlatformGestureAnimation::creat
     return adoptPtr(new ActivePlatformGestureAnimation(curve, target));
 }
 
+PassOwnPtr<ActivePlatformGestureAnimation> ActivePlatformGestureAnimation::create(PassOwnPtr<PlatformGestureCurve> curve, PlatformGestureCurveTarget* target, double startTime)
+{
+    return adoptPtr(new ActivePlatformGestureAnimation(curve, target, startTime));
+}
+
 ActivePlatformGestureAnimation::~ActivePlatformGestureAnimation()
 {
 #if PLATFORM(CHROMIUM)
@@ -59,6 +64,17 @@ ActivePlatformGestureAnimation::ActivePlatformGestureAnimation(PassOwnPtr<Platfo
 #endif
 }
 
+ActivePlatformGestureAnimation::ActivePlatformGestureAnimation(PassOwnPtr<PlatformGestureCurve> curve, PlatformGestureCurveTarget* target, double startTime)
+    : m_startTime(startTime)
+    , m_waitingForFirstTick(false)
+    , m_curve(curve)
+    , m_target(target)
+{
+#if PLATFORM(CHROMIUM)
+    TRACE_EVENT_START1("input", "GestureAnimation", this, "curve", m_curve->debugName());
+#endif
+}
+
 bool ActivePlatformGestureAnimation::animate(double time)
 {
     if (m_waitingForFirstTick) {
index 9d563e4..21f199c 100644 (file)
@@ -43,6 +43,7 @@ class ActivePlatformGestureAnimation {
     WTF_MAKE_NONCOPYABLE(ActivePlatformGestureAnimation);
 public:
     static PassOwnPtr<ActivePlatformGestureAnimation> create(PassOwnPtr<PlatformGestureCurve>, PlatformGestureCurveTarget*);
+    static PassOwnPtr<ActivePlatformGestureAnimation> create(PassOwnPtr<PlatformGestureCurve>, PlatformGestureCurveTarget*, double startTime);
     ~ActivePlatformGestureAnimation();
 
     bool animate(double time);
@@ -50,6 +51,7 @@ public:
 private:
     // Assumes a valid PlatformGestureCurveTarget that outlives the animation.
     ActivePlatformGestureAnimation(PassOwnPtr<PlatformGestureCurve>, PlatformGestureCurveTarget*);
+    ActivePlatformGestureAnimation(PassOwnPtr<PlatformGestureCurve>, PlatformGestureCurveTarget*, double startTime);
 
     double m_startTime;
     bool m_waitingForFirstTick;
index 66f7ab0..718770d 100644 (file)
@@ -33,19 +33,20 @@ namespace WebCore {
 
 using namespace std;
 
-PassOwnPtr<PlatformGestureCurve> TouchpadFlingPlatformGestureCurve::create(const FloatPoint& velocity)
+PassOwnPtr<PlatformGestureCurve> TouchpadFlingPlatformGestureCurve::create(const FloatPoint& velocity, IntPoint cumulativeScroll)
 {
-    return create(velocity, 3, FloatPoint(0.3333, 0.6666), FloatPoint(0.6666, 1));
+    return create(velocity, 3, FloatPoint(0.3333, 0.6666), FloatPoint(0.6666, 1), cumulativeScroll);
 }
 
-PassOwnPtr<PlatformGestureCurve> TouchpadFlingPlatformGestureCurve::create(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2)
+PassOwnPtr<PlatformGestureCurve> TouchpadFlingPlatformGestureCurve::create(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2, IntPoint cumulativeScroll)
 {
-    return adoptPtr(new TouchpadFlingPlatformGestureCurve(velocity, unitTimeScaleLog10, bezierP1, bezierP2));
+    return adoptPtr(new TouchpadFlingPlatformGestureCurve(velocity, unitTimeScaleLog10, bezierP1, bezierP2, cumulativeScroll));
 }
 
-TouchpadFlingPlatformGestureCurve::TouchpadFlingPlatformGestureCurve(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2)
+TouchpadFlingPlatformGestureCurve::TouchpadFlingPlatformGestureCurve(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2, const IntPoint& cumulativeScroll)
     : m_velocity(velocity)
     , m_timeScaleFactor(unitTimeScaleLog10 / log10(max(10.f, max(fabs(velocity.x()), fabs(velocity.y())))))
+    , m_cumulativeScroll(cumulativeScroll)
     , m_flingBezier(bezierP1.x(), bezierP1.y(), bezierP2.x(), bezierP2.y())
 {
     ASSERT(velocity != FloatPoint::zero());
index 6bfa04e..4b7e466 100644 (file)
@@ -41,15 +41,15 @@ class PlatformGestureCurveTarget;
 // the initial velocity.
 class TouchpadFlingPlatformGestureCurve : public PlatformGestureCurve {
 public:
-    static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity);
-    static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2);
+    static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity, IntPoint cumulativeScroll = IntPoint());
+    static PassOwnPtr<PlatformGestureCurve> create(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2, IntPoint cumulativeScroll = IntPoint());
     virtual ~TouchpadFlingPlatformGestureCurve();
 
     virtual const char* debugName() const { return "TouchpadFling"; }
     virtual bool apply(double monotonicTime, PlatformGestureCurveTarget*);
 
 private:
-    explicit TouchpadFlingPlatformGestureCurve(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2);
+    TouchpadFlingPlatformGestureCurve(const FloatPoint& velocity, const float unitTimeScaleLog10, const FloatPoint& bezierP1, const FloatPoint& bezierP2, const IntPoint& cumulativeScroll);
 
     FloatPoint m_velocity;
     float m_timeScaleFactor;
index 1a4fcee..5f0acbc 100644 (file)
@@ -1,5 +1,52 @@
 2012-03-27  James Robinson  <jamesr@chromium.org>
 
+        [chromium] Transfer wheel fling via WebCompositorInputHandlerClient
+        https://bugs.webkit.org/show_bug.cgi?id=81740
+
+        Reviewed by Adrienne Walker.
+
+        Adds a path for transfering an active wheel fling animation out to the embedder from the compositor and back in
+        to a WebViewImpl via the embedder. This is used when we start a wheel fling animation on the compositor thread
+        but then hit a condition that we can't handle from the compositor, such as registered wheel event listeners or a
+        scrollable area we can't handle.
+
+        New tests added to WebCompositorInputHandlerTest for the transfering logic.
+
+        * public/WebActiveWheelFlingParameters.h: Copied from Source/WebKit/chromium/public/WebCompositorInputHandlerClient.h.
+        (WebKit):
+        (WebActiveWheelFlingParameters):
+        (WebKit::WebActiveWheelFlingParameters::WebActiveWheelFlingParameters):
+        * public/WebCompositorInputHandlerClient.h:
+        (WebKit):
+        (WebCompositorInputHandlerClient):
+        (WebKit::WebCompositorInputHandlerClient::transferActiveWheelFlingAnimation):
+        * public/WebView.h:
+        (WebKit):
+        (WebView):
+        * src/WebCompositorInputHandlerImpl.cpp:
+        (WebKit::WebCompositorInputHandlerImpl::handleGestureFling):
+        (WebKit::WebCompositorInputHandlerImpl::animate):
+        (WebKit::WebCompositorInputHandlerImpl::cancelCurrentFling):
+        (WebKit::WebCompositorInputHandlerImpl::scrollBy):
+        * src/WebCompositorInputHandlerImpl.h:
+        (WebCore):
+        * src/WebViewImpl.cpp:
+        (WebKit::WebViewImpl::transferActiveWheelFlingAnimation):
+        (WebKit):
+        * src/WebViewImpl.h:
+        (WebViewImpl):
+        * tests/WebCompositorInputHandlerImplTest.cpp:
+        (WebKit::MockWebCompositorInputHandlerClient::MockWebCompositorInputHandlerClient):
+        (MockWebCompositorInputHandlerClient):
+        (WebKit::TEST):
+        (WebKit::WebCompositorInputHandlerImplTest::WebCompositorInputHandlerImplTest):
+        (WebKit::WebCompositorInputHandlerImplTest::~WebCompositorInputHandlerImplTest):
+        (WebCompositorInputHandlerImplTest):
+        (WebKit::TEST_F):
+        (WebKit):
+
+2012-03-27  James Robinson  <jamesr@chromium.org>
+
         [chromium] Send wheel events to main thread even if we think nothing is scrollable
         https://bugs.webkit.org/show_bug.cgi?id=82408
 
diff --git a/Source/WebKit/chromium/public/WebActiveWheelFlingParameters.h b/Source/WebKit/chromium/public/WebActiveWheelFlingParameters.h
new file mode 100644 (file)
index 0000000..ef277d5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebActiveWheelFlingParameters_h
+#define WebActiveWheelFlingParameters_h
+
+#include "platform/WebCommon.h"
+
+#include "platform/WebFloatPoint.h"
+#include "platform/WebPoint.h"
+#include "platform/WebSize.h"
+
+namespace WebKit {
+
+struct WebActiveWheelFlingParameters {
+    WebFloatPoint delta;
+    WebPoint point;
+    WebPoint globalPoint;
+    int modifiers;
+    WebSize cumulativeScroll;
+    double startTime;
+
+    WebActiveWheelFlingParameters()
+        : modifiers(0)
+        , startTime(0)
+    {
+    }
+};
+
+}
+
+#endif
index e8bb555..7c911d8 100644 (file)
@@ -28,6 +28,8 @@
 
 namespace WebKit {
 
+struct WebActiveWheelFlingParameters;
+
 class WebCompositorInputHandlerClient {
 public:
     // Callbacks invoked from the compositor thread.
@@ -42,6 +44,10 @@ public:
     // should be forwarded to the WebWidget associated with this compositor for further processing.
     virtual void didNotHandleInputEvent(bool sendToWidget) = 0;
 
+    // Transfers an active wheel fling animation initiated by a previously handled input event out to the client.
+    // FIXME: Make pure virtual once implementation lands on Chromium side.
+    virtual void transferActiveWheelFlingAnimation(const WebActiveWheelFlingParameters&) { }
+
 protected:
     virtual ~WebCompositorInputHandlerClient() { }
 };
index 3c8ae44..4d995e0 100644 (file)
@@ -54,6 +54,7 @@ class WebSettings;
 class WebSpellCheckClient;
 class WebString;
 class WebViewClient;
+struct WebActiveWheelFlingParameters;
 struct WebMediaPlayerAction;
 struct WebPluginAction;
 struct WebPoint;
@@ -428,6 +429,10 @@ public:
     // Can be used for allocating resources that the compositor will later access.
     virtual WebGraphicsContext3D* sharedGraphicsContext3D() = 0;
 
+    // Called to inform the WebView that a wheel fling animation was started externally (for instance
+    // by the compositor) but must be completed by the WebView.
+    virtual void transferActiveWheelFlingAnimation(const WebActiveWheelFlingParameters&) = 0;
+
     // Visibility -----------------------------------------------------------
 
     // Sets the visibility of the WebView.
@@ -452,7 +457,6 @@ public:
     // Simulates a compositor lost context.
     virtual void loseCompositorContext(int numTimes) = 0;
 
-
 protected:
     ~WebView() {}
 };
index 2b52d99..8e0c613 100644 (file)
@@ -269,9 +269,12 @@ WebCompositorInputHandlerImpl::EventDisposition WebCompositorInputHandlerImpl::h
     switch (scrollStatus) {
     case CCInputHandlerClient::ScrollStarted: {
         TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::handleGestureFling::started");
-        OwnPtr<PlatformGestureCurve> flingCurve = TouchpadFlingPlatformGestureCurve::create(FloatPoint(-gestureEvent.deltaX, -gestureEvent.deltaY));
+        OwnPtr<PlatformGestureCurve> flingCurve = TouchpadFlingPlatformGestureCurve::create(FloatPoint(gestureEvent.deltaX, gestureEvent.deltaY));
         m_wheelFlingAnimation = CCActiveGestureAnimation::create(PlatformGestureToCCGestureAdapter::create(flingCurve.release()), this);
-        m_wheelFlingPoint = IntPoint(gestureEvent.x, gestureEvent.y);
+        m_wheelFlingParameters.delta = WebFloatPoint(gestureEvent.deltaX, gestureEvent.deltaY);
+        m_wheelFlingParameters.point = WebPoint(gestureEvent.x, gestureEvent.y);
+        m_wheelFlingParameters.globalPoint = WebPoint(gestureEvent.globalX, gestureEvent.globalY);
+        m_wheelFlingParameters.modifiers = gestureEvent.modifiers;
         m_inputHandlerClient->scheduleAnimation();
         return DidHandle;
     }
@@ -300,11 +303,14 @@ void WebCompositorInputHandlerImpl::animate(double monotonicTime)
     if (!m_wheelFlingAnimation)
         return;
 
+    if (!m_wheelFlingParameters.startTime)
+        m_wheelFlingParameters.startTime = monotonicTime;
+
     if (m_wheelFlingAnimation->animate(monotonicTime))
         m_inputHandlerClient->scheduleAnimation();
     else {
         TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::animate::flingOver");
-        m_wheelFlingAnimation.clear();
+        cancelCurrentFling();
     }
 }
 
@@ -313,6 +319,7 @@ bool WebCompositorInputHandlerImpl::cancelCurrentFling()
     bool hadFlingAnimation = m_wheelFlingAnimation;
     TRACE_EVENT_INSTANT1("cc", "WebCompositorInputHandlerImpl::cancelCurrentFling", "hadFlingAnimation", hadFlingAnimation);
     m_wheelFlingAnimation.clear();
+    m_wheelFlingParameters = WebActiveWheelFlingParameters();
     return hadFlingAnimation;
 }
 
@@ -322,30 +329,35 @@ void WebCompositorInputHandlerImpl::scrollBy(const IntPoint& increment)
         return;
 
     TRACE_EVENT2("cc", "WebCompositorInputHandlerImpl::scrollBy", "x", increment.x(), "y", increment.y());
-
-    WebMouseWheelEvent event;
-    event.type = WebInputEvent::MouseWheel;
-    event.deltaX = -increment.x();
-    event.deltaY = -increment.y();
-    event.hasPreciseScrollingDeltas = true;
-    event.x = m_wheelFlingPoint.x();
-    event.y = m_wheelFlingPoint.y();
-
-    WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(event);
+    WebMouseWheelEvent syntheticWheel;
+    syntheticWheel.type = WebInputEvent::MouseWheel;
+    syntheticWheel.deltaX = increment.x();
+    syntheticWheel.deltaY = increment.y();
+    syntheticWheel.hasPreciseScrollingDeltas = true;
+    syntheticWheel.x = m_wheelFlingParameters.point.x;
+    syntheticWheel.y = m_wheelFlingParameters.point.y;
+    syntheticWheel.globalX = m_wheelFlingParameters.globalPoint.x;
+    syntheticWheel.globalY = m_wheelFlingParameters.globalPoint.y;
+    syntheticWheel.modifiers = m_wheelFlingParameters.modifiers;
+
+    WebCompositorInputHandlerImpl::EventDisposition disposition = handleInputEventInternal(syntheticWheel);
     switch (disposition) {
     case DidHandle:
+        m_wheelFlingParameters.cumulativeScroll.width += increment.x();
+        m_wheelFlingParameters.cumulativeScroll.height += increment.y();
     case DropEvent:
         break;
     case DidNotHandle:
         TRACE_EVENT_INSTANT0("cc", "WebCompositorInputHandlerImpl::scrollBy::AbortFling");
-        // FIXME: If we got a DidNotHandle, that means we need to deliver wheels on the main thread.
+        // If we got a DidNotHandle, that means we need to deliver wheels on the main thread.
         // In this case we need to schedule a commit and transfer the fling curve over to the main
         // thread and run the rest of the wheels from there.
         // This can happen when flinging a page that contains a scrollable subarea that we can't
         // scroll on the thread if the fling starts outside the subarea but then is flung "under" the
         // pointer.
-        // For now, just abort the fling.
+        m_client->transferActiveWheelFlingAnimation(m_wheelFlingParameters);
         cancelCurrentFling();
+        break;
     }
 }
 
index c9ff5e3..6464710 100644 (file)
@@ -26,7 +26,7 @@
 #ifndef WebCompositorInputHandlerImpl_h
 #define WebCompositorInputHandlerImpl_h
 
-#include "IntPoint.h"
+#include "WebActiveWheelFlingParameters.h"
 #include "WebCompositor.h"
 #include "WebCompositorInputHandler.h"
 #include "WebInputEvent.h"
@@ -41,6 +41,7 @@ class Mutex;
 }
 
 namespace WebCore {
+class IntPoint;
 class CCGestureCurveTarget;
 class CCInputHandlerClient;
 class CCThread;
@@ -83,7 +84,8 @@ private:
     bool cancelCurrentFling();
 
     OwnPtr<WebCore::CCActiveGestureAnimation> m_wheelFlingAnimation;
-    WebCore::IntPoint m_wheelFlingPoint; // Pointer position for the current fling.
+    // Parameters for the active fling animation, stored in case we need to transfer it out later.
+    WebActiveWheelFlingParameters m_wheelFlingParameters;
 
     WebCompositorInputHandlerClient* m_client;
     int m_identifier;
index 63a421b..c0c4396 100644 (file)
 #include "config.h"
 #include "WebViewImpl.h"
 
+#include "AXObjectCache.h"
 #include "ActivePlatformGestureAnimation.h"
 #include "AutofillPopupMenuClient.h"
-#include "AXObjectCache.h"
 #include "BackForwardListChromium.h"
-#include "cc/CCProxy.h"
 #include "CSSStyleSelector.h"
 #include "CSSValueKeywords.h"
 #include "Chrome.h"
 #include "TypingCommand.h"
 #include "UserGestureIndicator.h"
 #include "WebAccessibilityObject.h"
+#include "WebActiveWheelFlingParameters.h"
 #include "WebAutofillClient.h"
 #include "WebCompositorImpl.h"
 #include "WebDevToolsAgentImpl.h"
 #include "WebDevToolsAgentPrivate.h"
-#include "platform/WebDragData.h"
 #include "WebFrameImpl.h"
-#include "platform/WebGraphicsContext3D.h"
-#include "platform/WebImage.h"
 #include "WebInputElement.h"
 #include "WebInputEvent.h"
 #include "WebInputEventConversion.h"
 #include "WebKit.h"
-#include "platform/WebKitPlatformSupport.h"
-#include "platform/WebLayer.h"
-#include "platform/WebLayerTreeView.h"
 #include "WebMediaPlayerAction.h"
 #include "WebNode.h"
 #include "WebPlugin.h"
 #include "WebPluginAction.h"
 #include "WebPluginContainerImpl.h"
-#include "platform/WebPoint.h"
 #include "WebPopupMenuImpl.h"
 #include "WebRange.h"
-#include "platform/WebRect.h"
 #include "WebRuntimeFeatures.h"
 #include "WebSettingsImpl.h"
-#include "platform/WebString.h"
-#include "platform/WebVector.h"
 #include "WebViewClient.h"
 #include "WheelEvent.h"
+#include "cc/CCProxy.h"
+#include "platform/WebDragData.h"
+#include "platform/WebImage.h"
+#include "platform/WebKitPlatformSupport.h"
+#include "platform/WebString.h"
+#include "platform/WebVector.h"
+#include <public/WebFloatPoint.h>
+#include <public/WebGraphicsContext3D.h>
+#include <public/WebLayer.h>
+#include <public/WebLayerTreeView.h>
+#include <public/WebPoint.h>
+#include <public/WebRect.h>
 #include <wtf/ByteArray.h>
 #include <wtf/CurrentTime.h>
 #include <wtf/MainThread.h>
@@ -677,6 +679,18 @@ bool WebViewImpl::gestureEvent(const WebGestureEvent& event)
     return false;
 }
 
+void WebViewImpl::transferActiveWheelFlingAnimation(const WebActiveWheelFlingParameters& parameters)
+{
+    TRACE_EVENT0("webkit", "WebViewImpl::transferActiveWheelFlingAnimation");
+    ASSERT(!m_gestureAnimation);
+    m_lastWheelPosition = parameters.point;
+    m_lastWheelGlobalPosition = parameters.globalPoint;
+    m_flingModifier = parameters.modifiers;
+    OwnPtr<PlatformGestureCurve> curve = TouchpadFlingPlatformGestureCurve::create(parameters.delta, IntPoint(parameters.cumulativeScroll));
+    m_gestureAnimation = ActivePlatformGestureAnimation::create(curve.release(), this, parameters.startTime);
+    scheduleAnimation();
+}
+
 void WebViewImpl::startPageScaleAnimation(const IntPoint& scroll, bool useAnchor, float newScale, double durationSec)
 {
     if (!m_layerTreeView.isNull())
index b25ddde..3082d4e 100644 (file)
@@ -253,6 +253,7 @@ public:
     virtual void performCustomContextMenuAction(unsigned action);
     virtual void addPageOverlay(WebPageOverlay*, int /* zOrder */);
     virtual void removePageOverlay(WebPageOverlay*);
+    virtual void transferActiveWheelFlingAnimation(const WebActiveWheelFlingParameters&);
 
     // WebLayerTreeViewClient
     virtual void willBeginFrame();
index fa8247b..92f0061 100644 (file)
 #include "cc/CCActiveGestureAnimation.h"
 #include "cc/CCInputHandler.h"
 #include "cc/CCSingleThreadProxy.h"
+#include "platform/WebFloatPoint.h"
+#include "platform/WebPoint.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <wtf/OwnPtr.h>
 
-using WebKit::WebCompositorInputHandler;
-using WebKit::WebCompositorInputHandlerImpl;
+using namespace WebKit;
 
 namespace {
 
@@ -73,11 +74,11 @@ private:
     virtual void setActiveGestureAnimation(PassOwnPtr<WebCore::CCActiveGestureAnimation>) OVERRIDE { }
 };
 
-class MockWebCompositorInputHandlerClient : public WebKit::WebCompositorInputHandlerClient {
+class MockWebCompositorInputHandlerClient : public WebCompositorInputHandlerClient {
     WTF_MAKE_NONCOPYABLE(MockWebCompositorInputHandlerClient);
 public:
     MockWebCompositorInputHandlerClient()
-        : WebKit::WebCompositorInputHandlerClient()
+        : WebCompositorInputHandlerClient()
     {
     }
     virtual ~MockWebCompositorInputHandlerClient() { }
@@ -85,11 +86,14 @@ public:
     MOCK_METHOD0(willShutdown, void());
     MOCK_METHOD0(didHandleInputEvent, void());
     MOCK_METHOD1(didNotHandleInputEvent, void(bool sendToWidget));
+
+    MOCK_METHOD1(transferActiveWheelFlingAnimation, void(const WebActiveWheelFlingParameters&));
+
 };
 
 TEST(WebCompositorInputHandlerImpl, fromIdentifier)
 {
-    WebKit::WebCompositor::initialize(0);
+    WebCompositor::initialize(0);
     WebCore::DebugScopedSetImplThread alwaysImplThread;
 
     // Before creating any WebCompositorInputHandlers, lookups for any value should fail and not crash.
@@ -110,7 +114,7 @@ TEST(WebCompositorInputHandlerImpl, fromIdentifier)
 
     // After the compositor is destroyed, its entry should be removed from the map.
     EXPECT_EQ(0, WebCompositorInputHandler::fromIdentifier(compositorIdentifier));
-    WebKit::WebCompositor::shutdown();
+    WebCompositor::shutdown();
 }
 
 class WebCompositorInputHandlerImplTest : public testing::Test {
@@ -118,7 +122,7 @@ public:
     WebCompositorInputHandlerImplTest()
         : m_expectedDisposition(DidHandle)
     {
-        WebKit::WebCompositor::initialize(0);
+        WebCompositor::initialize(0);
         m_inputHandler = WebCompositorInputHandlerImpl::create(&m_mockCCInputHandlerClient);
         m_inputHandler->setClient(&m_mockClient);
     }
@@ -127,39 +131,41 @@ public:
     {
         m_inputHandler->setClient(0);
         m_inputHandler.clear();
-        WebKit::WebCompositor::shutdown();
+        WebCompositor::shutdown();
     }
 
-    void verifyAndResetMocks()
-    {
-        testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
-        testing::Mock::VerifyAndClearExpectations(&m_mockClient);
-        switch (m_expectedDisposition) {
-        case DidHandle:
-            // If we expect to handle events, we shouldn't get any didNotHandleInputEvent() calls with any parameter.
-            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0);
-            EXPECT_CALL(m_mockClient, didHandleInputEvent());
-            break;
-        case DidNotHandle:
-            // If we aren't expecting to handle events, we shouldn't call didHandleInputEvent().
-            EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);
-            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false)).Times(0);
-            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
-            break;
-        case DropEvent:
-            // If we're expecting to drop, we shouldn't get any didHandle..() or didNotHandleInputEvent(true /* sendToWidget */) calls.
-            EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);
-            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)).Times(0);
-            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false));
-            break;
-        }
-    }
+    // This is defined as a macro because when an expectation is not satisfied the only output you get
+    // out of gmock is the line number that set the expectation.
+#define VERIFY_AND_RESET_MOCKS() do                                                                                      \
+    {                                                                                                                    \
+        testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);                                          \
+        testing::Mock::VerifyAndClearExpectations(&m_mockClient);                                                        \
+        switch (m_expectedDisposition) {                                                                                 \
+        case DidHandle:                                                                                                  \
+            /* If we expect to handle events, we shouldn't get any didNotHandleInputEvent() calls with any parameter. */ \
+            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(::testing::_)).Times(0);                                    \
+            EXPECT_CALL(m_mockClient, didHandleInputEvent());                                                            \
+            break;                                                                                                       \
+        case DidNotHandle:                                                                                               \
+            /* If we aren't expecting to handle events, we shouldn't call didHandleInputEvent(). */                      \
+            EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);                                                   \
+            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false)).Times(0);                                           \
+            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));                                                     \
+            break;                                                                                                       \
+        case DropEvent:                                                                                                  \
+            /* If we're expecting to drop, we shouldn't get any didHandle..() or didNotHandleInputEvent(true) calls. */  \
+            EXPECT_CALL(m_mockClient, didHandleInputEvent()).Times(0);                                                   \
+            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true)).Times(0);                                            \
+            EXPECT_CALL(m_mockClient, didNotHandleInputEvent(false));                                                    \
+            break;                                                                                                       \
+        }                                                                                                                                                       \
+    } while (0)
 
 protected:
     MockCCInputHandlerClient m_mockCCInputHandlerClient;
     OwnPtr<WebCompositorInputHandlerImpl> m_inputHandler;
     MockWebCompositorInputHandlerClient m_mockClient;
-    WebKit::WebGestureEvent gesture;
+    WebGestureEvent gesture;
 
     enum ExpectedDisposition { DidHandle, DidNotHandle, DropEvent };
     ExpectedDisposition m_expectedDisposition;
@@ -173,24 +179,24 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureScrollStarted)
 {
     // We shouldn't send any events to the widget for this gesture.
     m_expectedDisposition = DidHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
+    gesture.type = WebInputEvent::GestureScrollBegin;
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollUpdate;
+    gesture.type = WebInputEvent::GestureScrollUpdate;
     gesture.deltaY = -40; // -Y means scroll down - i.e. in the +Y direction.
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::height, testing::Gt(0))));
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollEnd;
+    gesture.type = WebInputEvent::GestureScrollEnd;
     gesture.deltaY = 0;
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd());
     m_inputHandler->handleInputEvent(gesture);
@@ -200,23 +206,23 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureScrollFailed)
 {
     // We should send all events to the widget for this gesture.
     m_expectedDisposition = DidNotHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(::testing::_, ::testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
+    gesture.type = WebInputEvent::GestureScrollBegin;
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollUpdate;
+    gesture.type = WebInputEvent::GestureScrollUpdate;
     gesture.deltaY = 40;
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollEnd;
+    gesture.type = WebInputEvent::GestureScrollEnd;
     gesture.deltaY = 0;
     m_inputHandler->handleInputEvent(gesture);
 }
@@ -228,12 +234,12 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureScrollIgnored)
     // indicating that we could determine that there's nothing that could scroll or otherwise
     // react to this gesture sequence and thus we should drop the whole gesture sequence on the floor.
     m_expectedDisposition = DropEvent;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollIgnored));
 
-    gesture.type = WebKit::WebInputEvent::GestureScrollBegin;
+    gesture.type = WebInputEvent::GestureScrollBegin;
     m_inputHandler->handleInputEvent(gesture);
 }
 
@@ -241,33 +247,33 @@ TEST_F(WebCompositorInputHandlerImplTest, gesturePinch)
 {
     // We shouldn't send any events to the widget for this gesture.
     m_expectedDisposition = DidHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GesturePinchBegin;
+    gesture.type = WebInputEvent::GesturePinchBegin;
     EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureBegin());
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GesturePinchUpdate;
+    gesture.type = WebInputEvent::GesturePinchUpdate;
     gesture.deltaX = 1.5;
     gesture.x = 7;
     gesture.y = 13;
     EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureUpdate(1.5, WebCore::IntPoint(7, 13)));
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GesturePinchUpdate;
+    gesture.type = WebInputEvent::GesturePinchUpdate;
     gesture.deltaX = 0.5;
     gesture.x = 9;
     gesture.y = 6;
     EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureUpdate(.5, WebCore::IntPoint(9, 6)));
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
-    gesture.type = WebKit::WebInputEvent::GesturePinchEnd;
+    gesture.type = WebInputEvent::GesturePinchEnd;
     EXPECT_CALL(m_mockCCInputHandlerClient, pinchGestureEnd());
     m_inputHandler->handleInputEvent(gesture);
 }
@@ -276,20 +282,20 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingStarted)
 {
     // We shouldn't send any events to the widget for this gesture.
     m_expectedDisposition = DidHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
 
-    gesture.type = WebKit::WebInputEvent::GestureFlingStart;
+    gesture.type = WebInputEvent::GestureFlingStart;
     gesture.deltaX = 10;
     EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     // Verify that a GestureFlingCancel during an animation cancels it.
-    gesture.type = WebKit::WebInputEvent::GestureFlingCancel;
+    gesture.type = WebInputEvent::GestureFlingCancel;
     m_inputHandler->handleInputEvent(gesture);
 }
 
@@ -297,36 +303,36 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingFailed)
 {
     // We should send all events to the widget for this gesture.
     m_expectedDisposition = DidNotHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
 
-    gesture.type = WebKit::WebInputEvent::GestureFlingStart;
+    gesture.type = WebInputEvent::GestureFlingStart;
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     // Even if we didn't start a fling ourselves, we still need to send the cancel event to the widget.
-    gesture.type = WebKit::WebInputEvent::GestureFlingCancel;
+    gesture.type = WebInputEvent::GestureFlingCancel;
     m_inputHandler->handleInputEvent(gesture);
 }
 
 TEST_F(WebCompositorInputHandlerImplTest, gestureFlingIgnored)
 {
     m_expectedDisposition = DidNotHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollIgnored));
 
-    gesture.type = WebKit::WebInputEvent::GestureFlingStart;
+    gesture.type = WebInputEvent::GestureFlingStart;
     m_inputHandler->handleInputEvent(gesture);
 
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     // Even if we didn't start a fling ourselves, we still need to send the cancel event to the widget.
-    gesture.type = WebKit::WebInputEvent::GestureFlingCancel;
+    gesture.type = WebInputEvent::GestureFlingCancel;
     m_inputHandler->handleInputEvent(gesture);
 }
 
@@ -334,12 +340,22 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimates)
 {
     // We shouldn't send any events to the widget for this gesture.
     m_expectedDisposition = DidHandle;
-    verifyAndResetMocks();
+    VERIFY_AND_RESET_MOCKS();
 
     // On the fling start, we should schedule an animation but not actually start
     // scrolling.
-    gesture.type = WebKit::WebInputEvent::GestureFlingStart;
-    gesture.deltaX = -1000;
+    gesture.type = WebInputEvent::GestureFlingStart;
+    WebFloatPoint flingDelta = WebFloatPoint(1000, 0);
+    WebPoint flingPoint = WebPoint(7, 13);
+    WebPoint flingGlobalPoint = WebPoint(17, 23);
+    int modifiers = 7;
+    gesture.deltaX = flingDelta.x;
+    gesture.deltaY = flingDelta.y;
+    gesture.x = flingPoint.x;
+    gesture.y = flingPoint.y;
+    gesture.globalX = flingGlobalPoint.x;
+    gesture.globalY = flingGlobalPoint.y;
+    gesture.modifiers = modifiers;
     EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
@@ -356,33 +372,194 @@ TEST_F(WebCompositorInputHandlerImplTest, gestureFlingAnimates)
 
     testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
 
-    // The second call should start scrolling in the +X direction.
+    // The second call should start scrolling in the -X direction.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::width, testing::Lt(0))));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd());
+    m_inputHandler->animate(10.1);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // Let's say on the third call we hit a non-scrollable region. We should abort the fling and not scroll.
+    // We also should pass the current fling parameters out to the client so the rest of the fling can be
+    // transferred to the main thread.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0);
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0);
+
+    // Expected wheel fling animation parameters:
+    // *) flingDelta and flingPoint should match the original GestureFlingStart event
+    // *) startTime should be 10 to match the time parameter of the first animate() call after the GestureFlingStart
+    // *) cumulativeScroll depends on the curve, but since we've animated in the -X direction the X value should be < 0
+    EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf(
+        testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)),
+        testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)),
+        testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)),
+        testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)),
+        testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(10)),
+        testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
+            testing::Field(&WebSize::width, testing::Gt(0))))));
+    m_inputHandler->animate(10.2);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+    testing::Mock::VerifyAndClearExpectations(&m_mockClient);
+
+    // Since we've aborted the fling, the next animation should be a no-op and should not result in another
+    // frame being requested.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()).Times(0);
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0);
+    m_inputHandler->animate(10.3);
+
+    // Since we've transferred the fling to the main thread, we need to pass the next GestureFlingCancel to the main
+    // thread as well.
+    EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
+    gesture.type = WebInputEvent::GestureFlingCancel;
+    m_inputHandler->handleInputEvent(gesture);
+}
+
+TEST_F(WebCompositorInputHandlerImplTest, gestureFlingTransferResets)
+{
+    // We shouldn't send any events to the widget for this gesture.
+    m_expectedDisposition = DidHandle;
+    VERIFY_AND_RESET_MOCKS();
+
+    // Start a gesture fling in the -X direction with zero Y movement.
+    gesture.type = WebInputEvent::GestureFlingStart;
+    WebFloatPoint flingDelta = WebFloatPoint(1000, 0);
+    WebPoint flingPoint = WebPoint(7, 13);
+    WebPoint flingGlobalPoint = WebPoint(17, 23);
+    int modifiers = 1;
+    gesture.deltaX = flingDelta.x;
+    gesture.deltaY = flingDelta.y;
+    gesture.x = flingPoint.x;
+    gesture.y = flingPoint.y;
+    gesture.globalX = flingGlobalPoint.x;
+    gesture.globalY = flingGlobalPoint.y;
+    gesture.modifiers = modifiers;
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
+    m_inputHandler->handleInputEvent(gesture);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // Start the fling animation at time 10. This shouldn't actually scroll, just establish a start time.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0);
+    m_inputHandler->animate(10);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // The second call should start scrolling in the -X direction.
     EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
-    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::width, testing::Gt(0))));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::width, testing::Lt(0))));
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd());
     m_inputHandler->animate(10.1);
 
     testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
 
     // Let's say on the third call we hit a non-scrollable region. We should abort the fling and not scroll.
-    // FIXME: We also need to do some work to transfer the rest of this fling to the main thread.
-    // Add tests for this once it's implemented.
+    // We also should pass the current fling parameters out to the client so the rest of the fling can be
+    // transferred to the main thread.
     EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
         .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0);
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0);
+
+    // Expected wheel fling animation parameters:
+    // *) flingDelta and flingPoint should match the original GestureFlingStart event
+    // *) startTime should be 10 to match the time parameter of the first animate() call after the GestureFlingStart
+    // *) cumulativeScroll depends on the curve, but since we've animated in the -X direction the X value should be < 0
+    EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf(
+        testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)),
+        testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)),
+        testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)),
+        testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)),
+        testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(10)),
+        testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
+            testing::Field(&WebSize::width, testing::Gt(0))))));
     m_inputHandler->animate(10.2);
 
     testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+    testing::Mock::VerifyAndClearExpectations(&m_mockClient);
 
     // Since we've aborted the fling, the next animation should be a no-op and should not result in another
     // frame being requested.
     EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation()).Times(0);
     EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0);
     m_inputHandler->animate(10.3);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // Since we've transferred the fling to the main thread, we need to pass the next GestureFlingCancel to the main
+    // thread as well.
+    EXPECT_CALL(m_mockClient, didNotHandleInputEvent(true));
+    gesture.type = WebInputEvent::GestureFlingCancel;
+    m_inputHandler->handleInputEvent(gesture);
+
+    VERIFY_AND_RESET_MOCKS();
+
+    // Start a second gesture fling, this time in the +Y direction with no X.
+    gesture.type = WebInputEvent::GestureFlingStart;
+    flingDelta = WebFloatPoint(0, -1000);
+    flingPoint = WebPoint(95, 87);
+    flingGlobalPoint = WebPoint(32, 71);
+    modifiers = 2;
+    gesture.deltaX = flingDelta.x;
+    gesture.deltaY = flingDelta.y;
+    gesture.x = flingPoint.x;
+    gesture.y = flingPoint.y;
+    gesture.globalX = flingGlobalPoint.x;
+    gesture.globalY = flingGlobalPoint.y;
+    gesture.modifiers = modifiers;
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
+    m_inputHandler->handleInputEvent(gesture);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // Start the second fling animation at time 30.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_)).Times(0);
+    m_inputHandler->animate(30);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // Tick the second fling once normally.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollStarted));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::Property(&WebCore::IntSize::height, testing::Gt(0))));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd());
+    m_inputHandler->animate(30.1);
+
+    testing::Mock::VerifyAndClearExpectations(&m_mockCCInputHandlerClient);
+
+    // Then abort the second fling.
+    EXPECT_CALL(m_mockCCInputHandlerClient, scheduleAnimation());
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(WebCore::CCInputHandlerClient::ScrollFailed));
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollBy(testing::_)).Times(0);
+    EXPECT_CALL(m_mockCCInputHandlerClient, scrollEnd()).Times(0);
+
+    // We should get parameters from the second fling, nothing from the first fling should "leak".
+    EXPECT_CALL(m_mockClient, transferActiveWheelFlingAnimation(testing::AllOf(
+        testing::Field(&WebActiveWheelFlingParameters::delta, testing::Eq(flingDelta)),
+        testing::Field(&WebActiveWheelFlingParameters::point, testing::Eq(flingPoint)),
+        testing::Field(&WebActiveWheelFlingParameters::globalPoint, testing::Eq(flingGlobalPoint)),
+        testing::Field(&WebActiveWheelFlingParameters::modifiers, testing::Eq(modifiers)),
+        testing::Field(&WebActiveWheelFlingParameters::startTime, testing::Eq(30)),
+        testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
+            testing::Field(&WebSize::height, testing::Lt(0))))));
+    m_inputHandler->animate(30.2);
 }
 
 }