| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/basictypes.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "content/browser/renderer_host/input/timeout_monitor.h" |
| #include "content/browser/renderer_host/input/touch_event_queue.h" |
| #include "content/common/input/synthetic_web_input_event_builders.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/WebKit/public/web/WebInputEvent.h" |
| |
| using blink::WebGestureEvent; |
| using blink::WebInputEvent; |
| using blink::WebTouchEvent; |
| using blink::WebTouchPoint; |
| |
| namespace content { |
| namespace { |
| |
| const double kMinSecondsBetweenThrottledTouchmoves = 0.2; |
| |
| base::TimeDelta DefaultTouchTimeoutDelay() { |
| return base::TimeDelta::FromMilliseconds(1); |
| } |
| } // namespace |
| |
| class TouchEventQueueTest : public testing::Test, |
| public TouchEventQueueClient { |
| public: |
| TouchEventQueueTest() |
| : sent_event_count_(0), |
| acked_event_count_(0), |
| last_acked_event_state_(INPUT_EVENT_ACK_STATE_UNKNOWN), |
| slop_length_dips_(0), |
| touch_scrolling_mode_(TouchEventQueue::TOUCH_SCROLLING_MODE_DEFAULT) {} |
| |
| virtual ~TouchEventQueueTest() {} |
| |
| // testing::Test |
| virtual void SetUp() override { ResetQueueWithConfig(CreateConfig()); } |
| |
| virtual void TearDown() override { |
| queue_.reset(); |
| } |
| |
| // TouchEventQueueClient |
| void SendTouchEventImmediately( |
| const TouchEventWithLatencyInfo& event) override { |
| ++sent_event_count_; |
| last_sent_event_ = event.event; |
| if (sync_ack_result_) { |
| auto sync_ack_result = sync_ack_result_.Pass(); |
| SendTouchEventAck(*sync_ack_result); |
| } |
| } |
| |
| void OnTouchEventAck(const TouchEventWithLatencyInfo& event, |
| InputEventAckState ack_result) override { |
| ++acked_event_count_; |
| last_acked_event_ = event.event; |
| last_acked_event_state_ = ack_result; |
| if (followup_touch_event_) { |
| scoped_ptr<WebTouchEvent> followup_touch_event = |
| followup_touch_event_.Pass(); |
| SendTouchEvent(*followup_touch_event); |
| } |
| if (followup_gesture_event_) { |
| scoped_ptr<WebGestureEvent> followup_gesture_event = |
| followup_gesture_event_.Pass(); |
| queue_->OnGestureScrollEvent( |
| GestureEventWithLatencyInfo(*followup_gesture_event, |
| ui::LatencyInfo())); |
| } |
| } |
| |
| protected: |
| TouchEventQueue::Config CreateConfig() { |
| TouchEventQueue::Config config; |
| config.touch_scrolling_mode = touch_scrolling_mode_; |
| config.touchmove_slop_suppression_length_dips = slop_length_dips_; |
| return config; |
| } |
| |
| void SetTouchScrollingMode(TouchEventQueue::TouchScrollingMode mode) { |
| touch_scrolling_mode_ = mode; |
| ResetQueueWithConfig(CreateConfig()); |
| } |
| |
| void SetUpForTouchMoveSlopTesting(double slop_length_dips) { |
| slop_length_dips_ = slop_length_dips; |
| ResetQueueWithConfig(CreateConfig()); |
| } |
| |
| void SetUpForTimeoutTesting(base::TimeDelta timeout_delay) { |
| TouchEventQueue::Config config = CreateConfig(); |
| config.touch_ack_timeout_delay = timeout_delay; |
| config.touch_ack_timeout_supported = true; |
| ResetQueueWithConfig(config); |
| } |
| |
| void SendTouchEvent(const WebTouchEvent& event) { |
| queue_->QueueEvent(TouchEventWithLatencyInfo(event, ui::LatencyInfo())); |
| } |
| |
| void SendGestureEvent(WebInputEvent::Type type) { |
| WebGestureEvent event; |
| event.type = type; |
| queue_->OnGestureScrollEvent( |
| GestureEventWithLatencyInfo(event, ui::LatencyInfo())); |
| } |
| |
| void SendTouchEventAck(InputEventAckState ack_result) { |
| queue_->ProcessTouchAck(ack_result, ui::LatencyInfo()); |
| } |
| |
| void SendGestureEventAck(WebInputEvent::Type type, |
| InputEventAckState ack_result) { |
| blink::WebGestureEvent gesture_event; |
| gesture_event.type = type; |
| GestureEventWithLatencyInfo event(gesture_event, ui::LatencyInfo()); |
| queue_->OnGestureEventAck(event, ack_result); |
| } |
| |
| void SetFollowupEvent(const WebTouchEvent& event) { |
| followup_touch_event_.reset(new WebTouchEvent(event)); |
| } |
| |
| void SetFollowupEvent(const WebGestureEvent& event) { |
| followup_gesture_event_.reset(new WebGestureEvent(event)); |
| } |
| |
| void SetSyncAckResult(InputEventAckState sync_ack_result) { |
| sync_ack_result_.reset(new InputEventAckState(sync_ack_result)); |
| } |
| |
| void PressTouchPoint(float x, float y) { |
| touch_event_.PressPoint(x, y); |
| SendTouchEvent(); |
| } |
| |
| void MoveTouchPoint(int index, float x, float y) { |
| touch_event_.MovePoint(index, x, y); |
| SendTouchEvent(); |
| } |
| |
| void MoveTouchPoints(int index0, |
| float x0, |
| float y0, |
| int index1, |
| float x1, |
| float y1) { |
| touch_event_.MovePoint(index0, x0, y0); |
| touch_event_.MovePoint(index1, x1, y1); |
| SendTouchEvent(); |
| } |
| |
| void ReleaseTouchPoint(int index) { |
| touch_event_.ReleasePoint(index); |
| SendTouchEvent(); |
| } |
| |
| void CancelTouchPoint(int index) { |
| touch_event_.CancelPoint(index); |
| SendTouchEvent(); |
| } |
| |
| void AdvanceTouchTime(double seconds) { |
| touch_event_.timeStampSeconds += seconds; |
| } |
| |
| void ResetTouchEvent() { |
| touch_event_ = SyntheticWebTouchEvent(); |
| } |
| |
| size_t GetAndResetAckedEventCount() { |
| size_t count = acked_event_count_; |
| acked_event_count_ = 0; |
| return count; |
| } |
| |
| size_t GetAndResetSentEventCount() { |
| size_t count = sent_event_count_; |
| sent_event_count_ = 0; |
| return count; |
| } |
| |
| bool IsPendingAckTouchStart() const { |
| return queue_->IsPendingAckTouchStart(); |
| } |
| |
| void OnHasTouchEventHandlers(bool has_handlers) { |
| queue_->OnHasTouchEventHandlers(has_handlers); |
| } |
| |
| void SetAckTimeoutDisabled() { queue_->SetAckTimeoutEnabled(false); } |
| |
| bool IsTimeoutRunning() const { return queue_->IsTimeoutRunningForTesting(); } |
| |
| bool HasPendingAsyncTouchMove() const { |
| return queue_->HasPendingAsyncTouchMoveForTesting(); |
| } |
| |
| size_t queued_event_count() const { |
| return queue_->size(); |
| } |
| |
| const WebTouchEvent& latest_event() const { |
| return queue_->GetLatestEventForTesting().event; |
| } |
| |
| const WebTouchEvent& acked_event() const { |
| return last_acked_event_; |
| } |
| |
| const WebTouchEvent& sent_event() const { |
| return last_sent_event_; |
| } |
| |
| InputEventAckState acked_event_state() const { |
| return last_acked_event_state_; |
| } |
| |
| static void RunTasksAndWait(base::TimeDelta delay) { |
| base::MessageLoop::current()->PostDelayedTask( |
| FROM_HERE, base::MessageLoop::QuitClosure(), delay); |
| base::MessageLoop::current()->Run(); |
| } |
| |
| private: |
| void SendTouchEvent() { |
| SendTouchEvent(touch_event_); |
| touch_event_.ResetPoints(); |
| } |
| |
| void ResetQueueWithConfig(const TouchEventQueue::Config& config) { |
| queue_.reset(new TouchEventQueue(this, config)); |
| queue_->OnHasTouchEventHandlers(true); |
| } |
| |
| scoped_ptr<TouchEventQueue> queue_; |
| size_t sent_event_count_; |
| size_t acked_event_count_; |
| WebTouchEvent last_sent_event_; |
| WebTouchEvent last_acked_event_; |
| InputEventAckState last_acked_event_state_; |
| SyntheticWebTouchEvent touch_event_; |
| scoped_ptr<WebTouchEvent> followup_touch_event_; |
| scoped_ptr<WebGestureEvent> followup_gesture_event_; |
| scoped_ptr<InputEventAckState> sync_ack_result_; |
| double slop_length_dips_; |
| TouchEventQueue::TouchScrollingMode touch_scrolling_mode_; |
| base::MessageLoopForUI message_loop_; |
| }; |
| |
| |
| // Tests that touch-events are queued properly. |
| TEST_F(TouchEventQueueTest, Basic) { |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // The second touch should not be sent since one is already in queue. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Receive an ACK for the first touch-event. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type); |
| EXPECT_TRUE(acked_event().cancelable); |
| |
| // Receive an ACK for the second touch-event. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| EXPECT_TRUE(acked_event().cancelable); |
| } |
| |
| // Tests that touch-events with multiple points are queued properly. |
| TEST_F(TouchEventQueueTest, BasicMultiTouch) { |
| const size_t kPointerCount = 10; |
| for (float i = 0; i < kPointerCount; ++i) |
| PressTouchPoint(i, i); |
| |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(kPointerCount, queued_event_count()); |
| |
| for (int i = 0; i < static_cast<int>(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 (int i = 0; i < static_cast<int>(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()); |
| |
| // Send a touch-press event. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // Signal that all touch handlers have been removed. |
| OnHasTouchEventHandlers(false); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // 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(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state()); |
| |
| // 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(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 |
| // the remaining sequence to be forwarded. |
| TEST_F(TouchEventQueueTest, ActiveSequenceNotForwardedWhenHandlersAdded) { |
| OnHasTouchEventHandlers(false); |
| |
| // Send a touch-press event while there is no handler. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| OnHasTouchEventHandlers(true); |
| |
| // The remaining touch sequence should not be forwarded. |
| MoveTouchPoint(0, 5, 5); |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(2U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| // A new touch sequence should resume forwarding. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| } |
| |
| // Tests that removal of a touch handler during a touch sequence will prevent |
| // the remaining sequence from being forwarded, even if another touch handler is |
| // registered during the same touch sequence. |
| TEST_F(TouchEventQueueTest, ActiveSequenceDroppedWhenHandlersRemoved) { |
| // Send a touch-press event. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // Queue a touch-move event. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Unregister all touch handlers. |
| OnHasTouchEventHandlers(false); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // 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. |
| MoveTouchPoint(0, 10, 10); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Simulate touch handler registration in the middle of a touch sequence. |
| OnHasTouchEventHandlers(true); |
| |
| // The touch end for the interrupted sequence should be dropped. |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // A new touch sequence should be forwarded properly. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, queued_event_count()); |
| 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. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // Send a few touch-move events, followed by a touch-release event. All the |
| // touch-move events should be coalesced into a single event. |
| for (float i = 5; i < 15; ++i) |
| MoveTouchPoint(0, i, i); |
| |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(3U, queued_event_count()); |
| |
| // ACK the press. Coalesced touch-move events should be sent. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state()); |
| |
| // ACK the moves. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(10U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| |
| // ACK the release. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchEnd, acked_event().type); |
| } |
| |
| // Tests that an event that has already been sent but hasn't been ack'ed yet |
| // doesn't get coalesced with newer events. |
| TEST_F(TouchEventQueueTest, SentTouchEventDoesNotCoalesce) { |
| // Send a touch-press event. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // Send a few touch-move events, followed by a touch-release event. All the |
| // touch-move events should be coalesced into a single event. |
| for (float i = 5; i < 15; ++i) |
| MoveTouchPoint(0, i, i); |
| |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // The coalesced touch-move event has been sent to the renderer. Any new |
| // touch-move event should not be coalesced with the sent event. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| MoveTouchPoint(0, 7, 7); |
| EXPECT_EQ(2U, queued_event_count()); |
| } |
| |
| // Tests that coalescing works correctly for multi-touch events. |
| TEST_F(TouchEventQueueTest, MultiTouch) { |
| // Press the first finger. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // Move the finger. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // Now press a second finger. |
| PressTouchPoint(2, 2); |
| EXPECT_EQ(3U, queued_event_count()); |
| |
| // Move both fingers. |
| MoveTouchPoints(0, 10, 10, 1, 20, 20); |
| MoveTouchPoint(1, 20, 20); |
| EXPECT_EQ(4U, queued_event_count()); |
| |
| // Move only one finger now. |
| MoveTouchPoint(0, 15, 15); |
| EXPECT_EQ(4U, queued_event_count()); |
| |
| // Move the other finger. |
| MoveTouchPoint(1, 25, 25); |
| EXPECT_EQ(4U, queued_event_count()); |
| |
| // Make sure both fingers are marked as having been moved in the coalesced |
| // event. |
| const WebTouchEvent& event = latest_event(); |
| EXPECT_EQ(WebTouchPoint::StateMoved, event.touches[0].state); |
| EXPECT_EQ(WebTouchPoint::StateMoved, event.touches[1].state); |
| } |
| |
| // 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()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| // Trigger a spurious ack. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that touch-move events are not sent to the renderer if the preceding |
| // touch-press event did not have a consumer (and consequently, did not hit the |
| // main thread in the renderer). Also tests that all queued/coalesced touch |
| // events are flushed immediately when the ACK for the touch-press comes back |
| // with NO_CONSUMER status. |
| TEST_F(TouchEventQueueTest, NoConsumer) { |
| // The first touch-press should reach the renderer. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // The second touch should not be sent since one is already in queue. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // Receive an ACK for the first touch-event. This should release the queued |
| // touch-event, but it should not be sent to the renderer. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| EXPECT_EQ(2U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Send a release event. This should not reach the renderer. |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchEnd, acked_event().type); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Send a press-event, followed by move and release events, and another press |
| // event, before the ACK for the first press event comes back. All of the |
| // events should be queued first. After the NO_CONSUMER ack for the first |
| // touch-press, all events upto the second touch-press should be flushed. |
| PressTouchPoint(10, 10); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| MoveTouchPoint(0, 5, 5); |
| MoveTouchPoint(0, 6, 5); |
| ReleaseTouchPoint(0); |
| |
| PressTouchPoint(6, 5); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| // The queue should hold the first sent touch-press event, the coalesced |
| // touch-move event, the touch-end event and the second touch-press event. |
| EXPECT_EQ(4U, queued_event_count()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchEnd, acked_event().type); |
| EXPECT_EQ(4U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // ACK the second press event as NO_CONSUMER too. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| // Send a second press event. Even though the first touch press had |
| // NO_CONSUMER, this press event should reach the renderer. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| TEST_F(TouchEventQueueTest, ConsumerIgnoreMultiFinger) { |
| // Press two touch points and move them around a bit. The renderer consumes |
| // the events for the first touch point, but returns NO_CONSUMER_EXISTS for |
| // the second touch point. |
| |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| MoveTouchPoint(0, 5, 5); |
| |
| PressTouchPoint(10, 10); |
| |
| MoveTouchPoint(0, 2, 2); |
| |
| MoveTouchPoint(1, 4, 10); |
| |
| MoveTouchPoints(0, 10, 10, 1, 20, 20); |
| |
| // Since the first touch-press is still pending ACK, no other event should |
| // have been sent to the renderer. |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| // The queue includes the two presses, the first touch-move of the first |
| // point, and a coalesced touch-move of both points. |
| EXPECT_EQ(4U, queued_event_count()); |
| |
| // ACK the first press as CONSUMED. This should cause the first touch-move of |
| // the first touch-point to be dispatched. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(3U, queued_event_count()); |
| |
| // ACK the first move as CONSUMED. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // ACK the second press as NO_CONSUMER_EXISTS. This will dequeue the coalesced |
| // touch-move event (which contains both touch points). Although the second |
| // touch-point does not need to be sent to the renderer, the first touch-point |
| // did move, and so the coalesced touch-event will be sent to the renderer. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // ACK the coalesced move as NOT_CONSUMED. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| // Move just the second touch point. Because the first touch point did not |
| // move, this event should not reach the renderer. |
| MoveTouchPoint(1, 30, 30); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| // Move just the first touch point. This should reach the renderer. |
| MoveTouchPoint(0, 10, 10); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // Move both fingers. This event should reach the renderer (after the ACK of |
| // the previous move event is received), because the first touch point did |
| // move. |
| MoveTouchPoints(0, 15, 15, 1, 25, 25); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| |
| // Release the first finger. Then move the second finger around some, then |
| // press another finger. Once the release event is ACKed, the move events of |
| // the second finger should be immediately released to the view, and the |
| // touch-press event should be dispatched to the renderer. |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| MoveTouchPoint(1, 40, 40); |
| |
| MoveTouchPoint(1, 50, 50); |
| |
| PressTouchPoint(1, 1); |
| |
| MoveTouchPoint(1, 30, 30); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(4U, queued_event_count()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| |
| // ACK the press with NO_CONSUMED_EXISTS. This should release the queued |
| // touch-move events to the view. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| |
| ReleaseTouchPoint(2); |
| ReleaseTouchPoint(1); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| } |
| |
| // Tests that touch-event's enqueued via a touch ack are properly handled. |
| TEST_F(TouchEventQueueTest, AckWithFollowupEvents) { |
| // Queue a touch down. |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // 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::TouchMove; |
| followup_event.touchesLength = 1; |
| 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 |
| // be sent to the renderer. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, acked_event_state()); |
| EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type); |
| |
| // Queue another event. |
| MoveTouchPoint(0, 2, 2); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // Receive an ACK for the touch-move followup event. This should cause the |
| // subsequent touch move event be sent to the renderer. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that touch-events can be synchronously ack'ed. |
| TEST_F(TouchEventQueueTest, SynchronousAcks) { |
| // TouchStart |
| SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED); |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove |
| SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED); |
| MoveTouchPoint(0, 2, 2); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchEnd |
| SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED); |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchCancel (first inserting a TouchStart so the TouchCancel will be sent) |
| PressTouchPoint(1, 1); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED); |
| CancelTouchPoint(0); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that followup events triggered by an immediate ack from |
| // TouchEventQueue::QueueEvent() are properly handled. |
| TEST_F(TouchEventQueueTest, ImmediateAckWithFollowupEvents) { |
| // Create a touch event that will be queued synchronously by a touch ack. |
| WebTouchEvent followup_event; |
| followup_event.type = WebInputEvent::TouchStart; |
| followup_event.touchesLength = 1; |
| followup_event.touches[0].id = 1; |
| followup_event.touches[0].state = WebTouchPoint::StatePressed; |
| SetFollowupEvent(followup_event); |
| |
| // Now, enqueue a stationary touch that will not be forwarded. This should be |
| // immediately ack'ed with "NO_CONSUMER_EXISTS". The followup event should |
| // then be enqueued and immediately sent to the renderer. |
| WebTouchEvent stationary_event; |
| stationary_event.touchesLength = 1; |
| stationary_event.type = WebInputEvent::TouchMove; |
| stationary_event.touches[0].id = 1; |
| stationary_event.touches[0].state = WebTouchPoint::StateStationary; |
| SendTouchEvent(stationary_event); |
| |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| } |
| |
| // Tests basic TouchEvent forwarding suppression. |
| TEST_F(TouchEventQueueTest, NoTouchBasic) { |
| // Disable TouchEvent forwarding. |
| OnHasTouchEventHandlers(false); |
| PressTouchPoint(30, 5); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove should not be sent to renderer. |
| MoveTouchPoint(0, 65, 10); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchEnd should not be sent to renderer. |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Enable TouchEvent forwarding. |
| OnHasTouchEventHandlers(true); |
| |
| PressTouchPoint(80, 10); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 80, 20); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that no TouchEvents are sent to renderer during scrolling. |
| TEST_F(TouchEventQueueTest, TouchCancelOnScroll) { |
| SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL); |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 20, 5); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| MoveTouchPoint(0, 30, 15); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Queue another TouchStart. |
| PressTouchPoint(20, 20); |
| EXPECT_EQ(3U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, latest_event().type); |
| |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); |
| |
| // GestureScrollUpdate inserts a synthetic TouchCancel before the TouchStart. |
| followup_scroll.type = WebInputEvent::GestureScrollUpdate; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchCancel, sent_event().type); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(WebInputEvent::TouchStart, latest_event().type); |
| |
| // Acking the TouchCancel will result in dispatch of the next TouchStart. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| // The synthetic TouchCancel should not reach client, only the TouchStart. |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, acked_event().type); |
| |
| // TouchMove should not be sent to the renderer. |
| MoveTouchPoint(0, 30, 5); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| // GestureScrollUpdates should not change affect touch forwarding. |
| SendGestureEvent(WebInputEvent::GestureScrollUpdate); |
| |
| // TouchEnd should not be sent to the renderer. |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| // Touch events from a new gesture sequence should be forwarded normally. |
| PressTouchPoint(80, 10); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 80, 20); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that a scroll event will not insert a synthetic TouchCancel if there |
| // was no consumer for the current touch sequence. |
| TEST_F(TouchEventQueueTest, NoTouchCancelOnScrollIfNoConsumer) { |
| SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_TOUCHCANCEL); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type); |
| |
| // Queue a TouchMove that turns into a GestureScrollBegin. |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| MoveTouchPoint(0, 20, 5); |
| |
| // The TouchMove has no consumer, and should be ack'ed immediately. However, |
| // *no* synthetic TouchCancel should be inserted as the touch sequence |
| // had no consumer. |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type); |
| |
| // Subsequent TouchMove's should not be sent to the renderer. |
| MoveTouchPoint(0, 30, 5); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| // TouchEnd should not be sent to the renderer. |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| // Touch events from a new gesture sequence should be forwarded normally. |
| PressTouchPoint(80, 10); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that IsTouchStartPendingAck works correctly. |
| TEST_F(TouchEventQueueTest, PendingStart) { |
| |
| EXPECT_FALSE(IsPendingAckTouchStart()); |
| |
| // Send the touchstart for one point (#1). |
| PressTouchPoint(1, 1); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_TRUE(IsPendingAckTouchStart()); |
| |
| // Send a touchmove for that point (#2). |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_TRUE(IsPendingAckTouchStart()); |
| |
| // Ack the touchstart (#1). |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_FALSE(IsPendingAckTouchStart()); |
| |
| // Send a touchstart for another point (#3). |
| PressTouchPoint(10, 10); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_FALSE(IsPendingAckTouchStart()); |
| |
| // Ack the touchmove (#2). |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_TRUE(IsPendingAckTouchStart()); |
| |
| // Send a touchstart for a third point (#4). |
| PressTouchPoint(15, 15); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_TRUE(IsPendingAckTouchStart()); |
| |
| // Ack the touchstart for the second point (#3). |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_TRUE(IsPendingAckTouchStart()); |
| |
| // Ack the touchstart for the third point (#4). |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_FALSE(IsPendingAckTouchStart()); |
| } |
| |
| // Tests that the touch timeout is started when sending certain touch types. |
| TEST_F(TouchEventQueueTest, TouchTimeoutTypes) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Sending a TouchStart will start the timeout. |
| PressTouchPoint(0, 1); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| |
| // A TouchMove should start the timeout. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| |
| // A TouchEnd should not start the timeout. |
| ReleaseTouchPoint(0); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| |
| // A TouchCancel should not start the timeout. |
| PressTouchPoint(0, 1); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| ASSERT_FALSE(IsTimeoutRunning()); |
| CancelTouchPoint(0); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| } |
| |
| // Tests that a delayed TouchEvent ack will trigger a TouchCancel timeout, |
| // disabling touch forwarding until the next TouchStart is received after |
| // the timeout events are ack'ed. |
| TEST_F(TouchEventQueueTest, TouchTimeoutBasic) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| GetAndResetSentEventCount(); |
| GetAndResetAckedEventCount(); |
| PressTouchPoint(0, 1); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| |
| // Delay the ack. |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| |
| // The timeout should have fired, synthetically ack'ing the timed-out event. |
| // TouchEvent forwarding is disabled until the ack is received for the |
| // timed-out event and the future cancel event. |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Ack'ing the original event should trigger a cancel event. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchCancel, sent_event().type); |
| EXPECT_FALSE(sent_event().cancelable); |
| |
| // Touch events should not be forwarded until we receive the cancel acks. |
| MoveTouchPoint(0, 1, 1); |
| ASSERT_EQ(0U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| ReleaseTouchPoint(0); |
| ASSERT_EQ(0U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // The synthetic TouchCancel ack should not reach the client, but should |
| // resume touch forwarding. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // Subsequent events should be handled normally. |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type); |
| EXPECT_TRUE(sent_event().cancelable); |
| } |
| |
| // Tests that the timeout is never started if the renderer consumes |
| // a TouchEvent from the current touch sequence. |
| TEST_F(TouchEventQueueTest, NoTouchTimeoutIfRendererIsConsumingGesture) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| ASSERT_TRUE(IsTimeoutRunning()); |
| |
| // Mark the event as consumed. This should prevent the timeout from |
| // being activated on subsequent TouchEvents in this gesture. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| |
| // A TouchMove should not start the timeout. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // A secondary TouchStart should not start the timeout. |
| PressTouchPoint(1, 0); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // A TouchEnd should not start the timeout. |
| ReleaseTouchPoint(1); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // A TouchCancel should not start the timeout. |
| CancelTouchPoint(0); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| } |
| |
| // Tests that the timeout is never started if the renderer consumes |
| // a TouchEvent from the current touch sequence. |
| TEST_F(TouchEventQueueTest, NoTouchTimeoutIfDisabledAfterTouchStart) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| ASSERT_TRUE(IsTimeoutRunning()); |
| |
| // Send the ack immediately. The timeout should not have fired. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Now explicitly disable the timeout. |
| SetAckTimeoutDisabled(); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| |
| // A TouchMove should not start or trigger the timeout. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that the timeout is never started if the ack is synchronous. |
| TEST_F(TouchEventQueueTest, NoTouchTimeoutIfAckIsSynchronous) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| SetSyncAckResult(INPUT_EVENT_ACK_STATE_CONSUMED); |
| ASSERT_FALSE(IsTimeoutRunning()); |
| PressTouchPoint(0, 1); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| } |
| |
| // Tests that the timeout does not fire if explicitly disabled while an event |
| // is in-flight. |
| TEST_F(TouchEventQueueTest, NoTouchTimeoutIfDisabledWhileTimerIsActive) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| ASSERT_TRUE(IsTimeoutRunning()); |
| |
| // Verify that disabling the timeout also turns off the timer. |
| SetAckTimeoutDisabled(); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that a TouchCancel timeout plays nice when the timed out touch stream |
| // turns into a scroll gesture sequence. |
| TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGesture) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // The cancelled sequence may turn into a scroll gesture. |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| |
| // Delay the ack. |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| |
| // The timeout should have fired, disabling touch forwarding until both acks |
| // are received, acking the timed out event. |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Ack the original event, triggering a TouchCancel. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // Ack the cancel event. Normally, this would resume touch forwarding, |
| // but we're still within a scroll gesture so it remains disabled. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // Try to forward touch events for the current sequence. |
| GetAndResetSentEventCount(); |
| GetAndResetAckedEventCount(); |
| MoveTouchPoint(0, 1, 1); |
| ReleaseTouchPoint(0); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, GetAndResetAckedEventCount()); |
| |
| // Now end the scroll sequence, resuming touch handling. |
| SendGestureEvent(blink::WebInputEvent::GestureScrollEnd); |
| PressTouchPoint(0, 1); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that a TouchCancel timeout plays nice when the timed out touch stream |
| // turns into a scroll gesture sequence, but the original event acks are |
| // significantly delayed. |
| TEST_F(TouchEventQueueTest, TouchTimeoutWithFollowupGestureAndDelayedAck) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // The cancelled sequence may turn into a scroll gesture. |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| |
| // Delay the ack. |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| |
| // The timeout should have fired, disabling touch forwarding until both acks |
| // are received and acking the timed out event. |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Try to forward a touch event. |
| GetAndResetSentEventCount(); |
| GetAndResetAckedEventCount(); |
| MoveTouchPoint(0, 1, 1); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Now end the scroll sequence. Events will not be forwarded until the two |
| // outstanding touch acks are received. |
| SendGestureEvent(blink::WebInputEvent::GestureScrollEnd); |
| MoveTouchPoint(0, 2, 2); |
| ReleaseTouchPoint(0); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, GetAndResetAckedEventCount()); |
| |
| // Ack the original event, triggering a cancel. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // Ack the cancel event, resuming touch forwarding. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| PressTouchPoint(0, 1); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| } |
| |
| // Tests that a delayed TouchEvent ack will not trigger a TouchCancel timeout if |
| // the timed-out event had no consumer. |
| TEST_F(TouchEventQueueTest, NoCancelOnTouchTimeoutWithoutConsumer) { |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| |
| // Delay the ack. |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| |
| // The timeout should have fired, synthetically ack'ing the timed out event. |
| // TouchEvent forwarding is disabled until the original ack is received. |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Touch events should not be forwarded until we receive the original ack. |
| MoveTouchPoint(0, 1, 1); |
| ReleaseTouchPoint(0); |
| ASSERT_EQ(0U, GetAndResetSentEventCount()); |
| ASSERT_EQ(2U, GetAndResetAckedEventCount()); |
| |
| // Ack'ing the original event should not trigger a cancel event, as the |
| // TouchStart had no consumer. However, it should re-enable touch forwarding. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Subsequent events should be handled normally. |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that TouchMove's are dropped if within the boundary-inclusive slop |
| // suppression region for an unconsumed TouchStart. |
| TEST_F(TouchEventQueueTest, TouchMoveSuppressionIncludingSlopBoundary) { |
| const float kSlopLengthDips = 10; |
| const float kHalfSlopLengthDips = kSlopLengthDips / 2; |
| SetUpForTouchMoveSlopTesting(kSlopLengthDips); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove's within the region should be suppressed. |
| 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, kHalfSlopLengthDips, 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()); |
| |
| MoveTouchPoint(0, -kHalfSlopLengthDips, 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()); |
| |
| MoveTouchPoint(0, -kSlopLengthDips, 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()); |
| |
| MoveTouchPoint(0, 0, kSlopLengthDips); |
| 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 exceeds the (Euclidean) distance, no more |
| // TouchMove's should be suppressed. |
| const float kFortyFiveDegreeSlopLengthXY = |
| kSlopLengthDips * std::sqrt(2.f) / 2; |
| MoveTouchPoint(0, kFortyFiveDegreeSlopLengthXY + .2f, |
| kFortyFiveDegreeSlopLengthXY + .2f); |
| 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()); |
| |
| // Even TouchMove's within the original slop region should now be forwarded. |
| MoveTouchPoint(0, 0, 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()); |
| |
| // A new touch sequence should reset suppression. |
| ReleaseTouchPoint(0); |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| ASSERT_EQ(2U, GetAndResetSentEventCount()); |
| ASSERT_EQ(2U, GetAndResetAckedEventCount()); |
| ASSERT_EQ(0U, queued_event_count()); |
| |
| // The slop region is boundary-inclusive. |
| MoveTouchPoint(0, kSlopLengthDips - 1, 0); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, kSlopLengthDips, 0); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| 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 float kSlopLengthDips = 10; |
| const float kHalfSlopLengthDips = kSlopLengthDips / 2; |
| SetUpForTouchMoveSlopTesting(kSlopLengthDips); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_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); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // 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_NOT_CONSUMED); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // 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()); |
| } |
| |
| // Tests that TouchMove's are not dropped due to incorrect handling of DPI |
| // scaling. |
| TEST_F(TouchEventQueueTest, TouchMoveSuppressionWithDIPScaling) { |
| const float kSlopLengthPixels = 7.f; |
| const float kDPIScale = 3.f; |
| SetUpForTouchMoveSlopTesting(kSlopLengthPixels / kDPIScale); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove's along the slop boundary should be suppresed. |
| MoveTouchPoint(0, 0, kSlopLengthPixels / kDPIScale); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Reset the touch sequence. |
| ReleaseTouchPoint(0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| GetAndResetSentEventCount(); |
| GetAndResetAckedEventCount(); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove's outside the region should not be suppressed. |
| const float kPixelCoordOutsideSlopRegion = kSlopLengthPixels + 0.5f; |
| MoveTouchPoint(0, 0, kPixelCoordOutsideSlopRegion / kDPIScale); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that TouchMove's are not dropped if a secondary pointer is present |
| // during any movement. |
| TEST_F(TouchEventQueueTest, NoTouchMoveSuppressionAfterMultiTouch) { |
| const float kSlopLengthDips = 10; |
| const float kHalfSlopLengthDips = kSlopLengthDips / 2; |
| SetUpForTouchMoveSlopTesting(kSlopLengthDips); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove's within the region should be suppressed. |
| MoveTouchPoint(0, 0, kHalfSlopLengthDips); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Simulate a secondary pointer press. |
| PressTouchPoint(kSlopLengthDips, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove with a secondary pointer should not be suppressed. |
| MoveTouchPoint(1, kSlopLengthDips, 0); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Release the secondary pointer. |
| ReleaseTouchPoint(0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove's should not should be suppressed, even with the original |
| // unmoved pointer. |
| MoveTouchPoint(0, 0, 0); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that secondary touch points can be forwarded even if the primary touch |
| // point had no consumer. |
| TEST_F(TouchEventQueueTest, SecondaryTouchForwardedAfterPrimaryHadNoConsumer) { |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Events should not be forwarded, as the point had no consumer. |
| MoveTouchPoint(0, 0, 15); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Simulate a secondary pointer press. |
| PressTouchPoint(20, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // TouchMove with a secondary pointer should not be suppressed. |
| MoveTouchPoint(1, 25, 0); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Tests that no touch points will be forwarded after scrolling begins while no |
| // touch points have a consumer. |
| TEST_F(TouchEventQueueTest, NoForwardingAfterScrollWithNoTouchConsumers) { |
| // Queue a TouchStart. |
| PressTouchPoint(0, 0); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
| ASSERT_EQ(1U, GetAndResetSentEventCount()); |
| ASSERT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| MoveTouchPoint(0, 20, 5); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| // The secondary pointer press should not be forwarded. |
| PressTouchPoint(20, 0); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| |
| // Neither should any further touchmoves be forwarded. |
| MoveTouchPoint(1, 25, 0); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, acked_event_state()); |
| } |
| |
| TEST_F(TouchEventQueueTest, SyncTouchMoveDoesntCancelTouchOnScroll) { |
| SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE); |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 20, 5); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // GestureScrollBegin doesn't insert a synthetic TouchCancel. |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, queued_event_count()); |
| } |
| |
| TEST_F(TouchEventQueueTest, AsyncTouch) { |
| SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| for (int i = 0; i < 3; ++i) { |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| MoveTouchPoint(0, 10, 5); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // Consuming a scroll event will throttle subsequent touchmoves. |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| MoveTouchPoint(0, 10, 5); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| } |
| } |
| |
| // Ensure that touchmove's are appropriately throttled during a typical |
| // scroll sequences that transitions between scrolls consumed and unconsumed. |
| TEST_F(TouchEventQueueTest, AsyncTouchThrottledAfterScroll) { |
| SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE); |
| |
| // Process a TouchStart |
| PressTouchPoint(0, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Now send the first touch move and associated GestureScrollBegin. |
| MoveTouchPoint(0, 0, 5); |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| SendGestureEventAck(WebInputEvent::GestureScrollBegin, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // Send the second touch move and associated GestureScrollUpdate, but don't |
| // ACK the gesture event yet. |
| MoveTouchPoint(0, 0, 50); |
| followup_scroll.type = WebInputEvent::GestureScrollUpdate; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Now queue a second touchmove and verify it's not (yet) dispatched. |
| MoveTouchPoint(0, 0, 100); |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Queuing the final touchend should flush the pending, async touchmove. |
| ReleaseTouchPoint(0); |
| followup_scroll.type = WebInputEvent::GestureScrollEnd; |
| SetFollowupEvent(followup_scroll); |
| EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // Ack the flushed, async touchmove. The ack should not reach the client, but |
| // it should trigger sending of the (now non-cancelable) touchend. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, queued_event_count()); |
| |
| // Ack the touchend. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Now mark the scrolls as not consumed (which would cause future touchmoves |
| // in the active sequence to be sent if there was one). |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // Start a new touch sequence and verify that throttling has been reset. |
| // Touch moves after the start of scrolling will again be throttled. |
| PressTouchPoint(0, 0); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| MoveTouchPoint(0, 0, 5); |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 0, 5); |
| followup_scroll.type = WebInputEvent::GestureScrollUpdate; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 0, 10); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // As soon as a touchmove exceeds the outer slop region it will be forwarded |
| // immediately. |
| MoveTouchPoint(0, 0, 20); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Subsequent touchmove's should be deferred. |
| MoveTouchPoint(0, 0, 25); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // The pending touchmove should be flushed with the the new touchmove if |
| // sufficient time has passed. |
| AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1); |
| MoveTouchPoint(0, 0, 15); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Non-touchmove events should always flush any pending touchmove events. |
| MoveTouchPoint(0, 0, 25); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| PressTouchPoint(30, 30); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(2U, queued_event_count()); |
| |
| // Ack'ing the flushed, async touchmove will dispatch the touchstart. Note |
| // that the flushed touchmove's ack will not reach the client (its |
| // constituent events have already been ack'ed). |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(WebInputEvent::TouchStart, sent_event().type); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // Ack the touchstart. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // Send a secondary touchmove. |
| MoveTouchPoint(1, 0, 25); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| // An unconsumed scroll should resume synchronous touch handling. |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| |
| // The pending touchmove should be coalesced with the next (now synchronous) |
| // touchmove. |
| MoveTouchPoint(0, 0, 25); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); |
| EXPECT_EQ(WebTouchPoint::StateMoved, sent_event().touches[0].state); |
| EXPECT_EQ(WebTouchPoint::StateMoved, sent_event().touches[1].state); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| |
| // Subsequent touches will queue until the preceding, synchronous touches are |
| // ack'ed. |
| ReleaseTouchPoint(1); |
| EXPECT_EQ(2U, queued_event_count()); |
| ReleaseTouchPoint(0); |
| EXPECT_EQ(3U, queued_event_count()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(WebInputEvent::TouchEnd, sent_event().type); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| } |
| |
| // Ensure that async touch dispatch and touch ack timeout interactions work |
| // appropriately. |
| TEST_F(TouchEventQueueTest, AsyncTouchWithAckTimeout) { |
| SetTouchScrollingMode(TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE); |
| SetUpForTimeoutTesting(DefaultTouchTimeoutDelay()); |
| |
| // The touchstart should start the timeout. |
| PressTouchPoint(0, 0); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| |
| // The start of a scroll gesture should trigger async touch event dispatch. |
| MoveTouchPoint(0, 1, 1); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollBegin; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| |
| // An async touch should fire after the throttling interval has expired, but |
| // it should not start the touch ack timeout. |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_TRUE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| AdvanceTouchTime(kMinSecondsBetweenThrottledTouchmoves + 0.1); |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_FALSE(sent_event().cancelable); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // An unconsumed scroll event will resume synchronous touchmoves, which are |
| // subject to the ack timeout. |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| MoveTouchPoint(0, 20, 5); |
| EXPECT_TRUE(IsTimeoutRunning()); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // The timeout should fire, disabling touch forwarding until both acks are |
| // received and acking the timed out event. |
| RunTasksAndWait(DefaultTouchTimeoutDelay() * 2); |
| EXPECT_FALSE(IsTimeoutRunning()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| // Ack'ing the original event should trigger a cancel event. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // Subsequent touchmove's should not be forwarded, even as the scroll gesture |
| // goes from unconsumed to consumed. |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| MoveTouchPoint(0, 20, 5); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| MoveTouchPoint(0, 25, 5); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(0U, GetAndResetSentEventCount()); |
| } |
| |
| // Ensure that if the touch ack for an async touchmove triggers a follow-up |
| // touch event, that follow-up touch will be forwarded appropriately. |
| TEST_F(TouchEventQueueTest, AsyncTouchWithTouchCancelAfterAck) { |
| 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()); |
| |
| // The start of a scroll gesture should trigger async touch event 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 ack'ed immediately, but not forwarded. |
| // However, because the ack triggers a touchcancel, both the pending touch and |
| // the queued touchcancel should be flushed. |
| WebTouchEvent followup_cancel; |
| followup_cancel.type = WebInputEvent::TouchCancel; |
| followup_cancel.touchesLength = 1; |
| followup_cancel.touches[0].state = WebTouchPoint::StateCancelled; |
| SetFollowupEvent(followup_cancel); |
| MoveTouchPoint(0, 5, 5); |
| EXPECT_EQ(2U, queued_event_count()); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_FALSE(HasPendingAsyncTouchMove()); |
| EXPECT_EQ(WebInputEvent::TouchMove, acked_event().type); |
| EXPECT_EQ(WebInputEvent::TouchMove, sent_event().type); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // The ack for the async touchmove should not reach the client, as it has |
| // already been ack'ed. |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_FALSE(sent_event().cancelable); |
| EXPECT_EQ(1U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchCancel, sent_event().type); |
| EXPECT_EQ(0U, GetAndResetAckedEventCount()); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(WebInputEvent::TouchCancel, acked_event().type); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| 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); |
| |
| // Queue a TouchStart. |
| PressTouchPoint(0, 1); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(1U, GetAndResetAckedEventCount()); |
| |
| MoveTouchPoint(0, 20, 5); |
| SendGestureEvent(blink::WebInputEvent::GestureScrollBegin); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_EQ(2U, GetAndResetSentEventCount()); |
| |
| // Even if the first touchmove event was consumed, subsequent unconsumed |
| // touchmove events should trigger scrolling. |
| MoveTouchPoint(0, 60, 5); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| MoveTouchPoint(0, 20, 5); |
| WebGestureEvent followup_scroll; |
| followup_scroll.type = WebInputEvent::GestureScrollUpdate; |
| SetFollowupEvent(followup_scroll); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
| SendGestureEventAck(WebInputEvent::GestureScrollUpdate, |
| INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| EXPECT_TRUE(sent_event().cancelable); |
| EXPECT_EQ(1U, GetAndResetSentEventCount()); |
| |
| // Touch move event is throttled. |
| MoveTouchPoint(0, 60, 5); |
| SendTouchEventAck(INPUT_EVENT_ACK_STATE_CONSUMED); |
| EXPECT_EQ(0U, queued_event_count()); |
| 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 |