1 // Copyright 2014 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 "ui/events/gesture_detection/touch_disposition_gesture_filter.h"
6
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "ui/events/gesture_event_details.h"
10
11 namespace ui {
12 namespace {
13
14 // A BitSet32 is used for tracking dropped gesture types.
15 COMPILE_ASSERT(ET_GESTURE_TYPE_END - ET_GESTURE_TYPE_START < 32,
16 gesture_type_count_too_large);
17
CreateGesture(EventType type,int motion_event_id,const GestureEventDataPacket & packet)18 GestureEventData CreateGesture(EventType type,
19 int motion_event_id,
20 const GestureEventDataPacket& packet) {
21 return GestureEventData(GestureEventDetails(type, 0, 0),
22 motion_event_id,
23 packet.timestamp(),
24 packet.touch_location().x(),
25 packet.touch_location().y(),
26 packet.raw_touch_location().x(),
27 packet.raw_touch_location().y(),
28 1,
29 gfx::RectF(packet.touch_location(), gfx::SizeF()));
30 }
31
32 enum RequiredTouches {
33 RT_NONE = 0,
34 RT_START = 1 << 0,
35 RT_CURRENT = 1 << 1,
36 };
37
38 struct DispositionHandlingInfo {
39 // A bitwise-OR of |RequiredTouches|.
40 int required_touches;
41 EventType antecedent_event_type;
42
DispositionHandlingInfoui::__anon957c65da0111::DispositionHandlingInfo43 explicit DispositionHandlingInfo(int required_touches)
44 : required_touches(required_touches), antecedent_event_type(ET_UNKNOWN) {}
45
DispositionHandlingInfoui::__anon957c65da0111::DispositionHandlingInfo46 DispositionHandlingInfo(int required_touches,
47 EventType antecedent_event_type)
48 : required_touches(required_touches),
49 antecedent_event_type(antecedent_event_type) {}
50 };
51
Info(int required_touches)52 DispositionHandlingInfo Info(int required_touches) {
53 return DispositionHandlingInfo(required_touches);
54 }
55
Info(int required_touches,EventType antecedent_event_type)56 DispositionHandlingInfo Info(int required_touches,
57 EventType antecedent_event_type) {
58 return DispositionHandlingInfo(required_touches, antecedent_event_type);
59 }
60
61 // This approach to disposition handling is described at http://goo.gl/5G8PWJ.
GetDispositionHandlingInfo(EventType type)62 DispositionHandlingInfo GetDispositionHandlingInfo(EventType type) {
63 switch (type) {
64 case ET_GESTURE_TAP_DOWN:
65 return Info(RT_START);
66 case ET_GESTURE_TAP_CANCEL:
67 return Info(RT_START);
68 case ET_GESTURE_SHOW_PRESS:
69 return Info(RT_START);
70 case ET_GESTURE_LONG_PRESS:
71 return Info(RT_START);
72 case ET_GESTURE_LONG_TAP:
73 return Info(RT_START | RT_CURRENT);
74 case ET_GESTURE_TAP:
75 return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
76 case ET_GESTURE_TAP_UNCONFIRMED:
77 return Info(RT_START | RT_CURRENT);
78 case ET_GESTURE_DOUBLE_TAP:
79 return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
80 case ET_GESTURE_SCROLL_BEGIN:
81 return Info(RT_START | RT_CURRENT);
82 case ET_GESTURE_SCROLL_UPDATE:
83 return Info(RT_CURRENT, ET_GESTURE_SCROLL_BEGIN);
84 case ET_GESTURE_SCROLL_END:
85 return Info(RT_NONE, ET_GESTURE_SCROLL_BEGIN);
86 case ET_SCROLL_FLING_START:
87 // We rely on |EndScrollGestureIfNecessary| to end the scroll if the fling
88 // start is prevented.
89 return Info(RT_NONE, ET_GESTURE_SCROLL_UPDATE);
90 case ET_SCROLL_FLING_CANCEL:
91 return Info(RT_NONE, ET_SCROLL_FLING_START);
92 case ET_GESTURE_PINCH_BEGIN:
93 return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
94 case ET_GESTURE_PINCH_UPDATE:
95 return Info(RT_CURRENT, ET_GESTURE_PINCH_BEGIN);
96 case ET_GESTURE_PINCH_END:
97 return Info(RT_NONE, ET_GESTURE_PINCH_BEGIN);
98 case ET_GESTURE_BEGIN:
99 return Info(RT_START);
100 case ET_GESTURE_END:
101 return Info(RT_NONE, ET_GESTURE_BEGIN);
102 case ET_GESTURE_SWIPE:
103 return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
104 case ET_GESTURE_TWO_FINGER_TAP:
105 return Info(RT_START);
106 default:
107 break;
108 }
109 NOTREACHED();
110 return Info(RT_NONE);
111 }
112
GetGestureTypeIndex(EventType type)113 int GetGestureTypeIndex(EventType type) {
114 DCHECK_GE(type, ET_GESTURE_TYPE_START);
115 DCHECK_LE(type, ET_GESTURE_TYPE_END);
116 return type - ET_GESTURE_TYPE_START;
117 }
118
IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source)119 bool IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source) {
120 return gesture_source == GestureEventDataPacket::TOUCH_SEQUENCE_START ||
121 gesture_source == GestureEventDataPacket::TOUCH_START;
122 }
123
124 } // namespace
125
126 // TouchDispositionGestureFilter
127
TouchDispositionGestureFilter(TouchDispositionGestureFilterClient * client)128 TouchDispositionGestureFilter::TouchDispositionGestureFilter(
129 TouchDispositionGestureFilterClient* client)
130 : client_(client),
131 needs_tap_ending_event_(false),
132 needs_show_press_event_(false),
133 needs_fling_ending_event_(false),
134 needs_scroll_ending_event_(false) {
135 DCHECK(client_);
136 }
137
~TouchDispositionGestureFilter()138 TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {
139 }
140
141 TouchDispositionGestureFilter::PacketResult
OnGesturePacket(const GestureEventDataPacket & packet)142 TouchDispositionGestureFilter::OnGesturePacket(
143 const GestureEventDataPacket& packet) {
144 if (packet.gesture_source() == GestureEventDataPacket::UNDEFINED ||
145 packet.gesture_source() == GestureEventDataPacket::INVALID)
146 return INVALID_PACKET_TYPE;
147
148 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START)
149 sequences_.push(GestureSequence());
150
151 if (IsEmpty())
152 return INVALID_PACKET_ORDER;
153
154 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
155 Tail().empty()) {
156 // Handle the timeout packet immediately if the packet preceding the timeout
157 // has already been dispatched.
158 FilterAndSendPacket(packet);
159 return SUCCESS;
160 }
161
162 Tail().push(packet);
163 return SUCCESS;
164 }
165
OnTouchEventAck(bool event_consumed)166 void TouchDispositionGestureFilter::OnTouchEventAck(bool event_consumed) {
167 // Spurious touch acks from the renderer should not trigger a crash.
168 if (IsEmpty() || (Head().empty() && sequences_.size() == 1))
169 return;
170
171 if (Head().empty())
172 PopGestureSequence();
173
174 GestureSequence& sequence = Head();
175
176 // Dispatch the packet corresponding to the ack'ed touch, as well as any
177 // additional timeout-based packets queued before the ack was received.
178 bool touch_packet_for_current_ack_handled = false;
179 while (!sequence.empty()) {
180 DCHECK_NE(sequence.front().gesture_source(),
181 GestureEventDataPacket::UNDEFINED);
182 DCHECK_NE(sequence.front().gesture_source(),
183 GestureEventDataPacket::INVALID);
184
185 GestureEventDataPacket::GestureSource source =
186 sequence.front().gesture_source();
187 if (source != GestureEventDataPacket::TOUCH_TIMEOUT) {
188 // We should handle at most one non-timeout based packet.
189 if (touch_packet_for_current_ack_handled)
190 break;
191 state_.OnTouchEventAck(event_consumed, IsTouchStartEvent(source));
192 touch_packet_for_current_ack_handled = true;
193 }
194 // We need to pop the current sequence before sending the packet, because
195 // sending the packet could result in this method being re-entered (e.g. on
196 // Aura, we could trigger a touch-cancel). As popping the sequence destroys
197 // the packet, we copy the packet before popping it.
198 const GestureEventDataPacket packet = sequence.front();
199 sequence.pop();
200 FilterAndSendPacket(packet);
201 }
202 DCHECK(touch_packet_for_current_ack_handled);
203 }
204
IsEmpty() const205 bool TouchDispositionGestureFilter::IsEmpty() const {
206 return sequences_.empty();
207 }
208
FilterAndSendPacket(const GestureEventDataPacket & packet)209 void TouchDispositionGestureFilter::FilterAndSendPacket(
210 const GestureEventDataPacket& packet) {
211 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) {
212 CancelTapIfNecessary(packet);
213 EndScrollIfNecessary(packet);
214 CancelFlingIfNecessary(packet);
215 } else if (packet.gesture_source() == GestureEventDataPacket::TOUCH_START) {
216 CancelTapIfNecessary(packet);
217 }
218
219 for (size_t i = 0; i < packet.gesture_count(); ++i) {
220 const GestureEventData& gesture = packet.gesture(i);
221 DCHECK_GE(gesture.details.type(), ET_GESTURE_TYPE_START);
222 DCHECK_LE(gesture.details.type(), ET_GESTURE_TYPE_END);
223 if (state_.Filter(gesture.details.type())) {
224 CancelTapIfNecessary(packet);
225 continue;
226 }
227 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT) {
228 // Sending a timed gesture could delete |this|, so we need to return
229 // directly after the |SendGesture| call.
230 SendGesture(gesture, packet);
231 return;
232 }
233
234 SendGesture(gesture, packet);
235 }
236
237 if (packet.gesture_source() ==
238 GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL) {
239 EndScrollIfNecessary(packet);
240 CancelTapIfNecessary(packet);
241 } else if (packet.gesture_source() ==
242 GestureEventDataPacket::TOUCH_SEQUENCE_END) {
243 EndScrollIfNecessary(packet);
244 }
245 }
246
SendGesture(const GestureEventData & event,const GestureEventDataPacket & packet_being_sent)247 void TouchDispositionGestureFilter::SendGesture(
248 const GestureEventData& event,
249 const GestureEventDataPacket& packet_being_sent) {
250 // TODO(jdduke): Factor out gesture stream reparation code into a standalone
251 // utility class.
252 switch (event.type()) {
253 case ET_GESTURE_LONG_TAP:
254 if (!needs_tap_ending_event_)
255 return;
256 CancelTapIfNecessary(packet_being_sent);
257 CancelFlingIfNecessary(packet_being_sent);
258 break;
259 case ET_GESTURE_TAP_DOWN:
260 DCHECK(!needs_tap_ending_event_);
261 ending_event_motion_event_id_ = event.motion_event_id;
262 needs_show_press_event_ = true;
263 needs_tap_ending_event_ = true;
264 break;
265 case ET_GESTURE_SHOW_PRESS:
266 if (!needs_show_press_event_)
267 return;
268 needs_show_press_event_ = false;
269 break;
270 case ET_GESTURE_DOUBLE_TAP:
271 CancelTapIfNecessary(packet_being_sent);
272 needs_show_press_event_ = false;
273 break;
274 case ET_GESTURE_TAP:
275 DCHECK(needs_tap_ending_event_);
276 if (needs_show_press_event_) {
277 SendGesture(GestureEventData(ET_GESTURE_SHOW_PRESS, event),
278 packet_being_sent);
279 DCHECK(!needs_show_press_event_);
280 }
281 needs_tap_ending_event_ = false;
282 break;
283 case ET_GESTURE_TAP_CANCEL:
284 needs_show_press_event_ = false;
285 needs_tap_ending_event_ = false;
286 break;
287 case ET_GESTURE_SCROLL_BEGIN:
288 CancelTapIfNecessary(packet_being_sent);
289 CancelFlingIfNecessary(packet_being_sent);
290 EndScrollIfNecessary(packet_being_sent);
291 ending_event_motion_event_id_ = event.motion_event_id;
292 needs_scroll_ending_event_ = true;
293 break;
294 case ET_GESTURE_SCROLL_END:
295 needs_scroll_ending_event_ = false;
296 break;
297 case ET_SCROLL_FLING_START:
298 CancelFlingIfNecessary(packet_being_sent);
299 ending_event_motion_event_id_ = event.motion_event_id;
300 needs_fling_ending_event_ = true;
301 needs_scroll_ending_event_ = false;
302 break;
303 case ET_SCROLL_FLING_CANCEL:
304 needs_fling_ending_event_ = false;
305 break;
306 default:
307 break;
308 }
309 client_->ForwardGestureEvent(event);
310 }
311
CancelTapIfNecessary(const GestureEventDataPacket & packet_being_sent)312 void TouchDispositionGestureFilter::CancelTapIfNecessary(
313 const GestureEventDataPacket& packet_being_sent) {
314 if (!needs_tap_ending_event_)
315 return;
316
317 SendGesture(CreateGesture(ET_GESTURE_TAP_CANCEL,
318 ending_event_motion_event_id_,
319 packet_being_sent),
320 packet_being_sent);
321 DCHECK(!needs_tap_ending_event_);
322 }
323
CancelFlingIfNecessary(const GestureEventDataPacket & packet_being_sent)324 void TouchDispositionGestureFilter::CancelFlingIfNecessary(
325 const GestureEventDataPacket& packet_being_sent) {
326 if (!needs_fling_ending_event_)
327 return;
328
329 SendGesture(CreateGesture(ET_SCROLL_FLING_CANCEL,
330 ending_event_motion_event_id_,
331 packet_being_sent),
332 packet_being_sent);
333 DCHECK(!needs_fling_ending_event_);
334 }
335
EndScrollIfNecessary(const GestureEventDataPacket & packet_being_sent)336 void TouchDispositionGestureFilter::EndScrollIfNecessary(
337 const GestureEventDataPacket& packet_being_sent) {
338 if (!needs_scroll_ending_event_)
339 return;
340
341 SendGesture(CreateGesture(ET_GESTURE_SCROLL_END,
342 ending_event_motion_event_id_,
343 packet_being_sent),
344 packet_being_sent);
345 DCHECK(!needs_scroll_ending_event_);
346 }
347
PopGestureSequence()348 void TouchDispositionGestureFilter::PopGestureSequence() {
349 DCHECK(Head().empty());
350 state_ = GestureHandlingState();
351 sequences_.pop();
352 }
353
354 TouchDispositionGestureFilter::GestureSequence&
Head()355 TouchDispositionGestureFilter::Head() {
356 DCHECK(!sequences_.empty());
357 return sequences_.front();
358 }
359
360 TouchDispositionGestureFilter::GestureSequence&
Tail()361 TouchDispositionGestureFilter::Tail() {
362 DCHECK(!sequences_.empty());
363 return sequences_.back();
364 }
365
366 // TouchDispositionGestureFilter::GestureHandlingState
367
GestureHandlingState()368 TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
369 : start_touch_consumed_(false),
370 current_touch_consumed_(false) {}
371
OnTouchEventAck(bool event_consumed,bool is_touch_start_event)372 void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck(
373 bool event_consumed,
374 bool is_touch_start_event) {
375 current_touch_consumed_ = event_consumed;
376 if (event_consumed && is_touch_start_event)
377 start_touch_consumed_ = true;
378 }
379
Filter(EventType gesture_type)380 bool TouchDispositionGestureFilter::GestureHandlingState::Filter(
381 EventType gesture_type) {
382 DispositionHandlingInfo disposition_handling_info =
383 GetDispositionHandlingInfo(gesture_type);
384
385 int required_touches = disposition_handling_info.required_touches;
386 EventType antecedent_event_type =
387 disposition_handling_info.antecedent_event_type;
388 if ((required_touches & RT_START && start_touch_consumed_) ||
389 (required_touches & RT_CURRENT && current_touch_consumed_) ||
390 (antecedent_event_type != ET_UNKNOWN &&
391 last_gesture_of_type_dropped_.has_bit(
392 GetGestureTypeIndex(antecedent_event_type)))) {
393 last_gesture_of_type_dropped_.mark_bit(GetGestureTypeIndex(gesture_type));
394 return true;
395 }
396 last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type));
397 return false;
398 }
399
400 } // namespace content
401