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

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 on throttling change Created 3 years, 9 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"
tdresser 2017/02/24 20:04:28 Are we using all these includes?
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"
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,
tdresser 2017/02/24 20:04:28 Comment doesn't really add anything. The name imp
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;
tdresser 2017/02/24 20:04:28 Every 2 years or so of use, we'll wrap around the
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);
88
89 InputEventAckState ack_state =
90 filter_result == ACK_WITH_NO_CONSUMER_EXISTS
91 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
92 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
93 TouchEventWithLatencyInfoAndAckState event_with_ack_state = event;
94 event_with_ack_state.set_ack_state(ack_state);
95 outstanding_touches_.insert(event_with_ack_state);
96 AckCompletedEvents();
97 return;
98 }
99 TouchEventWithLatencyInfo cloned_event(event);
100 SendTouchEventImmediately(&cloned_event, true);
101 }
102
103 void PassthroughTouchEventQueue::PrependTouchScrollNotification() {
104 TRACE_EVENT0("input",
105 "PassthroughTouchEventQueue::PrependTouchScrollNotification");
106
107 TouchEventWithLatencyInfo touch(
108 WebInputEvent::TouchScrollStarted, WebInputEvent::NoModifiers,
109 ui::EventTimeStampToSeconds(ui::EventTimeForNow()), LatencyInfo());
110 touch.event.dispatchType = WebInputEvent::EventNonBlocking;
111 SendTouchEventImmediately(&touch, true);
112 }
113
114 void PassthroughTouchEventQueue::ProcessTouchAck(
115 InputEventAckState ack_result,
116 const LatencyInfo& latency_info,
117 const uint32_t unique_touch_event_id) {
118 TRACE_EVENT0("input", "PassthroughTouchEventQueue::ProcessTouchAck");
119 if (timeout_handler_ &&
120 timeout_handler_->ConfirmTouchEvent(unique_touch_event_id, ack_result))
121 return;
122
123 auto touch_event_iter = outstanding_touches_.begin();
124 while (touch_event_iter != outstanding_touches_.end()) {
125 if (unique_touch_event_id == touch_event_iter->event.uniqueTouchEventId)
126 break;
127 ++touch_event_iter;
128 }
129
130 if (touch_event_iter == outstanding_touches_.end())
131 return;
132
133 TouchEventWithLatencyInfoAndAckState event = *touch_event_iter;
134 touch_event_iter = outstanding_touches_.erase(touch_event_iter);
135 event.latency.AddNewLatencyFrom(latency_info);
136 event.set_ack_state(ack_result);
137 outstanding_touches_.insert(touch_event_iter, event);
138
139 AckCompletedEvents();
140 }
141
142 void PassthroughTouchEventQueue::OnGestureScrollEvent(
143 const GestureEventWithLatencyInfo& gesture_event) {
144 if (gesture_event.event.type() == blink::WebInputEvent::GestureScrollUpdate &&
145 gesture_event.event.resendingPluginId == -1) {
146 send_touch_events_async_ = true;
147 }
148 }
149
150 void PassthroughTouchEventQueue::OnGestureEventAck(
151 const GestureEventWithLatencyInfo& event,
152 InputEventAckState ack_result) {
153 // Turn events sent during gesture scrolls to be async.
154 if (event.event.type() == blink::WebInputEvent::GestureScrollUpdate &&
155 event.event.resendingPluginId == -1) {
156 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
157 }
158 }
159
160 void PassthroughTouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
161 has_handlers_ = has_handlers;
162 }
163
164 bool PassthroughTouchEventQueue::IsPendingAckTouchStart() const {
165 if (outstanding_touches_.empty())
166 return false;
167
168 for (auto& iter : outstanding_touches_) {
169 if (iter.event.type() == WebInputEvent::TouchStart)
170 return true;
171 }
172 return false;
173 }
174
175 void PassthroughTouchEventQueue::SetAckTimeoutEnabled(bool enabled) {
176 if (timeout_handler_)
177 timeout_handler_->SetEnabled(enabled);
178 }
179
180 void PassthroughTouchEventQueue::SetIsMobileOptimizedSite(
181 bool mobile_optimized_site) {
182 if (timeout_handler_)
183 timeout_handler_->SetUseMobileTimeout(mobile_optimized_site);
184 }
185
186 bool PassthroughTouchEventQueue::IsAckTimeoutEnabled() const {
187 return timeout_handler_ && timeout_handler_->IsEnabled();
188 }
189
190 bool PassthroughTouchEventQueue::Empty() const {
191 return outstanding_touches_.empty();
192 }
193
194 void PassthroughTouchEventQueue::FlushQueue() {
195 drop_remaining_touches_in_sequence_ = true;
196 while (!outstanding_touches_.empty()) {
197 auto iter = outstanding_touches_.begin();
198 TouchEventWithLatencyInfoAndAckState event = *iter;
199 outstanding_touches_.erase(iter);
200 if (event.ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN)
201 event.set_ack_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
202 AckTouchEventToClient(event, event.ack_state());
203 }
204 }
205
206 void PassthroughTouchEventQueue::AckCompletedEvents() {
207 while (!outstanding_touches_.empty()) {
208 auto iter = outstanding_touches_.begin();
209 if (iter->ack_state() == INPUT_EVENT_ACK_STATE_UNKNOWN)
210 break;
211 TouchEventWithLatencyInfoAndAckState event = *iter;
212 outstanding_touches_.erase(iter);
213 AckTouchEventToClient(event, event.ack_state());
214 }
215 }
216
217 void PassthroughTouchEventQueue::AckTouchEventToClient(
218 const TouchEventWithLatencyInfo& acked_event,
219 InputEventAckState ack_result) {
220 UpdateTouchConsumerStates(acked_event.event, ack_result);
221
222 // Skip ack for TouchScrollStarted since it was synthesized within the queue.
223 if (acked_event.event.type() != WebInputEvent::TouchScrollStarted) {
224 client_->OnTouchEventAck(acked_event, ack_result);
225 }
226 }
227
228 void PassthroughTouchEventQueue::SendTouchEventImmediately(
229 TouchEventWithLatencyInfo* touch,
230 bool wait_for_ack) {
231 // Note: Touchstart events are marked cancelable to allow transitions between
232 // platform scrolling and JS pinching. Touchend events, however, remain
233 // uncancelable, mitigating the risk of jank when transitioning to a fling.
234 if (send_touch_events_async_ &&
235 touch->event.type() != WebInputEvent::TouchStart)
236 touch->event.dispatchType = WebInputEvent::EventNonBlocking;
237
238 if (touch->event.type() == WebInputEvent::TouchStart)
239 touch->event.touchStartOrFirstTouchMove = true;
240
241 // For touchmove events, compare touch points position from current event
242 // to last sent event and update touch points state.
243 if (touch->event.type() == WebInputEvent::TouchMove) {
244 CHECK(last_sent_touchevent_);
245 if (last_sent_touchevent_->type() == WebInputEvent::TouchStart)
246 touch->event.touchStartOrFirstTouchMove = true;
247 for (unsigned int i = 0; i < last_sent_touchevent_->touchesLength; ++i) {
248 const WebTouchPoint& last_touch_point = last_sent_touchevent_->touches[i];
249 // Touches with same id may not have same index in Touches array.
250 for (unsigned int j = 0; j < touch->event.touchesLength; ++j) {
251 const WebTouchPoint& current_touchmove_point = touch->event.touches[j];
252 if (current_touchmove_point.id != last_touch_point.id)
253 continue;
254
255 if (!HasPointChanged(last_touch_point, current_touchmove_point))
256 touch->event.touches[j].state = WebTouchPoint::StateStationary;
257
258 break;
259 }
260 }
261 }
262
263 if (touch->event.type() != WebInputEvent::TouchScrollStarted) {
264 if (last_sent_touchevent_)
265 *last_sent_touchevent_ = touch->event;
266 else
267 last_sent_touchevent_.reset(new WebTouchEvent(touch->event));
268 }
269
270 if (timeout_handler_)
271 timeout_handler_->StartIfNecessary(*touch);
272 if (wait_for_ack)
273 outstanding_touches_.insert(*touch);
274 client_->SendTouchEventImmediately(*touch);
275 }
276
277 PassthroughTouchEventQueue::PreFilterResult
278 PassthroughTouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
279 if (event.type() == WebInputEvent::TouchScrollStarted)
280 return FORWARD_TO_RENDERER;
281
282 if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
283 // We don't know if we have a handler until we get the ACK back so
284 // assume it is true.
285 maybe_has_handler_for_current_sequence_ = true;
286 send_touch_events_async_ = false;
287 last_sent_touchevent_.reset();
288
289 drop_remaining_touches_in_sequence_ = false;
290 if (!has_handlers_) {
291 drop_remaining_touches_in_sequence_ = true;
292 return ACK_WITH_NO_CONSUMER_EXISTS;
293 }
294 }
295
296 if (timeout_handler_ && timeout_handler_->FilterEvent(event))
297 return ACK_WITH_NO_CONSUMER_EXISTS;
298
299 if (drop_remaining_touches_in_sequence_ &&
300 event.type() != WebInputEvent::TouchCancel) {
301 return ACK_WITH_NO_CONSUMER_EXISTS;
302 }
303
304 if (event.type() == WebInputEvent::TouchStart) {
305 return (has_handlers_ || maybe_has_handler_for_current_sequence_)
306 ? FORWARD_TO_RENDERER
307 : ACK_WITH_NO_CONSUMER_EXISTS;
308 }
309
310 if (maybe_has_handler_for_current_sequence_) {
311 // Only forward a touch if it has a non-stationary pointer that is active
312 // in the current touch sequence.
313 for (size_t i = 0; i < event.touchesLength; ++i) {
314 const WebTouchPoint& point = event.touches[i];
315 if (point.state == WebTouchPoint::StateStationary)
316 continue;
317
318 // |last_sent_touchevent_| will be non-null as long as there is an
319 // active touch sequence being forwarded to the renderer.
320 if (!last_sent_touchevent_)
321 continue;
322
323 for (size_t j = 0; j < last_sent_touchevent_->touchesLength; ++j) {
324 if (point.id != last_sent_touchevent_->touches[j].id)
325 continue;
326
327 if (event.type() != WebInputEvent::TouchMove)
328 return FORWARD_TO_RENDERER;
329
330 // All pointers in TouchMove events may have state as StateMoved,
331 // even though none of the pointers have not changed in real.
332 // Forward these events when at least one pointer has changed.
333 if (HasPointChanged(last_sent_touchevent_->touches[j], point))
334 return FORWARD_TO_RENDERER;
335
336 // This is a TouchMove event for which we have yet to find a
337 // non-stationary pointer. Continue checking the next pointers
338 // in the |event|.
339 break;
340 }
341 }
342 }
343
344 return ACK_WITH_NO_CONSUMER_EXISTS;
345 }
346
347 void PassthroughTouchEventQueue::UpdateTouchConsumerStates(
348 const WebTouchEvent& event,
349 InputEventAckState ack_result) {
350 if (event.type() == WebInputEvent::TouchStart) {
351 if (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED)
352 send_touch_events_async_ = false;
353
354 // Once we have the ack back for the sequence we know if there
355 // is a handler or not. Other touch-starts sent can upgrade
356 // whether we have a handler or not as well.
357 if (WebTouchEventTraits::IsTouchSequenceStart(event)) {
358 maybe_has_handler_for_current_sequence_ =
359 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
360 } else {
361 maybe_has_handler_for_current_sequence_ |=
362 ack_result != INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
363 }
364 } else if (WebTouchEventTraits::IsTouchSequenceEnd(event)) {
365 maybe_has_handler_for_current_sequence_ = false;
366 }
367 }
368
369 size_t PassthroughTouchEventQueue::SizeForTesting() const {
370 return outstanding_touches_.size();
371 }
372
373 bool PassthroughTouchEventQueue::IsTimeoutRunningForTesting() const {
374 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
375 }
376
377 const TouchEventWithLatencyInfo&
378 PassthroughTouchEventQueue::GetLatestEventForTesting() const {
379 return *outstanding_touches_.rbegin();
380 }
381
382 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698