touch_event_queue.h revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
3// found in the LICENSE file.
4
5#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
6#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
7
8#include <deque>
9#include <map>
10
11#include "base/basictypes.h"
12#include "base/time/time.h"
13#include "content/browser/renderer_host/event_with_latency_info.h"
14#include "content/common/content_export.h"
15#include "content/common/input/input_event_ack_state.h"
16#include "third_party/WebKit/public/web/WebInputEvent.h"
17#include "ui/events/gesture_detection/bitset_32.h"
18#include "ui/gfx/geometry/point_f.h"
19
20namespace content {
21
22class CoalescedWebTouchEvent;
23
24// Interface with which TouchEventQueue can forward touch events, and dispatch
25// touch event responses.
26class CONTENT_EXPORT TouchEventQueueClient {
27 public:
28  virtual ~TouchEventQueueClient() {}
29
30  virtual void SendTouchEventImmediately(
31      const TouchEventWithLatencyInfo& event) = 0;
32
33  virtual void OnTouchEventAck(
34      const TouchEventWithLatencyInfo& event,
35      InputEventAckState ack_result) = 0;
36};
37
38// A queue for throttling and coalescing touch-events.
39class CONTENT_EXPORT TouchEventQueue {
40 public:
41  // Different ways of dealing with touch events during scrolling.
42  // TODO(rbyers): Remove this once we're confident that touch move absorption
43  // is OK. http://crbug.com/350430
44  enum TouchScrollingMode {
45    // Send a touchcancel on scroll start and no further touch events for the
46    // duration of the scroll.  Chrome Android's traditional behavior.
47    TOUCH_SCROLLING_MODE_TOUCHCANCEL,
48    // Send touchmove events throughout a scroll, blocking on each ACK and
49    // using the disposition to determine whether a scroll update should be
50    // sent.  Mobile Safari's default overflow scroll behavior.
51    TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
52    // Send touchmove events throughout a scroll, but throttle sending and
53    // ignore the ACK as long as scrolling remains possible.  Unconsumed scroll
54    // events return touchmove events to being dispatched synchronously.
55    TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE,
56    TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE
57  };
58
59  struct CONTENT_EXPORT Config {
60    Config();
61
62    // Determines the bounds of the (square) touchmove slop suppression region.
63    // Defaults to 0 (disabled).
64    double touchmove_slop_suppression_length_dips;
65
66    // Determines the type of touch scrolling.
67    // Defaults to TouchEventQueue:::TOUCH_SCROLLING_MODE_DEFAULT.
68    TouchEventQueue::TouchScrollingMode touch_scrolling_mode;
69
70    // Controls whether touch ack timeouts will trigger touch cancellation.
71    // Defaults to 200ms.
72    base::TimeDelta touch_ack_timeout_delay;
73
74    // Whether the platform supports touch ack timeout behavior.
75    // Defaults to false (disabled).
76    bool touch_ack_timeout_supported;
77  };
78
79  // The |client| must outlive the TouchEventQueue.
80  TouchEventQueue(TouchEventQueueClient* client, const Config& config);
81
82  ~TouchEventQueue();
83
84  // Adds an event to the queue. The event may be coalesced with previously
85  // queued events (e.g. consecutive touch-move events can be coalesced into a
86  // single touch-move event). The event may also be immediately forwarded to
87  // the renderer (e.g. when there are no other queued touch event).
88  void QueueEvent(const TouchEventWithLatencyInfo& event);
89
90  // Notifies the queue that a touch-event has been processed by the renderer.
91  // At this point, the queue may send one or more gesture events and/or
92  // additional queued touch-events to the renderer.
93  void ProcessTouchAck(InputEventAckState ack_result,
94                       const ui::LatencyInfo& latency_info);
95
96  // When GestureScrollBegin is received, we send a touch cancel to renderer,
97  // route all the following touch events directly to client, and ignore the
98  // ack for the touch cancel. When Gesture{ScrollEnd,FlingStart} is received,
99  // resume the normal flow of sending touch events to the renderer.
100  void OnGestureScrollEvent(const GestureEventWithLatencyInfo& gesture_event);
101
102  void OnGestureEventAck(
103      const GestureEventWithLatencyInfo& event,
104      InputEventAckState ack_result);
105
106  // Notifies the queue whether the renderer has at least one touch handler.
107  void OnHasTouchEventHandlers(bool has_handlers);
108
109  // Returns whether the currently pending touch event (waiting ACK) is for
110  // a touch start event.
111  bool IsPendingAckTouchStart() const;
112
113  // Sets whether a delayed touch ack will cancel and flush the current
114  // touch sequence. Note that, if the timeout was previously disabled, enabling
115  // it will take effect only for the following touch sequence.
116  void SetAckTimeoutEnabled(bool enabled);
117
118  bool IsAckTimeoutEnabled() const;
119
120  bool IsForwardingTouches();
121
122  bool empty() const WARN_UNUSED_RESULT {
123    return touch_queue_.empty();
124  }
125
126  size_t size() const {
127    return touch_queue_.size();
128  }
129
130  bool has_handlers() const { return has_handlers_; }
131
132 private:
133  class TouchTimeoutHandler;
134  class TouchMoveSlopSuppressor;
135  friend class TouchTimeoutHandler;
136  friend class TouchEventQueueTest;
137
138  bool HasPendingAsyncTouchMoveForTesting() const;
139  bool IsTimeoutRunningForTesting() const;
140  const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;
141
142  // Empties the queue of touch events. This may result in any number of gesture
143  // events being sent to the renderer.
144  void FlushQueue();
145
146  // Walks the queue, checking each event with |FilterBeforeForwarding()|.
147  // If allowed, forwards the touch event and stops processing further events.
148  // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
149  void TryForwardNextEventToRenderer();
150
151  // Forwards the event at the head of the queue to the renderer.
152  void ForwardNextEventToRenderer();
153
154  // Pops the touch-event from the head of the queue and acks it to the client.
155  void PopTouchEventToClient(InputEventAckState ack_result);
156
157  // Pops the touch-event from the top of the queue and acks it to the client,
158  // updating the event with |renderer_latency_info|.
159  void PopTouchEventToClient(InputEventAckState ack_result,
160                             const ui::LatencyInfo& renderer_latency_info);
161
162  // Ack all coalesced events in |acked_event| to the client with |ack_result|,
163  // updating the acked events with |optional_latency_info| if it exists.
164  void AckTouchEventToClient(InputEventAckState ack_result,
165                             scoped_ptr<CoalescedWebTouchEvent> acked_event,
166                             const ui::LatencyInfo* optional_latency_info);
167
168  // Safely pop the head of the queue.
169  scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent();
170
171  // Dispatch |touch| to the client.
172  void SendTouchEventImmediately(const TouchEventWithLatencyInfo& touch);
173
174  enum PreFilterResult {
175    ACK_WITH_NO_CONSUMER_EXISTS,
176    ACK_WITH_NOT_CONSUMED,
177    FORWARD_TO_RENDERER,
178  };
179  // Filter touches prior to forwarding to the renderer, e.g., if the renderer
180  // has no touch handler.
181  PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
182  void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
183  void UpdateTouchConsumerStates(const blink::WebTouchEvent& event,
184                                 InputEventAckState ack_result);
185
186  // Handles touch event forwarding and ack'ed event dispatch.
187  TouchEventQueueClient* client_;
188
189  typedef std::deque<CoalescedWebTouchEvent*> TouchQueue;
190  TouchQueue touch_queue_;
191
192  // Maps whether each active pointer has a consumer (i.e., a touch point has a
193  // valid consumer iff |touch_consumer_states[pointer.id]| is true.).
194  // TODO(jdduke): Consider simply tracking whether *any* touchstart had a
195  // consumer, crbug.com/416497.
196  ui::BitSet32 touch_consumer_states_;
197
198  // Position of the first touch in the most recent sequence forwarded to the
199  // client.
200  gfx::PointF touch_sequence_start_position_;
201
202  // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
203  // If not NULL, |dispatching_touch_ack_| is the touch event of which the ack
204  // is being dispatched.
205  const CoalescedWebTouchEvent* dispatching_touch_ack_;
206
207  // Used to prevent touch timeout scheduling if we receive a synchronous
208  // ack after forwarding a touch event to the client.
209  bool dispatching_touch_;
210
211  // Whether the renderer has at least one touch handler.
212  bool has_handlers_;
213
214  // Whether to allow any remaining touches for the current sequence. Note that
215  // this is a stricter condition than an empty |touch_consumer_states_|, as it
216  // also prevents forwarding of touchstart events for new pointers in the
217  // current sequence. This is only used when the event is synthetically
218  // cancelled after a touch timeout, or after a scroll event when the
219  // mode is TOUCH_SCROLLING_MODE_TOUCHCANCEL.
220  bool drop_remaining_touches_in_sequence_;
221
222  // Optional handler for timed-out touch event acks.
223  scoped_ptr<TouchTimeoutHandler> timeout_handler_;
224
225  // Suppression of TouchMove's within a slop region when a sequence has not yet
226  // been preventDefaulted.
227  scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
228
229  // Whether touch events should remain buffered and dispatched asynchronously
230  // while a scroll sequence is active.  In this mode, touchmove's are throttled
231  // and ack'ed immediately, but remain buffered in |pending_async_touchmove_|
232  // until a sufficient time period has elapsed since the last sent touch event.
233  // For details see the design doc at http://goo.gl/lVyJAa.
234  bool send_touch_events_async_;
235  bool needs_async_touchmove_for_outer_slop_region_;
236  scoped_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;
237  double last_sent_touch_timestamp_sec_;
238
239  // How touch events are handled during scrolling.  For now this is a global
240  // setting for experimentation, but we may evolve it into an app-controlled
241  // mode.
242  const TouchScrollingMode touch_scrolling_mode_;
243
244  DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
245};
246
247}  // namespace content
248
249#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
250