• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
20 namespace content {
21 
22 class CoalescedWebTouchEvent;
23 
24 // Interface with which TouchEventQueue can forward touch events, and dispatch
25 // touch event responses.
26 class CONTENT_EXPORT TouchEventQueueClient {
27  public:
~TouchEventQueueClient()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.
39 class 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 
empty()122   bool empty() const WARN_UNUSED_RESULT {
123     return touch_queue_.empty();
124   }
125 
size()126   size_t size() const {
127     return touch_queue_.size();
128   }
129 
has_handlers()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