Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1738)

Unified Diff: content/browser/renderer_host/input/touch_event_queue.cc

Issue 2663493003: Move the TouchEventQueue to be completely virtual. (Closed)
Patch Set: Fix nits Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/input/touch_event_queue.cc
diff --git a/content/browser/renderer_host/input/touch_event_queue.cc b/content/browser/renderer_host/input/touch_event_queue.cc
deleted file mode 100644
index 5b594addabcb2a729098642010cedc4c7f277e12..0000000000000000000000000000000000000000
--- a/content/browser/renderer_host/input/touch_event_queue.cc
+++ /dev/null
@@ -1,945 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/browser/renderer_host/input/touch_event_queue.h"
-
-#include <utility>
-
-#include "base/auto_reset.h"
-#include "base/macros.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/trace_event/trace_event.h"
-#include "content/browser/renderer_host/input/timeout_monitor.h"
-#include "content/common/input/web_touch_event_traits.h"
-#include "ui/events/base_event_utils.h"
-#include "ui/gfx/geometry/point_f.h"
-
-using blink::WebInputEvent;
-using blink::WebTouchEvent;
-using blink::WebTouchPoint;
-using ui::LatencyInfo;
-
-namespace content {
-namespace {
-
-// Time interval at which touchmove events will be forwarded to the client while
-// scrolling is active and possible.
-const double kAsyncTouchMoveIntervalSec = .2;
-
-// A sanity check on touches received to ensure that touch movement outside
-// the platform slop region will cause scrolling.
-const double kMaxConceivablePlatformSlopRegionLengthDipsSquared = 60. * 60.;
-
-TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent(
- const TouchEventWithLatencyInfo& event_to_cancel) {
- TouchEventWithLatencyInfo event = event_to_cancel;
- WebTouchEventTraits::ResetTypeAndTouchStates(
- WebInputEvent::TouchCancel,
- // TODO(rbyers): Shouldn't we use a fresh timestamp?
- event.event.timeStampSeconds(), &event.event);
- return event;
-}
-
-bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) {
- return (event.type() == WebInputEvent::TouchStart ||
- event.type() == WebInputEvent::TouchMove) &&
- event.dispatchType == WebInputEvent::Blocking;
-}
-
-// Compare all properties of touch points to determine the state.
-bool HasPointChanged(const WebTouchPoint& point_1,
- const WebTouchPoint& point_2) {
- DCHECK_EQ(point_1.id, point_2.id);
- if (point_1.screenPosition != point_2.screenPosition ||
- point_1.position != point_2.position ||
- point_1.radiusX != point_2.radiusX ||
- point_1.radiusY != point_2.radiusY ||
- point_1.rotationAngle != point_2.rotationAngle ||
- point_1.force != point_2.force ||
- point_1.tiltX != point_2.tiltX ||
- point_1.tiltY != point_2.tiltY) {
- return true;
- }
- return false;
-}
-
-} // namespace
-
-
-// Cancels a touch sequence if a touchstart or touchmove ack response is
-// sufficiently delayed.
-class TouchEventQueue::TouchTimeoutHandler {
- public:
- TouchTimeoutHandler(TouchEventQueue* touch_queue,
- base::TimeDelta desktop_timeout_delay,
- base::TimeDelta mobile_timeout_delay)
- : touch_queue_(touch_queue),
- desktop_timeout_delay_(desktop_timeout_delay),
- mobile_timeout_delay_(mobile_timeout_delay),
- use_mobile_timeout_(false),
- pending_ack_state_(PENDING_ACK_NONE),
- timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
- base::Unretained(this))),
- enabled_(true),
- enabled_for_current_sequence_(false),
- sequence_awaiting_uma_update_(false),
- sequence_using_mobile_timeout_(false) {
- SetUseMobileTimeout(false);
- }
-
- ~TouchTimeoutHandler() {
- LogSequenceEndForUMAIfNecessary(false);
- }
-
- void StartIfNecessary(const TouchEventWithLatencyInfo& event) {
- if (pending_ack_state_ != PENDING_ACK_NONE)
- return;
-
- if (!enabled_)
- return;
-
- const base::TimeDelta timeout_delay = GetTimeoutDelay();
- if (timeout_delay.is_zero())
- return;
-
- if (!ShouldTouchTriggerTimeout(event.event))
- return;
-
- if (WebTouchEventTraits::IsTouchSequenceStart(event.event)) {
- LogSequenceStartForUMA();
- enabled_for_current_sequence_ = true;
- }
-
- if (!enabled_for_current_sequence_)
- return;
-
- timeout_event_ = event;
- timeout_monitor_.Restart(timeout_delay);
- }
-
- bool ConfirmTouchEvent(InputEventAckState ack_result) {
- switch (pending_ack_state_) {
- case PENDING_ACK_NONE:
- if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
- enabled_for_current_sequence_ = false;
- timeout_monitor_.Stop();
- return false;
- case PENDING_ACK_ORIGINAL_EVENT:
- if (AckedTimeoutEventRequiresCancel(ack_result)) {
- SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
- TouchEventWithLatencyInfo cancel_event =
- ObtainCancelEventForTouchEvent(timeout_event_);
- touch_queue_->SendTouchEventImmediately(&cancel_event);
- } else {
- SetPendingAckState(PENDING_ACK_NONE);
- touch_queue_->UpdateTouchConsumerStates(timeout_event_.event,
- ack_result);
- }
- return true;
- case PENDING_ACK_CANCEL_EVENT:
- SetPendingAckState(PENDING_ACK_NONE);
- return true;
- }
- return false;
- }
-
- bool FilterEvent(const WebTouchEvent& event) {
- if (!HasTimeoutEvent())
- return false;
-
- if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
- // If a new sequence is observed while we're still waiting on the
- // timed-out sequence response, also count the new sequence as timed-out.
- LogSequenceStartForUMA();
- LogSequenceEndForUMAIfNecessary(true);
- }
-
- return true;
- }
-
- void SetEnabled(bool enabled) {
- if (enabled_ == enabled)
- return;
-
- enabled_ = enabled;
-
- if (enabled_)
- return;
-
- enabled_for_current_sequence_ = false;
- // Only reset the |timeout_handler_| if the timer is running and has not
- // yet timed out. This ensures that an already timed out sequence is
- // properly flushed by the handler.
- if (IsTimeoutTimerRunning()) {
- pending_ack_state_ = PENDING_ACK_NONE;
- timeout_monitor_.Stop();
- }
- }
-
- void SetUseMobileTimeout(bool use_mobile_timeout) {
- use_mobile_timeout_ = use_mobile_timeout;
- }
-
- bool IsTimeoutTimerRunning() const { return timeout_monitor_.IsRunning(); }
-
- bool IsEnabled() const {
- return enabled_ && !GetTimeoutDelay().is_zero();
- }
-
- private:
- enum PendingAckState {
- PENDING_ACK_NONE,
- PENDING_ACK_ORIGINAL_EVENT,
- PENDING_ACK_CANCEL_EVENT,
- };
-
- void OnTimeOut() {
- LogSequenceEndForUMAIfNecessary(true);
- SetPendingAckState(PENDING_ACK_ORIGINAL_EVENT);
- touch_queue_->FlushQueue();
- }
-
- // Skip a cancel event if the timed-out event had no consumer and was the
- // initial event in the gesture.
- bool AckedTimeoutEventRequiresCancel(InputEventAckState ack_result) const {
- DCHECK(HasTimeoutEvent());
- if (ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
- return true;
- return !WebTouchEventTraits::IsTouchSequenceStart(timeout_event_.event);
- }
-
- void SetPendingAckState(PendingAckState new_pending_ack_state) {
- DCHECK_NE(pending_ack_state_, new_pending_ack_state);
- switch (new_pending_ack_state) {
- case PENDING_ACK_ORIGINAL_EVENT:
- DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
- TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventTimeout", this);
- break;
- case PENDING_ACK_CANCEL_EVENT:
- DCHECK_EQ(pending_ack_state_, PENDING_ACK_ORIGINAL_EVENT);
- DCHECK(!timeout_monitor_.IsRunning());
- DCHECK(touch_queue_->empty());
- TRACE_EVENT_ASYNC_STEP_INTO0(
- "input", "TouchEventTimeout", this, "CancelEvent");
- break;
- case PENDING_ACK_NONE:
- DCHECK(!timeout_monitor_.IsRunning());
- DCHECK(touch_queue_->empty());
- TRACE_EVENT_ASYNC_END0("input", "TouchEventTimeout", this);
- break;
- }
- pending_ack_state_ = new_pending_ack_state;
- }
-
- void LogSequenceStartForUMA() {
- // Always flush any unlogged entries before starting a new one.
- LogSequenceEndForUMAIfNecessary(false);
- sequence_awaiting_uma_update_ = true;
- sequence_using_mobile_timeout_ = use_mobile_timeout_;
- }
-
- void LogSequenceEndForUMAIfNecessary(bool timed_out) {
- if (!sequence_awaiting_uma_update_)
- return;
-
- sequence_awaiting_uma_update_ = false;
-
- if (sequence_using_mobile_timeout_) {
- UMA_HISTOGRAM_BOOLEAN("Event.Touch.TimedOutOnMobileSite", timed_out);
- } else {
- UMA_HISTOGRAM_BOOLEAN("Event.Touch.TimedOutOnDesktopSite", timed_out);
- }
- }
-
- base::TimeDelta GetTimeoutDelay() const {
- return use_mobile_timeout_ ? mobile_timeout_delay_ : desktop_timeout_delay_;
- }
-
- bool HasTimeoutEvent() const {
- return pending_ack_state_ != PENDING_ACK_NONE;
- }
-
- TouchEventQueue* touch_queue_;
-
- // How long to wait on a touch ack before cancelling the touch sequence.
- const base::TimeDelta desktop_timeout_delay_;
- const base::TimeDelta mobile_timeout_delay_;
- bool use_mobile_timeout_;
-
- // The touch event source for which we expect the next ack.
- PendingAckState pending_ack_state_;
-
- // The event for which the ack timeout is triggered.
- TouchEventWithLatencyInfo timeout_event_;
-
- // Provides timeout-based callback behavior.
- TimeoutMonitor timeout_monitor_;
-
- bool enabled_;
- bool enabled_for_current_sequence_;
-
- // Bookkeeping to classify and log whether a touch sequence times out.
- bool sequence_awaiting_uma_update_;
- bool sequence_using_mobile_timeout_;
-};
-
-// Provides touchmove slop suppression for a touch sequence until a
-// (unprevented) touch will trigger immediate scrolling.
-class TouchEventQueue::TouchMoveSlopSuppressor {
- public:
- TouchMoveSlopSuppressor() : suppressing_touchmoves_(false) {}
-
- bool FilterEvent(const WebTouchEvent& event) {
- if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
- suppressing_touchmoves_ = true;
- touch_start_location_ = gfx::PointF(event.touches[0].position);
- }
-
- if (event.type() == WebInputEvent::TouchEnd ||
- event.type() == WebInputEvent::TouchCancel)
- suppressing_touchmoves_ = false;
-
- if (event.type() != WebInputEvent::TouchMove)
- return false;
-
- if (suppressing_touchmoves_) {
- if (event.touchesLength > 1) {
- suppressing_touchmoves_ = false;
- } else if (event.movedBeyondSlopRegion) {
- suppressing_touchmoves_ = false;
- } else {
- // No sane slop region should be larger than 60 DIPs.
- DCHECK_LT((gfx::PointF(event.touches[0].position) -
- touch_start_location_).LengthSquared(),
- kMaxConceivablePlatformSlopRegionLengthDipsSquared);
- }
- }
-
- return suppressing_touchmoves_;
- }
-
- void ConfirmTouchEvent(InputEventAckState ack_result) {
- if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
- suppressing_touchmoves_ = false;
- }
-
- bool suppressing_touchmoves() const { return suppressing_touchmoves_; }
-
- private:
- bool suppressing_touchmoves_;
-
- // Sanity check that the upstream touch provider is properly reporting whether
- // the touch sequence will cause scrolling.
- gfx::PointF touch_start_location_;
-
- DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor);
-};
-
-// This class represents a single coalesced touch event. However, it also keeps
-// track of all the original touch-events that were coalesced into a single
-// event. The coalesced event is forwarded to the renderer, while the original
-// touch-events are sent to the Client (on ACK for the coalesced event) so that
-// the Client receives the event with their original timestamp.
-class CoalescedWebTouchEvent {
- public:
- CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
- bool suppress_client_ack)
- : coalesced_event_(event), suppress_client_ack_(suppress_client_ack) {
- TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
- }
-
- ~CoalescedWebTouchEvent() {
- TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
- }
-
- // Coalesces the event with the existing event if possible. Returns whether
- // the event was coalesced.
- bool CoalesceEventIfPossible(
- const TouchEventWithLatencyInfo& event_with_latency) {
- if (suppress_client_ack_)
- return false;
-
- if (!coalesced_event_.CanCoalesceWith(event_with_latency))
- return false;
-
- // Addition of the first event to |uncoaleseced_events_to_ack_| is deferred
- // until the first coalesced event, optimizing the (common) case where the
- // event is not coalesced at all.
- if (uncoaleseced_events_to_ack_.empty())
- uncoaleseced_events_to_ack_.push_back(coalesced_event_);
-
- TRACE_EVENT_INSTANT0(
- "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
- coalesced_event_.CoalesceWith(event_with_latency);
- uncoaleseced_events_to_ack_.push_back(event_with_latency);
- DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
- return true;
- }
-
- void DispatchAckToClient(InputEventAckState ack_result,
- const ui::LatencyInfo* optional_latency_info,
- TouchEventQueueClient* client) {
- DCHECK(client);
- if (suppress_client_ack_)
- return;
-
- if (uncoaleseced_events_to_ack_.empty()) {
- if (optional_latency_info)
- coalesced_event_.latency.AddNewLatencyFrom(*optional_latency_info);
- client->OnTouchEventAck(coalesced_event_, ack_result);
- return;
- }
-
- DCHECK_GE(uncoaleseced_events_to_ack_.size(), 2U);
- for (WebTouchEventWithLatencyList::iterator
- iter = uncoaleseced_events_to_ack_.begin(),
- end = uncoaleseced_events_to_ack_.end();
- iter != end;
- ++iter) {
- if (optional_latency_info)
- iter->latency.AddNewLatencyFrom(*optional_latency_info);
- client->OnTouchEventAck(*iter, ack_result);
- }
- }
-
- const TouchEventWithLatencyInfo& coalesced_event() const {
- return coalesced_event_;
- }
-
- private:
- // This is the event that is forwarded to the renderer.
- TouchEventWithLatencyInfo coalesced_event_;
-
- // This is the list of the original events that were coalesced, each requiring
- // future ack dispatch to the client.
- // Note that this will be empty if no coalescing has occurred.
- typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
- WebTouchEventWithLatencyList uncoaleseced_events_to_ack_;
-
- bool suppress_client_ack_;
-
- DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
-};
-
-TouchEventQueue::Config::Config()
- : desktop_touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(200)),
- mobile_touch_ack_timeout_delay(base::TimeDelta::FromMilliseconds(1000)),
- touch_ack_timeout_supported(false) {
-}
-
-TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
- const Config& config)
- : client_(client),
- dispatching_touch_ack_(false),
- dispatching_touch_(false),
- has_handlers_(true),
- has_handler_for_current_sequence_(false),
- drop_remaining_touches_in_sequence_(false),
- touchmove_slop_suppressor_(new TouchMoveSlopSuppressor),
- send_touch_events_async_(false),
- last_sent_touch_timestamp_sec_(0) {
- DCHECK(client);
- if (config.touch_ack_timeout_supported) {
- timeout_handler_.reset(
- new TouchTimeoutHandler(this,
- config.desktop_touch_ack_timeout_delay,
- config.mobile_touch_ack_timeout_delay));
- }
-}
-
-TouchEventQueue::~TouchEventQueue() {
-}
-
-void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
- TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
-
- // If the queueing of |event| was triggered by an ack dispatch, defer
- // processing the event until the dispatch has finished.
- if (touch_queue_.empty() && !dispatching_touch_ack_) {
- // Optimization of the case without touch handlers. Removing this path
- // yields identical results, but this avoids unnecessary allocations.
- PreFilterResult filter_result = FilterBeforeForwarding(event.event);
- if (filter_result != FORWARD_TO_RENDERER) {
- client_->OnFilteringTouchEvent(event.event);
- client_->OnTouchEventAck(event,
- filter_result == ACK_WITH_NO_CONSUMER_EXISTS
- ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
- : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- return;
- }
-
- // There is no touch event in the queue. Forward it to the renderer
- // immediately.
- touch_queue_.push_back(
- base::MakeUnique<CoalescedWebTouchEvent>(event, false));
- ForwardNextEventToRenderer();
- return;
- }
-
- // If the last queued touch-event was a touch-move, and the current event is
- // also a touch-move, then the events can be coalesced into a single event.
- if (touch_queue_.size() > 1) {
- CoalescedWebTouchEvent* last_event = touch_queue_.back().get();
- if (last_event->CoalesceEventIfPossible(event))
- return;
- }
- touch_queue_.push_back(
- base::MakeUnique<CoalescedWebTouchEvent>(event, false));
-}
-
-void TouchEventQueue::PrependTouchScrollNotification() {
- TRACE_EVENT0("input", "TouchEventQueue::PrependTouchScrollNotification");
-
- // The queue should have an in-flight event when this method is called because
- // this method is triggered by InputRouterImpl::SendGestureEvent, which is
- // triggered by TouchEventQueue::AckTouchEventToClient, which has just
- // received an ack for the in-flight event. We leave the head of the queue
- // untouched since it is the in-flight event.
- //
- // However, for the (integration) tests in RenderWidgetHostTest that trigger
- // this method indirectly, they push the TouchScrollStarted event into
- // TouchEventQueue without any way to dispatch it. Below we added a check for
- // non-empty queue to keep those tests as-is w/o exposing internals of this
- // class all the way up.
- if (!touch_queue_.empty()) {
- TouchEventWithLatencyInfo touch(
- WebInputEvent::TouchScrollStarted, WebInputEvent::NoModifiers,
- ui::EventTimeStampToSeconds(ui::EventTimeForNow()), LatencyInfo());
- touch.event.dispatchType = WebInputEvent::EventNonBlocking;
-
- auto it = touch_queue_.begin();
- DCHECK(it != touch_queue_.end());
- touch_queue_.insert(++it,
- base::MakeUnique<CoalescedWebTouchEvent>(touch, false));
- }
-}
-
-void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
- const LatencyInfo& latency_info,
- const uint32_t unique_touch_event_id) {
- TRACE_EVENT0("input", "TouchEventQueue::ProcessTouchAck");
-
- // We receive an ack for async touchmove from render.
- if (!ack_pending_async_touchmove_ids_.empty() &&
- ack_pending_async_touchmove_ids_.front() == unique_touch_event_id) {
- // Remove the first touchmove from the ack_pending_async_touchmove queue.
- ack_pending_async_touchmove_ids_.pop_front();
- // Send the next pending async touch move once we receive all acks back.
- if (pending_async_touchmove_ && ack_pending_async_touchmove_ids_.empty()) {
- DCHECK(touch_queue_.empty());
-
- // Dispatch the next pending async touch move when time expires.
- if (pending_async_touchmove_->event.timeStampSeconds() >=
- last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) {
- FlushPendingAsyncTouchmove();
- }
- }
- return;
- }
-
- DCHECK(!dispatching_touch_ack_);
- dispatching_touch_ = false;
-
- if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent(ack_result))
- return;
-
- touchmove_slop_suppressor_->ConfirmTouchEvent(ack_result);
-
- if (touch_queue_.empty())
- return;
-
- // We don't care about the ordering of the acks vs the ordering of the
- // dispatched events because we can receive the ack for event B before the ack
- // for event A even though A was sent before B. This seems to be happening
- // when, for example, A is acked from renderer but B isn't, so the ack for B
- // is synthesized "locally" in InputRouter.
- //
- // TODO(crbug.com/600773): Bring the id checks back when dispatch triggering
- // is sane.
-
- PopTouchEventToClient(ack_result, latency_info);
- TryForwardNextEventToRenderer();
-}
-
-void TouchEventQueue::TryForwardNextEventToRenderer() {
- DCHECK(!dispatching_touch_ack_);
- // If there are queued touch events, then try to forward them to the renderer
- // immediately, or ACK the events back to the client if appropriate.
- while (!touch_queue_.empty()) {
- const WebTouchEvent& event = touch_queue_.front()->coalesced_event().event;
- PreFilterResult filter_result = FilterBeforeForwarding(event);
- if (filter_result != FORWARD_TO_RENDERER)
- client_->OnFilteringTouchEvent(event);
- switch (filter_result) {
- case ACK_WITH_NO_CONSUMER_EXISTS:
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
- break;
- case ACK_WITH_NOT_CONSUMED:
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- break;
- case FORWARD_TO_RENDERER:
- ForwardNextEventToRenderer();
- return;
- }
- }
-}
-
-void TouchEventQueue::ForwardNextEventToRenderer() {
- TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
-
- DCHECK(!empty());
- DCHECK(!dispatching_touch_);
- TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
-
- if (send_touch_events_async_ &&
- touch.event.type() == WebInputEvent::TouchMove) {
- // Throttling touchmove's in a continuous touchmove stream while scrolling
- // reduces the risk of jank. However, it's still important that the web
- // application be sent touches at key points in the gesture stream,
- // e.g., when the application slop region is exceeded or touchmove
- // coalescing fails because of different modifiers.
- bool send_touchmove_now = size() > 1;
- send_touchmove_now |= pending_async_touchmove_ &&
- !pending_async_touchmove_->CanCoalesceWith(touch);
- send_touchmove_now |=
- ack_pending_async_touchmove_ids_.empty() &&
- (touch.event.timeStampSeconds() >=
- last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec);
-
- if (!send_touchmove_now) {
- if (!pending_async_touchmove_) {
- pending_async_touchmove_.reset(new TouchEventWithLatencyInfo(touch));
- } else {
- DCHECK(pending_async_touchmove_->CanCoalesceWith(touch));
- pending_async_touchmove_->CoalesceWith(touch);
- }
- DCHECK_EQ(1U, size());
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- // It's possible (though unlikely) that ack'ing the current touch will
- // trigger the queueing of another touch event (e.g., a touchcancel). As
- // forwarding of the queued event will be deferred while the ack is being
- // dispatched (see |OnTouchEvent()|), try forwarding it now.
- TryForwardNextEventToRenderer();
- return;
- }
- }
-
- last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds();
-
- // Flush any pending async touch move. If it can be combined with the current
- // (touchmove) event, great, otherwise send it immediately but separately. Its
- // ack will trigger forwarding of the original |touch| event.
- if (pending_async_touchmove_) {
- if (pending_async_touchmove_->CanCoalesceWith(touch)) {
- pending_async_touchmove_->CoalesceWith(touch);
- pending_async_touchmove_->event.dispatchType =
- send_touch_events_async_ ? WebInputEvent::EventNonBlocking
- : WebInputEvent::Blocking;
- touch = *pending_async_touchmove_;
- pending_async_touchmove_.reset();
- } else {
- FlushPendingAsyncTouchmove();
- return;
- }
- }
-
- // Note: Touchstart events are marked cancelable to allow transitions between
- // platform scrolling and JS pinching. Touchend events, however, remain
- // uncancelable, mitigating the risk of jank when transitioning to a fling.
- if (send_touch_events_async_ &&
- touch.event.type() != WebInputEvent::TouchStart)
- touch.event.dispatchType = WebInputEvent::EventNonBlocking;
-
- SendTouchEventImmediately(&touch);
-}
-
-void TouchEventQueue::FlushPendingAsyncTouchmove() {
- DCHECK(!dispatching_touch_);
- std::unique_ptr<TouchEventWithLatencyInfo> touch =
- std::move(pending_async_touchmove_);
- touch->event.dispatchType = WebInputEvent::EventNonBlocking;
- touch_queue_.push_front(
- base::MakeUnique<CoalescedWebTouchEvent>(*touch, true));
- SendTouchEventImmediately(touch.get());
-}
-
-void TouchEventQueue::OnGestureScrollEvent(
- const GestureEventWithLatencyInfo& gesture_event) {
- if (gesture_event.event.type() == blink::WebInputEvent::GestureScrollBegin) {
- if (has_handler_for_current_sequence_ &&
- !drop_remaining_touches_in_sequence_) {
- DCHECK(!touchmove_slop_suppressor_->suppressing_touchmoves())
- << "A touch handler should be offered a touchmove before scrolling.";
- }
-
- pending_async_touchmove_.reset();
-
- return;
- }
-
- if (gesture_event.event.type() == blink::WebInputEvent::GestureScrollUpdate &&
- gesture_event.event.resendingPluginId == -1) {
- send_touch_events_async_ = true;
- }
-}
-
-void TouchEventQueue::OnGestureEventAck(
- const GestureEventWithLatencyInfo& event,
- InputEventAckState ack_result) {
- // Throttle sending touchmove events as long as the scroll events are handled.
- // Note that there's no guarantee that this ACK is for the most recent
- // gesture event (or even part of the current sequence). Worst case, the
- // delay in updating the absorption state will result in minor UI glitches.
- // A valid |pending_async_touchmove_| will be flushed when the next event is
- // forwarded. Scroll updates that are being resent from a GuestView are
- // ignored.
- if (event.event.type() == blink::WebInputEvent::GestureScrollUpdate &&
- event.event.resendingPluginId == -1) {
- send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
- }
-}
-
-void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
- DCHECK(!dispatching_touch_ack_);
- DCHECK(!dispatching_touch_);
- has_handlers_ = has_handlers;
-}
-
-bool TouchEventQueue::IsPendingAckTouchStart() const {
- DCHECK(!dispatching_touch_ack_);
- if (touch_queue_.empty())
- return false;
-
- const blink::WebTouchEvent& event =
- touch_queue_.front()->coalesced_event().event;
- return (event.type() == WebInputEvent::TouchStart);
-}
-
-void TouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
- if (timeout_handler_)
- timeout_handler_->SetEnabled(enabled);
-}
-
-void TouchEventQueue::SetIsMobileOptimizedSite(bool mobile_optimized_site) {
- if (timeout_handler_)
- timeout_handler_->SetUseMobileTimeout(mobile_optimized_site);
-}
-
-bool TouchEventQueue::IsAckTimeoutEnabled() const {
- return timeout_handler_ && timeout_handler_->IsEnabled();
-}
-
-bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
- return !!pending_async_touchmove_;
-}
-
-bool TouchEventQueue::IsTimeoutRunningForTesting() const {
- return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
-}
-
-const TouchEventWithLatencyInfo&
-TouchEventQueue::GetLatestEventForTesting() const {
- return touch_queue_.back()->coalesced_event();
-}
-
-void TouchEventQueue::FlushQueue() {
- DCHECK(!dispatching_touch_ack_);
- DCHECK(!dispatching_touch_);
- pending_async_touchmove_.reset();
- drop_remaining_touches_in_sequence_ = true;
- while (!touch_queue_.empty())
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
-}
-
-void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
- AckTouchEventToClient(ack_result, nullptr);
-}
-
-void TouchEventQueue::PopTouchEventToClient(
- InputEventAckState ack_result,
- const LatencyInfo& renderer_latency_info) {
- AckTouchEventToClient(ack_result, &renderer_latency_info);
-}
-
-void TouchEventQueue::AckTouchEventToClient(
- InputEventAckState ack_result,
- const ui::LatencyInfo* optional_latency_info) {
- DCHECK(!dispatching_touch_ack_);
- if (touch_queue_.empty()) {
- NOTREACHED() << "Too many acks";
- return;
- }
- std::unique_ptr<CoalescedWebTouchEvent> acked_event =
- std::move(touch_queue_.front());
- DCHECK(acked_event);
-
- UpdateTouchConsumerStates(acked_event->coalesced_event().event, ack_result);
-
- // Note that acking the touch-event may result in multiple gestures being sent
- // to the renderer, or touch-events being queued.
- base::AutoReset<bool> dispatching_touch_ack(&dispatching_touch_ack_, true);
-
- // Skip ack for TouchScrollStarted since it was synthesized within the queue.
- if (acked_event->coalesced_event().event.type() !=
- WebInputEvent::TouchScrollStarted) {
- acked_event->DispatchAckToClient(ack_result, optional_latency_info,
- client_);
- }
-
- touch_queue_.pop_front();
-}
-
-void TouchEventQueue::SendTouchEventImmediately(
- TouchEventWithLatencyInfo* touch) {
- // TODO(crbug.com/600773): Hack to avoid cyclic reentry to this method.
- if (dispatching_touch_)
- return;
-
- if (touch->event.type() == WebInputEvent::TouchStart)
- touch->event.touchStartOrFirstTouchMove = true;
-
- // For touchmove events, compare touch points position from current event
- // to last sent event and update touch points state.
- if (touch->event.type() == WebInputEvent::TouchMove) {
- CHECK(last_sent_touchevent_);
- if (last_sent_touchevent_->type() == WebInputEvent::TouchStart)
- touch->event.touchStartOrFirstTouchMove = true;
- for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) {
- const WebTouchPoint& last_touch_point =
- last_sent_touchevent_->touches[i];
- // Touches with same id may not have same index in Touches array.
- for (unsigned int j = 0; j < touch->event.touchesLength; ++j) {
- const WebTouchPoint& current_touchmove_point = touch->event.touches[j];
- if (current_touchmove_point.id != last_touch_point.id)
- continue;
-
- if (!HasPointChanged(last_touch_point, current_touchmove_point))
- touch->event.touches[j].state = WebTouchPoint::StateStationary;
-
- break;
- }
- }
- }
-
- if (touch->event.type() != WebInputEvent::TouchScrollStarted) {
- if (last_sent_touchevent_)
- *last_sent_touchevent_ = touch->event;
- else
- last_sent_touchevent_.reset(new WebTouchEvent(touch->event));
- }
-
- base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
-
- client_->SendTouchEventImmediately(*touch);
-
- // A synchronous ack will reset |dispatching_touch_|, in which case the touch
- // timeout should not be started and the count also should not be increased.
- if (dispatching_touch_) {
- if (touch->event.type() == WebInputEvent::TouchMove &&
- touch->event.dispatchType != WebInputEvent::Blocking) {
- // When we send out a uncancelable touch move, we increase the count and
- // we do not process input event ack any more, we will just ack to client
- // and wait for the ack from render. Also we will remove it from the front
- // of the queue.
- ack_pending_async_touchmove_ids_.push_back(
- touch->event.uniqueTouchEventId);
- dispatching_touch_ = false;
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_IGNORED);
- TryForwardNextEventToRenderer();
- return;
- }
-
- if (timeout_handler_)
- timeout_handler_->StartIfNecessary(*touch);
- }
-}
-
-TouchEventQueue::PreFilterResult
-TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
- if (event.type() == WebInputEvent::TouchScrollStarted)
- return FORWARD_TO_RENDERER;
-
- if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
- has_handler_for_current_sequence_ = false;
- send_touch_events_async_ = false;
- pending_async_touchmove_.reset();
- last_sent_touchevent_.reset();
-
- touch_sequence_start_position_ = gfx::PointF(event.touches[0].position);
- drop_remaining_touches_in_sequence_ = false;
- if (!has_handlers_) {
- drop_remaining_touches_in_sequence_ = true;
- return ACK_WITH_NO_CONSUMER_EXISTS;
- }
- }
-
- if (timeout_handler_ && timeout_handler_->FilterEvent(event))
- return ACK_WITH_NO_CONSUMER_EXISTS;
-
- if (touchmove_slop_suppressor_->FilterEvent(event))
- return ACK_WITH_NOT_CONSUMED;
-
- if (drop_remaining_touches_in_sequence_ &&
- event.type() != WebInputEvent::TouchCancel) {
- return ACK_WITH_NO_CONSUMER_EXISTS;
- }
-
- if (event.type() == WebInputEvent::TouchStart) {
- return (has_handlers_ || has_handler_for_current_sequence_)
- ? FORWARD_TO_RENDERER
- : ACK_WITH_NO_CONSUMER_EXISTS;
- }
-
- if (has_handler_for_current_sequence_) {
- // Only forward a touch if it has a non-stationary pointer that is active
- // in the current touch sequence.
- for (size_t i = 0; i < event.touchesLength; ++i) {
- const WebTouchPoint& point = event.touches[i];
- if (point.state == WebTouchPoint::StateStationary)
- continue;
-
- // |last_sent_touchevent_| will be non-null as long as there is an
- // active touch sequence being forwarded to the renderer.
- if (!last_sent_touchevent_)
- continue;
-
- for (size_t j = 0; j < last_sent_touchevent_->touchesLength; ++j) {
- if (point.id != last_sent_touchevent_->touches[j].id)
- continue;
-
- if (event.type() != WebInputEvent::TouchMove)
- return FORWARD_TO_RENDERER;
-
- // All pointers in TouchMove events may have state as StateMoved,
- // even though none of the pointers have not changed in real.
- // Forward these events when at least one pointer has changed.
- if (HasPointChanged(last_sent_touchevent_->touches[j], point))
- return FORWARD_TO_RENDERER;
-
- // This is a TouchMove event for which we have yet to find a
- // non-stationary pointer. Continue checking the next pointers
- // in the |event|.
- break;
- }
-
- }
- }
-
- return ACK_WITH_NO_CONSUMER_EXISTS;
-}
-
-void TouchEventQueue::UpdateTouchConsumerStates(const WebTouchEvent& event,
- InputEventAckState ack_result) {
- if (event.type() == WebInputEvent::TouchStart) {
- if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
- send_touch_events_async_ = false;
- has_handler_for_current_sequence_ |=
- ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
- } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) {
- has_handler_for_current_sequence_ = false;
- }
-}
-
-} // namespace content

Powered by Google App Engine
This is Rietveld 408576698