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