summaryrefslogtreecommitdiff
path: root/chromium/content/browser/renderer_host/input/touch_event_queue.h
blob: 3a4bbdf4355065faca7457c6fd6693810be26741 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// 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.

#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
#define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_

#include <deque>
#include <map>

#include "base/basictypes.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/event_with_latency_info.h"
#include "content/common/content_export.h"
#include "content/common/input/input_event_ack_state.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/geometry/point_f.h"

namespace content {

class CoalescedWebTouchEvent;

// Interface with which TouchEventQueue can forward touch events, and dispatch
// touch event responses.
class CONTENT_EXPORT TouchEventQueueClient {
 public:
  virtual ~TouchEventQueueClient() {}

  virtual void SendTouchEventImmediately(
      const TouchEventWithLatencyInfo& event) = 0;

  virtual void OnTouchEventAck(
      const TouchEventWithLatencyInfo& event,
      InputEventAckState ack_result) = 0;
};

// A queue for throttling and coalescing touch-events.
class CONTENT_EXPORT TouchEventQueue {
 public:
  struct CONTENT_EXPORT Config {
    Config();

    // Touch ack timeout delay for desktop sites. If zero, timeout behavior
    // is disabled for such sites. Defaults to 200ms.
    base::TimeDelta desktop_touch_ack_timeout_delay;

    // Touch ack timeout delay for mobile sites. If zero, timeout behavior
    // is disabled for such sites. Defaults to 1000ms.
    base::TimeDelta mobile_touch_ack_timeout_delay;

    // Whether the platform supports touch ack timeout behavior.
    // Defaults to false (disabled).
    bool touch_ack_timeout_supported;
  };

  // The |client| must outlive the TouchEventQueue.
  TouchEventQueue(TouchEventQueueClient* client, const Config& config);

  ~TouchEventQueue();

  // Adds an event to the queue. The event may be coalesced with previously
  // queued events (e.g. consecutive touch-move events can be coalesced into a
  // single touch-move event). The event may also be immediately forwarded to
  // the renderer (e.g. when there are no other queued touch event).
  void QueueEvent(const TouchEventWithLatencyInfo& event);

  // Notifies the queue that a touch-event has been processed by the renderer.
  // At this point, if the ack is for async touchmove, remove the uncancelable
  // touchmove from the front of the queue and decide if it should dispatch the
  // next pending async touch move event, otherwise the queue may send one or
  // more gesture events and/or additional queued touch-events to the renderer.
  void ProcessTouchAck(InputEventAckState ack_result,
                       const ui::LatencyInfo& latency_info,
                       const uint32 unique_touch_event_id);

  // When GestureScrollBegin is received, we send a touch cancel to renderer,
  // route all the following touch events directly to client, and ignore the
  // ack for the touch cancel. When Gesture{ScrollEnd,FlingStart} is received,
  // resume the normal flow of sending touch events to the renderer.
  void OnGestureScrollEvent(const GestureEventWithLatencyInfo& gesture_event);

  void OnGestureEventAck(
      const GestureEventWithLatencyInfo& event,
      InputEventAckState ack_result);

  // Notifies the queue whether the renderer has at least one touch handler.
  void OnHasTouchEventHandlers(bool has_handlers);

  // Returns whether the currently pending touch event (waiting ACK) is for
  // a touch start event.
  bool IsPendingAckTouchStart() const;

  // Sets whether a delayed touch ack will cancel and flush the current
  // touch sequence. Note that, if the timeout was previously disabled, enabling
  // it will take effect only for the following touch sequence.
  void SetAckTimeoutEnabled(bool enabled);

  // Sets whether the current site has a mobile friendly viewport. This
  // determines which ack timeout delay will be used for *future* touch events.
  // The default assumption is that the site is *not* mobile-optimized.
  void SetIsMobileOptimizedSite(bool mobile_optimized_site);

  // Whether ack timeout behavior is supported and enabled for the current site.
  bool IsAckTimeoutEnabled() const;

  bool IsForwardingTouches();

  bool empty() const WARN_UNUSED_RESULT {
    return touch_queue_.empty();
  }

  size_t size() const {
    return touch_queue_.size();
  }

  bool has_handlers() const { return has_handlers_; }

  size_t uncancelable_touch_moves_pending_ack_count() const {
    return ack_pending_async_touchmove_ids_.size();
  }

 private:
  class TouchTimeoutHandler;
  class TouchMoveSlopSuppressor;
  friend class TouchTimeoutHandler;
  friend class TouchEventQueueTest;

  bool HasPendingAsyncTouchMoveForTesting() const;
  bool IsTimeoutRunningForTesting() const;
  const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;

  // Empties the queue of touch events. This may result in any number of gesture
  // events being sent to the renderer.
  void FlushQueue();

  // Walks the queue, checking each event with |FilterBeforeForwarding()|.
  // If allowed, forwards the touch event and stops processing further events.
  // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
  void TryForwardNextEventToRenderer();

  // Forwards the event at the head of the queue to the renderer.
  void ForwardNextEventToRenderer();

  // Pops the touch-event from the head of the queue and acks it to the client.
  void PopTouchEventToClient(InputEventAckState ack_result);

  // Pops the touch-event from the top of the queue and acks it to the client,
  // updating the event with |renderer_latency_info|.
  void PopTouchEventToClient(InputEventAckState ack_result,
                             const ui::LatencyInfo& renderer_latency_info);

  // Ack all coalesced events in |acked_event| to the client with |ack_result|,
  // updating the acked events with |optional_latency_info| if it exists.
  void AckTouchEventToClient(InputEventAckState ack_result,
                             scoped_ptr<CoalescedWebTouchEvent> acked_event,
                             const ui::LatencyInfo* optional_latency_info);

  // Safely pop the head of the queue.
  scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent();

  // Dispatch |touch| to the client. Before dispatching, updates pointer
  // states in touchmove events for pointers that have not changed position.
  void SendTouchEventImmediately(TouchEventWithLatencyInfo* touch);

  enum PreFilterResult {
    ACK_WITH_NO_CONSUMER_EXISTS,
    ACK_WITH_NOT_CONSUMED,
    FORWARD_TO_RENDERER,
  };
  // Filter touches prior to forwarding to the renderer, e.g., if the renderer
  // has no touch handler.
  PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
  void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
  void UpdateTouchConsumerStates(const blink::WebTouchEvent& event,
                                 InputEventAckState ack_result);
  void FlushPendingAsyncTouchmove();

  // Handles touch event forwarding and ack'ed event dispatch.
  TouchEventQueueClient* client_;

  typedef std::deque<CoalescedWebTouchEvent*> TouchQueue;
  TouchQueue touch_queue_;

  // Position of the first touch in the most recent sequence forwarded to the
  // client.
  gfx::PointF touch_sequence_start_position_;

  // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
  // True within the scope of |AckTouchEventToClient()|.
  bool dispatching_touch_ack_;

  // Used to prevent touch timeout scheduling and increase the count for async
  // touchmove if we receive a synchronous ack after forwarding a touch event
  // to the client.
  bool dispatching_touch_;

  // Whether the renderer has at least one touch handler.
  bool has_handlers_;

  // Whether any pointer in the touch sequence reported having a consumer.
  bool has_handler_for_current_sequence_;

  // Whether to allow any remaining touches for the current sequence. Note that
  // this is a stricter condition than an empty |touch_consumer_states_|, as it
  // also prevents forwarding of touchstart events for new pointers in the
  // current sequence. This is only used when the event is synthetically
  // cancelled after a touch timeout.
  bool drop_remaining_touches_in_sequence_;

  // Optional handler for timed-out touch event acks.
  scoped_ptr<TouchTimeoutHandler> timeout_handler_;

  // Suppression of TouchMove's within a slop region when a sequence has not yet
  // been preventDefaulted.
  scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;

  // Whether touch events should remain buffered and dispatched asynchronously
  // while a scroll sequence is active.  In this mode, touchmove's are throttled
  // and ack'ed immediately, but remain buffered in |pending_async_touchmove_|
  // until a sufficient time period has elapsed since the last sent touch event.
  // For details see the design doc at http://goo.gl/lVyJAa.
  bool send_touch_events_async_;
  scoped_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;

  // For uncancelable touch moves, not only we send a fake ack, but also a real
  // ack from render, which we use to decide when to send the next async
  // touchmove. This can help avoid the touch event queue keep growing when
  // render handles touchmove slow. We use a queue
  // ack_pending_async_touchmove_ids to store the recent dispatched
  // uncancelable touchmoves which are still waiting for their acks back from
  // render. We do not put them back to the front the touch_event_queue any
  // more.
  std::deque<uint32> ack_pending_async_touchmove_ids_;

  double last_sent_touch_timestamp_sec_;

  // Event is saved to compare pointer positions for new touchmove events.
  scoped_ptr<blink::WebTouchEvent> last_sent_touchevent_;

  DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_