| 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..abac1131749d6da80664264ba78007d7a673a0bf
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/gesture_event_queue.cc
|
| @@ -0,0 +1,216 @@
|
| +// 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"
|
| +
|
| +using blink::WebGestureEvent;
|
| +using blink::WebInputEvent;
|
| +using blink::WebTouchEvent;
|
| +using blink::WebTouchPoint;
|
| +
|
| +namespace content {
|
| +namespace {
|
| +
|
| +WebGestureEvent CreateGesture(WebInputEvent::Type type) {
|
| + DCHECK(WebInputEvent::isGestureEventType(type));
|
| + WebGestureEvent event;
|
| + event.type = type;
|
| + event.sourceDevice = WebGestureEvent::Touchscreen;
|
| + return event;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// GestureEventQueue
|
| +
|
| +GestureEventQueue::GestureEventQueue(GestureEventQueueClient* client)
|
| + : client_(client),
|
| + needs_tap_ending_event_(false),
|
| + needs_fling_ending_event_(false) {
|
| + DCHECK(client_);
|
| +}
|
| +
|
| +GestureEventQueue::~GestureEventQueue() {}
|
| +
|
| +void GestureEventQueue::OnGestureEventPacket(const GestureEventPacket& packet) {
|
| + switch (packet.gesture_source()) {
|
| + case GestureEventPacket::TOUCH_BEGIN:
|
| + sequences_.push(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().IsGesturePrevented())
|
| + SendPacket(packet);
|
| + return;
|
| + }
|
| + break;
|
| +
|
| + case GestureEventPacket::INVALID:
|
| + NOTREACHED() << "Invalid gesture packet detected.";
|
| + break;
|
| + }
|
| +
|
| + Tail().Push(packet);
|
| +}
|
| +
|
| +void GestureEventQueue::OnTouchEventAck(InputEventAckState ack_state) {
|
| + if (Head().IsEmpty()) {
|
| + CancelTapIfNecessary();
|
| + CancelFlingIfNecessary();
|
| + sequences_.pop();
|
| + }
|
| +
|
| + GestureSequence& sequence = Head();
|
| + sequence.UpdateState(ack_state);
|
| +
|
| + // Dispatch the packet corresponding to the ack'ed touch, as well as any
|
| + // additional timeout-based packets queued before the ack was received.
|
| + bool touch_packet_for_current_ack_handled = false;
|
| + while (!sequence.IsEmpty()) {
|
| + const GestureEventPacket& packet = sequence.Front();
|
| +
|
| + if (packet.gesture_source() == GestureEventPacket::TOUCH_BEGIN ||
|
| + packet.gesture_source() == GestureEventPacket::TOUCH) {
|
| + // We should handle at most one touch-based packet corresponding to a
|
| + // given ack.
|
| + if (touch_packet_for_current_ack_handled)
|
| + break;
|
| + touch_packet_for_current_ack_handled = true;
|
| + }
|
| +
|
| + if (!sequence.IsGesturePrevented())
|
| + SendPacket(packet);
|
| +
|
| + sequence.Pop();
|
| + }
|
| + DCHECK(touch_packet_for_current_ack_handled);
|
| +
|
| + // Immediately cancel a TapDown if TouchStart went unconsumed, but a
|
| + // subsequent TouchMove is consumed.
|
| + if (sequence.IsGesturePrevented())
|
| + 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::GestureLongTap:
|
| + CancelTapIfNecessary();
|
| + CancelFlingIfNecessary();
|
| + break;
|
| + case WebInputEvent::GestureTapDown:
|
| + needs_tap_ending_event_ = true;
|
| + break;
|
| + case WebInputEvent::GestureTapCancel:
|
| + case WebInputEvent::GestureTap:
|
| + case WebInputEvent::GestureTapUnconfirmed:
|
| + case WebInputEvent::GestureDoubleTap:
|
| + needs_tap_ending_event_ = false;
|
| + break;
|
| + case WebInputEvent::GestureScrollBegin:
|
| + CancelTapIfNecessary();
|
| + CancelFlingIfNecessary();
|
| + break;
|
| + case WebInputEvent::GestureFlingStart:
|
| + CancelFlingIfNecessary();
|
| + needs_fling_ending_event_ = true;
|
| + break;
|
| + case WebInputEvent::GestureFlingCancel:
|
| + needs_fling_ending_event_ = false;
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| + client_->ForwardGestureEvent(event);
|
| +}
|
| +
|
| +void GestureEventQueue::CancelTapIfNecessary() {
|
| + if (!needs_tap_ending_event_)
|
| + return;
|
| +
|
| + SendGesture(CreateGesture(WebInputEvent::GestureTapCancel));
|
| + DCHECK(!needs_tap_ending_event_);
|
| +}
|
| +
|
| +void GestureEventQueue::CancelFlingIfNecessary() {
|
| + if (!needs_fling_ending_event_)
|
| + return;
|
| +
|
| + SendGesture(CreateGesture(WebInputEvent::GestureFlingCancel));
|
| + DCHECK(!needs_fling_ending_event_);
|
| +}
|
| +
|
| +GestureEventQueue::GestureSequence& GestureEventQueue::Head() {
|
| + DCHECK(!sequences_.empty());
|
| + return sequences_.front();
|
| +}
|
| +
|
| +GestureEventQueue::GestureSequence& GestureEventQueue::Tail() {
|
| + DCHECK(!sequences_.empty());
|
| + return sequences_.back();
|
| +}
|
| +
|
| +
|
| +// GestureEventQueue::GestureSequence
|
| +
|
| +GestureEventQueue::GestureSequence::GestureSequence()
|
| + : state_(PENDING) {}
|
| +
|
| +GestureEventQueue::GestureSequence::~GestureSequence() {}
|
| +
|
| +void GestureEventQueue::GestureSequence::Push(
|
| + const GestureEventPacket& packet) {
|
| + packets_.push(packet);
|
| +}
|
| +
|
| +void GestureEventQueue::GestureSequence::Pop() {
|
| + DCHECK(!IsEmpty());
|
| + packets_.pop();
|
| +}
|
| +
|
| +const GestureEventPacket& GestureEventQueue::GestureSequence::Front() const {
|
| + DCHECK(!IsEmpty());
|
| + return packets_.front();
|
| +}
|
| +
|
| +void GestureEventQueue::GestureSequence::UpdateState(
|
| + InputEventAckState ack_state) {
|
| + DCHECK_NE(INPUT_EVENT_ACK_STATE_UNKNOWN, ack_state);
|
| + // Permanent states will not be affected by subsequent ack's.
|
| + if (state_ != PENDING && state_ != ALLOWED_UNTIL_PREVENTED)
|
| + return;
|
| +
|
| + // |NO_CONSUMER| should only be effective when the *first* touch is ack'ed.
|
| + if (state_ == PENDING &&
|
| + ack_state == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
|
| + state_ = ALWAYS_ALLOWED;
|
| + else if (ack_state == INPUT_EVENT_ACK_STATE_CONSUMED)
|
| + state_ = ALWAYS_PREVENTED;
|
| + else
|
| + state_ = ALLOWED_UNTIL_PREVENTED;
|
| +}
|
| +
|
| +bool GestureEventQueue::GestureSequence::IsGesturePrevented() const {
|
| + return state_ == ALWAYS_PREVENTED;
|
| +}
|
| +
|
| +bool GestureEventQueue::GestureSequence::IsEmpty() const {
|
| + return packets_.empty();
|
| +}
|
| +
|
| +} // namespace content
|
|
|