touch_disposition_gesture_filter.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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 base::TimeTicks& timestamp, 21 const gfx::PointF& location) { 22 GestureEventDetails details(type, 0, 0); 23 return GestureEventData(details, 24 motion_event_id, 25 timestamp, 26 location.x(), 27 location.y(), 28 1, 29 gfx::RectF(location.x(), location.y(), 0, 0)); 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 | RT_CURRENT); 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 packet_being_sent_(NULL) { 136 DCHECK(client_); 137} 138 139TouchDispositionGestureFilter::~TouchDispositionGestureFilter() { 140 DCHECK(!packet_being_sent_); 141} 142 143TouchDispositionGestureFilter::PacketResult 144TouchDispositionGestureFilter::OnGesturePacket( 145 const GestureEventDataPacket& packet) { 146 if (packet.gesture_source() == GestureEventDataPacket::UNDEFINED || 147 packet.gesture_source() == GestureEventDataPacket::INVALID) 148 return INVALID_PACKET_TYPE; 149 150 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) 151 sequences_.push(GestureSequence()); 152 153 if (IsEmpty()) 154 return INVALID_PACKET_ORDER; 155 156 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT && 157 Tail().empty()) { 158 // Handle the timeout packet immediately if the packet preceding the timeout 159 // has already been dispatched. 160 FilterAndSendPacket(packet); 161 return SUCCESS; 162 } 163 164 Tail().push(packet); 165 return SUCCESS; 166} 167 168void TouchDispositionGestureFilter::OnTouchEventAck(bool event_consumed) { 169 // Spurious touch acks from the renderer should not trigger a crash. 170 if (IsEmpty() || (Head().empty() && sequences_.size() == 1)) 171 return; 172 173 if (Head().empty()) 174 PopGestureSequence(); 175 176 GestureSequence& sequence = Head(); 177 178 // Dispatch the packet corresponding to the ack'ed touch, as well as any 179 // additional timeout-based packets queued before the ack was received. 180 bool touch_packet_for_current_ack_handled = false; 181 while (!sequence.empty()) { 182 DCHECK_NE(sequence.front().gesture_source(), 183 GestureEventDataPacket::UNDEFINED); 184 DCHECK_NE(sequence.front().gesture_source(), 185 GestureEventDataPacket::INVALID); 186 187 GestureEventDataPacket::GestureSource source = 188 sequence.front().gesture_source(); 189 if (source != GestureEventDataPacket::TOUCH_TIMEOUT) { 190 // We should handle at most one non-timeout based packet. 191 if (touch_packet_for_current_ack_handled) 192 break; 193 state_.OnTouchEventAck(event_consumed, IsTouchStartEvent(source)); 194 touch_packet_for_current_ack_handled = true; 195 } 196 // We need to pop the current sequence before sending the packet, because 197 // sending the packet could result in this method being re-entered (e.g. on 198 // Aura, we could trigger a touch-cancel). As popping the sequence destroys 199 // the packet, we copy the packet before popping it. 200 const GestureEventDataPacket packet = sequence.front(); 201 sequence.pop(); 202 FilterAndSendPacket(packet); 203 } 204 DCHECK(touch_packet_for_current_ack_handled); 205} 206 207bool TouchDispositionGestureFilter::IsEmpty() const { 208 return sequences_.empty(); 209} 210 211void TouchDispositionGestureFilter::FilterAndSendPacket( 212 const GestureEventDataPacket& packet) { 213 base::AutoReset<const GestureEventDataPacket*> packet_being_sent( 214 &packet_being_sent_, &packet); 215 if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) { 216 CancelTapIfNecessary(); 217 EndScrollIfNecessary(); 218 CancelFlingIfNecessary(); 219 } else if (packet.gesture_source() == GestureEventDataPacket::TOUCH_START) { 220 CancelTapIfNecessary(); 221 } 222 223 for (size_t i = 0; i < packet.gesture_count(); ++i) { 224 const GestureEventData& gesture = packet.gesture(i); 225 DCHECK_GE(gesture.details.type(), ET_GESTURE_TYPE_START); 226 DCHECK_LE(gesture.details.type(), ET_GESTURE_TYPE_END); 227 if (state_.Filter(gesture.details.type())) { 228 CancelTapIfNecessary(); 229 continue; 230 } 231 SendGesture(gesture); 232 } 233 234 if (packet.gesture_source() == 235 GestureEventDataPacket::TOUCH_SEQUENCE_CANCEL) { 236 EndScrollIfNecessary(); 237 CancelTapIfNecessary(); 238 } else if (packet.gesture_source() == 239 GestureEventDataPacket::TOUCH_SEQUENCE_END) { 240 EndScrollIfNecessary(); 241 } 242} 243 244void TouchDispositionGestureFilter::SendGesture(const GestureEventData& event) { 245 // TODO(jdduke): Factor out gesture stream reparation code into a standalone 246 // utility class. 247 switch (event.type()) { 248 case ET_GESTURE_LONG_TAP: 249 if (!needs_tap_ending_event_) 250 return; 251 CancelTapIfNecessary(); 252 CancelFlingIfNecessary(); 253 break; 254 case ET_GESTURE_TAP_DOWN: 255 DCHECK(!needs_tap_ending_event_); 256 ending_event_motion_event_id_ = event.motion_event_id; 257 needs_show_press_event_ = true; 258 needs_tap_ending_event_ = true; 259 break; 260 case ET_GESTURE_SHOW_PRESS: 261 if (!needs_show_press_event_) 262 return; 263 needs_show_press_event_ = false; 264 break; 265 case ET_GESTURE_DOUBLE_TAP: 266 CancelTapIfNecessary(); 267 needs_show_press_event_ = false; 268 break; 269 case ET_GESTURE_TAP: 270 DCHECK(needs_tap_ending_event_); 271 if (needs_show_press_event_) { 272 GestureEventData show_press_event(ET_GESTURE_SHOW_PRESS, 273 event.motion_event_id, 274 event.time, 275 event.x, 276 event.y, 277 event.details.touch_points(), 278 event.details.bounding_box_f()); 279 SendGesture(show_press_event); 280 DCHECK(!needs_show_press_event_); 281 } 282 needs_tap_ending_event_ = false; 283 break; 284 case ET_GESTURE_TAP_CANCEL: 285 needs_show_press_event_ = false; 286 needs_tap_ending_event_ = false; 287 break; 288 case ET_GESTURE_SCROLL_BEGIN: 289 CancelTapIfNecessary(); 290 CancelFlingIfNecessary(); 291 EndScrollIfNecessary(); 292 ending_event_motion_event_id_ = event.motion_event_id; 293 needs_scroll_ending_event_ = true; 294 break; 295 case ET_GESTURE_SCROLL_END: 296 needs_scroll_ending_event_ = false; 297 break; 298 case ET_SCROLL_FLING_START: 299 CancelFlingIfNecessary(); 300 ending_event_motion_event_id_ = event.motion_event_id; 301 needs_fling_ending_event_ = true; 302 needs_scroll_ending_event_ = false; 303 break; 304 case ET_SCROLL_FLING_CANCEL: 305 needs_fling_ending_event_ = false; 306 break; 307 default: 308 break; 309 } 310 client_->ForwardGestureEvent(event); 311} 312 313void TouchDispositionGestureFilter::CancelTapIfNecessary() { 314 DCHECK(packet_being_sent_); 315 if (!needs_tap_ending_event_ || !packet_being_sent_) 316 return; 317 318 SendGesture(CreateGesture(ET_GESTURE_TAP_CANCEL, 319 ending_event_motion_event_id_, 320 packet_being_sent_->timestamp(), 321 packet_being_sent_->touch_location())); 322 DCHECK(!needs_tap_ending_event_); 323} 324 325void TouchDispositionGestureFilter::CancelFlingIfNecessary() { 326 DCHECK(packet_being_sent_); 327 if (!needs_fling_ending_event_ || !packet_being_sent_) 328 return; 329 330 SendGesture(CreateGesture(ET_SCROLL_FLING_CANCEL, 331 ending_event_motion_event_id_, 332 packet_being_sent_->timestamp(), 333 packet_being_sent_->touch_location())); 334 DCHECK(!needs_fling_ending_event_); 335} 336 337void TouchDispositionGestureFilter::EndScrollIfNecessary() { 338 DCHECK(packet_being_sent_); 339 if (!needs_scroll_ending_event_ || !packet_being_sent_) 340 return; 341 342 SendGesture(CreateGesture(ET_GESTURE_SCROLL_END, 343 ending_event_motion_event_id_, 344 packet_being_sent_->timestamp(), 345 packet_being_sent_->touch_location())); 346 DCHECK(!needs_scroll_ending_event_); 347} 348 349void TouchDispositionGestureFilter::PopGestureSequence() { 350 DCHECK(Head().empty()); 351 state_ = GestureHandlingState(); 352 sequences_.pop(); 353} 354 355TouchDispositionGestureFilter::GestureSequence& 356TouchDispositionGestureFilter::Head() { 357 DCHECK(!sequences_.empty()); 358 return sequences_.front(); 359} 360 361TouchDispositionGestureFilter::GestureSequence& 362TouchDispositionGestureFilter::Tail() { 363 DCHECK(!sequences_.empty()); 364 return sequences_.back(); 365} 366 367// TouchDispositionGestureFilter::GestureHandlingState 368 369TouchDispositionGestureFilter::GestureHandlingState::GestureHandlingState() 370 : start_touch_consumed_(false), 371 current_touch_consumed_(false) {} 372 373void TouchDispositionGestureFilter::GestureHandlingState::OnTouchEventAck( 374 bool event_consumed, 375 bool is_touch_start_event) { 376 current_touch_consumed_ = event_consumed; 377 if (event_consumed && is_touch_start_event) 378 start_touch_consumed_ = true; 379} 380 381bool TouchDispositionGestureFilter::GestureHandlingState::Filter( 382 EventType gesture_type) { 383 DispositionHandlingInfo disposition_handling_info = 384 GetDispositionHandlingInfo(gesture_type); 385 386 int required_touches = disposition_handling_info.required_touches; 387 EventType antecedent_event_type = 388 disposition_handling_info.antecedent_event_type; 389 if ((required_touches & RT_START && start_touch_consumed_) || 390 (required_touches & RT_CURRENT && current_touch_consumed_) || 391 (antecedent_event_type != ET_UNKNOWN && 392 last_gesture_of_type_dropped_.has_bit( 393 GetGestureTypeIndex(antecedent_event_type)))) { 394 last_gesture_of_type_dropped_.mark_bit(GetGestureTypeIndex(gesture_type)); 395 return true; 396 } 397 last_gesture_of_type_dropped_.clear_bit(GetGestureTypeIndex(gesture_type)); 398 return false; 399} 400 401} // namespace content 402