OLD | NEW |
---|---|
(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 | |
OLD | NEW |