| 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..8250d651830eb0efa0162c21881ba167cd553482
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/gesture_event_queue.cc
|
| @@ -0,0 +1,220 @@
|
| +// 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),
|
| + packet_sender_(base::Bind(&GestureEventQueue::SendPacket,
|
| + base::Unretained(this))),
|
| + 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) {
|
| + DCHECK(!sequences_.empty());
|
| + if (Head().IsEmpty()) {
|
| + CancelTapIfNecessary();
|
| + CancelFlingIfNecessary();
|
| + sequences_.pop();
|
| + }
|
| +
|
| + DCHECK(!sequences_.empty());
|
| + Head().OnTouchEventAck(ack_state, packet_sender_);
|
| +
|
| + // Immediately cancel a TapDown if TouchStart went unconsumed, but a
|
| + // subsequent TouchMove is consumed.
|
| + if (Head().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) {
|
| + // Timeout-derived gestures should have been forwarded or dropped
|
| + // immediately if the sequence was empty.
|
| + DCHECK(packet.gesture_source() != GestureEventPacket::TOUCH_TIMEOUT ||
|
| + !IsEmpty());
|
| + sequence_.push(packet);
|
| +}
|
| +
|
| +void GestureEventQueue::GestureSequence::OnTouchEventAck(
|
| + InputEventAckState ack_state,
|
| + const PacketSender& packet_sender) {
|
| + DCHECK_NE(INPUT_EVENT_ACK_STATE_UNKNOWN, ack_state);
|
| +
|
| + if (state_ == PENDING || state_ == ALLOWED_UNTIL_PREVENTED) {
|
| + // |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 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 (!IsGesturePrevented())
|
| + packet_sender.Run(packet);
|
| + break;
|
| +
|
| + case GestureEventPacket::TOUCH_TIMEOUT:
|
| + if (!IsGesturePrevented())
|
| + packet_sender.Run(packet);
|
| + break;
|
| +
|
| + case GestureEventPacket::INVALID:
|
| + NOTREACHED() << "Invalid gesture packet in sequence.";
|
| + };
|
| +
|
| + sequence_.pop();
|
| + }
|
| + DCHECK(packet_for_current_ack_handled);
|
| +}
|
| +
|
| +bool GestureEventQueue::GestureSequence::IsGesturePrevented() const {
|
| + return state_ == ALWAYS_PREVENTED;
|
| +}
|
| +
|
| +bool GestureEventQueue::GestureSequence::IsEmpty() const {
|
| + return sequence_.empty();
|
| +}
|
| +
|
| +} // namespace content
|
|
|