Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / input / touch_event_queue_unittest.cc
index 4697b49..90e922b 100644 (file)
@@ -35,7 +35,6 @@ class TouchEventQueueTest : public testing::Test,
         acked_event_count_(0),
         last_acked_event_state_(INPUT_EVENT_ACK_STATE_UNKNOWN),
         slop_length_dips_(0),
-        slop_includes_boundary_(true),
         touch_scrolling_mode_(TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT) {}
 
   virtual ~TouchEventQueueTest() {}
@@ -81,8 +80,6 @@ class TouchEventQueueTest : public testing::Test,
     TouchEventQueue::Config config;
     config.touch_scrolling_mode = touch_scrolling_mode_;
     config.touchmove_slop_suppression_length_dips = slop_length_dips_;
-    config.touchmove_slop_suppression_region_includes_boundary =
-        slop_includes_boundary_;
     return config;
   }
 
@@ -91,10 +88,8 @@ class TouchEventQueueTest : public testing::Test,
     ResetQueueWithConfig(CreateConfig());
   }
 
-  void SetUpForTouchMoveSlopTesting(double slop_length_dips,
-                                    bool slop_includes_boundary) {
+  void SetUpForTouchMoveSlopTesting(double slop_length_dips) {
     slop_length_dips_ = slop_length_dips;
-    slop_includes_boundary_ = slop_includes_boundary;
     ResetQueueWithConfig(CreateConfig());
   }
 
@@ -175,6 +170,10 @@ class TouchEventQueueTest : public testing::Test,
     touch_event_.timeStampSeconds += seconds;
   }
 
+  void ResetTouchEvent() {
+    touch_event_ = SyntheticWebTouchEvent();
+  }
+
   size_t GetAndResetAckedEventCount() {
     size_t count = acked_event_count_;
     acked_event_count_ = 0;
@@ -197,8 +196,6 @@ class TouchEventQueueTest : public testing::Test,
 
   void SetAckTimeoutDisabled() { queue_->SetAckTimeoutEnabled(false); }
 
-  bool IsTimeoutEnabled() const { return queue_->ack_timeout_enabled(); }
-
   bool IsTimeoutRunning() const { return queue_->IsTimeoutRunningForTesting(); }
 
   bool HasPendingAsyncTouchMove() const {
@@ -253,7 +250,6 @@ class TouchEventQueueTest : public testing::Test,
   scoped_ptr<WebGestureEvent> followup_gesture_event_;
   scoped_ptr<InputEventAckState> sync_ack_result_;
   double slop_length_dips_;
-  bool slop_includes_boundary_;
   TouchEventQueue::TouchScrollingMode touch_scrolling_mode_;
   base::MessageLoopForUI message_loop_;
 };
@@ -287,9 +283,55 @@ TEST_F(TouchEventQueueTest, Basic) {
   EXPECT_TRUE(acked_event().cancelable);
 }
 
-// Tests that the touch-queue is emptied if a page stops listening for touch
-// events.
-TEST_F(TouchEventQueueTest, QueueFlushedWhenHandlersRemoved) {
+// Tests that touch-events with multiple points are queued properly.
+TEST_F(TouchEventQueueTest, BasicMultiTouch) {
+  const size_t kPointerCount = 10;
+  for (size_t i = 0; i < kPointerCount; ++i)
+    PressTouchPoint(i, i);
+
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(kPointerCount, queued_event_count());
+
+  for (size_t i = 0; i < kPointerCount; ++i)
+    MoveTouchPoint(i, 1.f + i, 2.f + i);
+
+  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  // All moves should coalesce.
+  EXPECT_EQ(kPointerCount + 1, queued_event_count());
+
+  for (size_t i = 0; i < kPointerCount; ++i)
+    ReleaseTouchPoint(kPointerCount - 1 - i);
+
+  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(kPointerCount * 2 + 1, queued_event_count());
+
+  // Ack all presses.
+  for (size_t i = 0; i < kPointerCount; ++i)
+    SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+
+  EXPECT_EQ(kPointerCount, GetAndResetAckedEventCount());
+  EXPECT_EQ(kPointerCount, GetAndResetSentEventCount());
+
+  // Ack the coalesced move.
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(kPointerCount, GetAndResetAckedEventCount());
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+
+  // Ack all releases.
+  for (size_t i = 0; i < kPointerCount; ++i)
+    SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+
+  EXPECT_EQ(kPointerCount, GetAndResetAckedEventCount());
+  EXPECT_EQ(kPointerCount - 1, GetAndResetSentEventCount());
+}
+
+// Tests that the touch-queue continues delivering events for an active pointer
+// after all handlers are removed, but acks new pointers immediately as having
+// no consumer.
+TEST_F(TouchEventQueueTest, NoNewTouchesForwardedAfterHandlersRemoved) {
   OnHasTouchEventHandlers(true);
   EXPECT_EQ(0U, queued_event_count());
   EXPECT_EQ(0U, GetAndResetSentEventCount());
@@ -297,32 +339,51 @@ TEST_F(TouchEventQueueTest, QueueFlushedWhenHandlersRemoved) {
   // Send a touch-press event.
   PressTouchPoint(1, 1);
   EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, queued_event_count());
 
-  ReleaseTouchPoint(0);
-
-  // Events will be queued until the first sent event is ack'ed.
-  for (int i = 5; i < 15; ++i) {
-    PressTouchPoint(1, 1);
-    MoveTouchPoint(0, i, i);
-    ReleaseTouchPoint(0);
-  }
-  EXPECT_EQ(32U, queued_event_count());
-  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  // Signal that all touch handlers have been removed.
+  OnHasTouchEventHandlers(false);
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(1U, queued_event_count());
 
-  // Receive an ACK for the first touch-event. One of the queued touch-event
-  // should be forwarded.
+  // Process the ack for the sent touch, ensuring that it is honored (despite
+  // the touch handler having been removed).
   SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(31U, queued_event_count());
-  EXPECT_EQ(1U, GetAndResetSentEventCount());
   EXPECT_EQ(1U, GetAndResetAckedEventCount());
-  EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type);
+  EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
 
-  // Flush the queue. The touch-event queue should now be emptied, but none of
-  // the queued touch-events should be sent to the renderer.
-  OnHasTouchEventHandlers(false);
+  // Try forwarding a new pointer. It should be rejected immediately.
+  PressTouchPoint(2, 2);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
   EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+
+  // Further events for the pointer without a handler should not be forwarded.
+  MoveTouchPoint(1, 3, 3);
+  ReleaseTouchPoint(1);
+  EXPECT_EQ(2U, GetAndResetAckedEventCount());
+  EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state());
+
+  // Events for the first pointer, that had a handler, should be forwarded, even
+  // if the renderer reports that no handlers exist.
+  MoveTouchPoint(0, 4, 4);
+  ReleaseTouchPoint(0);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(2U, queued_event_count());
+
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, queued_event_count());
+  EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
+
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
   EXPECT_EQ(0U, GetAndResetSentEventCount());
-  EXPECT_EQ(31U, GetAndResetAckedEventCount());
+  EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state());
 }
 
 // Tests that addition of a touch handler during a touch sequence will not cause
@@ -366,14 +427,23 @@ TEST_F(TouchEventQueueTest, ActiveSequenceDroppedWhenHandlersRemoved) {
   EXPECT_EQ(0U, GetAndResetAckedEventCount());
   EXPECT_EQ(0U, GetAndResetSentEventCount());
 
-  // Touch handle deregistration should flush the queue.
+  // Unregister all touch handlers.
   OnHasTouchEventHandlers(false);
-  EXPECT_EQ(2U, GetAndResetAckedEventCount());
-  EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(2U, queued_event_count());
 
-  // The ack should be ignored as the touch queue is now empty.
-  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+  // Repeated registration/unregstration of handlers should have no effect as
+  // we're still awaiting the ack arrival.
+  OnHasTouchEventHandlers(true);
   EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(2U, queued_event_count());
+  OnHasTouchEventHandlers(false);
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(2U, queued_event_count());
+
+  // The ack should be flush the queue.
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+  EXPECT_EQ(2U, GetAndResetAckedEventCount());
   EXPECT_EQ(0U, queued_event_count());
 
   // Events should be dropped while there is no touch handler.
@@ -397,6 +467,34 @@ TEST_F(TouchEventQueueTest, ActiveSequenceDroppedWhenHandlersRemoved) {
   EXPECT_EQ(1U, GetAndResetSentEventCount());
 }
 
+// Tests that removal/addition of a touch handler without any intervening
+// touch activity has no affect on touch forwarding.
+TEST_F(TouchEventQueueTest,
+       ActiveSequenceUnaffectedByRepeatedHandlerRemovalAndAddition) {
+  // Send a touch-press event.
+  PressTouchPoint(1, 1);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, queued_event_count());
+
+  // Simulate the case where the touchstart handler removes itself, and adds a
+  // touchmove handler.
+  OnHasTouchEventHandlers(false);
+  OnHasTouchEventHandlers(true);
+
+  // Queue a touch-move event.
+  MoveTouchPoint(0, 5, 5);
+  EXPECT_EQ(2U, queued_event_count());
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+  EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+  // The ack should trigger forwarding of the touchmove, as if no touch
+  // handler registration changes have occurred.
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, queued_event_count());
+}
+
 // Tests that touch-events are coalesced properly in the queue.
 TEST_F(TouchEventQueueTest, Coalesce) {
   // Send a touch-press event.
@@ -498,33 +596,23 @@ TEST_F(TouchEventQueueTest, MultiTouch) {
   EXPECT_EQ(WebTouchPoint::StateMoved, event.touches[1].state);
 }
 
-// Tests that if a touch-event queue is destroyed in response to a touch-event
-// in the renderer, then there is no crash when the ACK for that touch-event
-// comes back.
-TEST_F(TouchEventQueueTest, AckAfterQueueFlushed) {
-  // Send some touch-events to the renderer.
+// Tests that the touch-event queue is robust to redundant acks.
+TEST_F(TouchEventQueueTest, SpuriousAcksIgnored) {
+  // Trigger a spurious ack.
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
+
+  // Send and ack a touch press.
   PressTouchPoint(1, 1);
   EXPECT_EQ(1U, GetAndResetSentEventCount());
   EXPECT_EQ(1U, queued_event_count());
-
-  MoveTouchPoint(0, 10, 10);
-  EXPECT_EQ(0U, GetAndResetSentEventCount());
-  EXPECT_EQ(2U, queued_event_count());
-
-  // Receive an ACK for the press. This should cause the queued touch-move to
-  // be sent to the renderer.
   SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(1U, GetAndResetSentEventCount());
-  EXPECT_EQ(1U, queued_event_count());
-
-  OnHasTouchEventHandlers(false);
-  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
   EXPECT_EQ(0U, queued_event_count());
 
-  // Now receive an ACK for the move.
+  // Trigger a spurious ack.
   SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
-  EXPECT_EQ(0U, GetAndResetSentEventCount());
-  EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(0U, GetAndResetAckedEventCount());
 }
 
 // Tests that touch-move events are not sent to the renderer if the preceding
@@ -717,10 +805,10 @@ TEST_F(TouchEventQueueTest, AckWithFollowupEvents) {
   // Create a touch event that will be queued synchronously by a touch ack.
   // Note, this will be triggered by all subsequent touch acks.
   WebTouchEvent followup_event;
-  followup_event.type = WebInputEvent::TouchStart;
+  followup_event.type = WebInputEvent::TouchMove;
   followup_event.touchesLength = 1;
-  followup_event.touches[0].id = 1;
-  followup_event.touches[0].state = WebTouchPoint::StatePressed;
+  followup_event.touches[0].id = 0;
+  followup_event.touches[0].state = WebTouchPoint::StateMoved;
   SetFollowupEvent(followup_event);
 
   // Receive an ACK for the press. This should cause the followup touch-move to
@@ -1159,14 +1247,12 @@ TEST_F(TouchEventQueueTest, NoTouchTimeoutIfDisabledAfterTouchStart) {
   // Send the ack immediately. The timeout should not have fired.
   SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
   EXPECT_FALSE(IsTimeoutRunning());
-  EXPECT_TRUE(IsTimeoutEnabled());
   EXPECT_EQ(1U, GetAndResetSentEventCount());
   EXPECT_EQ(1U, GetAndResetAckedEventCount());
 
   // Now explicitly disable the timeout.
   SetAckTimeoutDisabled();
   EXPECT_FALSE(IsTimeoutRunning());
-  EXPECT_FALSE(IsTimeoutEnabled());
 
   // A TouchMove should not start or trigger the timeout.
   MoveTouchPoint(0, 5, 5);
@@ -1187,19 +1273,6 @@ TEST_F(TouchEventQueueTest, NoTouchTimeoutIfAckIsSynchronous) {
   EXPECT_FALSE(IsTimeoutRunning());
 }
 
-// Tests that the timeout is disabled if the touch handler disappears.
-TEST_F(TouchEventQueueTest, NoTouchTimeoutIfTouchHandlerRemoved) {
-  SetUpForTimeoutTesting(DefaultTouchTimeoutDelay());
-
-  // Queue a TouchStart.
-  PressTouchPoint(0, 1);
-  ASSERT_TRUE(IsTimeoutRunning());
-
-  // Unload the touch handler.
-  OnHasTouchEventHandlers(false);
-  EXPECT_FALSE(IsTimeoutRunning());
-}
-
 // Tests that the timeout does not fire if explicitly disabled while an event
 // is in-flight.
 TEST_F(TouchEventQueueTest, NoTouchTimeoutIfDisabledWhileTimerIsActive) {
@@ -1371,8 +1444,7 @@ TEST_F(TouchEventQueueTest, NoCancelOnTouchTimeoutWithoutConsumer) {
 TEST_F(TouchEventQueueTest, TouchMoveSuppressionIncludingSlopBoundary) {
   const double kSlopLengthDips = 10.;
   const double kHalfSlopLengthDips = kSlopLengthDips / 2;
-  const bool slop_includes_boundary = true;
-  SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+  SetUpForTouchMoveSlopTesting(kSlopLengthDips);
 
   // Queue a TouchStart.
   PressTouchPoint(0, 0);
@@ -1452,67 +1524,38 @@ TEST_F(TouchEventQueueTest, TouchMoveSuppressionIncludingSlopBoundary) {
   EXPECT_EQ(1U, GetAndResetAckedEventCount());
 }
 
-// Tests that TouchMove's are dropped if within the boundary-exclusive slop
-// suppression region for an unconsumed TouchStart.
-TEST_F(TouchEventQueueTest, TouchMoveSuppressionExcludingSlopBoundary) {
+// Tests that TouchMove's are not dropped within the slop suppression region if
+// the touchstart was consumed.
+TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterTouchConsumed) {
   const double kSlopLengthDips = 10.;
   const double kHalfSlopLengthDips = kSlopLengthDips / 2;
-  const bool slop_includes_boundary = false;
-  SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+  SetUpForTouchMoveSlopTesting(kSlopLengthDips);
 
   // Queue a TouchStart.
   PressTouchPoint(0, 0);
-  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
   ASSERT_EQ(1U, GetAndResetSentEventCount());
   ASSERT_EQ(1U, GetAndResetAckedEventCount());
 
-  // TouchMove's within the region should be suppressed.
+  // TouchMove's within the region should not be suppressed, as a touch was
+  // consumed.
   MoveTouchPoint(0, 0, kHalfSlopLengthDips);
-  EXPECT_EQ(0U, queued_event_count());
-  EXPECT_EQ(0U, GetAndResetSentEventCount());
-  EXPECT_EQ(1U, GetAndResetAckedEventCount());
-  EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
-
-  MoveTouchPoint(0, kSlopLengthDips - 0.2f, 0);
-  EXPECT_EQ(0U, queued_event_count());
-  EXPECT_EQ(0U, GetAndResetSentEventCount());
-  EXPECT_EQ(1U, GetAndResetAckedEventCount());
-  EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, acked_event_state());
-
-  // As soon as a TouchMove reaches the (Euclidean) slop distance, no more
-  // TouchMove's should be suppressed.
-  MoveTouchPoint(0, kSlopLengthDips, 0);
   EXPECT_EQ(1U, queued_event_count());
   EXPECT_EQ(1U, GetAndResetSentEventCount());
   EXPECT_EQ(0U, GetAndResetAckedEventCount());
-  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-  EXPECT_EQ(1U, GetAndResetAckedEventCount());
-
-  MoveTouchPoint(0, kHalfSlopLengthDips, 0);
-  EXPECT_EQ(1U, queued_event_count());
-  EXPECT_EQ(1U, GetAndResetSentEventCount());
-  EXPECT_EQ(0U, GetAndResetAckedEventCount());
-  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
-  EXPECT_EQ(1U, GetAndResetAckedEventCount());
 }
 
-// Tests that TouchMove's are not dropped within the slop suppression region if
-// the touchstart was consumed.
-TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterTouchConsumed) {
-  const double kSlopLengthDips = 10.;
-  const double kHalfSlopLengthDips = kSlopLengthDips / 2;
-  const bool slop_includes_boundary = true;
-  SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
-
+// Tests that even very small TouchMove's are not suppressed when suppression is
+// disabled.
+TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionIfDisabled) {
   // Queue a TouchStart.
   PressTouchPoint(0, 0);
-  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
   ASSERT_EQ(1U, GetAndResetSentEventCount());
   ASSERT_EQ(1U, GetAndResetAckedEventCount());
 
-  // TouchMove's within the region should not be suppressed, as a touch was
-  // consumed.
-  MoveTouchPoint(0, 0, kHalfSlopLengthDips);
+  // Small TouchMove's should not be suppressed.
+  MoveTouchPoint(0, 0.001f, 0.001f);
   EXPECT_EQ(1U, queued_event_count());
   EXPECT_EQ(1U, GetAndResetSentEventCount());
   EXPECT_EQ(0U, GetAndResetAckedEventCount());
@@ -1523,9 +1566,7 @@ TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterTouchConsumed) {
 TEST_F(TouchEventQueueTest, TouchMoveSuppressionWithDIPScaling) {
   const float kSlopLengthPixels = 7.f;
   const float kDPIScale = 3.f;
-  const bool slop_includes_boundary = true;
-  SetUpForTouchMoveSlopTesting(kSlopLengthPixels / kDPIScale,
-                               slop_includes_boundary);
+  SetUpForTouchMoveSlopTesting(kSlopLengthPixels / kDPIScale);
 
   // Queue a TouchStart.
   PressTouchPoint(0, 0);
@@ -1565,8 +1606,7 @@ TEST_F(TouchEventQueueTest, TouchMoveSuppressionWithDIPScaling) {
 TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterMultiTouch) {
   const double kSlopLengthDips = 10.;
   const double kHalfSlopLengthDips = kSlopLengthDips / 2;
-  const bool slop_includes_boundary = true;
-  SetUpForTouchMoveSlopTesting(kSlopLengthDips, slop_includes_boundary);
+  SetUpForTouchMoveSlopTesting(kSlopLengthDips);
 
   // Queue a TouchStart.
   PressTouchPoint(0, 0);
@@ -2066,6 +2106,47 @@ TEST_F(TouchEventQueueTest, AsyncTouchWithTouchCancelAfterAck) {
   EXPECT_EQ(0U, GetAndResetSentEventCount());
 }
 
+// Ensure that the async touch is fully reset if the touch sequence restarts
+// without properly terminating.
+TEST_F(TouchEventQueueTest, AsyncTouchWithHardTouchStartReset) {
+  SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE);
+
+  PressTouchPoint(0, 0);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+  // Trigger async touchmove dispatch.
+  MoveTouchPoint(0, 1, 1);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  WebGestureEvent followup_scroll;
+  followup_scroll.type = WebInputEvent::GestureScrollBegin;
+  SetFollowupEvent(followup_scroll);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+  EXPECT_EQ(0U, queued_event_count());
+  SendGestureEvent(WebInputEvent::GestureScrollUpdate);
+
+  // The async touchmove should be immediately ack'ed but delivery is deferred.
+  MoveTouchPoint(0, 2, 2);
+  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+  EXPECT_EQ(0U, queued_event_count());
+  EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type);
+
+  // The queue should be robust to hard touch restarts with a new touch
+  // sequence. In this case, the deferred async touch should not be flushed
+  // by the new touch sequence.
+  SendGestureEvent(WebInputEvent::GestureScrollEnd);
+  ResetTouchEvent();
+
+  PressTouchPoint(0, 0);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type);
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
 TEST_F(TouchEventQueueTest, TouchAbsorptionWithConsumedFirstMove) {
   SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE);
 
@@ -2107,4 +2188,42 @@ TEST_F(TouchEventQueueTest, TouchAbsorptionWithConsumedFirstMove) {
   EXPECT_EQ(0U, GetAndResetSentEventCount());
 }
 
+TEST_F(TouchEventQueueTest, UnseenTouchPointerIdsNotForwarded) {
+  SyntheticWebTouchEvent event;
+  event.PressPoint(0, 0);
+  SendTouchEvent(event);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+  // Give the touchmove a previously unseen pointer id; it should not be sent.
+  int press_id = event.touches[0].id;
+  event.MovePoint(0, 1, 1);
+  event.touches[0].id = 7;
+  SendTouchEvent(event);
+  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+  // Give the touchmove a valid id; it should be sent.
+  event.touches[0].id = press_id;
+  SendTouchEvent(event);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+  // Do the same for release.
+  event.ReleasePoint(0);
+  event.touches[0].id = 11;
+  SendTouchEvent(event);
+  EXPECT_EQ(0U, GetAndResetSentEventCount());
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+
+  // Give the touchmove a valid id; it should be sent.
+  event.touches[0].id = press_id;
+  SendTouchEvent(event);
+  EXPECT_EQ(1U, GetAndResetSentEventCount());
+  SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED);
+  EXPECT_EQ(1U, GetAndResetAckedEventCount());
+}
+
 }  // namespace content