Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(236)

Side by Side Diff: content/browser/renderer_host/input/passthrough_touch_event_queue.cc

Issue 2715623002: Add a passthrough touch event queue. (Closed)
Patch Set: Rebase Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 #include "content/browser/renderer_host/input/passthrough_touch_event_queue.h"
6
7 #include <utility>
8
9 #include "base/auto_reset.h"
10 #include "base/macros.h"
11 #include "base/memory/ptr_util.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/browser/renderer_host/input/touch_timeout_handler.h"
15 #include "content/common/input/web_touch_event_traits.h"
16 #include "ui/events/base_event_utils.h"
17 #include "ui/gfx/geometry/point_f.h"
tdresser 2017/02/23 15:10:45 How thoroughly has this changed from legacy_touch_
dtapuska 2017/02/23 16:52:12 Will comment on the interesting bits.
18
19 using blink::WebInputEvent;
20 using blink::WebTouchEvent;
21 using blink::WebTouchPoint;
22 using ui::LatencyInfo;
23
24 namespace content {
25 namespace {
26
27 // Compare all properties of touch points to determine the state.
28 bool HasPointChanged(const WebTouchPoint& point_1,
29 const WebTouchPoint& point_2) {
30 DCHECK_EQ(point_1.id, point_2.id);
31 if (point_1.screenPosition != point_2.screenPosition ||
32 point_1.position != point_2.position ||
33 point_1.radiusX != point_2.radiusX ||
34 point_1.radiusY != point_2.radiusY ||
35 point_1.rotationAngle != point_2.rotationAngle ||
36 point_1.force != point_2.force || point_1.tiltX != point_2.tiltX ||
37 point_1.tiltY != point_2.tiltY) {
38 return true;
39 }
40 return false;
41 }
42
43 } // namespace
44
45 PassthroughTouchEventQueue::TouchEventWithLatencyInfoAndAckState::
46 TouchEventWithLatencyInfoAndAckState(const TouchEventWithLatencyInfo& event)
47 : TouchEventWithLatencyInfo(event),
48 ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
49
50 bool PassthroughTouchEventQueue::TouchEventWithLatencyInfoAndAckState::
51 operator<(const TouchEventWithLatencyInfoAndAckState& other) const {
52 return event.uniqueTouchEventId < other.event.uniqueTouchEventId;
53 }
54
55 PassthroughTouchEventQueue::PassthroughTouchEventQueue(
56 TouchEventQueueClient* client,
57 const Config& config)
58 : client_(client),
59 has_handlers_(true),
60 maybe_has_handler_for_current_sequence_(false),
61 drop_remaining_touches_in_sequence_(false),
62 send_touch_events_async_(false) {
63 if (config.touch_ack_timeout_supported) {
64 timeout_handler_.reset(
65 new TouchTimeoutHandler(this, config.desktop_touch_ack_timeout_delay,
66 config.mobile_touch_ack_timeout_delay));
67 }
68 }
69
70 PassthroughTouchEventQueue::~PassthroughTouchEventQueue() {}
71
72 void PassthroughTouchEventQueue::SendTouchCancelEventForTouchEvent(
73 const TouchEventWithLatencyInfo& event_to_cancel) {
74 TouchEventWithLatencyInfo event = event_to_cancel;
75 WebTouchEventTraits::ResetTypeAndTouchStates(
76 WebInputEvent::TouchCancel,
77 // TODO(rbyers): Shouldn't we use a fresh timestamp?
78 event.event.timeStampSeconds(), &event.event);
79 SendTouchEventImmediately(&event, false);
80 }
81
82 void PassthroughTouchEventQueue::QueueEvent(
83 const TouchEventWithLatencyInfo& event) {
84 TRACE_EVENT0("input", "PassthroughTouchEventQueue::QueueEvent");
85 PreFilterResult filter_result = FilterBeforeForwarding(event.event);
86 if (filter_result != FORWARD_TO_RENDERER) {
87 client_->OnFilteringTouchEvent(event.event);
dtapuska 2017/02/23 16:52:12 This is interesting code here. The Filtered touch
88 client_->OnTouchEventAck(event,
89 filter_result == ACK_WITH_NO_CONSUMER_EXISTS
90 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
91 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
92 return;
93 }
94 TouchEventWithLatencyInfo cloned_event(event);
95 SendTouchEventImmediately(&cloned_event, true);
96 }
97
98 void PassthroughTouchEventQueue::PrependTouchScrollNotification() {
99 TRACE_EVENT0("input",
100 "PassthroughTouchEventQueue::PrependTouchScrollNotification");
101
102 TouchEventWithLatencyInfo touch(
103 WebInputEvent::TouchScrollStarted, WebInputEvent::NoModifiers,
104 ui::EventTimeStampToSeconds(ui::EventTimeForNow()), LatencyInfo());
105 touch.event.dispatchType = WebInputEvent::EventNonBlocking;
106 SendTouchEventImmediately(&touch, true);
107 }
108
109 void PassthroughTouchEventQueue::ProcessTouchAck(
110 InputEventAckState ack_result,
111 const LatencyInfo& latency_info,
112 const uint32_t unique_touch_event_id) {
113 TRACE_EVENT0("input", "PassthroughTouchEventQueue::ProcessTouchAck");
114 if (timeout_handler_ &&
115 timeout_handler_->ConfirmTouchEvent(unique_touch_event_id, ack_result))
116 return;
117
118 auto touch_event_iter = outstanding_touches_.begin();
119 while (touch_event_iter != outstanding_touches_.end()) {
120 if (unique_touch_event_id == touch_event_iter->event.uniqueTouchEventId)
121 break;
122 ++touch_event_iter;
123 }
124
125 if (touch_event_iter == outstanding_touches_.end()) {
126 NOTREACHED();
127 return;
128 }
129
130 TouchEventWithLatencyInfoAndAckState event = *touch_event_iter;
131 touch_event_iter = outstanding_touches_.erase(touch_event_iter);
132 event.latency.AddNewLatencyFrom(latency_info);
133 event.set_ack_state(ack_result);
134 outstanding_touches_.insert(touch_event_iter, event);
135
136 AckCompletedEvents();
137 }
138
139 void PassthroughTouchEventQueue::OnGestureScrollEvent(
140 const GestureEventWithLatencyInfo& gesture_event) {
141 if (gesture_event.event.type() == blink::WebInputEvent::GestureScrollUpdate &&
142 gesture_event.event.resendingPluginId == -1) {
143 send_touch_events_async_ = true;
144 }
145 }
146
147 void PassthroughTouchEventQueue::OnGestureEventAck(
148 const GestureEventWithLatencyInfo& event,
149 InputEventAckState ack_result) {
150 // Turn events sent during gesture scrolls to be async.
151 if (event.event.type() == blink::WebInputEvent::GestureScrollUpdate &&
152 event.event.resendingPluginId == -1) {
153 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
154 }
155 }
156
157 void PassthroughTouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
158 has_handlers_ = has_handlers;
159 }
160
161 bool PassthroughTouchEventQueue::IsPendingAckTouchStart() const {
162 if (outstanding_touches_.empty())
163 return false;
164
165 return (outstanding_touches_.begin()->event.type() ==
166 WebInputEvent::TouchStart);
167 }
168
169 void PassthroughTouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
170 if (timeout_handler_)
171 timeout_handler_->SetEnabled(enabled);
172 }
173
174 void PassthroughTouchEventQueue::SetIsMobileOptimizedSite(
175 bool mobile_optimized_site) {
176 if (timeout_handler_)
177 timeout_handler_->SetUseMobileTimeout(mobile_optimized_site);
178 }
179
180 bool PassthroughTouchEventQueue::IsAckTimeoutEnabled() const {
181 return timeout_handler_ && timeout_handler_->IsEnabled();
182 }
183
184 bool PassthroughTouchEventQueue::Empty() const {
185 return outstanding_touches_.empty();
186 }
187
188 void PassthroughTouchEventQueue::FlushQueue() {
189 drop_remaining_touches_in_sequence_ = true;
dtapuska 2017/02/23 16:52:11 This is interesting
190 while (!outstanding_touches_.empty()) {
191 auto iter = outstanding_touches_.begin();
192 TouchEventWithLatencyInfoAndAckState event = *iter;
193 outstanding_touches_.erase(iter);
194 if (event.ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN)
195 event.set_ack_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
196 AckTouchEventToClient(event, event.ack_state());
197 }
198 }
199
200 void PassthroughTouchEventQueue::AckCompletedEvents() {
dtapuska 2017/02/23 16:52:11 Interesting
201 while (!outstanding_touches_.empty()) {
202 auto iter = outstanding_touches_.begin();
203 if (iter->ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN)
204 break;
205 TouchEventWithLatencyInfoAndAckState event = *iter;
206 outstanding_touches_.erase(iter);
207 AckTouchEventToClient(event, event.ack_state());
208 }
209 }
210
211 void PassthroughTouchEventQueue::AckTouchEventToClient(
212 const TouchEventWithLatencyInfo& acked_event,
213 InputEventAckState ack_result) {
214 UpdateTouchConsumerStates(acked_event.event, ack_result);
215
216 // Skip ack for TouchScrollStarted since it was synthesized within the queue.
217 if (acked_event.event.type() != WebInputEvent::TouchScrollStarted) {
218 client_->OnTouchEventAck(acked_event, ack_result);
219 }
220 }
221
222 void PassthroughTouchEventQueue::SendTouchEventImmediately(
223 TouchEventWithLatencyInfo* touch,
224 bool wait_for_ack) {
225 // Note: Touchstart events are marked cancelable to allow transitions between
226 // platform scrolling and JS pinching. Touchend events, however, remain
227 // uncancelable, mitigating the risk of jank when transitioning to a fling.
228 if (send_touch_events_async_ &&
229 touch->event.type() != WebInputEvent::TouchStart)
230 touch->event.dispatchType = WebInputEvent::EventNonBlocking;
231
232 if (touch->event.type() == WebInputEvent::TouchStart)
233 touch->event.touchStartOrFirstTouchMove = true;
234
235 // For touchmove events, compare touch points position from current event
236 // to last sent event and update touch points state.
237 if (touch->event.type() == WebInputEvent::TouchMove) {
238 CHECK(last_sent_touchevent_);
239 if (last_sent_touchevent_->type() == WebInputEvent::TouchStart)
240 touch->event.touchStartOrFirstTouchMove = true;
241 for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) {
242 const WebTouchPoint& last_touch_point = last_sent_touchevent_->touches[i];
243 // Touches with same id may not have same index in Touches array.
244 for (unsigned int j = 0; j < touch->event.touchesLength; ++j) {
245 const WebTouchPoint& current_touchmove_point = touch->event.touches[j];
246 if (current_touchmove_point.id != last_touch_point.id)
247 continue;
248
249 if (!HasPointChanged(last_touch_point, current_touchmove_point))
250 touch->event.touches[j].state = WebTouchPoint::StateStationary;
251
252 break;
253 }
254 }
255 }
256
257 if (touch->event.type() != WebInputEvent::TouchScrollStarted) {
258 if (last_sent_touchevent_)
259 *last_sent_touchevent_ = touch->event;
260 else
261 last_sent_touchevent_.reset(new WebTouchEvent(touch->event));
262 }
263
264 if (timeout_handler_)
265 timeout_handler_->StartIfNecessary(*touch);
266 if (wait_for_ack)
267 outstanding_touches_.insert(*touch);
268 client_->SendTouchEventImmediately(*touch);
269 }
270
271 PassthroughTouchEventQueue::PreFilterResult
272 PassthroughTouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
273 if (event.type() == WebInputEvent::TouchScrollStarted)
274 return FORWARD_TO_RENDERER;
275
276 if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
277 // We don't know if we have a handler until we get the ACK back so
278 // assume it is true.
279 maybe_has_handler_for_current_sequence_ = true;
dtapuska 2017/02/23 16:52:11 maybe_ has been added here and inverted.
280 send_touch_events_async_ = false;
281 last_sent_touchevent_.reset();
282
283 drop_remaining_touches_in_sequence_ = false;
284 if (!has_handlers_) {
285 drop_remaining_touches_in_sequence_ = true;
286 return ACK_WITH_NO_CONSUMER_EXISTS;
287 }
288 }
289
290 if (timeout_handler_ && timeout_handler_->FilterEvent(event))
291 return ACK_WITH_NO_CONSUMER_EXISTS;
292
293 if (drop_remaining_touches_in_sequence_ &&
294 event.type() != WebInputEvent::TouchCancel) {
295 return ACK_WITH_NO_CONSUMER_EXISTS;
296 }
297
298 if (event.type() == WebInputEvent::TouchStart) {
299 return (has_handlers_ || maybe_has_handler_for_current_sequence_)
300 ? FORWARD_TO_RENDERER
301 : ACK_WITH_NO_CONSUMER_EXISTS;
302 }
303
304 if (maybe_has_handler_for_current_sequence_) {
305 // Only forward a touch if it has a non-stationary pointer that is active
306 // in the current touch sequence.
307 for (size_t i = 0; i < event.touchesLength; ++i) {
308 const WebTouchPoint& point = event.touches[i];
309 if (point.state == WebTouchPoint::StateStationary)
310 continue;
311
312 // |last_sent_touchevent_| will be non-null as long as there is an
313 // active touch sequence being forwarded to the renderer.
314 if (!last_sent_touchevent_)
315 continue;
316
317 for (size_t j = 0; j < last_sent_touchevent_->touchesLength; ++j) {
318 if (point.id != last_sent_touchevent_->touches[j].id)
319 continue;
320
321 if (event.type() != WebInputEvent::TouchMove)
322 return FORWARD_TO_RENDERER;
323
324 // All pointers in TouchMove events may have state as StateMoved,
325 // even though none of the pointers have not changed in real.
326 // Forward these events when at least one pointer has changed.
327 if (HasPointChanged(last_sent_touchevent_->touches[j], point))
328 return FORWARD_TO_RENDERER;
329
330 // This is a TouchMove event for which we have yet to find a
331 // non-stationary pointer. Continue checking the next pointers
332 // in the |event|.
333 break;
334 }
335 }
336 }
337
338 return ACK_WITH_NO_CONSUMER_EXISTS;
339 }
340
341 void PassthroughTouchEventQueue::UpdateTouchConsumerStates(
342 const WebTouchEvent& event,
343 InputEventAckState ack_result) {
344 if (event.type() == WebInputEvent::TouchStart) {
345 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
346 send_touch_events_async_ = false;
347
348 // Once we have the ack back for the sequence we know if there
dtapuska 2017/02/23 16:52:12 This is an interesting block.
349 // is a handler or not. Other touch-starts sent can upgrade
350 // whether we have a handler or not as well.
351 if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
352 maybe_has_handler_for_current_sequence_ =
353 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
354 } else {
355 maybe_has_handler_for_current_sequence_ |=
356 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
357 }
358 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) {
359 maybe_has_handler_for_current_sequence_ = false;
360 }
361 }
362
363 size_t PassthroughTouchEventQueue::SizeForTesting() const {
364 return outstanding_touches_.size();
365 }
366
367 bool PassthroughTouchEventQueue::IsTimeoutRunningForTesting() const {
368 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
369 }
370
371 const TouchEventWithLatencyInfo&
372 PassthroughTouchEventQueue::GetLatestEventForTesting() const {
373 return *outstanding_touches_.rbegin();
374 }
375
376 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698