Home | History | Annotate | Download | only in gesture_detection
      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 
     18 GestureEventData 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 
     37 enum RequiredTouches {
     38   RT_NONE = 0,
     39   RT_START = 1 << 0,
     40   RT_CURRENT = 1 << 1,
     41 };
     42 
     43 struct 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 
     57 DispositionHandlingInfo Info(int required_touches) {
     58   return DispositionHandlingInfo(required_touches);
     59 }
     60 
     61 DispositionHandlingInfo 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.
     67 DispositionHandlingInfo 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 
    118 int 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 
    124 bool 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 
    133 TouchDispositionGestureFilter::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 
    145 TouchDispositionGestureFilter::~TouchDispositionGestureFilter() {
    146 }
    147 
    148 TouchDispositionGestureFilter::PacketResult
    149 TouchDispositionGestureFilter::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 
    173 void 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 
    212 bool TouchDispositionGestureFilter::IsEmpty() const {
    213   return sequences_.empty();
    214 }
    215 
    216 void 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 
    268 void 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 
    336 void 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 
    349 void 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 
    362 void 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 
    375 void TouchDispositionGestureFilter::PopGestureSequence() {
    376   DCHECK(Head().empty());
    377   state_ = GestureHandlingState();
    378   sequences_.pop();
    379 }
    380 
    381 TouchDispositionGestureFilter::GestureSequence&
    382 TouchDispositionGestureFilter::Head() {
    383   DCHECK(!sequences_.empty());
    384   return sequences_.front();
    385 }
    386 
    387 TouchDispositionGestureFilter::GestureSequence&
    388 TouchDispositionGestureFilter::Tail() {
    389   DCHECK(!sequences_.empty());
    390   return sequences_.back();
    391 }
    392 
    393 // TouchDispositionGestureFilter::GestureHandlingState
    394 
    395 TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState()
    396     : start_touch_consumed_(false),
    397       current_touch_consumed_(false) {}
    398 
    399 void 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 
    407 bool 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