| 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 |