touch_disposition_gesture_filter.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
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  return GestureEventData(type,
21                          motion_event_id,
22                          base::TimeTicks(),
23                          0,
24                          0,
25                          1,
26                          GestureEventDetails(type, 0, 0));
27}
28
29enum RequiredTouches {
30  RT_NONE = 0,
31  RT_START = 1 << 0,
32  RT_CURRENT = 1 << 1,
33};
34
35struct DispositionHandlingInfo {
36  // A bitwise-OR of |RequiredTouches|.
37  int required_touches;
38  EventType antecedent_event_type;
39
40  DispositionHandlingInfo(int required_touches)
41      : required_touches(required_touches) {}
42
43  DispositionHandlingInfo(int required_touches,
44                          EventType antecedent_event_type)
45      : required_touches(required_touches),
46        antecedent_event_type(antecedent_event_type) {}
47};
48
49DispositionHandlingInfo Info(int required_touches) {
50  return DispositionHandlingInfo(required_touches);
51}
52
53DispositionHandlingInfo Info(int required_touches,
54                             EventType antecedent_event_type) {
55  return DispositionHandlingInfo(required_touches, antecedent_event_type);
56}
57
58// This approach to disposition handling is described at http://goo.gl/5G8PWJ.
59DispositionHandlingInfo GetDispositionHandlingInfo(EventType type) {
60  switch (type) {
61    case ET_GESTURE_TAP_DOWN:
62      return Info(RT_START);
63    case ET_GESTURE_TAP_CANCEL:
64      return Info(RT_START);
65    case ET_GESTURE_SHOW_PRESS:
66      return Info(RT_START);
67    case ET_GESTURE_LONG_PRESS:
68      return Info(RT_START);
69    case ET_GESTURE_LONG_TAP:
70      return Info(RT_START | RT_CURRENT);
71    case ET_GESTURE_TAP:
72      return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
73    case ET_GESTURE_TAP_UNCONFIRMED:
74      return Info(RT_START | RT_CURRENT);
75    case ET_GESTURE_DOUBLE_TAP:
76      return Info(RT_START | RT_CURRENT, ET_GESTURE_TAP_UNCONFIRMED);
77    case ET_GESTURE_SCROLL_BEGIN:
78      return Info(RT_START | RT_CURRENT);
79    case ET_GESTURE_SCROLL_UPDATE:
80      return Info(RT_CURRENT, ET_GESTURE_SCROLL_BEGIN);
81    case ET_GESTURE_SCROLL_END:
82      return Info(RT_NONE, ET_GESTURE_SCROLL_BEGIN);
83    case ET_SCROLL_FLING_START:
84      // We rely on |EndScrollGestureIfNecessary| to end the scroll if the fling
85      // start is prevented.
86      return Info(RT_NONE, ET_GESTURE_SCROLL_UPDATE);
87    case ET_SCROLL_FLING_CANCEL:
88      return Info(RT_NONE, ET_SCROLL_FLING_START);
89    case ET_GESTURE_PINCH_BEGIN:
90      return Info(RT_START, ET_GESTURE_SCROLL_BEGIN);
91    case ET_GESTURE_PINCH_UPDATE:
92      return Info(RT_CURRENT, ET_GESTURE_PINCH_BEGIN);
93    case ET_GESTURE_PINCH_END:
94      return Info(RT_NONE, ET_GESTURE_PINCH_BEGIN);
95    case ET_GESTURE_BEGIN:
96      return Info(RT_START);
97    case ET_GESTURE_END:
98      return Info(RT_NONE, ET_GESTURE_BEGIN);
99    default:
100      break;
101  }
102  NOTREACHED();
103  return Info(RT_NONE);
104}
105
106int GetGestureTypeIndex(EventType type) {
107  return type - ET_GESTURE_TYPE_START;
108}
109
110bool IsTouchStartEvent(GestureEventDataPacket::GestureSource gesture_source) {
111  return gesture_source == GestureEventDataPacket::TOUCH_SEQUENCE_START ||
112         gesture_source == GestureEventDataPacket::TOUCH_START;
113}
114
115}  // namespace
116
117// TouchDispositionGestureFilter
118
119TouchDispositionGestureFilter::TouchDispositionGestureFilter(
120    TouchDispositionGestureFilterClient* client)
121    : client_(client),
122      needs_tap_ending_event_(false),
123      needs_show_press_event_(false),
124      needs_fling_ending_event_(false),
125      needs_scroll_ending_event_(false) {
126  DCHECK(client_);
127}
128
129TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {}
130
131TouchDispositionGestureFilter::PacketResult
132TouchDispositionGestureFilter::OnGesturePacket(
133    const GestureEventDataPacket& packet) {
134  if (packet.gesture_source() == GestureEventDataPacket::UNDEFINED ||
135      packet.gesture_source() == GestureEventDataPacket::INVALID)
136    return INVALID_PACKET_TYPE;
137
138  if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START)
139    sequences_.push(GestureSequence());
140
141  if (IsEmpty())
142    return INVALID_PACKET_ORDER;
143
144  if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT &&
145      Tail().empty()) {
146    // Handle the timeout packet immediately if the packet preceding the timeout
147    // has already been dispatched.
148    FilterAndSendPacket(packet);
149    return SUCCESS;
150  }
151
152  Tail().push(packet);
153  return SUCCESS;
154}
155
156void TouchDispositionGestureFilter::OnTouchEventAck(bool event_consumed) {
157  // Spurious touch acks from the renderer should not trigger a crash.
158  if (IsEmpty() || (Head().empty() && sequences_.size() == 1))
159    return;
160
161  if (Head().empty())
162    PopGestureSequence();
163
164  GestureSequence& sequence = Head();
165
166  // Dispatch the packet corresponding to the ack'ed touch, as well as any
167  // additional timeout-based packets queued before the ack was received.
168  bool touch_packet_for_current_ack_handled = false;
169  while (!sequence.empty()) {
170    const GestureEventDataPacket& packet = sequence.front();
171    DCHECK_NE(packet.gesture_source(), GestureEventDataPacket::UNDEFINED);
172    DCHECK_NE(packet.gesture_source(), GestureEventDataPacket::INVALID);
173
174    GestureEventDataPacket::GestureSource source = packet.gesture_source();
175    if (source != GestureEventDataPacket::TOUCH_TIMEOUT) {
176      // We should handle at most one non-timeout based packet.
177      if (touch_packet_for_current_ack_handled)
178        break;
179      state_.OnTouchEventAck(event_consumed,
180                             IsTouchStartEvent(packet.gesture_source()));
181      touch_packet_for_current_ack_handled = true;
182    }
183    FilterAndSendPacket(packet);
184    sequence.pop();
185  }
186  DCHECK(touch_packet_for_current_ack_handled);
187}
188
189bool TouchDispositionGestureFilter::IsEmpty() const {
190  return sequences_.empty();
191}
192
193void TouchDispositionGestureFilter::FilterAndSendPacket(
194    const GestureEventDataPacket& packet) {
195  for (size_t i = 0; i < packet.gesture_count(); ++i) {
196    const GestureEventData& gesture = packet.gesture(i);
197    DCHECK(ET_GESTURE_TYPE_START <= gesture.type &&
198           gesture.type <= ET_GESTURE_TYPE_END);
199    if (state_.Filter(gesture.type)) {
200      CancelTapIfNecessary();
201      continue;
202    }
203    SendGesture(gesture);
204  }
205  if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_END)
206    EndScrollIfNecessary();
207}
208
209void TouchDispositionGestureFilter::SendGesture(const GestureEventData& event) {
210  // TODO(jdduke): Factor out gesture stream reparation code into a standalone
211  // utility class.
212  switch (event.type) {
213    case ET_GESTURE_LONG_TAP:
214      if (!needs_tap_ending_event_)
215        return;
216      CancelTapIfNecessary();
217      CancelFlingIfNecessary();
218      break;
219    case ET_GESTURE_TAP_DOWN:
220      DCHECK(!needs_tap_ending_event_);
221      ending_event_motion_event_id_ = event.motion_event_id;
222      needs_show_press_event_ = true;
223      needs_tap_ending_event_ = true;
224      break;
225    case ET_GESTURE_SHOW_PRESS:
226      if (!needs_show_press_event_)
227        return;
228      needs_show_press_event_ = false;
229      break;
230    case ET_GESTURE_DOUBLE_TAP:
231      CancelTapIfNecessary();
232      needs_show_press_event_ = false;
233      break;
234    case ET_GESTURE_TAP:
235      if (needs_show_press_event_) {
236        GestureEventData show_press_event(event);
237        show_press_event.type = ET_GESTURE_SHOW_PRESS;
238        SendGesture(show_press_event);
239        DCHECK(!needs_show_press_event_);
240      }
241      needs_tap_ending_event_ = false;
242      break;
243    case ET_GESTURE_TAP_CANCEL:
244      needs_show_press_event_ = false;
245      needs_tap_ending_event_ = false;
246      break;
247    case ET_GESTURE_SCROLL_BEGIN:
248      CancelTapIfNecessary();
249      CancelFlingIfNecessary();
250      EndScrollIfNecessary();
251      ending_event_motion_event_id_ = event.motion_event_id;
252      needs_scroll_ending_event_ = true;
253      break;
254    case ET_GESTURE_SCROLL_END:
255      needs_scroll_ending_event_ = false;
256      break;
257    case ET_SCROLL_FLING_START:
258      CancelFlingIfNecessary();
259      ending_event_motion_event_id_ = event.motion_event_id;
260      needs_fling_ending_event_ = true;
261      needs_scroll_ending_event_ = false;
262      break;
263    case ET_SCROLL_FLING_CANCEL:
264      needs_fling_ending_event_ = false;
265      break;
266    default:
267      break;
268  }
269  client_->ForwardGestureEvent(event);
270}
271
272void TouchDispositionGestureFilter::CancelTapIfNecessary() {
273  if (!needs_tap_ending_event_)
274    return;
275
276  SendGesture(
277      CreateGesture(ET_GESTURE_TAP_CANCEL, ending_event_motion_event_id_));
278  DCHECK(!needs_tap_ending_event_);
279}
280
281void TouchDispositionGestureFilter::CancelFlingIfNecessary() {
282  if (!needs_fling_ending_event_)
283    return;
284
285  SendGesture(
286      CreateGesture(ET_SCROLL_FLING_CANCEL, ending_event_motion_event_id_));
287  DCHECK(!needs_fling_ending_event_);
288}
289
290void TouchDispositionGestureFilter::EndScrollIfNecessary() {
291  if (!needs_scroll_ending_event_)
292    return;
293
294  SendGesture(
295      CreateGesture(ET_GESTURE_SCROLL_END, ending_event_motion_event_id_));
296  DCHECK(!needs_scroll_ending_event_);
297}
298
299void TouchDispositionGestureFilter::PopGestureSequence() {
300  DCHECK(Head().empty());
301  CancelTapIfNecessary();
302  CancelFlingIfNecessary();
303  EndScrollIfNecessary();
304  state_ = GestureHandlingState();
305  sequences_.pop();
306}
307
308TouchDispositionGestureFilter::GestureSequence&
309TouchDispositionGestureFilter::Head() {
310  DCHECK(!sequences_.empty());
311  return sequences_.front();
312}
313
314TouchDispositionGestureFilter::GestureSequence&
315TouchDispositionGestureFilter::Tail() {
316  DCHECK(!sequences_.empty());
317  return sequences_.back();
318}
319
320// TouchDispositionGestureFilter::GestureHandlingState
321
322TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
323    : start_touch_consumed_(false),
324      current_touch_consumed_(false) {}
325
326void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck(
327    bool event_consumed,
328    bool is_touch_start_event) {
329  current_touch_consumed_ = event_consumed;
330  if (event_consumed && is_touch_start_event)
331    start_touch_consumed_ = true;
332}
333
334bool TouchDispositionGestureFilter::GestureHandlingState::Filter(
335    EventType gesture_type) {
336  DispositionHandlingInfo disposition_handling_info =
337      GetDispositionHandlingInfo(gesture_type);
338
339  int required_touches = disposition_handling_info.required_touches;
340  if ((required_touches & RT_START && start_touch_consumed_) ||
341      (required_touches & RT_CURRENT && current_touch_consumed_) ||
342      (last_gesture_of_type_dropped_.has_bit(GetGestureTypeIndex(
343          disposition_handling_info.antecedent_event_type)))) {
344    last_gesture_of_type_dropped_.mark_bit(GetGestureTypeIndex(gesture_type));
345    return true;
346  }
347  last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type));
348  return false;
349}
350
351}  // namespace content
352