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 WebInputEventTraits::WillReceiveAckFromRenderer(event) && |
| 46 event.cancelable; |
46 } | 47 } |
47 | 48 |
48 // Compare all properties of touch points to determine the state. | 49 // Compare all properties of touch points to determine the state. |
49 bool HasPointChanged(const WebTouchPoint& point_1, | 50 bool HasPointChanged(const WebTouchPoint& point_1, |
50 const WebTouchPoint& point_2) { | 51 const WebTouchPoint& point_2) { |
51 DCHECK_EQ(point_1.id, point_2.id); | 52 DCHECK_EQ(point_1.id, point_2.id); |
52 if (point_1.screenPosition != point_2.screenPosition || | 53 if (point_1.screenPosition != point_2.screenPosition || |
53 point_1.position != point_2.position || | 54 point_1.position != point_2.position || |
54 point_1.radiusX != point_2.radiusX || | 55 point_1.radiusX != point_2.radiusX || |
55 point_1.radiusY != point_2.radiusY || | 56 point_1.radiusY != point_2.radiusY || |
(...skipping 19 matching lines...) Expand all Loading... |
75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, | 76 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, |
76 base::Unretained(this))), | 77 base::Unretained(this))), |
77 enabled_(true), | 78 enabled_(true), |
78 enabled_for_current_sequence_(false) { | 79 enabled_for_current_sequence_(false) { |
79 DCHECK(timeout_delay != base::TimeDelta()); | 80 DCHECK(timeout_delay != base::TimeDelta()); |
80 } | 81 } |
81 | 82 |
82 ~TouchTimeoutHandler() {} | 83 ~TouchTimeoutHandler() {} |
83 | 84 |
84 void StartIfNecessary(const TouchEventWithLatencyInfo& event) { | 85 void StartIfNecessary(const TouchEventWithLatencyInfo& event) { |
85 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); | 86 if (pending_ack_state_ != PENDING_ACK_NONE) |
| 87 return; |
| 88 |
86 if (!enabled_) | 89 if (!enabled_) |
87 return; | 90 return; |
88 | 91 |
89 if (!ShouldTouchTriggerTimeout(event.event)) | 92 if (!ShouldTouchTriggerTimeout(event.event)) |
90 return; | 93 return; |
91 | 94 |
92 if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) | 95 if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) |
93 enabled_for_current_sequence_ = true; | 96 enabled_for_current_sequence_ = true; |
94 | 97 |
95 if (!enabled_for_current_sequence_) | 98 if (!enabled_for_current_sequence_) |
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 // also a touch-move, then the events can be coalesced into a single event. | 416 // also a touch-move, then the events can be coalesced into a single event. |
414 if (touch_queue_.size() > 1) { | 417 if (touch_queue_.size() > 1) { |
415 CoalescedWebTouchEvent* last_event = touch_queue_.back(); | 418 CoalescedWebTouchEvent* last_event = touch_queue_.back(); |
416 if (last_event->CoalesceEventIfPossible(event)) | 419 if (last_event->CoalesceEventIfPossible(event)) |
417 return; | 420 return; |
418 } | 421 } |
419 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); | 422 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); |
420 } | 423 } |
421 | 424 |
422 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result, | 425 void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result, |
423 const LatencyInfo& latency_info) { | 426 const LatencyInfo& latency_info, |
| 427 const uint32 unique_touch_event_id) { |
424 TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck"); | 428 TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck"); |
425 | 429 |
| 430 // We receive an ack for async touchmove from render. |
| 431 if (!ack_pending_async_touchmove_ids_.empty() && |
| 432 ack_pending_async_touchmove_ids_.front() == unique_touch_event_id) { |
| 433 // Remove the first touchmove from the ack_pending_async_touchmove queue. |
| 434 ack_pending_async_touchmove_ids_.pop_front(); |
| 435 // Send the next pending async touch move once we receive all acks back. |
| 436 if (pending_async_touchmove_ && ack_pending_async_touchmove_ids_.empty()) { |
| 437 DCHECK(touch_queue_.empty()); |
| 438 |
| 439 // Dispatch the next pending async touch move when time expires. |
| 440 if (pending_async_touchmove_->event.timeStampSeconds >= |
| 441 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) { |
| 442 FlushPendingAsyncTouchmove(); |
| 443 } |
| 444 } |
| 445 return; |
| 446 } |
| 447 |
426 DCHECK(!dispatching_touch_ack_); | 448 DCHECK(!dispatching_touch_ack_); |
427 dispatching_touch_ = false; | 449 dispatching_touch_ = false; |
428 | 450 |
429 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) | 451 if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result)) |
430 return; | 452 return; |
431 | 453 |
432 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result); | 454 touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result); |
433 | 455 |
434 if (touch_queue_.empty()) | 456 if (touch_queue_.empty()) |
435 return; | 457 return; |
436 | 458 |
| 459 DCHECK_EQ(touch_queue_.front()->coalesced_event().event.uniqueTouchEventId, |
| 460 unique_touch_event_id); |
| 461 |
437 PopTouchEventToClient(ack_result, latency_info); | 462 PopTouchEventToClient(ack_result, latency_info); |
438 TryForwardNextEventToRenderer(); | 463 TryForwardNextEventToRenderer(); |
439 } | 464 } |
440 | 465 |
441 void TouchEventQueue::TryForwardNextEventToRenderer() { | 466 void TouchEventQueue::TryForwardNextEventToRenderer() { |
442 DCHECK(!dispatching_touch_ack_); | 467 DCHECK(!dispatching_touch_ack_); |
443 // If there are queued touch events, then try to forward them to the renderer | 468 // 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. | 469 // immediately, or ACK the events back to the client if appropriate. |
445 while (!touch_queue_.empty()) { | 470 while (!touch_queue_.empty()) { |
446 PreFilterResult filter_result = | 471 PreFilterResult filter_result = |
(...skipping 23 matching lines...) Expand all Loading... |
470 touch.event.type == WebInputEvent::TouchMove) { | 495 touch.event.type == WebInputEvent::TouchMove) { |
471 // Throttling touchmove's in a continuous touchmove stream while scrolling | 496 // Throttling touchmove's in a continuous touchmove stream while scrolling |
472 // reduces the risk of jank. However, it's still important that the web | 497 // 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, | 498 // application be sent touches at key points in the gesture stream, |
474 // e.g., when the application slop region is exceeded or touchmove | 499 // e.g., when the application slop region is exceeded or touchmove |
475 // coalescing fails because of different modifiers. | 500 // coalescing fails because of different modifiers. |
476 bool send_touchmove_now = size() > 1; | 501 bool send_touchmove_now = size() > 1; |
477 send_touchmove_now |= pending_async_touchmove_ && | 502 send_touchmove_now |= pending_async_touchmove_ && |
478 !pending_async_touchmove_->CanCoalesceWith(touch); | 503 !pending_async_touchmove_->CanCoalesceWith(touch); |
479 send_touchmove_now |= | 504 send_touchmove_now |= |
480 touch.event.timeStampSeconds >= | 505 ack_pending_async_touchmove_ids_.empty() && |
481 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec; | 506 (touch.event.timeStampSeconds >= |
| 507 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec); |
482 | 508 |
483 if (!send_touchmove_now) { | 509 if (!send_touchmove_now) { |
484 if (!pending_async_touchmove_) { | 510 if (!pending_async_touchmove_) { |
485 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); | 511 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); |
486 } else { | 512 } else { |
487 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); | 513 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); |
488 pending_async_touchmove_->CoalesceWith(touch); | 514 pending_async_touchmove_->CoalesceWith(touch); |
489 } | 515 } |
490 DCHECK_EQ(1U, size()); | 516 DCHECK_EQ(1U, size()); |
491 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 517 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 | 529 // 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 | 530 // (touchmove) event, great, otherwise send it immediately but separately. Its |
505 // ack will trigger forwarding of the original |touch| event. | 531 // ack will trigger forwarding of the original |touch| event. |
506 if (pending_async_touchmove_) { | 532 if (pending_async_touchmove_) { |
507 if (pending_async_touchmove_->CanCoalesceWith(touch)) { | 533 if (pending_async_touchmove_->CanCoalesceWith(touch)) { |
508 pending_async_touchmove_->CoalesceWith(touch); | 534 pending_async_touchmove_->CoalesceWith(touch); |
509 pending_async_touchmove_->event.cancelable = !send_touch_events_async_; | 535 pending_async_touchmove_->event.cancelable = !send_touch_events_async_; |
510 touch = *pending_async_touchmove_; | 536 touch = *pending_async_touchmove_; |
511 pending_async_touchmove_.reset(); | 537 pending_async_touchmove_.reset(); |
512 } else { | 538 } else { |
513 scoped_ptr<TouchEventWithLatencyInfo> async_move = | 539 FlushPendingAsyncTouchmove(); |
514 pending_async_touchmove_.Pass(); | |
515 async_move->event.cancelable = false; | |
516 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); | |
517 SendTouchEventImmediately(async_move.get()); | |
518 return; | 540 return; |
519 } | 541 } |
520 } | 542 } |
521 | 543 |
522 // Note: Touchstart events are marked cancelable to allow transitions between | 544 // Note: Touchstart events are marked cancelable to allow transitions between |
523 // platform scrolling and JS pinching. Touchend events, however, remain | 545 // platform scrolling and JS pinching. Touchend events, however, remain |
524 // uncancelable, mitigating the risk of jank when transitioning to a fling. | 546 // uncancelable, mitigating the risk of jank when transitioning to a fling. |
525 if (send_touch_events_async_ && touch.event.type != WebInputEvent::TouchStart) | 547 if (send_touch_events_async_ && touch.event.type != WebInputEvent::TouchStart) |
526 touch.event.cancelable = false; | 548 touch.event.cancelable = false; |
527 | 549 |
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); | 550 SendTouchEventImmediately(&touch); |
532 if (dispatching_touch_ && timeout_handler_) | 551 } |
533 timeout_handler_->StartIfNecessary(touch); | 552 |
| 553 void TouchEventQueue::FlushPendingAsyncTouchmove() { |
| 554 DCHECK(!dispatching_touch_); |
| 555 scoped_ptr<TouchEventWithLatencyInfo> touch = pending_async_touchmove_.Pass(); |
| 556 touch->event.cancelable = false; |
| 557 touch_queue_.push_front(new CoalescedWebTouchEvent(*touch, true)); |
| 558 SendTouchEventImmediately(touch.get()); |
534 } | 559 } |
535 | 560 |
536 void TouchEventQueue::OnGestureScrollEvent( | 561 void TouchEventQueue::OnGestureScrollEvent( |
537 const GestureEventWithLatencyInfo& gesture_event) { | 562 const GestureEventWithLatencyInfo& gesture_event) { |
538 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) { | 563 if (gesture_event.event.type == blink::WebInputEvent::GestureScrollBegin) { |
539 if (has_handler_for_current_sequence_ && | 564 if (has_handler_for_current_sequence_ && |
540 !drop_remaining_touches_in_sequence_) { | 565 !drop_remaining_touches_in_sequence_) { |
541 DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves()) | 566 DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves()) |
542 << "A touch handler should be offered a touchmove before scrolling."; | 567 << "A touch handler should be offered a touchmove before scrolling."; |
543 } | 568 } |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 break; | 688 break; |
664 } | 689 } |
665 } | 690 } |
666 } | 691 } |
667 | 692 |
668 if (last_sent_touchevent_) | 693 if (last_sent_touchevent_) |
669 *last_sent_touchevent_ = touch->event; | 694 *last_sent_touchevent_ = touch->event; |
670 else | 695 else |
671 last_sent_touchevent_.reset(new WebTouchEvent(touch->event)); | 696 last_sent_touchevent_.reset(new WebTouchEvent(touch->event)); |
672 | 697 |
| 698 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); |
| 699 |
673 client_->SendTouchEventImmediately(*touch); | 700 client_->SendTouchEventImmediately(*touch); |
| 701 |
| 702 // A synchronous ack will reset |dispatching_touch_|, in which case the touch |
| 703 // timeout should not be started and the count also should not be increased. |
| 704 if (dispatching_touch_) { |
| 705 if (touch->event.type == WebInputEvent::TouchMove && |
| 706 !touch->event.cancelable) { |
| 707 // When we send out a uncancelable touch move, we increase the count and |
| 708 // we do not process input event ack any more, we will just ack to client |
| 709 // and wait for the ack from render. Also we will remove it from the front |
| 710 // of the queue. |
| 711 ack_pending_async_touchmove_ids_.push_back( |
| 712 touch->event.uniqueTouchEventId); |
| 713 dispatching_touch_ = false; |
| 714 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_IGNORED); |
| 715 TryForwardNextEventToRenderer(); |
| 716 return; |
| 717 } |
| 718 |
| 719 if (timeout_handler_) |
| 720 timeout_handler_->StartIfNecessary(*touch); |
| 721 } |
674 } | 722 } |
675 | 723 |
676 TouchEventQueue::PreFilterResult | 724 TouchEventQueue::PreFilterResult |
677 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 725 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
678 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { | 726 if (WebTouchEventTraits::IsTouchSequenceStart(event)) { |
679 has_handler_for_current_sequence_ = false; | 727 has_handler_for_current_sequence_ = false; |
680 send_touch_events_async_ = false; | 728 send_touch_events_async_ = false; |
681 pending_async_touchmove_.reset(); | 729 pending_async_touchmove_.reset(); |
682 last_sent_touchevent_.reset(); | 730 last_sent_touchevent_.reset(); |
683 | 731 |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
750 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) | 798 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED) |
751 send_touch_events_async_ = false; | 799 send_touch_events_async_ = false; |
752 has_handler_for_current_sequence_ |= | 800 has_handler_for_current_sequence_ |= |
753 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; | 801 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; |
754 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) { | 802 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) { |
755 has_handler_for_current_sequence_ = false; | 803 has_handler_for_current_sequence_ = false; |
756 } | 804 } |
757 } | 805 } |
758 | 806 |
759 } // namespace content | 807 } // namespace content |
OLD | NEW |