OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/renderer_host/input/touch_to_gesture_queue.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "base/logging.h" | |
9 #include "content/common/input/web_input_event_traits.h" | |
10 | |
11 using blink::WebGestureEvent; | |
12 using blink::WebInputEvent; | |
13 using blink::WebTouchEvent; | |
14 using blink::WebTouchPoint; | |
15 | |
16 namespace content { | |
17 namespace { | |
18 | |
19 bool IsTouchSequenceBegin(const WebTouchEvent& event) { | |
20 if (event.type != WebInputEvent::TouchStart) | |
21 return false; | |
22 if (!event.touchesLength) | |
23 return false; | |
24 for (size_t i = 0; i < event.touchesLength; i++) { | |
25 if (event.touches[i].state != WebTouchPoint::StatePressed) | |
26 return false; | |
27 } | |
28 return true; | |
29 } | |
30 | |
31 bool IsScrollStart(const WebGestureEvent& event) { | |
32 return event.type == WebInputEvent::GestureScrollBegin; | |
33 } | |
34 | |
35 bool IsFlingCancel(const WebGestureEvent& event) { | |
36 return event.type == WebInputEvent::GestureFlingCancel; | |
37 } | |
38 | |
39 bool IsFlingStart(const WebGestureEvent& event) { | |
40 return event.type == WebInputEvent::GestureFlingStart; | |
41 } | |
42 | |
43 bool IsTapStart(const WebGestureEvent& event) { | |
44 return event.type == WebInputEvent::GestureTapDown; | |
45 } | |
46 | |
47 bool IsTapEnd(const WebGestureEvent& event) { | |
48 switch (event.type) { | |
49 case WebInputEvent::GestureTapCancel: | |
50 case WebInputEvent::GestureTap: | |
51 case WebInputEvent::GestureTapUnconfirmed: | |
52 case WebInputEvent::GestureDoubleTap: | |
53 return true; | |
54 default: | |
55 break; | |
56 } | |
57 return false; | |
58 } | |
59 | |
60 bool IsTimeout(const WebGestureEvent& event) { | |
tdresser
2014/01/14 16:25:30
Perhaps IsTimeout -> IsTriggeredByTimeout?
jdduke (slow)
2014/01/14 23:24:03
Done.
| |
61 switch (event.type) { | |
62 case WebInputEvent::GestureShowPress: | |
63 case WebInputEvent::GestureLongPress: | |
64 case WebInputEvent::GestureTapUnconfirmed: | |
65 return true; | |
66 default: | |
67 break; | |
68 } | |
69 return false; | |
70 } | |
71 | |
72 InputEventAckState Intersection(InputEventAckState ack_old, | |
tdresser
2014/01/14 16:25:30
The word "Intersection" applied to an enum could b
jdduke (slow)
2014/01/14 23:24:03
Hmm, I'll noodle on this.
| |
73 InputEventAckState ack_new) { | |
74 if (ack_new == INPUT_EVENT_ACK_STATE_UNKNOWN) { | |
75 NOTREACHED() << "New acks should never be undefined."; | |
76 return ack_old; | |
77 } | |
78 | |
79 if (ack_old == INPUT_EVENT_ACK_STATE_UNKNOWN || | |
80 ack_old == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) | |
81 return ack_new; | |
82 | |
83 // If a partial touch sequence has been consumed, or has no consumer, | |
84 // subsequent touches have no bearing on the overall gesture disposition. | |
85 return ack_old; | |
86 } | |
87 | |
88 WebGestureEvent CreateGesture(WebInputEvent::Type type) { | |
89 DCHECK(WebInputEvent::isGestureEventType(type)); | |
90 WebGestureEvent event; | |
91 event.type = type; | |
92 event.sourceDevice = WebGestureEvent::Touchscreen; | |
93 return event; | |
94 } | |
95 | |
96 } // namespace | |
97 | |
98 TouchToGestureQueue::TouchToGestureQueue(TouchToGestureQueueClient* client) | |
99 : client_(client), | |
100 handling_touch_event_(false), | |
101 outstanding_tap_(false), | |
102 outstanding_fling_(false) { | |
103 DCHECK(client_); | |
104 } | |
105 | |
106 TouchToGestureQueue::~TouchToGestureQueue() {} | |
107 | |
108 void TouchToGestureQueue::OnTouchEventHandlingBegin( | |
109 const WebTouchEvent& event) { | |
110 DCHECK(!handling_touch_event_); | |
111 handling_touch_event_ = true; | |
112 | |
113 if (IsTouchSequenceBegin(event)) | |
114 sequences_.push_back(TouchToGestureSequence()); | |
115 | |
116 Tail().Push(event); | |
117 } | |
118 | |
119 void TouchToGestureQueue::OnTouchEventHandlingEnd() { | |
120 DCHECK(handling_touch_event_); | |
121 DCHECK(!sequences_.empty()); | |
122 | |
123 handling_touch_event_ = false; | |
124 client_->ForwardTouchEvent(Tail().LastTouch()); | |
125 } | |
126 | |
127 void TouchToGestureQueue::OnGestureEvent(const WebGestureEvent& event) { | |
128 if (handling_touch_event_) { | |
129 Tail().Push(event); | |
130 return; | |
131 } | |
132 | |
133 if (IsTimeout(event)) { | |
134 // Touches preceding the timeout event are still awaiting an ack. | |
135 if (!Tail().IsEmpty()) | |
136 Tail().Push(event); | |
137 // Touches preceding the timeout have been handled and gestures are allowed. | |
138 else if (Tail().IsGestureAllowed()) | |
139 ForwardGestureEvent(event); | |
140 // The timeout gesture should be dropped. | |
141 return; | |
142 } | |
143 | |
144 // Synthetic gestures may be generated by the platform, in which case they | |
145 // should simply be forwarded immediately. | |
146 ForwardGestureEvent(event); | |
147 } | |
148 | |
149 void TouchToGestureQueue::OnTouchEventAck(InputEventAckState ack_state) { | |
150 // Transition to the next touch sequence. This transition is deferred | |
151 // until the ack in the event that the sequence receives a timeout event. | |
152 if (Head().IsEmpty()) { | |
153 CancelTapIfNecessary(); | |
154 CancelFlingIfNecessary(); | |
155 sequences_.pop_front(); | |
156 } | |
157 | |
158 // TODO(jdduke): Settle on touch-slop disposition behavior, crbug/333947. | |
159 TouchToGesture touch_to_gesture = Head().OnTouchEventAck(ack_state); | |
160 for (size_t i = 0; i < touch_to_gesture.gestures.size(); ++i) | |
161 ForwardGestureEvent(touch_to_gesture.gestures[i]); | |
162 | |
163 // Immediately cancel a TapDown if TouchStart went unconsumed, but a | |
164 // subsequent TouchMove is consumed. | |
165 if (!Head().IsGestureAllowed()) | |
166 CancelTapIfNecessary(); | |
167 } | |
168 | |
169 void TouchToGestureQueue::ForwardGestureEvent(const WebGestureEvent& event) { | |
tdresser
2014/01/14 16:25:30
I don't think this is any more readable than a big
jdduke (slow)
2014/01/14 23:24:03
You're totally right, this originally started with
| |
170 if (IsTapStart(event)) | |
171 outstanding_tap_ = true; | |
172 else if (IsTapEnd(event)) | |
173 outstanding_tap_ = false; | |
174 else if (IsFlingStart(event)) | |
175 outstanding_fling_ = true; | |
176 else if (IsFlingCancel(event)) | |
177 outstanding_fling_ = false; | |
178 else if (IsScrollStart(event)) | |
179 CancelTapIfNecessary(); | |
180 | |
181 client_->ForwardGestureEvent(event); | |
182 } | |
183 | |
184 void TouchToGestureQueue::CancelTapIfNecessary() { | |
185 if (!outstanding_tap_) | |
186 return; | |
187 | |
188 ForwardGestureEvent(CreateGesture(WebInputEvent::GestureTapCancel)); | |
189 DCHECK(!outstanding_tap_); | |
190 } | |
191 | |
192 void TouchToGestureQueue::CancelFlingIfNecessary() { | |
193 if (!outstanding_fling_) | |
194 return; | |
195 | |
196 ForwardGestureEvent(CreateGesture(WebInputEvent::GestureFlingCancel)); | |
197 DCHECK(!outstanding_fling_); | |
198 } | |
199 | |
200 TouchToGestureQueue::TouchToGestureSequence& TouchToGestureQueue::Head() { | |
201 DCHECK(!sequences_.empty()); | |
202 return sequences_.front(); | |
203 } | |
204 | |
205 TouchToGestureQueue::TouchToGestureSequence& TouchToGestureQueue::Tail() { | |
206 DCHECK(!sequences_.empty()); | |
207 return sequences_.back(); | |
208 } | |
209 | |
210 TouchToGestureQueue::TouchToGestureSequence::TouchToGestureSequence() | |
211 : ack_state(INPUT_EVENT_ACK_STATE_UNKNOWN) {} | |
212 | |
213 TouchToGestureQueue::TouchToGestureSequence::~TouchToGestureSequence() {} | |
214 | |
215 void TouchToGestureQueue::TouchToGestureSequence::Push( | |
216 const blink::WebTouchEvent& event) { | |
217 TouchToGesture touch_to_gesture; | |
218 touch_to_gesture.touch = event; | |
219 sequence.push_back(touch_to_gesture); | |
220 } | |
221 | |
222 void TouchToGestureQueue::TouchToGestureSequence::Push( | |
223 const blink::WebGestureEvent& event) { | |
224 DCHECK(!IsEmpty()); | |
225 sequence.back().gestures.push_back(event); | |
226 } | |
227 | |
228 TouchToGestureQueue::TouchToGesture | |
229 TouchToGestureQueue::TouchToGestureSequence::OnTouchEventAck( | |
230 InputEventAckState new_ack_state) { | |
231 DCHECK(!IsEmpty()); | |
232 | |
233 ack_state = Intersection(ack_state, new_ack_state); | |
234 DCHECK_NE(ack_state, INPUT_EVENT_ACK_STATE_UNKNOWN); | |
235 | |
236 TouchToGesture touch_to_gesture = | |
237 IsGestureAllowed() ? sequence.front() : TouchToGesture(); | |
238 sequence.pop_front(); | |
239 return touch_to_gesture; | |
240 } | |
241 | |
242 bool TouchToGestureQueue::TouchToGestureSequence::IsGestureAllowed() const { | |
243 return ack_state == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS || | |
244 ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
245 } | |
246 | |
247 bool TouchToGestureQueue::TouchToGestureSequence::IsEmpty() const { | |
248 return sequence.empty(); | |
249 } | |
250 | |
251 const blink::WebTouchEvent& | |
252 TouchToGestureQueue::TouchToGestureSequence::LastTouch() const { | |
253 DCHECK(!IsEmpty()); | |
254 return sequence.back().touch; | |
255 } | |
256 | |
257 } // namespace content | |
OLD | NEW |