Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / renderer / input / input_handler_proxy_unittest.cc
index d307144..baac5af 100644 (file)
@@ -7,6 +7,7 @@
 #include "base/basictypes.h"
 #include "base/memory/scoped_ptr.h"
 #include "cc/base/swap_promise_monitor.h"
+#include "content/common/input/did_overscroll_params.h"
 #include "content/renderer/input/input_handler_proxy_client.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 using blink::WebActiveWheelFlingParameters;
 using blink::WebFloatPoint;
 using blink::WebFloatSize;
+using blink::WebGestureDevice;
 using blink::WebGestureEvent;
 using blink::WebInputEvent;
+using blink::WebKeyboardEvent;
 using blink::WebMouseWheelEvent;
 using blink::WebPoint;
 using blink::WebSize;
@@ -31,6 +34,43 @@ using blink::WebTouchPoint;
 namespace content {
 namespace {
 
+double InSecondsF(const base::TimeTicks& time) {
+  return (time - base::TimeTicks()).InSecondsF();
+}
+
+WebGestureEvent CreateFling(base::TimeTicks timestamp,
+                            WebGestureDevice source_device,
+                            WebFloatPoint velocity,
+                            WebPoint point,
+                            WebPoint global_point,
+                            int modifiers) {
+  WebGestureEvent fling;
+  fling.type = WebInputEvent::GestureFlingStart;
+  fling.sourceDevice = source_device;
+  fling.timeStampSeconds = (timestamp - base::TimeTicks()).InSecondsF();
+  fling.data.flingStart.velocityX = velocity.x;
+  fling.data.flingStart.velocityY = velocity.y;
+  fling.x = point.x;
+  fling.y = point.y;
+  fling.globalX = global_point.x;
+  fling.globalY = global_point.y;
+  fling.modifiers = modifiers;
+  return fling;
+}
+
+WebGestureEvent CreateFling(WebGestureDevice source_device,
+                            WebFloatPoint velocity,
+                            WebPoint point,
+                            WebPoint global_point,
+                            int modifiers) {
+  return CreateFling(base::TimeTicks(),
+                     source_device,
+                     velocity,
+                     point,
+                     global_point,
+                     modifiers);
+}
+
 class MockInputHandler : public cc::InputHandler {
  public:
   MockInputHandler() {}
@@ -38,19 +78,22 @@ class MockInputHandler : public cc::InputHandler {
 
   MOCK_METHOD0(PinchGestureBegin, void());
   MOCK_METHOD2(PinchGestureUpdate,
-               void(float magnify_delta, gfx::Point anchor));
+               void(float magnify_delta, const gfx::Point& anchor));
   MOCK_METHOD0(PinchGestureEnd, void());
 
-  MOCK_METHOD0(ScheduleAnimation, void());
+  MOCK_METHOD0(SetNeedsAnimate, void());
 
   MOCK_METHOD2(ScrollBegin,
-               ScrollStatus(gfx::Point viewport_point,
+               ScrollStatus(const gfx::Point& viewport_point,
                             cc::InputHandler::ScrollInputType type));
+  MOCK_METHOD2(ScrollAnimated,
+               ScrollStatus(const gfx::Point& viewport_point,
+                            const gfx::Vector2dF& scroll_delta));
   MOCK_METHOD2(ScrollBy,
-               bool(gfx::Point viewport_point,
+               bool(const gfx::Point& viewport_point,
                     const gfx::Vector2dF& scroll_delta));
   MOCK_METHOD2(ScrollVerticallyByPage,
-               bool(gfx::Point viewport_point,
+               bool(const gfx::Point& viewport_point,
                     cc::ScrollDirection direction));
   MOCK_METHOD0(ScrollEnd, void());
   MOCK_METHOD0(FlingScrollBegin, cc::InputHandler::ScrollStatus());
@@ -62,17 +105,18 @@ class MockInputHandler : public cc::InputHandler {
 
   virtual void BindToClient(cc::InputHandlerClient* client) OVERRIDE {}
 
-  virtual void StartPageScaleAnimation(gfx::Vector2d target_offset,
+  virtual void StartPageScaleAnimation(const gfx::Vector2d& target_offset,
                                        bool anchor_point,
                                        float page_scale,
                                        base::TimeDelta duration) OVERRIDE {}
 
-  virtual void NotifyCurrentFlingVelocity(
-      const gfx::Vector2dF& velocity) OVERRIDE {}
-  virtual void MouseMoveAt(gfx::Point mouse_position) OVERRIDE {}
+  virtual void MouseMoveAt(const gfx::Point& mouse_position) OVERRIDE {}
 
-  MOCK_METHOD1(HaveTouchEventHandlersAt,
-               bool(gfx::Point point));
+  MOCK_METHOD2(IsCurrentlyScrollingLayerAt,
+               bool(const gfx::Point& point,
+                    cc::InputHandler::ScrollInputType type));
+
+  MOCK_METHOD1(HaveTouchEventHandlersAt, bool(const gfx::Point& point));
 
   virtual void SetRootLayerScrollOffsetDelegate(
       cc::LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate)
@@ -87,28 +131,28 @@ class MockInputHandler : public cc::InputHandler {
 // indefinitely.
 class FakeWebGestureCurve : public blink::WebGestureCurve {
  public:
-  FakeWebGestureCurve(const blink::WebFloatPoint& velocity,
-                      const blink::WebSize& cumulative_scroll)
+  FakeWebGestureCurve(const blink::WebFloatSize& velocity,
+                      const blink::WebFloatSize& cumulative_scroll)
       : velocity_(velocity), cumulative_scroll_(cumulative_scroll) {}
 
   virtual ~FakeWebGestureCurve() {}
 
   // Returns false if curve has finished and can no longer be applied.
   virtual bool apply(double time, blink::WebGestureCurveTarget* target) {
-    blink::WebSize displacement(velocity_.x * time, velocity_.y * time);
+    blink::WebFloatSize displacement(velocity_.width * time,
+                                     velocity_.height * time);
     blink::WebFloatSize increment(
         displacement.width - cumulative_scroll_.width,
         displacement.height - cumulative_scroll_.height);
     cumulative_scroll_ = displacement;
     // scrollBy() could delete this curve if the animation is over, so don't
     // touch any member variables after making that call.
-    target->scrollBy(increment);
-    return true;
+    return target->scrollBy(increment, velocity_);
   }
 
  private:
-  blink::WebFloatPoint velocity_;
-  blink::WebSize cumulative_scroll_;
+  blink::WebFloatSize velocity_;
+  blink::WebFloatSize cumulative_scroll_;
 
   DISALLOW_COPY_AND_ASSIGN(FakeWebGestureCurve);
 };
@@ -125,26 +169,49 @@ class MockInputHandlerProxyClient
                void(const WebActiveWheelFlingParameters&));
 
   virtual blink::WebGestureCurve* CreateFlingAnimationCurve(
-      int deviceSource,
+      WebGestureDevice deviceSource,
       const WebFloatPoint& velocity,
       const WebSize& cumulative_scroll) OVERRIDE {
-    return new FakeWebGestureCurve(velocity, cumulative_scroll);
+    return new FakeWebGestureCurve(
+        blink::WebFloatSize(velocity.x, velocity.y),
+        blink::WebFloatSize(cumulative_scroll.width, cumulative_scroll.height));
   }
 
-  virtual void DidOverscroll(const cc::DidOverscrollParams& params) OVERRIDE {}
+  MOCK_METHOD1(DidOverscroll, void(const DidOverscrollParams&));
   virtual void DidStopFlinging() OVERRIDE {}
+  virtual void DidReceiveInputEvent() OVERRIDE {}
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockInputHandlerProxyClient);
 };
 
+class MockInputHandlerProxyClientWithDidReceiveInputEvent
+    : public MockInputHandlerProxyClient {
+ public:
+  MockInputHandlerProxyClientWithDidReceiveInputEvent() {}
+  virtual ~MockInputHandlerProxyClientWithDidReceiveInputEvent() {}
+
+  MOCK_METHOD0(DidReceiveInputEvent, void());
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockInputHandlerProxyClientWithDidReceiveInputEvent);
+};
+
+WebTouchPoint CreateWebTouchPoint(WebTouchPoint::State state, float x,
+                                  float y) {
+  WebTouchPoint point;
+  point.state = state;
+  point.screenPosition = WebFloatPoint(x, y);
+  point.position = WebFloatPoint(x, y);
+  return point;
+}
+
 class InputHandlerProxyTest : public testing::Test {
  public:
   InputHandlerProxyTest()
       : expected_disposition_(InputHandlerProxy::DID_HANDLE) {
     input_handler_.reset(
-        new content::InputHandlerProxy(&mock_input_handler_));
-    input_handler_->SetClient(&mock_client_);
+        new content::InputHandlerProxy(&mock_input_handler_, &mock_client_));
   }
 
   ~InputHandlerProxyTest() {
@@ -160,6 +227,43 @@ class InputHandlerProxyTest : public testing::Test {
     testing::Mock::VerifyAndClearExpectations(&mock_client_);                 \
   } while (false)
 
+  void StartFling(base::TimeTicks timestamp,
+                  WebGestureDevice source_device,
+                  WebFloatPoint velocity,
+                  WebPoint position) {
+    expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+    VERIFY_AND_RESET_MOCKS();
+
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+    gesture_.type = WebInputEvent::GestureScrollBegin;
+    gesture_.sourceDevice = source_device;
+    EXPECT_EQ(expected_disposition_,
+              input_handler_->HandleInputEvent(gesture_));
+
+    VERIFY_AND_RESET_MOCKS();
+
+    EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+        .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+    EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+
+    gesture_ =
+        CreateFling(timestamp, source_device, velocity, position, position, 0);
+    EXPECT_EQ(expected_disposition_,
+              input_handler_->HandleInputEvent(gesture_));
+
+    VERIFY_AND_RESET_MOCKS();
+  }
+
+  void CancelFling(base::TimeTicks timestamp) {
+    gesture_.timeStampSeconds = InSecondsF(timestamp);
+    gesture_.type = WebInputEvent::GestureFlingCancel;
+    EXPECT_EQ(expected_disposition_,
+              input_handler_->HandleInputEvent(gesture_));
+
+    VERIFY_AND_RESET_MOCKS();
+  }
+
  protected:
   testing::StrictMock<MockInputHandler> mock_input_handler_;
   scoped_ptr<content::InputHandlerProxy> input_handler_;
@@ -179,6 +283,16 @@ TEST_F(InputHandlerProxyTest, MouseWheelByPageMainThread) {
   testing::Mock::VerifyAndClearExpectations(&mock_client_);
 }
 
+TEST_F(InputHandlerProxyTest, MouseWheelWithCtrl) {
+  expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+  WebMouseWheelEvent wheel;
+  wheel.type = WebInputEvent::MouseWheel;
+  wheel.modifiers = WebInputEvent::ControlKey;
+
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel));
+  testing::Mock::VerifyAndClearExpectations(&mock_client_);
+}
+
 TEST_F(InputHandlerProxyTest, GestureScrollStarted) {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
@@ -385,18 +499,18 @@ TEST_F(InputHandlerProxyTest, GestureFlingStartedTouchpad) {
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
 
   gesture_.type = WebInputEvent::GestureFlingStart;
   gesture_.data.flingStart.velocityX = 10;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
 
   // Verify that a GestureFlingCancel during an animation cancels it.
   gesture_.type = WebInputEvent::GestureFlingCancel;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
@@ -409,7 +523,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingOnMainThreadTouchpad) {
       .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
 
   gesture_.type = WebInputEvent::GestureFlingStart;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   // Since we returned ScrollStatusOnMainThread from scrollBegin, ensure the
@@ -421,7 +535,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingOnMainThreadTouchpad) {
   // Even if we didn't start a fling ourselves, we still need to send the cancel
   // event to the widget.
   gesture_.type = WebInputEvent::GestureFlingCancel;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
@@ -433,7 +547,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchpad) {
       .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
 
   gesture_.type = WebInputEvent::GestureFlingStart;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   expected_disposition_ = InputHandlerProxy::DROP_EVENT;
@@ -442,7 +556,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchpad) {
   // Since the previous fling was ignored, we should also be dropping the next
   // fling_cancel.
   gesture_.type = WebInputEvent::GestureFlingCancel;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
@@ -457,16 +571,15 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
   WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
   WebPoint fling_point = WebPoint(7, 13);
   WebPoint fling_global_point = WebPoint(17, 23);
-  int modifiers = 7;
-  gesture_.data.flingStart.velocityX = fling_delta.x;
-  gesture_.data.flingStart.velocityY = fling_delta.y;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
-  gesture_.x = fling_point.x;
-  gesture_.y = fling_point.y;
-  gesture_.globalX = fling_global_point.x;
-  gesture_.globalY = fling_global_point.y;
-  gesture_.modifiers = modifiers;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  // Note that for trackpad, wheel events with the Control modifier are
+  // special (reserved for zoom), so don't set that here.
+  int modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey;
+  gesture_ = CreateFling(blink::WebGestureDeviceTouchpad,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
@@ -478,7 +591,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
   // fling start will typically include the last scroll from the gesture that
   // lead to the scroll (either wheel or gesture scroll), so there should be no
   // visible hitch.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .Times(0);
   base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
@@ -487,7 +600,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The second call should start scrolling in the -X direction.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_,
@@ -505,7 +618,6 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
   // 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(mock_input_handler_, ScheduleAnimation());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
@@ -541,7 +653,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
   // 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(mock_input_handler_, ScheduleAnimation()).Times(0);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate()).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .Times(0);
   time += base::TimeDelta::FromMilliseconds(100);
@@ -561,20 +673,18 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   VERIFY_AND_RESET_MOCKS();
 
   // Start a gesture fling in the -X direction with zero Y movement.
-  gesture_.type = WebInputEvent::GestureFlingStart;
   WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
   WebPoint fling_point = WebPoint(7, 13);
   WebPoint fling_global_point = WebPoint(17, 23);
-  int modifiers = 1;
-  gesture_.data.flingStart.velocityX = fling_delta.x;
-  gesture_.data.flingStart.velocityY = fling_delta.y;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
-  gesture_.x = fling_point.x;
-  gesture_.y = fling_point.y;
-  gesture_.globalX = fling_global_point.x;
-  gesture_.globalY = fling_global_point.y;
-  gesture_.modifiers = modifiers;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  // Note that for trackpad, wheel events with the Control modifier are
+  // special (reserved for zoom), so don't set that here.
+  int modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey;
+  gesture_ = CreateFling(blink::WebGestureDeviceTouchpad,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
@@ -584,7 +694,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
 
   // Start the fling animation at time 10. This shouldn't actually scroll, just
   // establish a start time.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .Times(0);
   base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
@@ -593,7 +703,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The second call should start scrolling in the -X direction.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_,
@@ -611,7 +721,6 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   // 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(mock_input_handler_, ScheduleAnimation());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
@@ -648,7 +757,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   // 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(mock_input_handler_, ScheduleAnimation()).Times(0);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate()).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .Times(0);
   time += base::TimeDelta::FromMilliseconds(100);
@@ -667,20 +776,16 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   input_handler_->MainThreadHasStoppedFlinging();
 
   // Start a second gesture fling, this time in the +Y direction with no X.
-  gesture_.type = WebInputEvent::GestureFlingStart;
   fling_delta = WebFloatPoint(0, -1000);
   fling_point = WebPoint(95, 87);
   fling_global_point = WebPoint(32, 71);
-  modifiers = 2;
-  gesture_.data.flingStart.velocityX = fling_delta.x;
-  gesture_.data.flingStart.velocityY = fling_delta.y;
-  gesture_.sourceDevice = WebGestureEvent::Touchpad;
-  gesture_.x = fling_point.x;
-  gesture_.y = fling_point.y;
-  gesture_.globalX = fling_global_point.x;
-  gesture_.globalY = fling_global_point.y;
-  gesture_.modifiers = modifiers;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  modifiers = WebInputEvent::AltKey;
+  gesture_ = CreateFling(blink::WebGestureDeviceTouchpad,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
@@ -690,7 +795,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // Start the second fling animation at time 30.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .Times(0);
   time = base::TimeTicks() + base::TimeDelta::FromSeconds(30);
@@ -699,7 +804,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // Tick the second fling once normally.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_,
@@ -713,7 +818,6 @@ TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // Then abort the second fling.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
@@ -748,18 +852,18 @@ TEST_F(InputHandlerProxyTest, GestureFlingStartedTouchscreen) {
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   gesture_.type = WebInputEvent::GestureScrollBegin;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
 
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
 
   gesture_.type = WebInputEvent::GestureFlingStart;
   gesture_.data.flingStart.velocityX = 10;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
@@ -768,7 +872,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingStartedTouchscreen) {
 
   // Verify that a GestureFlingCancel during an animation cancels it.
   gesture_.type = WebInputEvent::GestureFlingCancel;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
@@ -788,7 +892,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingOnMainThreadTouchscreen) {
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin()).Times(0);
 
   gesture_.type = WebInputEvent::GestureFlingStart;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
@@ -796,7 +900,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingOnMainThreadTouchscreen) {
   // Even if we didn't start a fling ourselves, we still need to send the cancel
   // event to the widget.
   gesture_.type = WebInputEvent::GestureFlingCancel;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
@@ -808,7 +912,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) {
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   expected_disposition_ = InputHandlerProxy::DROP_EVENT;
@@ -818,7 +922,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) {
       .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
 
   gesture_.type = WebInputEvent::GestureFlingStart;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
@@ -826,7 +930,7 @@ TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) {
   // Even if we didn't start a fling ourselves, we still need to send the cancel
   // event to the widget.
   gesture_.type = WebInputEvent::GestureFlingCancel;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
@@ -839,27 +943,24 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) {
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
 
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
-  gesture_.type = WebInputEvent::GestureFlingStart;
-  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebFloatPoint fling_delta = WebFloatPoint(100, 0);
   WebPoint fling_point = WebPoint(7, 13);
   WebPoint fling_global_point = WebPoint(17, 23);
-  int modifiers = 7;
-  gesture_.data.flingStart.velocityX = fling_delta.x;
-  gesture_.data.flingStart.velocityY = fling_delta.y;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
-  gesture_.x = fling_point.x;
-  gesture_.y = fling_point.y;
-  gesture_.globalX = fling_global_point.x;
-  gesture_.globalY = fling_global_point.y;
-  gesture_.modifiers = modifiers;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  // Note that for touchscreen the control modifier is not special.
+  int modifiers = WebInputEvent::ControlKey;
+  gesture_ = CreateFling(blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -870,14 +971,14 @@ TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) {
   // fling start will typically include the last scroll from the gesture that
   // lead to the scroll (either wheel or gesture scroll), so there should be no
   // visible hitch.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
   input_handler_->Animate(time);
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The second call should start scrolling in the -X direction.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
@@ -901,45 +1002,105 @@ TEST_F(InputHandlerProxyTest, GestureFlingWithValidTimestamp) {
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
 
   gesture_.type = WebInputEvent::GestureScrollBegin;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
 
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
-  base::TimeDelta startTimeOffset = base::TimeDelta::FromMilliseconds(10);
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(100, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  WebPoint fling_global_point = WebPoint(17, 23);
+  int modifiers = WebInputEvent::ControlKey;
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+  // With a valid time stamp, the first animate call should skip start time
+  // initialization and immediately begin scroll update production. This reduces
+  // the likelihood of a hitch between the scroll preceding the fling and
+  // the first scroll generated by the fling.
+  // Scrolling should start in the -X direction.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+      .WillOnce(testing::Return(true));
+  time += dt;
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  gesture_.type = WebInputEvent::GestureFlingCancel;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingWithInvalidTimestamp) {
+  // We shouldn't send any events to the widget for this gesture.
+  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+  VERIFY_AND_RESET_MOCKS();
+
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // On the fling start, we should schedule an animation but not actually start
+  // scrolling.
+  base::TimeDelta start_time_offset = base::TimeDelta::FromMilliseconds(10);
   gesture_.type = WebInputEvent::GestureFlingStart;
-  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebFloatPoint fling_delta = WebFloatPoint(100, 0);
   WebPoint fling_point = WebPoint(7, 13);
   WebPoint fling_global_point = WebPoint(17, 23);
-  int modifiers = 7;
-  gesture_.timeStampSeconds = startTimeOffset.InSecondsF();
+  int modifiers = WebInputEvent::ControlKey;
+  gesture_.timeStampSeconds = start_time_offset.InSecondsF();
   gesture_.data.flingStart.velocityX = fling_delta.x;
   gesture_.data.flingStart.velocityY = fling_delta.y;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   gesture_.x = fling_point.x;
   gesture_.y = fling_point.y;
   gesture_.globalX = fling_global_point.x;
   gesture_.globalY = fling_global_point.y;
   gesture_.modifiers = modifiers;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
-  // With a valid time stamp, the first animate call should skip start time
-  // initialization and immediately begin scroll update production. This reduces
-  // the likelihood of a hitch between the scroll preceding the fling and
-  // the first scroll generated by the fling.
-  // Scrolling should start in the -X direction.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  // Event though a time stamp was provided for the fling event, it will be
+  // ignored as its too far in the past relative to the first animate call's
+  // timestamp.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  base::TimeTicks time =
+      base::TimeTicks() + start_time_offset + base::TimeDelta::FromSeconds(1);
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  // Further animation ticks should update the fling as usual.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
       .WillOnce(testing::Return(true));
-  base::TimeTicks time = base::TimeTicks() + 2 * startTimeOffset;
+  time += base::TimeDelta::FromMilliseconds(10);
   input_handler_->Animate(time);
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
@@ -970,20 +1131,16 @@ TEST_F(InputHandlerProxyTest,
 
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
-  gesture_.type = WebInputEvent::GestureFlingStart;
-  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebFloatPoint fling_delta = WebFloatPoint(100, 0);
   WebPoint fling_point = WebPoint(7, 13);
   WebPoint fling_global_point = WebPoint(17, 23);
-  int modifiers = 7;
-  gesture_.data.flingStart.velocityX = fling_delta.x;
-  gesture_.data.flingStart.velocityY = fling_delta.y;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
-  gesture_.x = fling_point.x;
-  gesture_.y = fling_point.y;
-  gesture_.globalX = fling_global_point.x;
-  gesture_.globalY = fling_global_point.y;
-  gesture_.modifiers = modifiers;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  int modifiers = WebInputEvent::ControlKey | WebInputEvent::AltKey;
+  gesture_ = CreateFling(blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
@@ -998,14 +1155,14 @@ TEST_F(InputHandlerProxyTest,
   // fling start will typically include the last scroll from the gesture that
   // lead to the scroll (either wheel or gesture scroll), so there should be no
   // visible hitch.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
   input_handler_->Animate(time);
 
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The second call should start scrolling in the -X direction.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
@@ -1032,10 +1189,10 @@ TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
   gesture_.type = WebInputEvent::GestureFlingStart;
-  WebFloatPoint fling_delta = WebFloatPoint(1000, 1000);
+  WebFloatPoint fling_delta = WebFloatPoint(100, 100);
   gesture_.data.flingStart.velocityX = fling_delta.x;
   gesture_.data.flingStart.velocityY = fling_delta.y;
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
@@ -1043,13 +1200,13 @@ TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The first animate doesn't cause any scrolling.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
   input_handler_->Animate(time);
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The second animate starts scrolling in the positive X and Y directions.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_,
@@ -1062,13 +1219,25 @@ TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // Simulate hitting the bottom content edge.
-  cc::DidOverscrollParams overscroll_params;
-  overscroll_params.accumulated_overscroll = gfx::Vector2dF(0, 100);
-  overscroll_params.current_fling_velocity = gfx::Vector2dF(0, 10);
-  input_handler_->DidOverscroll(overscroll_params);
+  gfx::Vector2dF accumulated_overscroll(0, 100);
+  gfx::Vector2dF latest_overscroll_delta(0, 10);
+  gfx::PointF scroll_point(10, 0);
+  EXPECT_CALL(
+      mock_client_,
+      DidOverscroll(testing::AllOf(
+          testing::Field(&DidOverscrollParams::accumulated_overscroll,
+                         testing::Eq(accumulated_overscroll)),
+          testing::Field(&DidOverscrollParams::latest_overscroll_delta,
+                         testing::Eq(latest_overscroll_delta)),
+          testing::Field(&DidOverscrollParams::current_fling_velocity,
+                         testing::Property(&gfx::Vector2dF::y, testing::Lt(0))),
+          testing::Field(&DidOverscrollParams::causal_event_viewport_point,
+                         testing::Eq(scroll_point)))));
+  input_handler_->DidOverscroll(
+      scroll_point, accumulated_overscroll, latest_overscroll_delta);
 
   // The next call to animate will no longer scroll vertically.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   EXPECT_CALL(mock_input_handler_,
@@ -1081,6 +1250,83 @@ TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 }
 
+TEST_F(InputHandlerProxyTest, GestureFlingNotCancelledBySmallTimeDelta) {
+  // We shouldn't send any events to the widget for this gesture.
+  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+  VERIFY_AND_RESET_MOCKS();
+
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // On the fling start, we should schedule an animation but not actually start
+  // scrolling.
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(100, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  WebPoint fling_global_point = WebPoint(17, 23);
+  int modifiers = WebInputEvent::ControlKey;
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+  // With an animation timestamp equivalent to the starting timestamp, the
+  // animation will simply be rescheduled.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  // A small time delta should not stop the fling, even if the client
+  // reports no scrolling.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+      .WillOnce(testing::Return(false));
+  time += base::TimeDelta::FromMicroseconds(5);
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  // A time delta of zero should not stop the fling, and neither should it
+  // trigger scrolling on the client.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  // Lack of movement on the client, with a non-trivial scroll delta, should
+  // terminate the fling.
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x, testing::Lt(1))))
+      .WillOnce(testing::Return(false));
+  time += base::TimeDelta::FromMilliseconds(100);
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+  EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+}
+
 TEST_F(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
@@ -1089,30 +1335,30 @@ TEST_F(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) {
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
   gesture_.type = WebInputEvent::GestureScrollBegin;
-  gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
   gesture_.type = WebInputEvent::GestureFlingStart;
-  WebFloatPoint fling_delta = WebFloatPoint(1000, 1000);
+  WebFloatPoint fling_delta = WebFloatPoint(100, 100);
   gesture_.data.flingStart.velocityX = fling_delta.x;
   gesture_.data.flingStart.velocityY = fling_delta.y;
   EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
       .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The first animate doesn't cause any scrolling.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
   input_handler_->Animate(time);
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // The second animate starts scrolling in the positive X and Y directions.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::y, testing::Lt(0))))
@@ -1122,12 +1368,25 @@ TEST_F(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // Simulate hitting the bottom content edge.
-  cc::DidOverscrollParams overscroll_params;
-  overscroll_params.accumulated_overscroll = gfx::Vector2dF(0, 100);
-  input_handler_->DidOverscroll(overscroll_params);
+  gfx::Vector2dF accumulated_overscroll(0, 100);
+  gfx::Vector2dF latest_overscroll_delta(0, 100);
+  gfx::PointF scroll_point(10, -50);
+  EXPECT_CALL(
+      mock_client_,
+      DidOverscroll(testing::AllOf(
+          testing::Field(&DidOverscrollParams::accumulated_overscroll,
+                         testing::Eq(accumulated_overscroll)),
+          testing::Field(&DidOverscrollParams::latest_overscroll_delta,
+                         testing::Eq(latest_overscroll_delta)),
+          testing::Field(&DidOverscrollParams::current_fling_velocity,
+                         testing::Property(&gfx::Vector2dF::y, testing::Lt(0))),
+          testing::Field(&DidOverscrollParams::causal_event_viewport_point,
+                         testing::Eq(scroll_point)))));
+  input_handler_->DidOverscroll(
+      scroll_point, accumulated_overscroll, latest_overscroll_delta);
 
   // The next call to animate will no longer scroll vertically.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
   EXPECT_CALL(mock_input_handler_,
               ScrollBy(testing::_,
                        testing::Property(&gfx::Vector2dF::y, testing::Eq(0))))
@@ -1137,12 +1396,25 @@ TEST_F(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) {
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
   // Simulate hitting the right content edge.
-  overscroll_params.accumulated_overscroll = gfx::Vector2dF(100, 100);
-  input_handler_->DidOverscroll(overscroll_params);
-
+  accumulated_overscroll = gfx::Vector2dF(100, 100);
+  latest_overscroll_delta = gfx::Vector2dF(100, 0);
+  scroll_point = gfx::PointF(50, 0);
+  EXPECT_CALL(
+      mock_client_,
+      DidOverscroll(testing::AllOf(
+          testing::Field(&DidOverscrollParams::accumulated_overscroll,
+                         testing::Eq(accumulated_overscroll)),
+          testing::Field(&DidOverscrollParams::latest_overscroll_delta,
+                         testing::Eq(latest_overscroll_delta)),
+          testing::Field(&DidOverscrollParams::current_fling_velocity,
+                         testing::Property(&gfx::Vector2dF::x, testing::Lt(0))),
+          testing::Field(&DidOverscrollParams::causal_event_viewport_point,
+                         testing::Eq(scroll_point)))));
+  input_handler_->DidOverscroll(
+      scroll_point, accumulated_overscroll, latest_overscroll_delta);
   // The next call to animate will no longer scroll horizontally or vertically,
   // and the fling should be cancelled.
-  EXPECT_CALL(mock_input_handler_, ScheduleAnimation()).Times(0);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate()).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
   EXPECT_CALL(mock_input_handler_, ScrollEnd());
   time += base::TimeDelta::FromMilliseconds(10);
@@ -1170,18 +1442,9 @@ TEST_F(InputHandlerProxyTest, MultiTouchPointHitTestNegative) {
   touch.type = WebInputEvent::TouchStart;
 
   touch.touchesLength = 3;
-  touch.touches[0].state = WebTouchPoint::StateStationary;
-  touch.touches[0].screenPosition = WebPoint();
-  touch.touches[0].position = WebPoint();
-
-  touch.touches[1].state = WebTouchPoint::StatePressed;
-  touch.touches[1].screenPosition = WebPoint(10, 10);
-  touch.touches[1].position = WebPoint(10, 10);
-
-  touch.touches[2].state = WebTouchPoint::StatePressed;
-  touch.touches[2].screenPosition = WebPoint(-10, 10);
-  touch.touches[2].position = WebPoint(-10, 10);
-
+  touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StateStationary, 0, 0);
+  touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 10, 10);
+  touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::StatePressed, -10, 10);
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch));
 }
 
@@ -1206,19 +1469,542 @@ TEST_F(InputHandlerProxyTest, MultiTouchPointHitTestPositive) {
   touch.type = WebInputEvent::TouchStart;
 
   touch.touchesLength = 3;
-  touch.touches[0].state = WebTouchPoint::StatePressed;
-  touch.touches[0].screenPosition = WebPoint();
-  touch.touches[0].position = WebPoint();
+  touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 0, 0);
+  touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 10, 10);
+  touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::StatePressed, -10, 10);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch));
+}
 
-  touch.touches[1].state = WebTouchPoint::StatePressed;
-  touch.touches[1].screenPosition = WebPoint(10, 10);
-  touch.touches[1].position = WebPoint(10, 10);
+TEST_F(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) {
+  // We shouldn't send any events to the widget for this gesture.
+  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+  VERIFY_AND_RESET_MOCKS();
+
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
-  touch.touches[2].state = WebTouchPoint::StatePressed;
-  touch.touches[2].screenPosition = WebPoint(-10, 10);
-  touch.touches[2].position = WebPoint(-10, 10);
+  // Keyboard events received during a scroll should have no effect.
+  WebKeyboardEvent key_event;
+  key_event.type = WebInputEvent::KeyDown;
+  EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE,
+            input_handler_->HandleInputEvent(key_event));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 
-  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch));
+  // On the fling start, animation should be scheduled, but no scrolling occurs.
+  gesture_.type = WebInputEvent::GestureFlingStart;
+  WebFloatPoint fling_delta = WebFloatPoint(100, 100);
+  gesture_.data.flingStart.velocityX = fling_delta.x;
+  gesture_.data.flingStart.velocityY = fling_delta.y;
+  EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  // Keyboard events received during a fling should cancel the active fling.
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_EQ(InputHandlerProxy::DID_NOT_HANDLE,
+            input_handler_->HandleInputEvent(key_event));
+  EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  // The call to animate should have no effect, as the fling was cancelled.
+  base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+  input_handler_->Animate(time);
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  // A fling cancel should be dropped, as there is nothing to cancel.
+  gesture_.type = WebInputEvent::GestureFlingCancel;
+  EXPECT_EQ(InputHandlerProxy::DROP_EVENT,
+            input_handler_->HandleInputEvent(gesture_));
+  EXPECT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingWithNegativeTimeDelta) {
+  // We shouldn't send any events to the widget for this gesture.
+  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+  VERIFY_AND_RESET_MOCKS();
+
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  gesture_.sourceDevice = blink::WebGestureDeviceTouchscreen;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // On the fling start, we should schedule an animation but not actually start
+  // scrolling.
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(100, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  WebPoint fling_global_point = WebPoint(17, 23);
+  int modifiers = WebInputEvent::ControlKey;
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_global_point,
+                         modifiers);
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  // If we get a negative time delta, that is, the Animation tick time happens
+  // before the fling's start time then we should *not* try scrolling and
+  // instead reset the fling start time.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::_)).Times(0);
+  time -= base::TimeDelta::FromMilliseconds(5);
+  input_handler_->Animate(time);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  // The first call should have reset the start time so subsequent calls should
+  // generate scroll events.
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+      .WillOnce(testing::Return(true));
+
+  input_handler_->Animate(time + base::TimeDelta::FromMilliseconds(1));
+
+  testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  gesture_.type = WebInputEvent::GestureFlingCancel;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, FlingBoost) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  base::TimeTicks last_animate_time = time;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Now cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // The GestureScrollBegin should be swallowed by the fling if it hits the same
+  // scrolling layer.
+  EXPECT_CALL(mock_input_handler_,
+              IsCurrentlyScrollingLayerAt(testing::_, testing::_))
+      .WillOnce(testing::Return(true));
+
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Animate calls within the deferred cancellation window should continue.
+  time += dt;
+  float expected_delta =
+      (time - last_animate_time).InSecondsF() * -fling_delta.x;
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  input_handler_->Animate(time);
+  last_animate_time = time;
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // GestureScrollUpdates in the same direction and at sufficient speed should
+  // be swallowed by the fling.
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollUpdate;
+  gesture_.data.scrollUpdate.deltaX = fling_delta.x;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Animate calls within the deferred cancellation window should continue.
+  time += dt;
+  expected_delta = (time - last_animate_time).InSecondsF() * -fling_delta.x;
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  input_handler_->Animate(time);
+  last_animate_time = time;
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // GestureFlingStart in the same direction and at sufficient speed should
+  // boost the active fling.
+
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_point,
+                         0);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  VERIFY_AND_RESET_MOCKS();
+
+  time += dt;
+  // Note we get *2x* as much delta because 2 flings have combined.
+  expected_delta = 2 * (time - last_animate_time).InSecondsF() * -fling_delta.x;
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  input_handler_->Animate(time);
+  last_animate_time = time;
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Repeated GestureFlingStarts should accumulate.
+
+  CancelFling(time);
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         fling_delta,
+                         fling_point,
+                         fling_point,
+                         0);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  VERIFY_AND_RESET_MOCKS();
+
+  time += dt;
+  // Note we get *3x* as much delta because 3 flings have combined.
+  expected_delta = 3 * (time - last_animate_time).InSecondsF() * -fling_delta.x;
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  input_handler_->Animate(time);
+  last_animate_time = time;
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // GestureFlingCancel should terminate the fling if no boosting gestures are
+  // received within the timeout window.
+
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureFlingCancel;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  time += base::TimeDelta::FromMilliseconds(100);
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  input_handler_->Animate(time);
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, NoFlingBoostIfScrollTargetsDifferentLayer) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // If the GestureScrollBegin targets a different layer, the fling should be
+  // cancelled and the scroll should be handled as usual.
+  EXPECT_CALL(mock_input_handler_,
+              IsCurrentlyScrollingLayerAt(testing::_, testing::_))
+      .WillOnce(testing::Return(false));
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, NoFlingBoostIfScrollDelayed) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // The GestureScrollBegin should be swallowed by the fling if it hits the same
+  // scrolling layer.
+  EXPECT_CALL(mock_input_handler_,
+              IsCurrentlyScrollingLayerAt(testing::_, testing::_))
+      .WillOnce(testing::Return(true));
+
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // If no GestureScrollUpdate or GestureFlingStart is received within the
+  // timeout window, the fling should be cancelled and scrolling should resume.
+  time += base::TimeDelta::FromMilliseconds(100);
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  input_handler_->Animate(time);
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, NoFlingBoostIfFlingInDifferentDirection) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // If the new fling is orthogonal to the existing fling, no boosting should
+  // take place, with the new fling replacing the old.
+  WebFloatPoint orthogonal_fling_delta =
+      WebFloatPoint(fling_delta.y, -fling_delta.x);
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         orthogonal_fling_delta,
+                         fling_point,
+                         fling_point,
+                         0);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Note that the new fling delta uses the orthogonal, unboosted fling
+  // velocity.
+  time += dt;
+  float expected_delta = dt.InSecondsF() * -orthogonal_fling_delta.y;
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::y,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  input_handler_->Animate(time);
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, NoFlingBoostIfScrollInDifferentDirection) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // The GestureScrollBegin should be swallowed by the fling if it hits the same
+  // scrolling layer.
+  EXPECT_CALL(mock_input_handler_,
+              IsCurrentlyScrollingLayerAt(testing::_, testing::_))
+      .WillOnce(testing::Return(true));
+
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // If the GestureScrollUpdate is in a different direction than the fling,
+  // the fling should be cancelled and scrolling should resume.
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollUpdate;
+  gesture_.data.scrollUpdate.deltaX = -fling_delta.x;
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(fling_delta.x))))
+      .WillOnce(testing::Return(true));
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, NoFlingBoostIfFlingTooSlow) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // If the new fling is too slow, no boosting should take place, with the new
+  // fling replacing the old.
+  WebFloatPoint small_fling_delta = WebFloatPoint(100, 0);
+  gesture_ = CreateFling(time,
+                         blink::WebGestureDeviceTouchscreen,
+                         small_fling_delta,
+                         fling_point,
+                         fling_point,
+                         0);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Note that the new fling delta uses the *slow*, unboosted fling velocity.
+  time += dt;
+  float expected_delta = dt.InSecondsF() * -small_fling_delta.x;
+  EXPECT_CALL(mock_input_handler_, SetNeedsAnimate());
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  input_handler_->Animate(time);
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, FlingBoostTerminatedDuringScrollSequence) {
+  base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10);
+  base::TimeTicks time = base::TimeTicks() + dt;
+  base::TimeTicks last_animate_time = time;
+  WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+  WebPoint fling_point = WebPoint(7, 13);
+  StartFling(
+      time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point);
+
+  // Now cancel the fling.  The fling cancellation should be deferred to allow
+  // fling boosting events to arrive.
+  time += dt;
+  CancelFling(time);
+
+  // The GestureScrollBegin should be swallowed by the fling.
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollBegin;
+  EXPECT_CALL(mock_input_handler_,
+              IsCurrentlyScrollingLayerAt(testing::_, testing::_))
+      .WillOnce(testing::Return(true));
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Now animate the fling to completion (in this case, the fling should
+  // terminate because the input handler reports a failed scroll). As the fling
+  // was cancelled during an active scroll sequence, a synthetic
+  // GestureScrollBegin should be processed, resuming the scroll.
+  time += dt;
+  float expected_delta =
+      (time - last_animate_time).InSecondsF() * -fling_delta.x;
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(false));
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+      .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+  input_handler_->Animate(time);
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // Subsequent GestureScrollUpdates after the cancelled, boosted fling should
+  // cause scrolling as usual.
+  time += dt;
+  expected_delta = 7.3f;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollUpdate;
+  gesture_.data.scrollUpdate.deltaX = -expected_delta;
+  EXPECT_CALL(mock_input_handler_,
+              ScrollBy(testing::_,
+                       testing::Property(&gfx::Vector2dF::x,
+                                         testing::Eq(expected_delta))))
+      .WillOnce(testing::Return(true));
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+
+  // GestureScrollEnd should terminate the resumed scroll properly.
+  time += dt;
+  gesture_.timeStampSeconds = InSecondsF(time);
+  gesture_.type = WebInputEvent::GestureScrollEnd;
+  EXPECT_CALL(mock_input_handler_, ScrollEnd());
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_F(InputHandlerProxyTest, DidReceiveInputEvent) {
+  testing::StrictMock<
+      MockInputHandlerProxyClientWithDidReceiveInputEvent> mock_client;
+  input_handler_.reset(
+        new content::InputHandlerProxy(&mock_input_handler_, &mock_client));
+
+  // Note the type of input event isn't important.
+  WebMouseWheelEvent wheel;
+  wheel.type = WebInputEvent::MouseWheel;
+  wheel.scrollByPage = true;
+
+  EXPECT_CALL(mock_client, DidReceiveInputEvent());
+
+  input_handler_->HandleInputEvent(wheel);
+  testing::Mock::VerifyAndClearExpectations(&mock_client);
 }
 
 } // namespace