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

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

Issue 48973005: Move TouchEvent timeout code to the TouchEventQueue (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Cleanup Created 7 years 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
index ecc12056729c1ec58ffeea36e929288e7f08c5ca..7d29f0f0e775fa8936bbb70cd3538aa2c5fb052a 100644
--- a/content/browser/renderer_host/input/touch_event_queue.cc
+++ b/content/browser/renderer_host/input/touch_event_queue.cc
@@ -5,12 +5,121 @@
#include "content/browser/renderer_host/input/touch_event_queue.h"
#include "base/auto_reset.h"
+#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/stl_util.h"
+#include "content/browser/renderer_host/input/timeout_monitor.h"
+#include "content/common/input/web_input_event_traits.h"
+#include "content/public/common/content_switches.h"
+
+using blink::WebInputEvent;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
namespace content {
+namespace {
+
+const InputEventAckState kDefaultNotForwardedAck =
+ INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
+typedef std::map<int, InputEventAckState> AckStates;
+
+TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent(
+ const TouchEventWithLatencyInfo& event_to_cancel) {
+ TouchEventWithLatencyInfo event = event_to_cancel;
+ event.event.type = WebInputEvent::TouchCancel;
+ for (size_t i = 0; i < event.event.touchesLength; i++)
+ event.event.touches[i].state = WebTouchPoint::StateCancelled;
+ return event;
+}
+
+} // namespace
+
+class TouchEventQueue::TouchTimeoutHandler {
+ public:
+ TouchTimeoutHandler(TouchEventQueue* touch_queue, size_t timeout_delay_ms)
+ : touch_queue_(touch_queue),
+ timeout_delay_(base::TimeDelta::FromMilliseconds(timeout_delay_ms)),
+ pending_ack_state_(PENDING_ACK_NONE),
+ timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
+ base::Unretained(this))) {}
+
+ ~TouchTimeoutHandler() {}
+
+ void Start(const TouchEventWithLatencyInfo& event) {
+ DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
+ timeout_event_ = event;
+ timeout_monitor_.Restart(timeout_delay_);
+ }
+
+ bool ConfirmTouchEvent() {
+ switch (pending_ack_state_) {
+ case PENDING_ACK_NONE:
+ timeout_monitor_.Stop();
+ return false;
+ case PENDING_ACK_ORIGINAL_EVENT:
Xianzhu 2013/12/05 17:48:40 In original Java method, because we can only have
jdduke (slow) 2013/12/05 18:26:04 The restriction is important only when we care abo
Xianzhu 2013/12/05 18:30:18 Then pending_ack_state_ could be pending_ack_count
+ DCHECK(!timeout_monitor_.IsRunning());
+ DCHECK(touch_queue_->empty());
+ TRACE_EVENT_ASYNC_STEP_INTO0(
+ "input", "TouchEventQueue::TouchEventTimeout", this, "CancelEvent");
+ pending_ack_state_ = PENDING_ACK_CANCEL_EVENT;
+ return true;
+ case PENDING_ACK_CANCEL_EVENT:
+ DCHECK(!timeout_monitor_.IsRunning());
+ DCHECK(touch_queue_->empty());
+ TRACE_EVENT_ASYNC_BEGIN0(
Xianzhu 2013/12/05 17:48:40 TRACE_EVENT_ASYNC_END0?
jdduke (slow) 2013/12/05 18:26:04 Good catch, fixed.
+ "input", "TouchEventQueue::TouchEventTimeout", this);
+ pending_ack_state_ = PENDING_ACK_NONE;
+ return true;
+ }
+ return false;
+ }
+
+ bool HasTimeoutEvent() const {
+ return pending_ack_state_ != PENDING_ACK_NONE;
+ }
+
+ bool IsTimeoutTimerRunning() const {
+ return timeout_monitor_.IsRunning();
+ }
+
+ private:
+ void OnTimeOut() {
+ TRACE_EVENT_ASYNC_BEGIN0(
+ "input", "TouchEventQueue::TouchEventTimeout", this);
+ pending_ack_state_ = PENDING_ACK_ORIGINAL_EVENT;
+ touch_queue_->FlushQueue();
+ DCHECK(touch_queue_->empty());
+
+ TouchEventWithLatencyInfo cancel_event =
+ ObtainCancelEventForTouchEvent(timeout_event_);
+ touch_queue_->UpdateTouchAckStates(
+ cancel_event.event, kDefaultNotForwardedAck);
+ touch_queue_->client_->SendTouchEventImmediately(cancel_event);
+ }
+
+
+ TouchEventQueue* touch_queue_;
+
+ // How long to wait on a touch ack before cancelling the touch sequence.
+ base::TimeDelta timeout_delay_;
+
+ // The touch event source for which we expect the next ack.
+ enum PendingAckState {
+ PENDING_ACK_NONE,
+ PENDING_ACK_ORIGINAL_EVENT,
+ PENDING_ACK_CANCEL_EVENT,
+ };
+ PendingAckState pending_ack_state_;
+
+ // The event for which the ack timeout is triggered.
+ TouchEventWithLatencyInfo timeout_event_;
+
+ // Provides timeout-based callback behavior.
+ TimeoutMonitor timeout_monitor_;
+};
+
// 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
@@ -19,9 +128,10 @@ typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList;
// the Client receives the event with their original timestamp.
class CoalescedWebTouchEvent {
public:
- explicit CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event)
+ explicit CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event,
+ bool ignore_ack)
: coalesced_event_(event),
- ignore_ack_(false) {
+ ignore_ack_(ignore_ack) {
events_.push_back(event);
TRACE_EVENT_ASYNC_BEGIN0(
"input", "TouchEventQueue::QueueEvent", this);
@@ -64,7 +174,6 @@ class CoalescedWebTouchEvent {
size_t size() const { return events_.size(); }
bool ignore_ack() const { return ignore_ack_; }
- void set_ignore_ack(bool value) { ignore_ack_ = value; }
private:
// This is the event that is forwarded to the renderer.
@@ -83,7 +192,10 @@ class CoalescedWebTouchEvent {
TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client)
: client_(client),
dispatching_touch_ack_(NULL),
- no_touch_to_renderer_(false) {
+ dispatching_touch_(false),
+ no_touch_to_renderer_(false),
+ renderer_is_consuming_gesture_(false),
+ ack_timeout_enabled_(false) {
DCHECK(client);
}
@@ -98,7 +210,7 @@ void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
if (touch_queue_.empty() && !dispatching_touch_ack_) {
// There is no touch event in the queue. Forward it to the renderer
// immediately.
- touch_queue_.push_back(new CoalescedWebTouchEvent(event));
+ touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
TryForwardNextEventToRenderer();
return;
}
@@ -110,35 +222,26 @@ void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
if (last_event->CoalesceEventIfPossible(event))
return;
}
- touch_queue_.push_back(new CoalescedWebTouchEvent(event));
+ touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
}
void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result,
const ui::LatencyInfo& latency_info) {
DCHECK(!dispatching_touch_ack_);
+ dispatching_touch_ = false;
+
+ if (timeout_handler_ && timeout_handler_->ConfirmTouchEvent())
+ return;
+
if (touch_queue_.empty())
return;
- // Update the ACK status for each touch point in the ACKed event.
- const blink::WebTouchEvent& event =
- touch_queue_.front()->coalesced_event().event;
- if (event.type == blink::WebInputEvent::TouchEnd ||
- event.type == blink::WebInputEvent::TouchCancel) {
- // The points have been released. Erase the ACK states.
- for (unsigned i = 0; i < event.touchesLength; ++i) {
- const blink::WebTouchPoint& point = event.touches[i];
- if (point.state == blink::WebTouchPoint::StateReleased ||
- point.state == blink::WebTouchPoint::StateCancelled)
- touch_ack_states_.erase(point.id);
- }
- } else if (event.type == blink::WebInputEvent::TouchStart) {
- for (unsigned i = 0; i < event.touchesLength; ++i) {
- const blink::WebTouchPoint& point = event.touches[i];
- if (point.state == blink::WebTouchPoint::StatePressed)
- touch_ack_states_[point.id] = ack_result;
- }
- }
+ if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
+ renderer_is_consuming_gesture_ = true;
+ const WebTouchEvent& acked_event =
+ touch_queue_.front()->coalesced_event().event;
+ UpdateTouchAckStates(acked_event, ack_result);
PopTouchEventToClient(ack_result, latency_info);
TryForwardNextEventToRenderer();
}
@@ -150,12 +253,30 @@ void TouchEventQueue::TryForwardNextEventToRenderer() {
while (!touch_queue_.empty()) {
const TouchEventWithLatencyInfo& touch =
touch_queue_.front()->coalesced_event();
+ if (touch.event.type == WebInputEvent::TouchStart)
+ renderer_is_consuming_gesture_ = false;
if (ShouldForwardToRenderer(touch.event)) {
- client_->SendTouchEventImmediately(touch);
+ ForwardToRenderer(touch);
break;
}
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
- ui::LatencyInfo());
+ PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo());
+ }
+}
+
+void TouchEventQueue::ForwardToRenderer(
+ const TouchEventWithLatencyInfo& touch) {
+ DCHECK(!dispatching_touch_);
+ // A synchronous ack will reset |dispatching_touch_|, in which case
+ // the touch timeout should not be started.
+ base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
+ client_->SendTouchEventImmediately(touch);
+ if (ack_timeout_enabled_ &&
+ dispatching_touch_ &&
+ !renderer_is_consuming_gesture_ &&
+ (touch.event.type == WebInputEvent::TouchStart ||
+ touch.event.type == WebInputEvent::TouchMove)) {
+ DCHECK(timeout_handler_);
+ timeout_handler_->Start(touch);
}
}
@@ -170,24 +291,22 @@ void TouchEventQueue::OnGestureScrollEvent(
if (no_touch_to_renderer_ || !dispatching_touch_ack_)
return;
no_touch_to_renderer_ = true;
+
+ // If we have a timeout event, a cancel has already been dispatched
+ // for the current touch stream.
+ if (HasTimeoutEvent())
+ return;
+
// Fake a TouchCancel to cancel the touch points of the touch event
// that is currently being acked.
- TouchEventWithLatencyInfo cancel_event =
- dispatching_touch_ack_->coalesced_event();
- cancel_event.event.type = blink::WebInputEvent::TouchCancel;
- for (size_t i = 0; i < cancel_event.event.touchesLength; i++)
- cancel_event.event.touches[i].state =
- blink::WebTouchPoint::StateCancelled;
- CoalescedWebTouchEvent* coalesced_cancel_event =
- new CoalescedWebTouchEvent(cancel_event);
- // Ignore the ack of the touch cancel so when it is acked, it won't get
- // sent to gesture recognizer.
- coalesced_cancel_event->set_ignore_ack(true);
- // |dispatching_touch_ack_| is non-null when we reach here, meaning we
+ // Note: |dispatching_touch_ack_| is non-null when we reach here, meaning we
// are in the scope of PopTouchEventToClient() and that no touch event
// in the queue is waiting for ack from renderer. So we can just insert
// the touch cancel at the beginning of the queue.
- touch_queue_.push_front(coalesced_cancel_event);
+ touch_queue_.push_front(new CoalescedWebTouchEvent(
+ ObtainCancelEventForTouchEvent(
+ dispatching_touch_ack_->coalesced_event()),
+ true));
} else if (type == blink::WebInputEvent::GestureScrollEnd ||
type == blink::WebInputEvent::GestureFlingStart) {
no_touch_to_renderer_ = false;
@@ -196,22 +315,42 @@ void TouchEventQueue::OnGestureScrollEvent(
void TouchEventQueue::FlushQueue() {
DCHECK(!dispatching_touch_ack_);
+ DCHECK(!dispatching_touch_);
while (!touch_queue_.empty())
- PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
- ui::LatencyInfo());
+ PopTouchEventToClient(kDefaultNotForwardedAck, ui::LatencyInfo());
+}
+
+void TouchEventQueue::SetAckTimeoutEnabled(bool enabled,
+ size_t ack_timeout_delay_ms) {
+ if (!enabled) {
+ // Avoid resetting |timeout_handler_|, as an outstanding timeout may
+ // be active and must be completed for ack handling consistency.
+ ack_timeout_enabled_ = false;
+ return;
+ }
+
+ ack_timeout_enabled_ = true;
+ if (!timeout_handler_)
+ timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay_ms));
}
-size_t TouchEventQueue::GetQueueSize() const {
- return touch_queue_.size();
+bool TouchEventQueue::HasTimeoutEvent() const {
+ return timeout_handler_ && timeout_handler_->HasTimeoutEvent();
}
-const TouchEventWithLatencyInfo& TouchEventQueue::GetLatestEvent() const {
+bool TouchEventQueue::IsTimeoutRunningForTesting() const {
+ return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
+}
+
+const TouchEventWithLatencyInfo&
+TouchEventQueue::GetLatestEventForTesting() const {
return touch_queue_.back()->coalesced_event();
}
void TouchEventQueue::PopTouchEventToClient(
InputEventAckState ack_result,
const ui::LatencyInfo& renderer_latency_info) {
+ DCHECK(!dispatching_touch_ack_);
if (touch_queue_.empty())
return;
scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front());
@@ -234,19 +373,22 @@ void TouchEventQueue::PopTouchEventToClient(
}
bool TouchEventQueue::ShouldForwardToRenderer(
- const blink::WebTouchEvent& event) const {
+ const WebTouchEvent& event) const {
+ if (HasTimeoutEvent())
+ return false;
+
if (no_touch_to_renderer_ &&
event.type != blink::WebInputEvent::TouchCancel)
return false;
// Touch press events should always be forwarded to the renderer.
- if (event.type == blink::WebInputEvent::TouchStart)
+ if (event.type == WebInputEvent::TouchStart)
return true;
for (unsigned int i = 0; i < event.touchesLength; ++i) {
- const blink::WebTouchPoint& point = event.touches[i];
+ const WebTouchPoint& point = event.touches[i];
// If a point has been stationary, then don't take it into account.
- if (point.state == blink::WebTouchPoint::StateStationary)
+ if (point.state == WebTouchPoint::StateStationary)
continue;
if (touch_ack_states_.count(point.id) > 0) {
@@ -263,4 +405,25 @@ bool TouchEventQueue::ShouldForwardToRenderer(
return false;
}
+void TouchEventQueue::UpdateTouchAckStates(const WebTouchEvent& event,
+ InputEventAckState ack_result) {
+ // Update the ACK status for each touch point in the ACKed event.
+ if (event.type == WebInputEvent::TouchEnd ||
+ event.type == WebInputEvent::TouchCancel) {
+ // The points have been released. Erase the ACK states.
+ for (unsigned i = 0; i < event.touchesLength; ++i) {
+ const WebTouchPoint& point = event.touches[i];
+ if (point.state == WebTouchPoint::StateReleased ||
+ point.state == WebTouchPoint::StateCancelled)
+ touch_ack_states_.erase(point.id);
+ }
+ } else if (event.type == WebInputEvent::TouchStart) {
+ for (unsigned i = 0; i < event.touchesLength; ++i) {
+ const WebTouchPoint& point = event.touches[i];
+ if (point.state == WebTouchPoint::StatePressed)
+ touch_ack_states_[point.id] = ack_result;
+ }
+ }
+}
+
} // namespace content

Powered by Google App Engine
This is Rietveld 408576698