| Index: content/browser/renderer_host/input/gesture_event_queue_unittest.cc
|
| diff --git a/content/browser/renderer_host/input/gesture_event_queue_unittest.cc b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8460faf8d185893a2393a58fce7da216ab771959
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/gesture_event_queue_unittest.cc
|
| @@ -0,0 +1,414 @@
|
| +// 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 "content/browser/renderer_host/input/gesture_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 {
|
| +
|
| +class GestureEventQueueTest : public testing::Test,
|
| + public GestureEventQueueClient {
|
| + public:
|
| + GestureEventQueueTest() : sent_gesture_count_(0) {}
|
| +
|
| + virtual ~GestureEventQueueTest() {}
|
| +
|
| + // testing::Test
|
| + virtual void SetUp() OVERRIDE {
|
| + queue_.reset(new GestureEventQueue(this));
|
| + }
|
| +
|
| + virtual void TearDown() OVERRIDE {
|
| + queue_.reset();
|
| + }
|
| +
|
| + // GestureEventQueueClient
|
| + virtual void ForwardGestureEvent(const WebGestureEvent& event) OVERRIDE {
|
| + ++sent_gesture_count_;
|
| + last_gesture_event_ = event;
|
| + }
|
| +
|
| + protected:
|
| + typedef std::vector<WebGestureEvent> Gestures;
|
| +
|
| + void SendTouchGestures() {
|
| + GestureEventPacket gesture_packet;
|
| + std::swap(gesture_packet, gesture_packet_);
|
| + SendTouchGestures(touch_event_, gesture_packet);
|
| + touch_event_.ResetPoints();
|
| + }
|
| +
|
| + void SendTouchGestures(const WebTouchEvent& touch,
|
| + const GestureEventPacket& packet) {
|
| + GestureEventPacket touch_packet = GestureEventPacket::FromTouch(touch);
|
| + for (size_t i = 0; i < packet.gesture_count(); ++i)
|
| + touch_packet.Push(packet.gesture(i));
|
| + queue_->OnGestureEventPacket(touch_packet);
|
| + }
|
| +
|
| + void SendGesture(GestureEventPacket::GestureSource source,
|
| + const WebGestureEvent& gesture) {
|
| + queue_->OnGestureEventPacket(
|
| + GestureEventPacket::FromGesture(source, gesture));
|
| + }
|
| +
|
| + void SendTimeoutGesture(const WebGestureEvent& gesture) {
|
| + SendGesture(GestureEventPacket::TOUCH_TIMEOUT, gesture);
|
| + }
|
| +
|
| + void SendSyntheticGesture(const WebGestureEvent& gesture) {
|
| + SendGesture(GestureEventPacket::SYNTHETIC, gesture);
|
| + }
|
| +
|
| + void SendTouchEventACK(InputEventAckState ack_result) {
|
| + queue_->OnTouchEventAck(ack_result);
|
| + }
|
| +
|
| + void PushGesture(const WebGestureEvent& gesture) {
|
| + gesture_packet_.Push(gesture);
|
| + }
|
| +
|
| + void PushGesture(WebInputEvent::Type type) {
|
| + DCHECK(WebInputEvent::isGestureEventType(type));
|
| + PushGesture(CreateGesture(type));
|
| + }
|
| +
|
| + void PressTouchPoint(int x, int y) {
|
| + touch_event_.PressPoint(x, y);
|
| + SendTouchGestures();
|
| + }
|
| +
|
| + void MoveTouchPoint(int index, int x, int y) {
|
| + touch_event_.MovePoint(index, x, y);
|
| + SendTouchGestures();
|
| + }
|
| +
|
| + void MoveTouchPoints(int index0, int x0, int y0, int index1, int x1, int y1) {
|
| + touch_event_.MovePoint(index0, x0, y0);
|
| + touch_event_.MovePoint(index1, x1, y1);
|
| + SendTouchGestures();
|
| + }
|
| +
|
| + void ReleaseTouchPoint(int index) {
|
| + touch_event_.ReleasePoint(index);
|
| + SendTouchGestures();
|
| + }
|
| +
|
| + void CancelTouchPoint(int index) {
|
| + touch_event_.CancelPoint(index);
|
| + SendTouchGestures();
|
| + }
|
| +
|
| + size_t GetAndResetSentGestureCount() {
|
| + size_t count = sent_gesture_count_;
|
| + sent_gesture_count_ = 0;
|
| + return count;
|
| + }
|
| +
|
| + const WebGestureEvent& sent_gesture() const {
|
| + return last_gesture_event_;
|
| + }
|
| +
|
| + static WebGestureEvent CreateGesture(WebInputEvent::Type type) {
|
| + return SyntheticWebGestureEventBuilder::Build(
|
| + type, WebGestureEvent::Touchscreen);
|
| + }
|
| +
|
| + private:
|
| +
|
| + scoped_ptr<GestureEventQueue> queue_;
|
| + SyntheticWebTouchEvent touch_event_;
|
| + GestureEventPacket gesture_packet_;
|
| + size_t sent_gesture_count_;
|
| + WebTouchEvent last_touch_event_;
|
| + WebGestureEvent last_gesture_event_;
|
| +};
|
| +
|
| +TEST_F(GestureEventQueueTest, BasicNoGestures) {
|
| + PressTouchPoint(1, 1);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + MoveTouchPoint(0, 2, 2);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // No gestures should be dispatched by the ack, as the queued packets
|
| + // contained no gestures.
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // Release the touch gesture.
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, BasicGestures) {
|
| + // An unconsumed touch's gesture should be sent.
|
| + PushGesture(WebInputEvent::GestureScrollBegin);
|
| + PressTouchPoint(1, 1);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureScrollBegin, sent_gesture().type);
|
| +
|
| + // Multiple gestures can be queued for a single event.
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + PushGesture(WebInputEvent::GestureFlingCancel);
|
| + MoveTouchPoint(0, 1, 1);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(2U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureFlingCancel, sent_gesture().type);
|
| +
|
| + // A consumed touch's gesture should not be sent.
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + PushGesture(WebInputEvent::GestureFlingCancel);
|
| + ReleaseTouchPoint(0);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, ConsumedThenNotConsumed) {
|
| + // A consumed touch's gesture should not be sent.
|
| + PushGesture(WebInputEvent::GestureScrollBegin);
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // Event if the subsequent touch is not consumed, continue dropping gestures.
|
| + PushGesture(WebInputEvent::GestureScrollUpdate);
|
| + MoveTouchPoint(0, 2, 2);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // Event if the subsequent touch had no consumer, continue dropping gestures.
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, NotConsumedThenNoConsumer) {
|
| + // An unconsumed touch's gesture should be sent.
|
| + PushGesture(WebInputEvent::GestureScrollBegin);
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| +
|
| + // If the subsequent touch has no consumer (e.g., a secondary pointer is
|
| + // pressed but not on a touch handling rect), send the gesture.
|
| + PushGesture(WebInputEvent::GesturePinchBegin);
|
| + PressTouchPoint(2, 2);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| +
|
| + // If the subsequent touch is consumed, then the remaining gesture sequence
|
| + // should be dropped, regardless of subsequent touch ack disposition.
|
| + PushGesture(WebInputEvent::GestureScrollUpdate);
|
| + PushGesture(WebInputEvent::GesturePinchUpdate);
|
| + MoveTouchPoint(0, 2, 2);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + PushGesture(WebInputEvent::GesturePinchEnd);
|
| + ReleaseTouchPoint(1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + PushGesture(WebInputEvent::GestureScrollEnd);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, MultipleTouchSequences) {
|
| + // Queue two touch-to-gestures sequences.
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + PressTouchPoint(1, 1);
|
| + PushGesture(WebInputEvent::GestureFlingCancel);
|
| + ReleaseTouchPoint(0);
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + PressTouchPoint(1, 1);
|
| + PushGesture(WebInputEvent::GestureFlingCancel);
|
| + ReleaseTouchPoint(0);
|
| +
|
| + // The first gesture sequence should not be allowed.
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // The subsequent sequence should "reset" allowance.
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(2U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, FlingCancelledOnNewTouchSequence) {
|
| + // Simulate a fling start that is sent.
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureFlingStart, sent_gesture().type);
|
| +
|
| + // A new touch seqeuence should cancel the outstanding fling.
|
| + PressTouchPoint(1, 1);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureFlingCancel, sent_gesture().type);
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, FlingNotCancelledIfGFCEventReceived) {
|
| + // Simulate a fling that is started then cancelled.
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| + PushGesture(WebInputEvent::GestureFlingCancel);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(2U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureFlingCancel, sent_gesture().type);
|
| +
|
| + // A new touch sequence will not inject a GestureFlingCancel, as the fling
|
| + // has already been cancelled.
|
| + PressTouchPoint(1, 1);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, TapCancelledWhenScrollBegins) {
|
| + PushGesture(WebInputEvent::GestureTapDown);
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureTapDown, sent_gesture().type);
|
| +
|
| + // If the subsequent touch turns into a scroll, the tap should be cancelled.
|
| + PushGesture(WebInputEvent::GestureScrollBegin);
|
| + MoveTouchPoint(0, 2, 2);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(2U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureScrollBegin, sent_gesture().type);
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, TapCancelledWhenTouchConsumed) {
|
| + PushGesture(WebInputEvent::GestureTapDown);
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureTapDown, sent_gesture().type);
|
| +
|
| + // If the subsequent touch is consumed, the tap should be cancelled.
|
| + PushGesture(WebInputEvent::GestureScrollBegin);
|
| + MoveTouchPoint(0, 2, 2);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureTapCancel, sent_gesture().type);
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, TapNotCancelledIfTapEndingEventReceived) {
|
| + PushGesture(WebInputEvent::GestureTapDown);
|
| + PressTouchPoint(1, 1);
|
| + PressTouchPoint(2, 2);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureTapDown, sent_gesture().type);
|
| +
|
| + PushGesture(WebInputEvent::GestureTap);
|
| + ReleaseTouchPoint(1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureTap, sent_gesture().type);
|
| +
|
| + // The tap should not be cancelled as it was terminated by a |GestureTap|.
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, TimeoutGestures) {
|
| + // If the sequence is allowed, and there are no preceding gestures, the
|
| + // timeout gestures should be forwarded immediately.
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + SendTimeoutGesture(CreateGesture(WebInputEvent::GestureShowPress));
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureShowPress, sent_gesture().type);
|
| + SendTimeoutGesture(CreateGesture(WebInputEvent::GestureLongPress));
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureLongPress, sent_gesture().type);
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| +
|
| + // If the sequence is disallowed, and there are no preceding gestures, the
|
| + // timeout gestures should be dropped immediately.
|
| + PressTouchPoint(1, 1);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_CONSUMED);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + SendTimeoutGesture(CreateGesture(WebInputEvent::GestureShowPress));
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| + ReleaseTouchPoint(0);
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| +
|
| + // If the sequence has a pending ack, the timeout gestures should
|
| + // remain queued until the ack is received.
|
| + PressTouchPoint(1, 1);
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + SendTimeoutGesture(CreateGesture(WebInputEvent::GestureLongPress));
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(1U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GestureLongPress, sent_gesture().type);
|
| +}
|
| +
|
| +TEST_F(GestureEventQueueTest, SyntheticGestures) {
|
| + // Synthetic gestures without an associated touch event should be
|
| + // forwarded immediately if there are no preceding gestures.
|
| + SendSyntheticGesture(CreateGesture(WebInputEvent::GesturePinchBegin));
|
| + SendSyntheticGesture(CreateGesture(WebInputEvent::GesturePinchUpdate));
|
| + SendSyntheticGesture(CreateGesture(WebInputEvent::GesturePinchEnd));
|
| + EXPECT_EQ(3U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GesturePinchEnd, sent_gesture().type);
|
| +
|
| + // Queue a blocking touch gesture.
|
| + PushGesture(WebInputEvent::GestureFlingStart);
|
| + PressTouchPoint(1, 1);
|
| + ASSERT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // Subsequent synthetic events should only be forwarded after the
|
| + // touch-derived gesture has been dispatched.
|
| + SendSyntheticGesture(CreateGesture(WebInputEvent::GesturePinchBegin));
|
| + SendSyntheticGesture(CreateGesture(WebInputEvent::GesturePinchUpdate));
|
| + SendSyntheticGesture(CreateGesture(WebInputEvent::GesturePinchEnd));
|
| + EXPECT_EQ(0U, GetAndResetSentGestureCount());
|
| +
|
| + // Dispatching the queued gesture should unblock the synthetic gestures.
|
| + SendTouchEventACK(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
|
| + EXPECT_EQ(4U, GetAndResetSentGestureCount());
|
| + EXPECT_EQ(WebInputEvent::GesturePinchEnd, sent_gesture().type);
|
| +}
|
| +
|
| +} // namespace content
|
|
|