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/stl_util.h" | 8 #include "base/stl_util.h" |
9 #include "base/trace_event/trace_event.h" | 9 #include "base/trace_event/trace_event.h" |
10 #include "content/browser/renderer_host/input/timeout_monitor.h" | 10 #include "content/browser/renderer_host/input/timeout_monitor.h" |
(...skipping 24 matching lines...) Expand all Loading... | |
35 WebInputEvent::TouchCancel, | 35 WebInputEvent::TouchCancel, |
36 // TODO(rbyers): Shouldn't we use a fresh timestamp? | 36 // TODO(rbyers): Shouldn't we use a fresh timestamp? |
37 event.event.timeStampSeconds, | 37 event.event.timeStampSeconds, |
38 &event.event); | 38 &event.event); |
39 return event; | 39 return event; |
40 } | 40 } |
41 | 41 |
42 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { | 42 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { |
43 return (event.type == WebInputEvent::TouchStart || | 43 return (event.type == WebInputEvent::TouchStart || |
44 event.type == WebInputEvent::TouchMove) && | 44 event.type == WebInputEvent::TouchMove) && |
45 !WebInputEventTraits::IgnoresAckDisposition(event); | 45 event.cancelable; |
46 } | 46 } |
47 | 47 |
48 // Compare all properties of touch points to determine the state. | 48 // Compare all properties of touch points to determine the state. |
49 bool HasPointChanged(const WebTouchPoint& point_1, | 49 bool HasPointChanged(const WebTouchPoint& point_1, |
50 const WebTouchPoint& point_2) { | 50 const WebTouchPoint& point_2) { |
51 DCHECK_EQ(point_1.id, point_2.id); | 51 DCHECK_EQ(point_1.id, point_2.id); |
52 if (point_1.screenPosition != point_2.screenPosition || | 52 if (point_1.screenPosition != point_2.screenPosition || |
53 point_1.position != point_2.position || | 53 point_1.position != point_2.position || |
54 point_1.radiusX != point_2.radiusX || | 54 point_1.radiusX != point_2.radiusX || |
55 point_1.radiusY != point_2.radiusY || | 55 point_1.radiusY != point_2.radiusY || |
(...skipping 19 matching lines...) Expand all Loading... | |
75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, | 75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, |
76 base::Unretained(this))), | 76 base::Unretained(this))), |
77 enabled_(true), | 77 enabled_(true), |
78 enabled_for_current_sequence_(false) { | 78 enabled_for_current_sequence_(false) { |
79 DCHECK(timeout_delay != base::TimeDelta()); | 79 DCHECK(timeout_delay != base::TimeDelta()); |
80 } | 80 } |
81 | 81 |
82 ~TouchTimeoutHandler() {} | 82 ~TouchTimeoutHandler() {} |
83 | 83 |
84 void StartIfNecessary(const TouchEventWithLatencyInfo& event) { | 84 void StartIfNecessary(const TouchEventWithLatencyInfo& event) { |
85 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); | 85 if (pending_ack_state_ != PENDING_ACK_NONE) |
86 return; | |
87 | |
86 if (!enabled_) | 88 if (!enabled_) |
87 return; | 89 return; |
88 | 90 |
89 if (!ShouldTouchTriggerTimeout(event.event)) | 91 if (!ShouldTouchTriggerTimeout(event.event)) |
90 return; | 92 return; |
91 | 93 |
92 if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) | 94 if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) |
93 enabled_for_current_sequence_ = true; | 95 enabled_for_current_sequence_ = true; |
94 | 96 |
95 if (!enabled_for_current_sequence_) | 97 if (!enabled_for_current_sequence_) |
(...skipping 283 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
379 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); | 381 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); |
380 } | 382 } |
381 } | 383 } |
382 | 384 |
383 TouchEventQueue::~TouchEventQueue() { | 385 TouchEventQueue::~TouchEventQueue() { |
384 if (!touch_queue_.empty()) | 386 if (!touch_queue_.empty()) |
385 STLDeleteElements(&touch_queue_); | 387 STLDeleteElements(&touch_queue_); |
386 } | 388 } |
387 | 389 |
388 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { | 390 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { |
389 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); | 391 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); |
jdduke (slow)
2015/05/07 21:11:15
What kind of touch id validation should be doing?
lanwei
2015/05/08 19:31:25
We cannot easily add the validation on this, I thi
| |
390 | 392 |
391 // If the queueing of |event| was triggered by an ack dispatch, defer | 393 // If the queueing of |event| was triggered by an ack dispatch, defer |
392 // processing the event until the dispatch has finished. | 394 // processing the event until the dispatch has finished. |
393 if (touch_queue_.empty() && !dispatching_touch_ack_) { | 395 if (touch_queue_.empty() && !dispatching_touch_ack_) { |
394 // Optimization of the case without touch handlers. Removing this path | 396 // Optimization of the case without touch handlers. Removing this path |
395 // yields identical results, but this avoids unnecessary allocations. | 397 // yields identical results, but this avoids unnecessary allocations. |
396 PreFilterResult filter_result = FilterBeforeForwarding(event.event); | 398 PreFilterResult filter_result = FilterBeforeForwarding(event.event); |
397 if (filter_result != FORWARD_TO_RENDERER) { | 399 if (filter_result != FORWARD_TO_RENDERER) { |
398 client_->OnTouchEventAck(event, | 400 client_->OnTouchEventAck(event, |
399 filter_result == ACK_WITH_NO_CONSUMER_EXISTS | 401 filter_result == ACK_WITH_NO_CONSUMER_EXISTS |
(...skipping 13 matching lines...) Expand all Loading... | |
413 // also a touch-move, then the events can be coalesced into a single event. | 415 // also a touch-move, then the events can be coalesced into a single event. |
414 if (touch_queue_.size() > 1) { | 416 if (touch_queue_.size() > 1) { |
415 CoalescedWebTouchEvent* last_event = touch_queue_.back(); | 417 CoalescedWebTouchEvent* last_event = touch_queue_.back(); |
416 if (last_event->CoalesceEventIfPossible(event)) | 418 if (last_event->CoalesceEventIfPossible(event)) |
417 return; | 419 return; |
418 } | 420 } |
419 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); | 421 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); |
420 } | 422 } |
421 | 423 |
422 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result, | 424 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result, |
423 const LatencyInfo& latency_info) { | 425 const LatencyInfo& latency_info, |
426 const uint32 unique_touch_event_id) { | |
424 TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck"); | 427 TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck"); |
425 | 428 |
429 // We receive an ack for async touchmove from render. | |
430 if (!ack_pending_async_touchmove_.empty() && | |
431 ack_pending_async_touchmove_.front() == unique_touch_event_id) { | |
432 // Remove the first touchmove from the ack_pending_async_touchmove queue. | |
433 ack_pending_async_touchmove_.pop_front(); | |
434 // Send the next pending async touch move once we receive all acks back. | |
435 if (pending_async_touchmove_ && ack_pending_async_touchmove_.empty()) { | |
jdduke (slow)
2015/05/07 21:11:15
Let's add a DCHECK(touch_queue_.empty());
lanwei
2015/05/08 19:31:25
Done.
| |
436 TouchEventWithLatencyInfo touch = *pending_async_touchmove_; | |
jdduke (slow)
2015/05/07 21:11:15
See comment below, I think we can share some logic
| |
437 | |
438 // Dispatch the next pending async touch move when time expires. | |
439 if (touch.event.timeStampSeconds >= | |
440 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) { | |
441 touch.event.cancelable = false; | |
442 pending_async_touchmove_.reset(); | |
443 touch_queue_.push_front(new CoalescedWebTouchEvent(touch, true)); | |
444 SendTouchEventImmediately(&touch); | |
445 } | |
446 } | |
447 return; | |
448 } | |
449 | |
426 DCHECK(!dispatching_touch_ack_); | 450 DCHECK(!dispatching_touch_ack_); |
427 dispatching_touch_ = false; | 451 dispatching_touch_ = false; |
428 | 452 |
429 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) | 453 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) |
430 return; | 454 return; |
431 | 455 |
432 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result); | 456 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result); |
433 | 457 |
434 if (touch_queue_.empty()) | 458 if (touch_queue_.empty()) |
435 return; | 459 return; |
436 | 460 |
461 DCHECK_EQ(touch_queue_.front()->coalesced_event().event.uniqueTouchEventId, | |
462 unique_touch_event_id); | |
463 | |
437 PopTouchEventToClient(ack_result, latency_info); | 464 PopTouchEventToClient(ack_result, latency_info); |
438 TryForwardNextEventToRenderer(); | 465 TryForwardNextEventToRenderer(); |
439 } | 466 } |
440 | 467 |
441 void TouchEventQueue::TryForwardNextEventToRenderer() { | 468 void TouchEventQueue::TryForwardNextEventToRenderer() { |
442 DCHECK(!dispatching_touch_ack_); | 469 DCHECK(!dispatching_touch_ack_); |
443 // If there are queued touch events, then try to forward them to the renderer | 470 // If there are queued touch events, then try to forward them to the renderer |
444 // immediately, or ACK the events back to the client if appropriate. | 471 // immediately, or ACK the events back to the client if appropriate. |
445 while (!touch_queue_.empty()) { | 472 while (!touch_queue_.empty()) { |
446 PreFilterResult filter_result = | 473 PreFilterResult filter_result = |
(...skipping 23 matching lines...) Expand all Loading... | |
470 touch.event.type == WebInputEvent::TouchMove) { | 497 touch.event.type == WebInputEvent::TouchMove) { |
471 // Throttling touchmove's in a continuous touchmove stream while scrolling | 498 // Throttling touchmove's in a continuous touchmove stream while scrolling |
472 // reduces the risk of jank. However, it's still important that the web | 499 // reduces the risk of jank. However, it's still important that the web |
473 // application be sent touches at key points in the gesture stream, | 500 // application be sent touches at key points in the gesture stream, |
474 // e.g., when the application slop region is exceeded or touchmove | 501 // e.g., when the application slop region is exceeded or touchmove |
475 // coalescing fails because of different modifiers. | 502 // coalescing fails because of different modifiers. |
476 bool send_touchmove_now = size() > 1; | 503 bool send_touchmove_now = size() > 1; |
477 send_touchmove_now |= pending_async_touchmove_ && | 504 send_touchmove_now |= pending_async_touchmove_ && |
478 !pending_async_touchmove_->CanCoalesceWith(touch); | 505 !pending_async_touchmove_->CanCoalesceWith(touch); |
479 send_touchmove_now |= | 506 send_touchmove_now |= |
480 touch.event.timeStampSeconds >= | 507 ack_pending_async_touchmove_.empty() && |
481 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec; | 508 (touch.event.timeStampSeconds >= |
509 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec); | |
482 | 510 |
483 if (!send_touchmove_now) { | 511 if (!send_touchmove_now) { |
484 if (!pending_async_touchmove_) { | 512 if (!pending_async_touchmove_) { |
485 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); | 513 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); |
486 } else { | 514 } else { |
487 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); | 515 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); |
488 pending_async_touchmove_->CoalesceWith(touch); | 516 pending_async_touchmove_->CoalesceWith(touch); |
489 } | 517 } |
490 DCHECK_EQ(1U, size()); | 518 DCHECK_EQ(1U, size()); |
491 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 519 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
(...skipping 11 matching lines...) Expand all Loading... | |
503 // Flush any pending async touch move. If it can be combined with the current | 531 // Flush any pending async touch move. If it can be combined with the current |
504 // (touchmove) event, great, otherwise send it immediately but separately. Its | 532 // (touchmove) event, great, otherwise send it immediately but separately. Its |
505 // ack will trigger forwarding of the original |touch| event. | 533 // ack will trigger forwarding of the original |touch| event. |
506 if (pending_async_touchmove_) { | 534 if (pending_async_touchmove_) { |
507 if (pending_async_touchmove_->CanCoalesceWith(touch)) { | 535 if (pending_async_touchmove_->CanCoalesceWith(touch)) { |
508 pending_async_touchmove_->CoalesceWith(touch); | 536 pending_async_touchmove_->CoalesceWith(touch); |
509 pending_async_touchmove_->event.cancelable = !send_touch_events_async_; | 537 pending_async_touchmove_->event.cancelable = !send_touch_events_async_; |
510 touch = *pending_async_touchmove_; | 538 touch = *pending_async_touchmove_; |
511 pending_async_touchmove_.reset(); | 539 pending_async_touchmove_.reset(); |
512 } else { | 540 } else { |
513 scoped_ptr<TouchEventWithLatencyInfo> async_move = | 541 scoped_ptr<TouchEventWithLatencyInfo> async_move = |
jdduke (slow)
2015/05/07 21:11:15
Perhaps add a |FlushPendingAsyncTouchMove()| helpe
lanwei
2015/05/08 19:31:25
Done.
| |
514 pending_async_touchmove_.Pass(); | 542 pending_async_touchmove_.Pass(); |
515 async_move->event.cancelable = false; | 543 async_move->event.cancelable = false; |
516 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); | 544 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); |
517 SendTouchEventImmediately(async_move.get()); | 545 SendTouchEventImmediately(async_move.get()); |
518 return; | 546 return; |
519 } | 547 } |
520 } | 548 } |
521 | 549 |
522 // Note: Touchstart events are marked cancelable to allow transitions between | 550 // Note: Touchstart events are marked cancelable to allow transitions between |
523 // platform scrolling and JS pinching. Touchend events, however, remain | 551 // platform scrolling and JS pinching. Touchend events, however, remain |
524 // uncancelable, mitigating the risk of jank when transitioning to a fling. | 552 // uncancelable, mitigating the risk of jank when transitioning to a fling. |
525 if (send_touch_events_async_ && touch.event.type != WebInputEvent::TouchStart) | 553 if (send_touch_events_async_ && touch.event.type != WebInputEvent::TouchStart) |
526 touch.event.cancelable = false; | 554 touch.event.cancelable = false; |
527 | 555 |
528 // A synchronous ack will reset |dispatching_touch_|, in which case | |
529 // the touch timeout should not be started. | |
530 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); | |
531 SendTouchEventImmediately(&touch); | 556 SendTouchEventImmediately(&touch); |
532 if (dispatching_touch_ && timeout_handler_) | |
533 timeout_handler_->StartIfNecessary(touch); | |
534 } | 557 } |
535 | 558 |
536 void TouchEventQueue::OnGestureScrollEvent( | 559 void TouchEventQueue::OnGestureScrollEvent( |
537 const GestureEventWithLatencyInfo& gesture_event) { | 560 const GestureEventWithLatencyInfo& gesture_event) { |
538 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) { | 561 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) { |
539 if (has_handler_for_current_sequence_ && | 562 if (has_handler_for_current_sequence_ && |
540 !drop_remaining_touches_in_sequence_) { | 563 !drop_remaining_touches_in_sequence_) { |
541 DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves()) | 564 DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves()) |
542 << "A touch handler should be offered a touchmove before scrolling."; | 565 << "A touch handler should be offered a touchmove before scrolling."; |
543 } | 566 } |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
663 break; | 686 break; |
664 } | 687 } |
665 } | 688 } |
666 } | 689 } |
667 | 690 |
668 if (last_sent_touchevent_) | 691 if (last_sent_touchevent_) |
669 *last_sent_touchevent_ = touch->event; | 692 *last_sent_touchevent_ = touch->event; |
670 else | 693 else |
671 last_sent_touchevent_.reset(new WebTouchEvent(touch->event)); | 694 last_sent_touchevent_.reset(new WebTouchEvent(touch->event)); |
672 | 695 |
696 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); | |
697 | |
673 client_->SendTouchEventImmediately(*touch); | 698 client_->SendTouchEventImmediately(*touch); |
699 | |
700 // A synchronous ack will reset |dispatching_touch_|, in which case the touch | |
701 // timeout should not be started and the count also should not be increased. | |
702 if (dispatching_touch_) { | |
703 if (touch->event.type == WebInputEvent::TouchMove && | |
704 !touch->event.cancelable) { | |
705 // When we send out a uncancelable touch move, we increase the count and | |
706 // we do not process input event ack any more, we will just ack to client | |
707 // and wait for the ack from render. Also we will remove it from the front | |
708 // of the queue. | |
709 ack_pending_async_touchmove_.push_back(touch->event.uniqueTouchEventId); | |
710 PopTouchEvent(); | |
711 dispatching_touch_ = false; | |
712 client_->OnTouchEventAck(*touch, INPUT_EVENT_ACK_STATE_IGNORED); | |
jdduke (slow)
2015/05/07 21:11:15
Can we make this PopTouchEventToClient(INPUT_EVENT
lanwei
2015/05/08 19:31:25
Done.
| |
713 TryForwardNextEventToRenderer(); | |
jdduke (slow)
2015/05/07 21:11:15
Let'd add a return here.
lanwei
2015/05/08 19:31:25
Done.
| |
714 } | |
715 | |
716 if (timeout_handler_) | |
717 timeout_handler_->StartIfNecessary(*touch); | |
718 } | |
674 } | 719 } |
675 | 720 |
676 TouchEventQueue::PreFilterResult | 721 TouchEventQueue::PreFilterResult |
677 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 722 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
678 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { | 723 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { |
679 has_handler_for_current_sequence_ = false; | 724 has_handler_for_current_sequence_ = false; |
680 send_touch_events_async_ = false; | 725 send_touch_events_async_ = false; |
681 pending_async_touchmove_.reset(); | 726 pending_async_touchmove_.reset(); |
682 last_sent_touchevent_.reset(); | 727 last_sent_touchevent_.reset(); |
683 | 728 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
750 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) | 795 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
751 send_touch_events_async_ = false; | 796 send_touch_events_async_ = false; |
752 has_handler_for_current_sequence_ |= | 797 has_handler_for_current_sequence_ |= |
753 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; | 798 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; |
754 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) { | 799 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) { |
755 has_handler_for_current_sequence_ = false; | 800 has_handler_for_current_sequence_ = false; |
756 } | 801 } |
757 } | 802 } |
758 | 803 |
759 } // namespace content | 804 } // namespace content |
OLD | NEW |