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 |