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 |