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.
5 #include "ui/events/gesture_detection/touch_disposition_gesture_filter.h"
7 #include "base/auto_reset.h"
8 #include "base/logging.h"
9 #include "ui/events/gesture_event_details.h"
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);
18 GestureEventData CreateGesture(EventType type,
20 MotionEvent::ToolType primary_tool_type,
21 const GestureEventDataPacket& packet) {
22 return GestureEventData(GestureEventDetails(type, 0, 0),
26 packet.touch_location().x(),
27 packet.touch_location().y(),
28 packet.raw_touch_location().x(),
29 packet.raw_touch_location().y(),
31 gfx::RectF(packet.touch_location(), gfx::SizeF()));
34 enum RequiredTouches {
40 struct DispositionHandlingInfo {
41 // A bitwise-OR of |RequiredTouches|.
43 EventType antecedent_event_type;
45 explicit DispositionHandlingInfo(int required_touches)
46 : required_touches(required_touches), antecedent_event_type(ET_UNKNOWN) {}
48 DispositionHandlingInfo(int required_touches,
49 EventType antecedent_event_type)
50 : required_touches(required_touches),
51 antecedent_event_type(antecedent_event_type) {}
54 DispositionHandlingInfo Info(int required_touches) {
55 return DispositionHandlingInfo(required_touches);
58 DispositionHandlingInfo Info(int required_touches,
59 EventType antecedent_event_type) {
60 return DispositionHandlingInfo(required_touches, antecedent_event_type);
63 // This approach to disposition handling is described at http://goo.gl/5G8PWJ.
64 DispositionHandlingInfo GetDispositionHandlingInfo(EventType type) {
66 case ET_GESTURE_TAP_DOWN:
67 return Info(RT_START);
68 case ET_GESTURE_TAP_CANCEL:
69 return Info(RT_START);
70 case ET_GESTURE_SHOW_PRESS:
71 return Info(RT_START);
72 case ET_GESTURE_LONG_PRESS:
73 return Info(RT_START);
74 case ET_GESTURE_LONG_TAP:
75 return Info(RT_START | RT_CURRENT);
77 return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
78 case ET_GESTURE_TAP_UNCONFIRMED:
79 return Info(RT_START | RT_CURRENT);
80 case ET_GESTURE_DOUBLE_TAP:
81 return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
82 case ET_GESTURE_SCROLL_BEGIN:
83 return Info(RT_START);
84 case ET_GESTURE_SCROLL_UPDATE:
85 return Info(RT_CURRENT, ET_GESTURE_SCROLL_BEGIN);
86 case ET_GESTURE_SCROLL_END:
87 return Info(RT_NONE, ET_GESTURE_SCROLL_BEGIN);
88 case ET_SCROLL_FLING_START:
89 // We rely on |EndScrollGestureIfNecessary| to end the scroll if the fling
90 // start is prevented.
91 return Info(RT_NONE, ET_GESTURE_SCROLL_UPDATE);
92 case ET_SCROLL_FLING_CANCEL:
93 return Info(RT_NONE, ET_SCROLL_FLING_START);
94 case ET_GESTURE_PINCH_BEGIN:
95 return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
96 case ET_GESTURE_PINCH_UPDATE:
97 return Info(RT_CURRENT, ET_GESTURE_PINCH_BEGIN);
98 case ET_GESTURE_PINCH_END:
99 return Info(RT_NONE, ET_GESTURE_PINCH_BEGIN);
100 case ET_GESTURE_BEGIN:
101 return Info(RT_START);
103 return Info(RT_NONE, ET_GESTURE_BEGIN);
104 case ET_GESTURE_SWIPE:
105 return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
106 case ET_GESTURE_TWO_FINGER_TAP:
107 return Info(RT_START);
112 return Info(RT_NONE);
115 int GetGestureTypeIndex(EventType type) {
116 DCHECK_GE(type, ET_GESTURE_TYPE_START);
117 DCHECK_LE(type, ET_GESTURE_TYPE_END);
118 return type - ET_GESTURE_TYPE_START;
121 bool IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source) {
122 return gesture_source == GestureEventDataPacket::TOUCH_SEQUENCE_START ||
123 gesture_source == GestureEventDataPacket::TOUCH_START;
128 // TouchDispositionGestureFilter
130 TouchDispositionGestureFilter::TouchDispositionGestureFilter(
131 TouchDispositionGestureFilterClient* client)
133 ending_event_motion_event_id_(0),
134 ending_event_primary_tool_type_(MotionEvent::TOOL_TYPE_UNKNOWN),
135 needs_tap_ending_event_(false),
136 needs_show_press_event_(false),
137 needs_fling_ending_event_(false),
138 needs_scroll_ending_event_(false) {
142 TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {
145 TouchDispositionGestureFilter::PacketResult
146 TouchDispositionGestureFilter::OnGesturePacket(
147 const GestureEventDataPacket& packet) {
148 if (packet.gesture_source() == GestureEventDataPacket::UNDEFINED ||
149 packet.gesture_source() == GestureEventDataPacket::INVALID)
150 return INVALID_PACKET_TYPE;
152 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START)
153 sequences_.push(GestureSequence());
156 return INVALID_PACKET_ORDER;
158 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
160 // Handle the timeout packet immediately if the packet preceding the timeout
161 // has already been dispatched.
162 FilterAndSendPacket(packet);
170 void TouchDispositionGestureFilter::OnTouchEventAck(bool event_consumed) {
171 // Spurious touch acks from the renderer should not trigger a crash.
172 if (IsEmpty() || (Head().empty() && sequences_.size() == 1))
176 PopGestureSequence();
178 GestureSequence& sequence = Head();
180 // Dispatch the packet corresponding to the ack'ed touch, as well as any
181 // additional timeout-based packets queued before the ack was received.
182 bool touch_packet_for_current_ack_handled = false;
183 while (!sequence.empty()) {
184 DCHECK_NE(sequence.front().gesture_source(),
185 GestureEventDataPacket::UNDEFINED);
186 DCHECK_NE(sequence.front().gesture_source(),
187 GestureEventDataPacket::INVALID);
189 GestureEventDataPacket::GestureSource source =
190 sequence.front().gesture_source();
191 if (source != GestureEventDataPacket::TOUCH_TIMEOUT) {
192 // We should handle at most one non-timeout based packet.
193 if (touch_packet_for_current_ack_handled)
195 state_.OnTouchEventAck(event_consumed, IsTouchStartEvent(source));
196 touch_packet_for_current_ack_handled = true;
198 // We need to pop the current sequence before sending the packet, because
199 // sending the packet could result in this method being re-entered (e.g. on
200 // Aura, we could trigger a touch-cancel). As popping the sequence destroys
201 // the packet, we copy the packet before popping it.
202 const GestureEventDataPacket packet = sequence.front();
204 FilterAndSendPacket(packet);
206 DCHECK(touch_packet_for_current_ack_handled);
209 bool TouchDispositionGestureFilter::IsEmpty() const {
210 return sequences_.empty();
213 void TouchDispositionGestureFilter::FilterAndSendPacket(
214 const GestureEventDataPacket& packet) {
215 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) {
216 CancelTapIfNecessary(packet);
217 EndScrollIfNecessary(packet);
218 CancelFlingIfNecessary(packet);
219 } else if (packet.gesture_source() == GestureEventDataPacket::TOUCH_START) {
220 CancelTapIfNecessary(packet);
223 for (size_t i = 0; i < packet.gesture_count(); ++i) {
224 const GestureEventData& gesture = packet.gesture(i);
225 DCHECK_GE(gesture.details.type(), ET_GESTURE_TYPE_START);
226 DCHECK_LE(gesture.details.type(), ET_GESTURE_TYPE_END);
227 if (state_.Filter(gesture.details.type())) {
228 CancelTapIfNecessary(packet);
231 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT) {
232 // Sending a timed gesture could delete |this|, so we need to return
233 // directly after the |SendGesture| call.
234 SendGesture(gesture, packet);
238 SendGesture(gesture, packet);
241 if (packet.gesture_source() ==
242 GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL) {
243 EndScrollIfNecessary(packet);
244 CancelTapIfNecessary(packet);
245 } else if (packet.gesture_source() ==
246 GestureEventDataPacket::TOUCH_SEQUENCE_END) {
247 EndScrollIfNecessary(packet);
251 void TouchDispositionGestureFilter::SendGesture(
252 const GestureEventData& event,
253 const GestureEventDataPacket& packet_being_sent) {
254 // TODO(jdduke): Factor out gesture stream reparation code into a standalone
256 switch (event.type()) {
257 case ET_GESTURE_LONG_TAP:
258 if (!needs_tap_ending_event_)
260 CancelTapIfNecessary(packet_being_sent);
261 CancelFlingIfNecessary(packet_being_sent);
263 case ET_GESTURE_TAP_DOWN:
264 DCHECK(!needs_tap_ending_event_);
265 ending_event_motion_event_id_ = event.motion_event_id;
266 ending_event_primary_tool_type_ = event.primary_tool_type;
267 needs_show_press_event_ = true;
268 needs_tap_ending_event_ = true;
270 case ET_GESTURE_SHOW_PRESS:
271 if (!needs_show_press_event_)
273 needs_show_press_event_ = false;
275 case ET_GESTURE_DOUBLE_TAP:
276 CancelTapIfNecessary(packet_being_sent);
277 needs_show_press_event_ = false;
280 DCHECK(needs_tap_ending_event_);
281 if (needs_show_press_event_) {
282 SendGesture(GestureEventData(ET_GESTURE_SHOW_PRESS, event),
284 DCHECK(!needs_show_press_event_);
286 needs_tap_ending_event_ = false;
288 case ET_GESTURE_TAP_CANCEL:
289 needs_show_press_event_ = false;
290 needs_tap_ending_event_ = false;
292 case ET_GESTURE_SCROLL_BEGIN:
293 CancelTapIfNecessary(packet_being_sent);
294 CancelFlingIfNecessary(packet_being_sent);
295 EndScrollIfNecessary(packet_being_sent);
296 ending_event_motion_event_id_ = event.motion_event_id;
297 ending_event_primary_tool_type_ = event.primary_tool_type;
298 needs_scroll_ending_event_ = true;
300 case ET_GESTURE_SCROLL_END:
301 needs_scroll_ending_event_ = false;
303 case ET_SCROLL_FLING_START:
304 CancelFlingIfNecessary(packet_being_sent);
305 ending_event_motion_event_id_ = event.motion_event_id;
306 ending_event_primary_tool_type_ = event.primary_tool_type;
307 needs_fling_ending_event_ = true;
308 needs_scroll_ending_event_ = false;
310 case ET_SCROLL_FLING_CANCEL:
311 needs_fling_ending_event_ = false;
316 client_->ForwardGestureEvent(event);
319 void TouchDispositionGestureFilter::CancelTapIfNecessary(
320 const GestureEventDataPacket& packet_being_sent) {
321 if (!needs_tap_ending_event_)
324 SendGesture(CreateGesture(ET_GESTURE_TAP_CANCEL,
325 ending_event_motion_event_id_,
326 ending_event_primary_tool_type_,
329 DCHECK(!needs_tap_ending_event_);
332 void TouchDispositionGestureFilter::CancelFlingIfNecessary(
333 const GestureEventDataPacket& packet_being_sent) {
334 if (!needs_fling_ending_event_)
337 SendGesture(CreateGesture(ET_SCROLL_FLING_CANCEL,
338 ending_event_motion_event_id_,
339 ending_event_primary_tool_type_,
342 DCHECK(!needs_fling_ending_event_);
345 void TouchDispositionGestureFilter::EndScrollIfNecessary(
346 const GestureEventDataPacket& packet_being_sent) {
347 if (!needs_scroll_ending_event_)
350 SendGesture(CreateGesture(ET_GESTURE_SCROLL_END,
351 ending_event_motion_event_id_,
352 ending_event_primary_tool_type_,
355 DCHECK(!needs_scroll_ending_event_);
358 void TouchDispositionGestureFilter::PopGestureSequence() {
359 DCHECK(Head().empty());
360 state_ = GestureHandlingState();
364 TouchDispositionGestureFilter::GestureSequence&
365 TouchDispositionGestureFilter::Head() {
366 DCHECK(!sequences_.empty());
367 return sequences_.front();
370 TouchDispositionGestureFilter::GestureSequence&
371 TouchDispositionGestureFilter::Tail() {
372 DCHECK(!sequences_.empty());
373 return sequences_.back();
376 // TouchDispositionGestureFilter::GestureHandlingState
378 TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
379 : start_touch_consumed_(false),
380 current_touch_consumed_(false) {}
382 void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck(
384 bool is_touch_start_event) {
385 current_touch_consumed_ = event_consumed;
386 if (event_consumed && is_touch_start_event)
387 start_touch_consumed_ = true;
390 bool TouchDispositionGestureFilter::GestureHandlingState::Filter(
391 EventType gesture_type) {
392 DispositionHandlingInfo disposition_handling_info =
393 GetDispositionHandlingInfo(gesture_type);
395 int required_touches = disposition_handling_info.required_touches;
396 EventType antecedent_event_type =
397 disposition_handling_info.antecedent_event_type;
398 if ((required_touches & RT_START && start_touch_consumed_) ||
399 (required_touches & RT_CURRENT && current_touch_consumed_) ||
400 (antecedent_event_type != ET_UNKNOWN &&
401 last_gesture_of_type_dropped_.has_bit(
402 GetGestureTypeIndex(antecedent_event_type)))) {
403 last_gesture_of_type_dropped_.mark_bit(GetGestureTypeIndex(gesture_type));
406 last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type));
410 } // namespace content