| Index: content/browser/renderer_host/input/gesture_event_queue.cc
|
| diff --git a/content/browser/renderer_host/input/gesture_event_queue.cc b/content/browser/renderer_host/input/gesture_event_queue.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0e9db9cf9a8ea5ddcb7645b5382b7e9ab301d5b0
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/gesture_event_queue.cc
|
| @@ -0,0 +1,231 @@
|
| +// 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 "content/browser/renderer_host/input/gesture_event_queue.h"
|
| +
|
| +#include "base/auto_reset.h"
|
| +#include "base/bind.h"
|
| +#include "base/logging.h"
|
| +#include "content/common/input/web_input_event_traits.h"
|
| +
|
| +using blink::WebGestureEvent;
|
| +using blink::WebInputEvent;
|
| +using blink::WebTouchEvent;
|
| +using blink::WebTouchPoint;
|
| +
|
| +namespace content {
|
| +namespace {
|
| +
|
| +InputEventAckState GetMostRestrictiveState(InputEventAckState ack_old,
|
| + InputEventAckState ack_new) {
|
| + DCHECK_NE(INPUT_EVENT_ACK_STATE_UNKNOWN, ack_new);
|
| +
|
| + switch (ack_old) {
|
| + case INPUT_EVENT_ACK_STATE_UNKNOWN:
|
| + return ack_new;
|
| +
|
| + case INPUT_EVENT_ACK_STATE_NOT_CONSUMED:
|
| + return ack_new == INPUT_EVENT_ACK_STATE_CONSUMED ? ack_new : ack_old;
|
| +
|
| + case INPUT_EVENT_ACK_STATE_CONSUMED:
|
| + case INPUT_EVENT_ACK_STATE_IGNORED:
|
| + case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS:
|
| + default:
|
| + return ack_old;
|
| + };
|
| +}
|
| +
|
| +WebGestureEvent CreateGesture(WebInputEvent::Type type) {
|
| + DCHECK(WebInputEvent::isGestureEventType(type));
|
| + WebGestureEvent event;
|
| + event.type = type;
|
| + event.sourceDevice = WebGestureEvent::Touchscreen;
|
| + return event;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +GestureEventQueue::GestureEventQueue(GestureEventQueueClient* client)
|
| + : client_(client),
|
| + packet_sender_(base::Bind(&GestureEventQueue::SendPacket,
|
| + base::Unretained(this))),
|
| + outstanding_tap_(false),
|
| + outstanding_fling_(false) {
|
| + DCHECK(client_);
|
| +}
|
| +
|
| +GestureEventQueue::~GestureEventQueue() {}
|
| +
|
| +void GestureEventQueue::OnGestureEventPacket(const GestureEventPacket& packet) {
|
| + switch (packet.gesture_source()) {
|
| + case GestureEventPacket::TOUCH_BEGIN:
|
| + sequences_.push_back(GestureSequence());
|
| + break;
|
| +
|
| + case GestureEventPacket::TOUCH:
|
| + break;
|
| +
|
| + case GestureEventPacket::TOUCH_TIMEOUT:
|
| + // Handle the timeout packet immediately if the packet preceding the
|
| + // timeout has already been dispatched.
|
| + if (Tail().IsEmpty()) {
|
| + if (Tail().IsGestureAllowed())
|
| + SendPacket(packet);
|
| + return;
|
| + }
|
| + break;
|
| +
|
| + case GestureEventPacket::SYNTHETIC:
|
| + // Handle the synthetic packet immediately if no other packets pending.
|
| + if (sequences_.empty() || Tail().IsEmpty()) {
|
| + SendPacket(packet);
|
| + return;
|
| + }
|
| + // Otherwise, queue the packet and process it in-order, irrespective
|
| + // of the touch sequence disposition.
|
| + break;
|
| + }
|
| +
|
| + Tail().Push(packet);
|
| +}
|
| +
|
| +void GestureEventQueue::OnTouchEventAck(InputEventAckState ack_state) {
|
| + if (Head().IsEmpty()) {
|
| + CancelTapIfNecessary();
|
| + CancelFlingIfNecessary();
|
| + sequences_.pop_front();
|
| + }
|
| +
|
| + Head().OnTouchEventAck(ack_state, packet_sender_);
|
| +
|
| + // Immediately cancel a TapDown if TouchStart went unconsumed, but a
|
| + // subsequent TouchMove is consumed.
|
| + if (!Head().IsGestureAllowed())
|
| + CancelTapIfNecessary();
|
| +}
|
| +
|
| +void GestureEventQueue::SendPacket(const GestureEventPacket& packet) {
|
| + for (size_t i = 0; i < packet.gesture_count(); ++i)
|
| + SendGesture(packet.gesture(i));
|
| +}
|
| +
|
| +void GestureEventQueue::SendGesture(const WebGestureEvent& event) {
|
| + switch (event.type) {
|
| + case WebInputEvent::GestureScrollBegin:
|
| + case WebInputEvent::GestureLongTap:
|
| + CancelTapIfNecessary();
|
| + break;
|
| + case WebInputEvent::GestureTapDown:
|
| + outstanding_tap_ = true;
|
| + break;
|
| + case WebInputEvent::GestureTapCancel:
|
| + case WebInputEvent::GestureTap:
|
| + case WebInputEvent::GestureTapUnconfirmed:
|
| + case WebInputEvent::GestureDoubleTap:
|
| + outstanding_tap_ = false;
|
| + break;
|
| + case WebInputEvent::GestureFlingStart:
|
| + outstanding_fling_ = true;
|
| + break;
|
| + case WebInputEvent::GestureFlingCancel:
|
| + outstanding_fling_ = false;
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| + client_->ForwardGestureEvent(event);
|
| +}
|
| +
|
| +void GestureEventQueue::CancelTapIfNecessary() {
|
| + if (!outstanding_tap_)
|
| + return;
|
| +
|
| + SendGesture(CreateGesture(WebInputEvent::GestureTapCancel));
|
| + DCHECK(!outstanding_tap_);
|
| +}
|
| +
|
| +void GestureEventQueue::CancelFlingIfNecessary() {
|
| + if (!outstanding_fling_)
|
| + return;
|
| +
|
| + SendGesture(CreateGesture(WebInputEvent::GestureFlingCancel));
|
| + DCHECK(!outstanding_fling_);
|
| +}
|
| +
|
| +GestureEventQueue::GestureSequence& GestureEventQueue::Head() {
|
| + DCHECK(!sequences_.empty());
|
| + return sequences_.front();
|
| +}
|
| +
|
| +GestureEventQueue::GestureSequence& GestureEventQueue::Tail() {
|
| + DCHECK(!sequences_.empty());
|
| + return sequences_.back();
|
| +}
|
| +
|
| +
|
| +GestureEventQueue::GestureSequence::GestureSequence()
|
| + : ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
|
| +
|
| +GestureEventQueue::GestureSequence::~GestureSequence() {}
|
| +
|
| +void GestureEventQueue::GestureSequence::Push(
|
| + const GestureEventPacket& packet) {
|
| + if (packet.gesture_source() == GestureEventPacket::TOUCH_TIMEOUT ||
|
| + packet.gesture_source() == GestureEventPacket::SYNTHETIC) {
|
| + // Timeout-derived gestures should have been forwarded or dropped
|
| + // immediately if the sequence was empty.
|
| + DCHECK(!IsEmpty());
|
| + }
|
| + sequence_.push_back(packet);
|
| +}
|
| +
|
| +void GestureEventQueue::GestureSequence::OnTouchEventAck(
|
| + InputEventAckState new_ack_state,
|
| + const PacketSender& packet_sender) {
|
| + InputEventAckState old_ack_state = ack_state_;
|
| + ack_state_ = GetMostRestrictiveState(old_ack_state, new_ack_state);
|
| +
|
| + bool packet_for_current_ack_handled = false;
|
| + while (!IsEmpty()) {
|
| + const GestureEventPacket& packet = sequence_.front();
|
| +
|
| + switch (packet.gesture_source()) {
|
| + case GestureEventPacket::TOUCH_BEGIN:
|
| + case GestureEventPacket::TOUCH:
|
| + // We should handle at most one packet corresponding to a given ack.
|
| + if (packet_for_current_ack_handled)
|
| + return;
|
| + packet_for_current_ack_handled = true;
|
| +
|
| + if (IsGestureAllowed())
|
| + packet_sender.Run(packet);
|
| + break;
|
| +
|
| + case GestureEventPacket::TOUCH_TIMEOUT:
|
| + if (IsGestureAllowed())
|
| + packet_sender.Run(packet);
|
| + break;
|
| +
|
| + case GestureEventPacket::SYNTHETIC:
|
| + // Synthetic gestures should be dispatched regardless of the touch
|
| + // sequence disposition.
|
| + packet_sender.Run(packet);
|
| + break;
|
| + };
|
| +
|
| + sequence_.pop_front();
|
| + }
|
| + DCHECK(packet_for_current_ack_handled);
|
| +}
|
| +
|
| +bool GestureEventQueue::GestureSequence::IsGestureAllowed() const {
|
| + return ack_state_ == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS ||
|
| + ack_state_ == INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
|
| +}
|
| +
|
| +bool GestureEventQueue::GestureSequence::IsEmpty() const {
|
| + return sequence_.empty();
|
| +}
|
| +
|
| +} // namespace content
|
|
|