| Index: content/browser/renderer_host/input/touch_timeout_handler.cc
|
| diff --git a/content/browser/renderer_host/input/touch_timeout_handler.cc b/content/browser/renderer_host/input/touch_timeout_handler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a3bf25642ff92c79a402a7534b9465ff22e58f09
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/input/touch_timeout_handler.cc
|
| @@ -0,0 +1,216 @@
|
| +// Copyright 2017 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_timeout_handler.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/touch_event_queue.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 {
|
| +
|
| +bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) {
|
| + return (event.type() == WebInputEvent::TouchStart ||
|
| + event.type() == WebInputEvent::TouchMove) &&
|
| + event.dispatchType == WebInputEvent::Blocking;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +TouchTimeoutHandler::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::~TouchTimeoutHandler() {
|
| + LogSequenceEndForUMAIfNecessary(false);
|
| +}
|
| +
|
| +void TouchTimeoutHandler::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 TouchTimeoutHandler::ConfirmTouchEvent(uint32_t unique_touch_event_id,
|
| + InputEventAckState ack_result) {
|
| + if (timeout_event_.event.uniqueTouchEventId != unique_touch_event_id)
|
| + return false;
|
| +
|
| + 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);
|
| + touch_queue_->SendTouchCancelEventForTouchEvent(timeout_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 TouchTimeoutHandler::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 TouchTimeoutHandler::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 TouchTimeoutHandler::SetUseMobileTimeout(bool use_mobile_timeout) {
|
| + use_mobile_timeout_ = use_mobile_timeout;
|
| +}
|
| +
|
| +void TouchTimeoutHandler::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 TouchTimeoutHandler::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 TouchTimeoutHandler::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 TouchTimeoutHandler::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 TouchTimeoutHandler::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 TouchTimeoutHandler::GetTimeoutDelay() const {
|
| + return use_mobile_timeout_ ? mobile_timeout_delay_ : desktop_timeout_delay_;
|
| +}
|
| +
|
| +bool TouchTimeoutHandler::HasTimeoutEvent() const {
|
| + return pending_ack_state_ != PENDING_ACK_NONE;
|
| +}
|
| +
|
| +} // namespace content
|
|
|