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