touch_disposition_gesture_filter.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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
11namespace ui {
12namespace {
13
14// A BitSet32 is used for tracking dropped gesture types.
15COMPILE_ASSERT(ET_GESTURE_TYPE_END - ET_GESTURE_TYPE_START < 32,
16               gesture_type_count_too_large);
17
18GestureEventData 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
32enum RequiredTouches {
33  RT_NONE = 0,
34  RT_START = 1 << 0,
35  RT_CURRENT = 1 << 1,
36};
37
38struct DispositionHandlingInfo {
39  // A bitwise-OR of |RequiredTouches|.
40  int required_touches;
41  EventType antecedent_event_type;
42
43  explicit DispositionHandlingInfo(int required_touches)
44      : required_touches(required_touches), antecedent_event_type(ET_UNKNOWN) {}
45
46  DispositionHandlingInfo(int required_touches,
47                          EventType antecedent_event_type)
48      : required_touches(required_touches),
49        antecedent_event_type(antecedent_event_type) {}
50};
51
52DispositionHandlingInfo Info(int required_touches) {
53  return DispositionHandlingInfo(required_touches);
54}
55
56DispositionHandlingInfo 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.
62DispositionHandlingInfo 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);
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
113int 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
119bool 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
128TouchDispositionGestureFilter::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
138TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {
139}
140
141TouchDispositionGestureFilter::PacketResult
142TouchDispositionGestureFilter::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
166void 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
205bool TouchDispositionGestureFilter::IsEmpty() const {
206  return sequences_.empty();
207}
208
209void 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
247void 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
312void 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
324void 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
336void 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
348void TouchDispositionGestureFilter::PopGestureSequence() {
349  DCHECK(Head().empty());
350  state_ = GestureHandlingState();
351  sequences_.pop();
352}
353
354TouchDispositionGestureFilter::GestureSequence&
355TouchDispositionGestureFilter::Head() {
356  DCHECK(!sequences_.empty());
357  return sequences_.front();
358}
359
360TouchDispositionGestureFilter::GestureSequence&
361TouchDispositionGestureFilter::Tail() {
362  DCHECK(!sequences_.empty());
363  return sequences_.back();
364}
365
366// TouchDispositionGestureFilter::GestureHandlingState
367
368TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
369    : start_touch_consumed_(false),
370      current_touch_consumed_(false) {}
371
372void 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
380bool 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