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
|
// Copyright (c) 2012 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.
#include "content/browser/renderer_host/touch_event_queue.h"
#include "base/debug/trace_event.h"
#include "base/stl_util.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/port/browser/render_widget_host_view_port.h"
namespace content {
typedef std::vector<WebKit::WebTouchEvent> WebTouchEventList;
// 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
// event. The coalesced event is forwarded to the renderer, while the original
// touch-events are sent to the View (on ACK for the coalesced event) so that
// the View receives the event with their original timestamp.
class CoalescedWebTouchEvent {
public:
explicit CoalescedWebTouchEvent(const WebKit::WebTouchEvent& event)
: coalesced_event_(event) {
events_.push_back(event);
TRACE_EVENT_ASYNC_BEGIN0(
"input", "TouchEventQueue::QueueEvent", this);
}
~CoalescedWebTouchEvent() {
TRACE_EVENT_ASYNC_END0(
"input", "TouchEventQueue::QueueEvent", this);
}
// Coalesces the event with the existing event if possible. Returns whether
// the event was coalesced.
bool CoalesceEventIfPossible(const WebKit::WebTouchEvent& event) {
if (coalesced_event_.type == WebKit::WebInputEvent::TouchMove &&
event.type == WebKit::WebInputEvent::TouchMove &&
coalesced_event_.modifiers == event.modifiers &&
coalesced_event_.touchesLength == event.touchesLength) {
events_.push_back(event);
// The WebTouchPoints include absolute position information. So it is
// sufficient to simply replace the previous event with the new event.
// However, it is necessary to make sure that all the points have the
// correct state, i.e. the touch-points that moved in the last event, but
// didn't change in the current event, will have Stationary state. It is
// necessary to change them back to Moved state.
const WebKit::WebTouchEvent last_event = coalesced_event_;
coalesced_event_ = event;
for (unsigned i = 0; i < last_event.touchesLength; ++i) {
if (last_event.touches[i].state == WebKit::WebTouchPoint::StateMoved)
coalesced_event_.touches[i].state = WebKit::WebTouchPoint::StateMoved;
}
return true;
}
return false;
}
const WebKit::WebTouchEvent& coalesced_event() const {
return coalesced_event_;
}
WebTouchEventList::const_iterator begin() const {
return events_.begin();
}
WebTouchEventList::const_iterator end() const {
return events_.end();
}
size_t size() const { return events_.size(); }
private:
// This is the event that is forwarded to the renderer.
WebKit::WebTouchEvent coalesced_event_;
// This is the list of the original events that were coalesced.
WebTouchEventList events_;
DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
};
TouchEventQueue::TouchEventQueue(RenderWidgetHostImpl* host)
: render_widget_host_(host) {
}
TouchEventQueue::~TouchEventQueue() {
if (!touch_queue_.empty())
STLDeleteElements(&touch_queue_);
}
void TouchEventQueue::QueueEvent(const WebKit::WebTouchEvent& event) {
if (touch_queue_.empty()) {
// There is no touch event in the queue. Forward it to the renderer
// immediately.
touch_queue_.push_back(new CoalescedWebTouchEvent(event));
if (ShouldForwardToRenderer(event))
render_widget_host_->ForwardTouchEventImmediately(event);
else
PopTouchEventToView(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
return;
}
// If the last queued touch-event was a touch-move, and the current event is
// also a touch-move, then the events can be coalesced into a single event.
if (touch_queue_.size() > 1) {
CoalescedWebTouchEvent* last_event = touch_queue_.back();
if (last_event->CoalesceEventIfPossible(event))
return;
}
touch_queue_.push_back(new CoalescedWebTouchEvent(event));
}
void TouchEventQueue::ProcessTouchAck(InputEventAckState ack_result) {
if (touch_queue_.empty())
return;
// Update the ACK status for each touch point in the ACKed event.
const WebKit::WebTouchEvent& event = touch_queue_.front()->coalesced_event();
if (event.type == WebKit::WebInputEvent::TouchEnd ||
event.type == WebKit::WebInputEvent::TouchCancel) {
// The points have been released. Erase the ACK states.
for (unsigned i = 0; i < event.touchesLength; ++i) {
const WebKit::WebTouchPoint& point = event.touches[i];
if (point.state == WebKit::WebTouchPoint::StateReleased ||
point.state == WebKit::WebTouchPoint::StateCancelled)
touch_ack_states_.erase(point.id);
}
} else if (event.type == WebKit::WebInputEvent::TouchStart) {
for (unsigned i = 0; i < event.touchesLength; ++i) {
const WebKit::WebTouchPoint& point = event.touches[i];
if (point.state == WebKit::WebTouchPoint::StatePressed)
touch_ack_states_[point.id] = ack_result;
}
}
PopTouchEventToView(ack_result);
// If there are queued touch events, then try to forward them to the renderer
// immediately, or ACK the events back to the view if appropriate.
while (!touch_queue_.empty()) {
const WebKit::WebTouchEvent& touch =
touch_queue_.front()->coalesced_event();
if (ShouldForwardToRenderer(touch)) {
render_widget_host_->ForwardTouchEventImmediately(touch);
break;
}
PopTouchEventToView(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
}
}
void TouchEventQueue::FlushQueue() {
while (!touch_queue_.empty())
PopTouchEventToView(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
}
void TouchEventQueue::Reset() {
touch_queue_.clear();
}
size_t TouchEventQueue::GetQueueSize() const {
return touch_queue_.size();
}
const WebKit::WebTouchEvent& TouchEventQueue::GetLatestEvent() const {
return touch_queue_.back()->coalesced_event();
}
void TouchEventQueue::PopTouchEventToView(InputEventAckState ack_result) {
if (touch_queue_.empty())
return;
scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front());
touch_queue_.pop_front();
// Note that acking the touch-event may result in multiple gestures being sent
// to the renderer.
RenderWidgetHostViewPort* view = RenderWidgetHostViewPort::FromRWHV(
render_widget_host_->GetView());
for (WebTouchEventList::const_iterator iter = acked_event->begin(),
end = acked_event->end();
iter != end; ++iter) {
view->ProcessAckedTouchEvent((*iter), ack_result);
}
}
bool TouchEventQueue::ShouldForwardToRenderer(
const WebKit::WebTouchEvent& event) const {
// Touch press events should always be forwarded to the renderer.
if (event.type == WebKit::WebInputEvent::TouchStart)
return true;
for (unsigned int i = 0; i < event.touchesLength; ++i) {
const WebKit::WebTouchPoint& point = event.touches[i];
// If a point has been stationary, then don't take it into account.
if (point.state == WebKit::WebTouchPoint::StateStationary)
continue;
if (touch_ack_states_.count(point.id) > 0) {
if (touch_ack_states_.find(point.id)->second !=
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS)
return true;
} else {
// If the ACK status of a point is unknown, then the event should be
// forwarded to the renderer.
return true;
}
}
return false;
}
} // namespace content
|