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
246
247
248
249
250
251
252
253
|
// 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:
// Different ways of dealing with touch events during scrolling.
// TODO(rbyers): Remove this once we're confident that touch move absorption
// is OK. http://crbug.com/350430
enum TouchScrollingMode {
// Send a touchcancel on scroll start and no further touch events for the
// duration of the scroll. Chrome Android's traditional behavior.
TOUCH_SCROLLING_MODE_TOUCHCANCEL,
// Send touchmove events throughout a scroll, blocking on each ACK and
// using the disposition to determine whether a scroll update should be
// sent. Mobile Safari's default overflow scroll behavior.
TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
// Send touchmove events throughout a scroll, but throttle sending and
// ignore the ACK as long as scrolling remains possible. Unconsumed scroll
// events return touchmove events to being dispatched synchronously.
TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE,
TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE
};
struct CONTENT_EXPORT Config {
Config();
// Determines the bounds of the (square) touchmove slop suppression region.
// Defaults to 0 (disabled).
double touchmove_slop_suppression_length_dips;
// Whether the touchmove slop suppression region is boundary inclusive.
// Defaults to true.
// TODO(jdduke): Remove when unified GR enabled, crbug.com/332418.
bool touchmove_slop_suppression_region_includes_boundary;
// Determines the type of touch scrolling.
// Defaults to TouchEventQueue:::TOUCH_SCROLLING_MODE_DEFAULT.
TouchEventQueue::TouchScrollingMode touch_scrolling_mode;
// Controls whether touch ack timeouts will trigger touch cancellation.
// Defaults to 200ms.
base::TimeDelta 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, 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);
// 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);
bool empty() const WARN_UNUSED_RESULT {
return touch_queue_.empty();
}
size_t size() const {
return touch_queue_.size();
}
bool ack_timeout_enabled() const {
return ack_timeout_enabled_;
}
bool has_handlers() const {
return touch_filtering_state_ != DROP_ALL_TOUCHES;
}
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|.
void AckTouchEventToClient(InputEventAckState ack_result,
scoped_ptr<CoalescedWebTouchEvent> acked_event);
// Safely pop the head of the queue.
scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent();
// Dispatch |touch| to the client.
void SendTouchEventImmediately(const 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 UpdateTouchAckStates(const blink::WebTouchEvent& event,
InputEventAckState ack_result);
bool AllTouchAckStatesHaveState(InputEventAckState ack_state) const;
// Handles touch event forwarding and ack'ed event dispatch.
TouchEventQueueClient* client_;
typedef std::deque<CoalescedWebTouchEvent*> TouchQueue;
TouchQueue touch_queue_;
// Maintain the ACK status for each touch point.
typedef std::map<int, InputEventAckState> TouchPointAckStates;
TouchPointAckStates touch_ack_states_;
// 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()|.
// If not NULL, |dispatching_touch_ack_| is the touch event of which the ack
// is being dispatched.
const CoalescedWebTouchEvent* dispatching_touch_ack_;
// Used to prevent touch timeout scheduling if we receive a synchronous
// ack after forwarding a touch event to the client.
bool dispatching_touch_;
enum TouchFilteringState {
FORWARD_ALL_TOUCHES, // Don't filter at all - the default.
FORWARD_TOUCHES_UNTIL_TIMEOUT, // Don't filter unless we get an ACK timeout.
DROP_TOUCHES_IN_SEQUENCE, // Filter all events until a new touch
// sequence is received.
DROP_ALL_TOUCHES, // Filter all events, e.g., no touch handler.
TOUCH_FILTERING_STATE_DEFAULT = FORWARD_ALL_TOUCHES,
};
TouchFilteringState touch_filtering_state_;
// Optional handler for timed-out touch event acks.
bool ack_timeout_enabled_;
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_;
bool needs_async_touchmove_for_outer_slop_region_;
scoped_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;
double last_sent_touch_timestamp_sec_;
// How touch events are handled during scrolling. For now this is a global
// setting for experimentation, but we may evolve it into an app-controlled
// mode.
const TouchScrollingMode touch_scrolling_mode_;
DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
|