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" |
11 #include "content/common/input/web_touch_event_traits.h" | 11 #include "content/common/input/web_touch_event_traits.h" |
12 #include "ui/gfx/geometry/point_f.h" | 12 #include "ui/gfx/geometry/point_f.h" |
13 | 13 |
14 using blink::WebInputEvent; | 14 using blink::WebInputEvent; |
15 using blink::WebTouchEvent; | 15 using blink::WebTouchEvent; |
16 using blink::WebTouchPoint; | 16 using blink::WebTouchPoint; |
17 using ui::LatencyInfo; | 17 using ui::LatencyInfo; |
18 | 18 |
19 namespace content { | 19 namespace content { |
20 namespace { | 20 namespace { |
21 | 21 |
22 // Time interval at which touchmove events will be forwarded to the client while | 22 // Time interval at which touchmove events will be forwarded to the client while |
23 // scrolling is active and possible. | 23 // scrolling is active and possible. |
24 const double kAsyncTouchMoveIntervalSec = .2; | 24 const double kAsyncTouchMoveIntervalSec = .2; |
25 | 25 |
26 // A slop region just larger than that used by many web applications. When | |
27 // touchmove's are being sent asynchronously, movement outside this region will | |
28 // trigger an immediate async touchmove to cancel potential tap-related logic. | |
29 const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.; | |
30 | |
31 // A sanity check on touches received to ensure that touch movement outside | 26 // A sanity check on touches received to ensure that touch movement outside |
32 // the platform slop region will cause scrolling, as indicated by the event's | 27 // the platform slop region will cause scrolling, as indicated by the event's |
33 // |causesScrollingIfUncanceled| bit. | 28 // |causesScrollingIfUncanceled| bit. |
34 const double kMaxConceivablePlatformSlopRegionLengthDipsSquared = 60. * 60.; | 29 const double kMaxConceivablePlatformSlopRegionLengthDipsSquared = 60. * 60.; |
35 | 30 |
36 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( | 31 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( |
37 const TouchEventWithLatencyInfo& event_to_cancel) { | 32 const TouchEventWithLatencyInfo& event_to_cancel) { |
38 TouchEventWithLatencyInfo event = event_to_cancel; | 33 TouchEventWithLatencyInfo event = event_to_cancel; |
39 WebTouchEventTraits::ResetTypeAndTouchStates( | 34 WebTouchEventTraits::ResetTypeAndTouchStates( |
40 WebInputEvent::TouchCancel, | 35 WebInputEvent::TouchCancel, |
41 // TODO(rbyers): Shouldn't we use a fresh timestamp? | 36 // TODO(rbyers): Shouldn't we use a fresh timestamp? |
42 event.event.timeStampSeconds, | 37 event.event.timeStampSeconds, |
43 &event.event); | 38 &event.event); |
44 return event; | 39 return event; |
45 } | 40 } |
46 | 41 |
47 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { | 42 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { |
48 return (event.type == WebInputEvent::TouchStart || | 43 return (event.type == WebInputEvent::TouchStart || |
49 event.type == WebInputEvent::TouchMove) && | 44 event.type == WebInputEvent::TouchMove) && |
50 !WebInputEventTraits::IgnoresAckDisposition(event); | 45 !WebInputEventTraits::IgnoresAckDisposition(event); |
51 } | 46 } |
52 | 47 |
53 bool OutsideApplicationSlopRegion(const WebTouchEvent& event, | |
54 const gfx::PointF& anchor) { | |
55 return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() > | |
56 kApplicationSlopRegionLengthDipsSqared; | |
57 } | |
58 | |
59 } // namespace | 48 } // namespace |
60 | 49 |
61 | 50 |
62 // Cancels a touch sequence if a touchstart or touchmove ack response is | 51 // Cancels a touch sequence if a touchstart or touchmove ack response is |
63 // sufficiently delayed. | 52 // sufficiently delayed. |
64 class TouchEventQueue::TouchTimeoutHandler { | 53 class TouchEventQueue::TouchTimeoutHandler { |
65 public: | 54 public: |
66 TouchTimeoutHandler(TouchEventQueue* touch_queue, | 55 TouchTimeoutHandler(TouchEventQueue* touch_queue, |
67 base::TimeDelta timeout_delay) | 56 base::TimeDelta timeout_delay) |
68 : touch_queue_(touch_queue), | 57 : touch_queue_(touch_queue), |
(...skipping 292 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 | 350 |
362 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, | 351 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, |
363 const Config& config) | 352 const Config& config) |
364 : client_(client), | 353 : client_(client), |
365 dispatching_touch_ack_(NULL), | 354 dispatching_touch_ack_(NULL), |
366 dispatching_touch_(false), | 355 dispatching_touch_(false), |
367 has_handlers_(true), | 356 has_handlers_(true), |
368 drop_remaining_touches_in_sequence_(false), | 357 drop_remaining_touches_in_sequence_(false), |
369 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor), | 358 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor), |
370 send_touch_events_async_(false), | 359 send_touch_events_async_(false), |
371 needs_async_touchmove_for_outer_slop_region_(false), | |
372 last_sent_touch_timestamp_sec_(0), | 360 last_sent_touch_timestamp_sec_(0), |
373 touch_scrolling_mode_(config.touch_scrolling_mode) { | 361 touch_scrolling_mode_(config.touch_scrolling_mode) { |
374 DCHECK(client); | 362 DCHECK(client); |
375 if (config.touch_ack_timeout_supported) { | 363 if (config.touch_ack_timeout_supported) { |
376 timeout_handler_.reset( | 364 timeout_handler_.reset( |
377 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); | 365 new TouchTimeoutHandler(this, config.touch_ack_timeout_delay)); |
378 } | 366 } |
379 } | 367 } |
380 | 368 |
381 TouchEventQueue::~TouchEventQueue() { | 369 TouchEventQueue::~TouchEventQueue() { |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
464 DCHECK(!dispatching_touch_); | 452 DCHECK(!dispatching_touch_); |
465 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event(); | 453 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event(); |
466 | 454 |
467 if (send_touch_events_async_ && | 455 if (send_touch_events_async_ && |
468 touch.event.type == WebInputEvent::TouchMove) { | 456 touch.event.type == WebInputEvent::TouchMove) { |
469 // Throttling touchmove's in a continuous touchmove stream while scrolling | 457 // Throttling touchmove's in a continuous touchmove stream while scrolling |
470 // reduces the risk of jank. However, it's still important that the web | 458 // reduces the risk of jank. However, it's still important that the web |
471 // application be sent touches at key points in the gesture stream, | 459 // application be sent touches at key points in the gesture stream, |
472 // e.g., when the application slop region is exceeded or touchmove | 460 // e.g., when the application slop region is exceeded or touchmove |
473 // coalescing fails because of different modifiers. | 461 // coalescing fails because of different modifiers. |
474 const bool send_touchmove_now = | 462 bool send_touchmove_now = size() > 1; |
475 size() > 1 || | 463 send_touchmove_now |= pending_async_touchmove_ && |
476 (touch.event.timeStampSeconds >= | 464 !pending_async_touchmove_->CanCoalesceWith(touch); |
477 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) || | 465 send_touchmove_now |= |
478 (needs_async_touchmove_for_outer_slop_region_ && | 466 touch.event.timeStampSeconds >= |
479 OutsideApplicationSlopRegion(touch.event, | 467 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec; |
480 touch_sequence_start_position_)) || | |
481 (pending_async_touchmove_ && | |
482 !pending_async_touchmove_->CanCoalesceWith(touch)); | |
483 | 468 |
484 if (!send_touchmove_now) { | 469 if (!send_touchmove_now) { |
485 if (!pending_async_touchmove_) { | 470 if (!pending_async_touchmove_) { |
486 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); | 471 pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch)); |
487 } else { | 472 } else { |
488 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); | 473 DCHECK(pending_async_touchmove_->CanCoalesceWith(touch)); |
489 pending_async_touchmove_->CoalesceWith(touch); | 474 pending_async_touchmove_->CoalesceWith(touch); |
490 } | 475 } |
491 DCHECK_EQ(1U, size()); | 476 DCHECK_EQ(1U, size()); |
492 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 477 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
546 !drop_remaining_touches_in_sequence_ && | 531 !drop_remaining_touches_in_sequence_ && |
547 touch_consumer_states_.is_empty()) { | 532 touch_consumer_states_.is_empty()) { |
548 // If no touch points have a consumer, prevent all subsequent touch events | 533 // If no touch points have a consumer, prevent all subsequent touch events |
549 // received during the scroll from reaching the renderer. This ensures | 534 // received during the scroll from reaching the renderer. This ensures |
550 // that the first touchstart the renderer sees in any given sequence can | 535 // that the first touchstart the renderer sees in any given sequence can |
551 // always be preventDefault'ed (cancelable == true). | 536 // always be preventDefault'ed (cancelable == true). |
552 // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable. | 537 // TODO(jdduke): Revisit if touchstarts during scroll are made cancelable. |
553 drop_remaining_touches_in_sequence_ = true; | 538 drop_remaining_touches_in_sequence_ = true; |
554 } | 539 } |
555 | 540 |
556 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) { | 541 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) |
557 needs_async_touchmove_for_outer_slop_region_ = true; | |
558 pending_async_touchmove_.reset(); | 542 pending_async_touchmove_.reset(); |
559 } | |
560 | 543 |
561 return; | 544 return; |
562 } | 545 } |
563 | 546 |
564 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollUpdate) | 547 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollUpdate) |
565 return; | 548 return; |
566 | 549 |
567 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) | 550 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) |
568 send_touch_events_async_ = true; | 551 send_touch_events_async_ = true; |
569 | 552 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) | 585 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) |
603 return; | 586 return; |
604 | 587 |
605 // Throttle sending touchmove events as long as the scroll events are handled. | 588 // Throttle sending touchmove events as long as the scroll events are handled. |
606 // Note that there's no guarantee that this ACK is for the most recent | 589 // Note that there's no guarantee that this ACK is for the most recent |
607 // gesture event (or even part of the current sequence). Worst case, the | 590 // gesture event (or even part of the current sequence). Worst case, the |
608 // delay in updating the absorption state will result in minor UI glitches. | 591 // delay in updating the absorption state will result in minor UI glitches. |
609 // A valid |pending_async_touchmove_| will be flushed when the next event is | 592 // A valid |pending_async_touchmove_| will be flushed when the next event is |
610 // forwarded. | 593 // forwarded. |
611 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | 594 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); |
612 if (!send_touch_events_async_) | |
613 needs_async_touchmove_for_outer_slop_region_ = false; | |
614 } | 595 } |
615 | 596 |
616 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | 597 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
617 DCHECK(!dispatching_touch_ack_); | 598 DCHECK(!dispatching_touch_ack_); |
618 DCHECK(!dispatching_touch_); | 599 DCHECK(!dispatching_touch_); |
619 has_handlers_ = has_handlers; | 600 has_handlers_ = has_handlers; |
620 } | 601 } |
621 | 602 |
622 bool TouchEventQueue::IsPendingAckTouchStart() const { | 603 bool TouchEventQueue::IsPendingAckTouchStart() const { |
623 DCHECK(!dispatching_touch_ack_); | 604 DCHECK(!dispatching_touch_ack_); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
687 | 668 |
688 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { | 669 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { |
689 DCHECK(!touch_queue_.empty()); | 670 DCHECK(!touch_queue_.empty()); |
690 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); | 671 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); |
691 touch_queue_.pop_front(); | 672 touch_queue_.pop_front(); |
692 return event.Pass(); | 673 return event.Pass(); |
693 } | 674 } |
694 | 675 |
695 void TouchEventQueue::SendTouchEventImmediately( | 676 void TouchEventQueue::SendTouchEventImmediately( |
696 const TouchEventWithLatencyInfo& touch) { | 677 const TouchEventWithLatencyInfo& touch) { |
697 if (needs_async_touchmove_for_outer_slop_region_) { | |
698 // Any event other than a touchmove (e.g., touchcancel or secondary | |
699 // touchstart) after a scroll has started will interrupt the need to send a | |
700 // an outer slop-region exceeding touchmove. | |
701 if (touch.event.type != WebInputEvent::TouchMove || | |
702 OutsideApplicationSlopRegion(touch.event, | |
703 touch_sequence_start_position_)) | |
704 needs_async_touchmove_for_outer_slop_region_ = false; | |
705 } | |
706 | |
707 client_->SendTouchEventImmediately(touch); | 678 client_->SendTouchEventImmediately(touch); |
708 } | 679 } |
709 | 680 |
710 TouchEventQueue::PreFilterResult | 681 TouchEventQueue::PreFilterResult |
711 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 682 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
712 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) | 683 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) |
713 return ACK_WITH_NO_CONSUMER_EXISTS; | 684 return ACK_WITH_NO_CONSUMER_EXISTS; |
714 | 685 |
715 if (touchmove_slop_suppressor_->FilterEvent(event)) | 686 if (touchmove_slop_suppressor_->FilterEvent(event)) |
716 return ACK_WITH_NOT_CONSUMED; | 687 return ACK_WITH_NOT_CONSUMED; |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
771 if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) | 742 if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS) |
772 touch_consumer_states_.mark_bit(point.id); | 743 touch_consumer_states_.mark_bit(point.id); |
773 else | 744 else |
774 touch_consumer_states_.clear_bit(point.id); | 745 touch_consumer_states_.clear_bit(point.id); |
775 } | 746 } |
776 } | 747 } |
777 } | 748 } |
778 } | 749 } |
779 | 750 |
780 } // namespace content | 751 } // namespace content |
OLD | NEW |