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