blob: 6bcec4fba6e86cb7ae6b4170957cddfc568e6061 [file] [log] [blame]
// 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