OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/input/touch_event_queue.h" | 5 #include "content/browser/renderer_host/input/touch_event_queue.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
73 bool ConfirmTouchEvent(InputEventAckState ack_result) { | 73 bool ConfirmTouchEvent(InputEventAckState ack_result) { |
74 switch (pending_ack_state_) { | 74 switch (pending_ack_state_) { |
75 case PENDING_ACK_NONE: | 75 case PENDING_ACK_NONE: |
76 timeout_monitor_.Stop(); | 76 timeout_monitor_.Stop(); |
77 return false; | 77 return false; |
78 case PENDING_ACK_ORIGINAL_EVENT: | 78 case PENDING_ACK_ORIGINAL_EVENT: |
79 if (AckedTimeoutEventRequiresCancel(ack_result)) { | 79 if (AckedTimeoutEventRequiresCancel(ack_result)) { |
80 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); | 80 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); |
81 TouchEventWithLatencyInfo cancel_event = | 81 TouchEventWithLatencyInfo cancel_event = |
82 ObtainCancelEventForTouchEvent(timeout_event_); | 82 ObtainCancelEventForTouchEvent(timeout_event_); |
83 touch_queue_->UpdateTouchAckStates( | |
84 cancel_event.event, kDefaultNotForwardedAck); | |
85 touch_queue_->client_->SendTouchEventImmediately(cancel_event); | 83 touch_queue_->client_->SendTouchEventImmediately(cancel_event); |
86 } else { | 84 } else { |
87 SetPendingAckState(PENDING_ACK_NONE); | 85 SetPendingAckState(PENDING_ACK_NONE); |
88 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); | 86 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); |
89 } | 87 } |
90 return true; | 88 return true; |
91 case PENDING_ACK_CANCEL_EVENT: | 89 case PENDING_ACK_CANCEL_EVENT: |
92 SetPendingAckState(PENDING_ACK_NONE); | 90 SetPendingAckState(PENDING_ACK_NONE); |
93 return true; | 91 return true; |
94 } | 92 } |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
234 // when the event is acked. | 232 // when the event is acked. |
235 bool ignore_ack_; | 233 bool ignore_ack_; |
236 | 234 |
237 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); | 235 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); |
238 }; | 236 }; |
239 | 237 |
240 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client) | 238 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client) |
241 : client_(client), | 239 : client_(client), |
242 dispatching_touch_ack_(NULL), | 240 dispatching_touch_ack_(NULL), |
243 dispatching_touch_(false), | 241 dispatching_touch_(false), |
244 has_handlers_(false), | 242 touch_handling_state_(TOUCH_HANDLING_STATE_DEFAULT), |
245 scroll_in_progress_(false), | |
246 renderer_is_consuming_touch_gesture_(false), | |
247 ack_timeout_enabled_(false) { | 243 ack_timeout_enabled_(false) { |
248 DCHECK(client); | 244 DCHECK(client); |
249 } | 245 } |
250 | 246 |
251 TouchEventQueue::~TouchEventQueue() { | 247 TouchEventQueue::~TouchEventQueue() { |
252 if (!touch_queue_.empty()) | 248 if (!touch_queue_.empty()) |
253 STLDeleteElements(&touch_queue_); | 249 STLDeleteElements(&touch_queue_); |
254 } | 250 } |
255 | 251 |
256 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { | 252 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { |
257 // Optimization of the case without touch handlers. Removing this path | 253 // Optimization of the case without touch handlers. Removing this path |
258 // yields identical results, but this avoids unnecessary allocations. | 254 // yields identical results, but this avoids unnecessary allocations. |
259 if (!has_handlers_) { | 255 if (touch_handling_state_ == NO_TOUCH_HANDLER) { |
Rick Byers
2014/01/21 22:00:25
Shouldn't we be able to handle the NO_TOUCH_HANDLE
jdduke (slow)
2014/01/22 02:47:00
Hmm, we'd have to check if it's a new touch sequen
| |
260 DCHECK(touch_queue_.empty()); | 256 DCHECK(touch_queue_.empty()); |
261 client_->OnTouchEventAck(event, kDefaultNotForwardedAck); | 257 client_->OnTouchEventAck(event, kDefaultNotForwardedAck); |
262 return; | 258 return; |
263 } | 259 } |
264 | 260 |
265 // If the queueing of |event| was triggered by an ack dispatch, defer | 261 // If the queueing of |event| was triggered by an ack dispatch, defer |
266 // processing the event until the dispatch has finished. | 262 // processing the event until the dispatch has finished. |
267 if (touch_queue_.empty() && !dispatching_touch_ack_) { | 263 if (touch_queue_.empty() && !dispatching_touch_ack_) { |
268 // There is no touch event in the queue. Forward it to the renderer | 264 // There is no touch event in the queue. Forward it to the renderer |
269 // immediately. | 265 // immediately. |
(...skipping 17 matching lines...) Expand all Loading... | |
287 DCHECK(!dispatching_touch_ack_); | 283 DCHECK(!dispatching_touch_ack_); |
288 dispatching_touch_ = false; | 284 dispatching_touch_ = false; |
289 | 285 |
290 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) | 286 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) |
291 return; | 287 return; |
292 | 288 |
293 if (touch_queue_.empty()) | 289 if (touch_queue_.empty()) |
294 return; | 290 return; |
295 | 291 |
296 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) | 292 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
297 renderer_is_consuming_touch_gesture_ = true; | 293 touch_handling_state_ = RENDERER_CONSUMING_GESTURE; |
Rick Byers
2014/01/21 22:00:25
can we DCHECK that we're in the HAS_TOUCH_HANDLER
jdduke (slow)
2014/01/22 02:47:00
I'm trying to think if we could ever get an ack if
| |
298 | 294 |
299 const WebTouchEvent& acked_event = | 295 const WebTouchEvent& acked_event = |
300 touch_queue_.front()->coalesced_event().event; | 296 touch_queue_.front()->coalesced_event().event; |
301 UpdateTouchAckStates(acked_event, ack_result); | 297 UpdateTouchAckStates(acked_event, ack_result); |
302 PopTouchEventToClient(ack_result, latency_info); | 298 PopTouchEventToClient(ack_result, latency_info); |
303 TryForwardNextEventToRenderer(); | 299 TryForwardNextEventToRenderer(); |
304 } | 300 } |
305 | 301 |
306 void TouchEventQueue::TryForwardNextEventToRenderer() { | 302 void TouchEventQueue::TryForwardNextEventToRenderer() { |
307 DCHECK(!dispatching_touch_ack_); | 303 DCHECK(!dispatching_touch_ack_); |
308 // If there are queued touch events, then try to forward them to the renderer | 304 // If there are queued touch events, then try to forward them to the renderer |
309 // immediately, or ACK the events back to the client if appropriate. | 305 // immediately, or ACK the events back to the client if appropriate. |
310 while (!touch_queue_.empty()) { | 306 while (!touch_queue_.empty()) { |
311 const TouchEventWithLatencyInfo& touch = | 307 const TouchEventWithLatencyInfo& touch = |
312 touch_queue_.front()->coalesced_event(); | 308 touch_queue_.front()->coalesced_event(); |
313 if (IsNewTouchGesture(touch.event)) { | |
314 touch_ack_states_.clear(); | |
315 renderer_is_consuming_touch_gesture_ = false; | |
316 } | |
317 if (ShouldForwardToRenderer(touch.event)) { | 309 if (ShouldForwardToRenderer(touch.event)) { |
318 ForwardToRenderer(touch); | 310 ForwardToRenderer(touch); |
319 break; | 311 break; |
320 } | 312 } |
321 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); | 313 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); |
322 } | 314 } |
323 } | 315 } |
324 | 316 |
325 void TouchEventQueue::ForwardToRenderer( | 317 void TouchEventQueue::ForwardToRenderer( |
326 const TouchEventWithLatencyInfo& touch) { | 318 const TouchEventWithLatencyInfo& touch) { |
327 DCHECK(!dispatching_touch_); | 319 DCHECK(!dispatching_touch_); |
320 DCHECK_NE(touch_handling_state_, NO_TOUCH_HANDLER); | |
321 | |
322 if (IsNewTouchGesture(touch.event)) { | |
323 touch_handling_state_ = HAS_TOUCH_HANDLER; | |
324 touch_ack_states_.clear(); | |
325 } | |
326 | |
328 // A synchronous ack will reset |dispatching_touch_|, in which case | 327 // A synchronous ack will reset |dispatching_touch_|, in which case |
329 // the touch timeout should not be started. | 328 // the touch timeout should not be started. |
330 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); | 329 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); |
331 client_->SendTouchEventImmediately(touch); | 330 client_->SendTouchEventImmediately(touch); |
332 if (ack_timeout_enabled_ && | 331 if (ack_timeout_enabled_ && |
333 dispatching_touch_ && | 332 dispatching_touch_ && |
334 !renderer_is_consuming_touch_gesture_ && | 333 touch_handling_state_ != RENDERER_CONSUMING_GESTURE && |
335 ShouldTouchTypeTriggerTimeout(touch.event.type)) { | 334 ShouldTouchTypeTriggerTimeout(touch.event.type)) { |
336 DCHECK(timeout_handler_); | 335 DCHECK(timeout_handler_); |
337 timeout_handler_->Start(touch); | 336 timeout_handler_->Start(touch); |
338 } | 337 } |
339 } | 338 } |
340 | 339 |
341 void TouchEventQueue::OnGestureScrollEvent( | 340 void TouchEventQueue::OnGestureScrollEvent( |
342 const GestureEventWithLatencyInfo& gesture_event) { | 341 const GestureEventWithLatencyInfo& gesture_event) { |
343 blink::WebInputEvent::Type type = gesture_event.event.type; | 342 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) |
344 if (type == blink::WebInputEvent::GestureScrollBegin) { | 343 return; |
345 // We assume the scroll event are generated synchronously from | |
346 // dispatching a touch event ack, so that we can fake a cancel | |
347 // event that has the correct touch ids as the touch event that | |
348 // is being acked. If not, we don't do the touch-cancel optimization. | |
349 if (scroll_in_progress_ || !dispatching_touch_ack_) | |
350 return; | |
351 scroll_in_progress_ = true; | |
352 | 344 |
353 // If we have a timeout event, a cancel has already been dispatched | 345 // We assume that scroll events are generated synchronously from |
354 // for the current touch stream. | 346 // dispatching a touch event ack. This allows us to generate a synthetic |
355 if (HasTimeoutEvent()) | 347 // cancel event that has the same touch ids as the touch event that |
356 return; | 348 // is being acked. Otherwise, we don't perform the touch-cancel optimization. |
349 if (!dispatching_touch_ack_) | |
350 return; | |
357 | 351 |
358 // Fake a TouchCancel to cancel the touch points of the touch event | 352 if (touch_handling_state_ == NO_TOUCH_HANDLER_FOR_GESTURE) |
359 // that is currently being acked. | 353 return; |
360 // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we | 354 |
361 // are in the scope of PopTouchEventToClient() and that no touch event | 355 touch_handling_state_ = NO_TOUCH_HANDLER_FOR_GESTURE; |
362 // in the queue is waiting for ack from renderer. So we can just insert | 356 |
363 // the touch cancel at the beginning of the queue. | 357 // If we have a timeout event, a cancel has already been dispatched |
364 touch_queue_.push_front(new CoalescedWebTouchEvent( | 358 // for the current touch stream. |
365 ObtainCancelEventForTouchEvent( | 359 if (HasTimeoutEvent()) |
366 dispatching_touch_ack_->coalesced_event()), true)); | 360 return; |
367 } else if (type == blink::WebInputEvent::GestureScrollEnd || | 361 |
368 type == blink::WebInputEvent::GestureFlingStart) { | 362 // Fake a TouchCancel to cancel the touch points of the touch event |
369 scroll_in_progress_ = false; | 363 // that is currently being acked. |
370 } | 364 // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we |
365 // are in the scope of PopTouchEventToClient() and that no touch event | |
366 // in the queue is waiting for ack from renderer. So we can just insert | |
367 // the touch cancel at the beginning of the queue. | |
368 touch_queue_.push_front(new CoalescedWebTouchEvent( | |
369 ObtainCancelEventForTouchEvent( | |
370 dispatching_touch_ack_->coalesced_event()), true)); | |
371 } | 371 } |
372 | 372 |
373 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | 373 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
374 DCHECK(!dispatching_touch_ack_); | 374 DCHECK(!dispatching_touch_ack_); |
375 DCHECK(!dispatching_touch_); | 375 DCHECK(!dispatching_touch_); |
376 if (has_handlers_ == has_handlers) | |
377 return; | |
378 | 376 |
379 has_handlers_ = has_handlers; | 377 if (has_handlers) { |
380 | 378 if (touch_handling_state_ == NO_TOUCH_HANDLER) { |
381 if (!has_handlers_) { | 379 // If no touch handler was previously registered, ensure that we don't |
382 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch | 380 // send a partial gesture to the renderer. |
383 // state tracking. | 381 DCHECK(touch_queue_.empty()); |
382 touch_handling_state_ = NO_TOUCH_HANDLER_FOR_GESTURE; | |
Rick Byers
2014/01/21 22:00:25
Your comment for this state says that no events ar
jdduke (slow)
2014/01/22 02:47:00
Sorry, by "partial gesture" I really mean "partial
| |
383 } | |
384 } else { | |
385 touch_handling_state_ = NO_TOUCH_HANDLER; | |
384 if (timeout_handler_) | 386 if (timeout_handler_) |
385 timeout_handler_->Reset(); | 387 timeout_handler_->Reset(); |
386 if (!touch_queue_.empty()) | 388 if (!touch_queue_.empty()) |
387 ProcessTouchAck(kDefaultNotForwardedAck, ui::LatencyInfo()); | 389 ProcessTouchAck(kDefaultNotForwardedAck, ui::LatencyInfo()); |
388 // As there is no touch handler, ack'ing the event should flush the queue. | 390 // As there is no touch handler, ack'ing the event should flush the queue. |
389 DCHECK(touch_queue_.empty()); | 391 DCHECK(touch_queue_.empty()); |
390 } else { | |
391 DCHECK(touch_queue_.empty()); | |
392 // Prevent a partial sequence from being sent to the renderer. | |
393 TouchPointAckStates::iterator ack_it = touch_ack_states_.begin(); | |
394 for (; ack_it != touch_ack_states_.end(); ++ack_it) | |
395 ack_it->second = kDefaultNotForwardedAck; | |
396 } | 392 } |
397 } | 393 } |
398 | 394 |
399 bool TouchEventQueue::IsPendingAckTouchStart() const { | 395 bool TouchEventQueue::IsPendingAckTouchStart() const { |
400 DCHECK(!dispatching_touch_ack_); | 396 DCHECK(!dispatching_touch_ack_); |
401 if (touch_queue_.empty()) | 397 if (touch_queue_.empty()) |
402 return false; | 398 return false; |
403 | 399 |
404 const blink::WebTouchEvent& event = | 400 const blink::WebTouchEvent& event = |
405 touch_queue_.front()->coalesced_event().event; | 401 touch_queue_.front()->coalesced_event().event; |
(...skipping 23 matching lines...) Expand all Loading... | |
429 } | 425 } |
430 | 426 |
431 const TouchEventWithLatencyInfo& | 427 const TouchEventWithLatencyInfo& |
432 TouchEventQueue::GetLatestEventForTesting() const { | 428 TouchEventQueue::GetLatestEventForTesting() const { |
433 return touch_queue_.back()->coalesced_event(); | 429 return touch_queue_.back()->coalesced_event(); |
434 } | 430 } |
435 | 431 |
436 void TouchEventQueue::FlushQueue() { | 432 void TouchEventQueue::FlushQueue() { |
437 DCHECK(!dispatching_touch_ack_); | 433 DCHECK(!dispatching_touch_ack_); |
438 DCHECK(!dispatching_touch_); | 434 DCHECK(!dispatching_touch_); |
435 DCHECK_NE(RENDERER_CONSUMING_GESTURE, touch_handling_state_); | |
436 if (touch_handling_state_ != NO_TOUCH_HANDLER) | |
437 touch_handling_state_ = NO_TOUCH_HANDLER_FOR_GESTURE; | |
439 while (!touch_queue_.empty()) | 438 while (!touch_queue_.empty()) |
440 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); | 439 PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo()); |
441 } | 440 } |
442 | 441 |
443 void TouchEventQueue::PopTouchEventToClient( | 442 void TouchEventQueue::PopTouchEventToClient( |
444 InputEventAckState ack_result, | 443 InputEventAckState ack_result, |
445 const ui::LatencyInfo& renderer_latency_info) { | 444 const ui::LatencyInfo& renderer_latency_info) { |
446 DCHECK(!dispatching_touch_ack_); | 445 DCHECK(!dispatching_touch_ack_); |
447 if (touch_queue_.empty()) | 446 if (touch_queue_.empty()) |
448 return; | 447 return; |
(...skipping 14 matching lines...) Expand all Loading... | |
463 iter->latency.AddNewLatencyFrom(renderer_latency_info); | 462 iter->latency.AddNewLatencyFrom(renderer_latency_info); |
464 client_->OnTouchEventAck((*iter), ack_result); | 463 client_->OnTouchEventAck((*iter), ack_result); |
465 } | 464 } |
466 } | 465 } |
467 | 466 |
468 bool TouchEventQueue::ShouldForwardToRenderer( | 467 bool TouchEventQueue::ShouldForwardToRenderer( |
469 const WebTouchEvent& event) const { | 468 const WebTouchEvent& event) const { |
470 if (HasTimeoutEvent()) | 469 if (HasTimeoutEvent()) |
471 return false; | 470 return false; |
472 | 471 |
473 if (!has_handlers_) | 472 if (touch_handling_state_ == NO_TOUCH_HANDLER) |
474 return false; | 473 return false; |
475 | 474 |
476 if (scroll_in_progress_ && | 475 if (touch_handling_state_ == NO_TOUCH_HANDLER_FOR_GESTURE) { |
477 event.type != blink::WebInputEvent::TouchCancel) | 476 if (IsNewTouchGesture(event)) |
477 return true; | |
478 if (event.type == WebInputEvent::TouchCancel) | |
479 return true; | |
478 return false; | 480 return false; |
481 } | |
479 | 482 |
480 // Touch press events should always be forwarded to the renderer. | 483 // Touch press events should always be forwarded to the renderer. |
481 if (event.type == WebInputEvent::TouchStart) | 484 if (event.type == WebInputEvent::TouchStart) |
482 return true; | 485 return true; |
483 | 486 |
484 for (unsigned int i = 0; i < event.touchesLength; ++i) { | 487 for (unsigned int i = 0; i < event.touchesLength; ++i) { |
485 const WebTouchPoint& point = event.touches[i]; | 488 const WebTouchPoint& point = event.touches[i]; |
486 // If a point has been stationary, then don't take it into account. | 489 // If a point has been stationary, then don't take it into account. |
487 if (point.state == WebTouchPoint::StateStationary) | 490 if (point.state == WebTouchPoint::StateStationary) |
488 continue; | 491 continue; |
(...skipping 27 matching lines...) Expand all Loading... | |
516 } else if (event.type == WebInputEvent::TouchStart) { | 519 } else if (event.type == WebInputEvent::TouchStart) { |
517 for (unsigned i = 0; i < event.touchesLength; ++i) { | 520 for (unsigned i = 0; i < event.touchesLength; ++i) { |
518 const WebTouchPoint& point = event.touches[i]; | 521 const WebTouchPoint& point = event.touches[i]; |
519 if (point.state == WebTouchPoint::StatePressed) | 522 if (point.state == WebTouchPoint::StatePressed) |
520 touch_ack_states_[point.id] = ack_result; | 523 touch_ack_states_[point.id] = ack_result; |
521 } | 524 } |
522 } | 525 } |
523 } | 526 } |
524 | 527 |
525 } // namespace content | 528 } // namespace content |
OLD | NEW |