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..d35f960de16399c73901c95787002891eafa3ac9 |
--- /dev/null |
+++ b/content/browser/renderer_host/input/gesture_event_queue.cc |
@@ -0,0 +1,198 @@ |
+// 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/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 TransitionToMostRestrictiveState( |
tdresser
2014/01/16 14:31:45
TransitionToMostRestrictiveState implies that this
jdduke (slow)
2014/01/16 17:17:53
Yeah, except the order matters (i.e., we never go
|
+ InputEventAckState ack_old, |
+ InputEventAckState ack_new) { |
+ if (ack_new == INPUT_EVENT_ACK_STATE_UNKNOWN) { |
+ NOTREACHED() << "New acks should never be undefined."; |
+ return ack_old; |
+ } |
+ |
+ if (ack_old == INPUT_EVENT_ACK_STATE_UNKNOWN || |
+ ack_old == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) |
+ return ack_new; |
+ |
+ // If a partial touch sequence has been consumed, or has no consumer, |
+ // subsequent touches have no bearing on the overall gesture disposition. |
+ 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), |
+ 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: |
+ // Touches preceding the timeout event are still awaiting an ack. |
+ if (!Tail().IsEmpty()) |
+ break; |
+ // Touches preceding the timeout were handled and gestures are allowed. |
+ if (Tail().IsGestureAllowed()) |
+ Forward(packet); |
+ // The timeout gesture should be dropped. |
+ return; |
+ case GestureEventPacket::SYNTHETIC: |
tdresser
2014/01/16 14:31:45
Couldn't this violate some ordering constraints?
jdduke (slow)
2014/01/16 17:17:53
Yeah, though on Android that constraint is already
tdresser
2014/01/16 18:28:29
SGTM.
|
+ Forward(packet); |
+ return; |
+ } |
+ |
+ Tail().Push(packet); |
+} |
+ |
+void GestureEventQueue::OnTouchEventAck(InputEventAckState ack_state) { |
+ // Transition to the next touch sequence. This transition is deferred |
+ // until the ack in the event that the sequence receives a timeout event. |
tdresser
2014/01/16 14:31:45
This is the only place where you're transitioning
jdduke (slow)
2014/01/16 17:17:53
It's a terrible comment. I was thinking of adding
|
+ if (Head().IsEmpty()) { |
+ CancelTapIfNecessary(); |
+ CancelFlingIfNecessary(); |
+ sequences_.pop_front(); |
+ } |
+ |
+ Forward(Head().OnTouchEventAck(ack_state)); |
+ |
+ // Immediately cancel a TapDown if TouchStart went unconsumed, but a |
+ // subsequent TouchMove is consumed. |
+ if (!Head().IsGestureAllowed()) |
+ CancelTapIfNecessary(); |
+} |
+ |
+void GestureEventQueue::Forward(const GestureEventPacket& packet) { |
+ for (size_t i = 0; i < packet.gesture_count(); ++i) |
+ Forward(packet.gesture(i)); |
+} |
+ |
+void GestureEventQueue::Forward(const WebGestureEvent& event) { |
+ switch (event.type) { |
+ case WebInputEvent::GestureScrollBegin: |
+ 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; |
+ |
+ Forward(CreateGesture(WebInputEvent::GestureTapCancel)); |
+ DCHECK(!outstanding_tap_); |
+} |
+ |
+void GestureEventQueue::CancelFlingIfNecessary() { |
+ if (!outstanding_fling_) |
+ return; |
+ |
+ Forward(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) { |
+ DCHECK_NE(GestureEventPacket::SYNTHETIC, packet.gesture_source()); |
+ if (packet.gesture_source() == GestureEventPacket::TOUCH_TIMEOUT) { |
+ // Timeout-derived gestures should have beeen forwarded or dropped |
tdresser
2014/01/16 14:31:45
beeen -> been
jdduke (slow)
2014/01/16 17:17:53
Done.
|
+ // immediately if the sequence was empty. |
+ DCHECK(!IsEmpty()); |
+ |
+ // Merge the timeout packet with the previous packet, simplifying dispatch |
+ // when the touch ack preceding the timeout is received. |
+ for (size_t i = 0; i < packet.gesture_count(); ++i) |
+ sequence_.back().Push(packet.gesture(i)); |
+ return; |
+ } |
+ sequence_.push_back(packet); |
+} |
+ |
+GestureEventPacket GestureEventQueue::GestureSequence::OnTouchEventAck( |
+ InputEventAckState new_ack_state) { |
+ DCHECK(!IsEmpty()); |
+ |
+ ack_state_ = TransitionToMostRestrictiveState(ack_state_, new_ack_state); |
+ DCHECK_NE(ack_state_, INPUT_EVENT_ACK_STATE_UNKNOWN); |
+ |
+ GestureEventPacket gestures = |
+ IsGestureAllowed() ? sequence_.front() : GestureEventPacket(); |
+ sequence_.pop_front(); |
+ return gestures; |
+} |
+ |
+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 |