OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/input/touch_event_queue.h" | 5 #include "content/browser/renderer_host/input/touch_event_queue.h" |
6 | 6 |
7 #include "base/auto_reset.h" | 7 #include "base/auto_reset.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
11 #include "content/browser/renderer_host/input/timeout_monitor.h" | 11 #include "content/browser/renderer_host/input/timeout_monitor.h" |
12 #include "content/browser/renderer_host/input/web_touch_event_traits.h" | 12 #include "content/browser/renderer_host/input/web_touch_event_traits.h" |
13 #include "content/public/common/content_switches.h" | 13 #include "content/public/common/content_switches.h" |
14 #include "ui/gfx/geometry/point_f.h" | 14 #include "ui/gfx/geometry/point_f.h" |
15 | 15 |
16 using blink::WebInputEvent; | 16 using blink::WebInputEvent; |
17 using blink::WebTouchEvent; | 17 using blink::WebTouchEvent; |
18 using blink::WebTouchPoint; | 18 using blink::WebTouchPoint; |
19 using ui::LatencyInfo; | 19 using ui::LatencyInfo; |
20 | 20 |
21 namespace content { | 21 namespace content { |
22 namespace { | 22 namespace { |
23 | 23 |
24 // Time interval at which touchmove events will be forwarded to the client while | |
25 // scrolling is active and possible. | |
26 const double kAsyncTouchMoveIntervalSec = .2; | |
27 | |
28 // A slop region just larger than that used by many sites. When touchmove's are | |
29 // being sent asynchronously, movement outside this region will trigger an | |
30 // immediate async touchmove to cancel any potential tap/press-related logic. | |
31 const double kOuterSlopRegionLengthDipsSqared = 15. * 15.; | |
Rick Byers
2014/04/23 21:56:17
How about we call this 'application slop region' t
jdduke (slow)
2014/04/23 22:56:48
Done.
| |
32 | |
24 // Using a small epsilon when comparing slop distances allows pixel perfect | 33 // Using a small epsilon when comparing slop distances allows pixel perfect |
25 // slop determination when using fractional DIP coordinates (assuming the slop | 34 // slop determination when using fractional DIP coordinates (assuming the slop |
26 // region and DPI scale are reasonably proportioned). | 35 // region and DPI scale are reasonably proportioned). |
27 const float kSlopEpsilon = .05f; | 36 const float kSlopEpsilon = .05f; |
28 | 37 |
29 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; | 38 typedef std::vector<TouchEventWithLatencyInfo> WebTouchEventWithLatencyList; |
30 | 39 |
31 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( | 40 TouchEventWithLatencyInfo ObtainCancelEventForTouchEvent( |
32 const TouchEventWithLatencyInfo& event_to_cancel) { | 41 const TouchEventWithLatencyInfo& event_to_cancel) { |
33 TouchEventWithLatencyInfo event = event_to_cancel; | 42 TouchEventWithLatencyInfo event = event_to_cancel; |
34 event.event.type = WebInputEvent::TouchCancel; | 43 event.event.type = WebInputEvent::TouchCancel; |
35 for (size_t i = 0; i < event.event.touchesLength; i++) | 44 for (size_t i = 0; i < event.event.touchesLength; i++) |
36 event.event.touches[i].state = WebTouchPoint::StateCancelled; | 45 event.event.touches[i].state = WebTouchPoint::StateCancelled; |
37 return event; | 46 return event; |
38 } | 47 } |
39 | 48 |
40 bool ShouldTouchTypeTriggerTimeout(WebInputEvent::Type type) { | 49 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) { |
41 return type == WebInputEvent::TouchStart || | 50 return (event.type == WebInputEvent::TouchStart || |
42 type == WebInputEvent::TouchMove; | 51 event.type == WebInputEvent::TouchMove) && |
52 !WebInputEventTraits::IgnoresAckDisposition(event); | |
53 } | |
54 | |
55 bool OutsideOuterSlopRegion(const WebTouchEvent& event, | |
56 const gfx::PointF& anchor) { | |
57 return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() > | |
58 kOuterSlopRegionLengthDipsSqared; | |
43 } | 59 } |
44 | 60 |
45 } // namespace | 61 } // namespace |
46 | 62 |
47 | 63 |
48 // Cancels a touch sequence if a touchstart or touchmove ack response is | 64 // Cancels a touch sequence if a touchstart or touchmove ack response is |
49 // sufficiently delayed. | 65 // sufficiently delayed. |
50 class TouchEventQueue::TouchTimeoutHandler { | 66 class TouchEventQueue::TouchTimeoutHandler { |
51 public: | 67 public: |
52 TouchTimeoutHandler(TouchEventQueue* touch_queue, | 68 TouchTimeoutHandler(TouchEventQueue* touch_queue, |
53 base::TimeDelta timeout_delay) | 69 base::TimeDelta timeout_delay) |
54 : touch_queue_(touch_queue), | 70 : touch_queue_(touch_queue), |
55 timeout_delay_(timeout_delay), | 71 timeout_delay_(timeout_delay), |
56 pending_ack_state_(PENDING_ACK_NONE), | 72 pending_ack_state_(PENDING_ACK_NONE), |
57 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, | 73 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, |
58 base::Unretained(this))) {} | 74 base::Unretained(this))) {} |
59 | 75 |
60 ~TouchTimeoutHandler() {} | 76 ~TouchTimeoutHandler() {} |
61 | 77 |
62 void Start(const TouchEventWithLatencyInfo& event) { | 78 void Start(const TouchEventWithLatencyInfo& event) { |
63 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); | 79 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); |
64 DCHECK(ShouldTouchTypeTriggerTimeout(event.event.type)); | 80 DCHECK(ShouldTouchTriggerTimeout(event.event)); |
65 timeout_event_ = event; | 81 timeout_event_ = event; |
66 timeout_monitor_.Restart(timeout_delay_); | 82 timeout_monitor_.Restart(timeout_delay_); |
67 } | 83 } |
68 | 84 |
69 bool ConfirmTouchEvent(InputEventAckState ack_result) { | 85 bool ConfirmTouchEvent(InputEventAckState ack_result) { |
70 switch (pending_ack_state_) { | 86 switch (pending_ack_state_) { |
71 case PENDING_ACK_NONE: | 87 case PENDING_ACK_NONE: |
72 timeout_monitor_.Stop(); | 88 timeout_monitor_.Stop(); |
73 return false; | 89 return false; |
74 case PENDING_ACK_ORIGINAL_EVENT: | 90 case PENDING_ACK_ORIGINAL_EVENT: |
75 if (AckedTimeoutEventRequiresCancel(ack_result)) { | 91 if (AckedTimeoutEventRequiresCancel(ack_result)) { |
76 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); | 92 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); |
77 TouchEventWithLatencyInfo cancel_event = | 93 TouchEventWithLatencyInfo cancel_event = |
78 ObtainCancelEventForTouchEvent(timeout_event_); | 94 ObtainCancelEventForTouchEvent(timeout_event_); |
79 touch_queue_->client_->SendTouchEventImmediately(cancel_event); | 95 touch_queue_->SendTouchEventImmediately(cancel_event); |
80 } else { | 96 } else { |
81 SetPendingAckState(PENDING_ACK_NONE); | 97 SetPendingAckState(PENDING_ACK_NONE); |
82 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); | 98 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); |
83 } | 99 } |
84 return true; | 100 return true; |
85 case PENDING_ACK_CANCEL_EVENT: | 101 case PENDING_ACK_CANCEL_EVENT: |
86 SetPendingAckState(PENDING_ACK_NONE); | 102 SetPendingAckState(PENDING_ACK_NONE); |
87 return true; | 103 return true; |
88 } | 104 } |
89 return false; | 105 return false; |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); | 235 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); |
220 }; | 236 }; |
221 | 237 |
222 // This class represents a single coalesced touch event. However, it also keeps | 238 // This class represents a single coalesced touch event. However, it also keeps |
223 // track of all the original touch-events that were coalesced into a single | 239 // track of all the original touch-events that were coalesced into a single |
224 // event. The coalesced event is forwarded to the renderer, while the original | 240 // event. The coalesced event is forwarded to the renderer, while the original |
225 // touch-events are sent to the Client (on ACK for the coalesced event) so that | 241 // touch-events are sent to the Client (on ACK for the coalesced event) so that |
226 // the Client receives the event with their original timestamp. | 242 // the Client receives the event with their original timestamp. |
227 class CoalescedWebTouchEvent { | 243 class CoalescedWebTouchEvent { |
228 public: | 244 public: |
229 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, | 245 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, bool async) |
230 bool ignore_ack) | |
231 : coalesced_event_(event), | 246 : coalesced_event_(event), |
232 ignore_ack_(ignore_ack) { | 247 async_(async) { |
233 events_.push_back(event); | 248 if (async) |
234 TRACE_EVENT_ASYNC_BEGIN0( | 249 coalesced_event_.event.cancelable = false; |
235 "input", "TouchEventQueue::QueueEvent", this); | 250 else |
251 events_.push_back(event); | |
252 | |
253 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this); | |
236 } | 254 } |
237 | 255 |
238 ~CoalescedWebTouchEvent() { | 256 ~CoalescedWebTouchEvent() { |
239 TRACE_EVENT_ASYNC_END0( | 257 TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this); |
240 "input", "TouchEventQueue::QueueEvent", this); | |
241 } | 258 } |
242 | 259 |
243 // Coalesces the event with the existing event if possible. Returns whether | 260 // Coalesces the event with the existing event if possible. Returns whether |
244 // the event was coalesced. | 261 // the event was coalesced. |
245 bool CoalesceEventIfPossible( | 262 bool CoalesceEventIfPossible( |
246 const TouchEventWithLatencyInfo& event_with_latency) { | 263 const TouchEventWithLatencyInfo& event_with_latency) { |
247 if (ignore_ack_) | 264 if (async_) |
248 return false; | 265 return false; |
249 | 266 |
250 if (!coalesced_event_.CanCoalesceWith(event_with_latency)) | 267 if (!coalesced_event_.CanCoalesceWith(event_with_latency)) |
251 return false; | 268 return false; |
252 | 269 |
253 TRACE_EVENT_INSTANT0( | 270 TRACE_EVENT_INSTANT0( |
254 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); | 271 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); |
255 coalesced_event_.CoalesceWith(event_with_latency); | 272 coalesced_event_.CoalesceWith(event_with_latency); |
256 events_.push_back(event_with_latency); | 273 events_.push_back(event_with_latency); |
257 return true; | 274 return true; |
258 } | 275 } |
259 | 276 |
277 void UpdateLatencyInfo(const ui::LatencyInfo& renderer_latency_info) { | |
Rick Byers
2014/04/23 21:56:17
Maybe 'updateLatencyInfoForAck'? Otherwise it's n
jdduke (slow)
2014/04/23 22:56:48
Done.
| |
278 if (async_) | |
279 return; | |
280 | |
281 for (WebTouchEventWithLatencyList::iterator iter = events_.begin(), | |
282 end = events_.end(); | |
283 iter != end; | |
284 ++iter) { | |
285 iter->latency.AddNewLatencyFrom(renderer_latency_info); | |
286 } | |
287 } | |
288 | |
289 void DispatchAckToClient(InputEventAckState ack_result, | |
290 TouchEventQueueClient* client) { | |
291 DCHECK(client); | |
292 if (async_) | |
293 return; | |
294 | |
295 for (WebTouchEventWithLatencyList::const_iterator iter = events_.begin(), | |
296 end = events_.end(); | |
297 iter != end; | |
298 ++iter) { | |
299 client->OnTouchEventAck(*iter, ack_result); | |
300 } | |
301 } | |
302 | |
260 const TouchEventWithLatencyInfo& coalesced_event() const { | 303 const TouchEventWithLatencyInfo& coalesced_event() const { |
261 return coalesced_event_; | 304 return coalesced_event_; |
262 } | 305 } |
263 | 306 |
264 WebTouchEventWithLatencyList::iterator begin() { | |
265 return events_.begin(); | |
266 } | |
267 | |
268 WebTouchEventWithLatencyList::iterator end() { | |
269 return events_.end(); | |
270 } | |
271 | |
272 size_t size() const { return events_.size(); } | |
273 | |
274 bool ignore_ack() const { return ignore_ack_; } | |
275 | |
276 private: | 307 private: |
277 // This is the event that is forwarded to the renderer. | 308 // This is the event that is forwarded to the renderer. |
278 TouchEventWithLatencyInfo coalesced_event_; | 309 TouchEventWithLatencyInfo coalesced_event_; |
279 | 310 |
280 // This is the list of the original events that were coalesced. | 311 // This is the list of the original events that were coalesced. |
281 WebTouchEventWithLatencyList events_; | 312 WebTouchEventWithLatencyList events_; |
aelias_OOO_until_Jul13
2014/04/23 21:42:22
Could you make this a scoped_ptr<> and only create
jdduke (slow)
2014/04/23 22:56:48
Eh, I guess. But then we're double heap allocatin
| |
282 | 313 |
283 // If |ignore_ack_| is true, don't send this touch event to client | 314 // If |async_| is true, the touch event will not be ack'ed to the client. |
284 // when the event is acked. | 315 bool async_; |
285 bool ignore_ack_; | |
286 | 316 |
287 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); | 317 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); |
288 }; | 318 }; |
289 | 319 |
290 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, | 320 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, |
291 TouchScrollingMode mode, | 321 TouchScrollingMode mode, |
292 double touchmove_suppression_length_dips) | 322 double touchmove_suppression_length_dips) |
293 : client_(client), | 323 : client_(client), |
294 dispatching_touch_ack_(NULL), | 324 dispatching_touch_ack_(NULL), |
295 dispatching_touch_(false), | 325 dispatching_touch_(false), |
296 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), | 326 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), |
297 ack_timeout_enabled_(false), | 327 ack_timeout_enabled_(false), |
298 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( | 328 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( |
299 touchmove_suppression_length_dips + kSlopEpsilon)), | 329 touchmove_suppression_length_dips + kSlopEpsilon)), |
300 absorbing_touch_moves_(false), | 330 async_touch_moves_(false), |
331 last_sent_touch_timestamp_sec_(0), | |
301 touch_scrolling_mode_(mode) { | 332 touch_scrolling_mode_(mode) { |
302 DCHECK(client); | 333 DCHECK(client); |
303 } | 334 } |
304 | 335 |
305 TouchEventQueue::~TouchEventQueue() { | 336 TouchEventQueue::~TouchEventQueue() { |
306 if (!touch_queue_.empty()) | 337 if (!touch_queue_.empty()) |
307 STLDeleteElements(&touch_queue_); | 338 STLDeleteElements(&touch_queue_); |
308 } | 339 } |
309 | 340 |
310 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { | 341 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
361 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) { | 392 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) { |
362 touch_filtering_state_ = FORWARD_ALL_TOUCHES; | 393 touch_filtering_state_ = FORWARD_ALL_TOUCHES; |
363 } | 394 } |
364 | 395 |
365 if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS && | 396 if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS && |
366 touch_filtering_state_ != DROP_ALL_TOUCHES && | 397 touch_filtering_state_ != DROP_ALL_TOUCHES && |
367 WebTouchEventTraits::IsTouchSequenceStart(acked_event)) { | 398 WebTouchEventTraits::IsTouchSequenceStart(acked_event)) { |
368 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 399 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
369 } | 400 } |
370 | 401 |
371 UpdateTouchAckStates(acked_event, ack_result); | |
372 PopTouchEventToClient(ack_result, latency_info); | 402 PopTouchEventToClient(ack_result, latency_info); |
373 TryForwardNextEventToRenderer(); | 403 TryForwardNextEventToRenderer(); |
374 } | 404 } |
375 | 405 |
376 void TouchEventQueue::TryForwardNextEventToRenderer() { | 406 void TouchEventQueue::TryForwardNextEventToRenderer() { |
377 DCHECK(!dispatching_touch_ack_); | 407 DCHECK(!dispatching_touch_ack_); |
378 // If there are queued touch events, then try to forward them to the renderer | 408 // If there are queued touch events, then try to forward them to the renderer |
379 // immediately, or ACK the events back to the client if appropriate. | 409 // immediately, or ACK the events back to the client if appropriate. |
380 while (!touch_queue_.empty()) { | 410 while (!touch_queue_.empty()) { |
381 const TouchEventWithLatencyInfo& touch = | 411 PreFilterResult result = |
382 touch_queue_.front()->coalesced_event(); | 412 FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event); |
383 PreFilterResult result = FilterBeforeForwarding(touch.event); | |
384 switch (result) { | 413 switch (result) { |
385 case ACK_WITH_NO_CONSUMER_EXISTS: | 414 case ACK_WITH_NO_CONSUMER_EXISTS: |
386 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, | 415 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
387 LatencyInfo()); | |
388 break; | 416 break; |
389 case ACK_WITH_NOT_CONSUMED: | 417 case ACK_WITH_NOT_CONSUMED: |
390 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, | 418 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); |
391 LatencyInfo()); | |
392 break; | 419 break; |
393 case FORWARD_TO_RENDERER: | 420 case FORWARD_TO_RENDERER: |
394 ForwardToRenderer(touch); | 421 ForwardNextEventToRenderer(); |
395 return; | 422 return; |
396 } | 423 } |
397 } | 424 } |
398 } | 425 } |
399 | 426 |
400 void TouchEventQueue::ForwardToRenderer( | 427 void TouchEventQueue::ForwardNextEventToRenderer() { |
401 const TouchEventWithLatencyInfo& touch) { | 428 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer"); |
402 TRACE_EVENT0("input", "TouchEventQueue::ForwardToRenderer"); | |
403 | 429 |
430 DCHECK(!empty()); | |
404 DCHECK(!dispatching_touch_); | 431 DCHECK(!dispatching_touch_); |
405 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); | 432 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); |
433 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event(); | |
406 | 434 |
407 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) { | 435 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) { |
408 touch_filtering_state_ = | 436 touch_filtering_state_ = |
409 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT | 437 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT |
410 : FORWARD_ALL_TOUCHES; | 438 : FORWARD_ALL_TOUCHES; |
411 touch_ack_states_.clear(); | 439 touch_ack_states_.clear(); |
412 absorbing_touch_moves_ = false; | 440 async_touch_moves_ = false; |
441 touch_sequence_start_position_ = | |
442 gfx::PointF(touch.event.touches[0].position); | |
413 } | 443 } |
414 | 444 |
445 if (async_touch_moves_ && touch.event.type == WebInputEvent::TouchMove) { | |
446 // Only forward the new touhcmove if there are additional pending events, | |
447 // or sufficient time has passed since sending the last touch event. | |
Rick Byers
2014/04/23 21:56:17
update comment to mention application slop region.
jdduke (slow)
2014/04/23 22:56:48
Done.
| |
448 const bool send_touch_move_now = | |
449 size() > 1 || | |
450 touch.event.timeStampSeconds >= | |
451 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec || | |
452 (needs_async_touch_move_for_outer_slop_region_ && | |
453 OutsideOuterSlopRegion(touch.event, touch_sequence_start_position_)); | |
454 | |
455 if (!send_touch_move_now) { | |
456 // If the touchmoves cannot be coalesced (e.g., different modifiers), the | |
457 // new touchmove will simply replace the pending touchmove. | |
aelias_OOO_until_Jul13
2014/04/23 21:42:22
This looks like it destroys information. Should w
Rick Byers
2014/04/23 21:58:13
I like that idea. Eg. if a modifier goes down and
jdduke (slow)
2014/04/23 22:56:48
If the app relies on async touches for modifier up
| |
458 if (!pending_async_touch_move_ || | |
459 !pending_async_touch_move_->CanCoalesceWith(touch)) { | |
460 pending_async_touch_move_.reset(new TouchEventWithLatencyInfo(touch)); | |
461 } else { | |
462 pending_async_touch_move_->CoalesceWith(touch); | |
463 } | |
464 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | |
465 return; | |
466 } | |
467 } | |
468 | |
469 last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds; | |
470 | |
471 // Flush any pending async touch move. If it can be combined with the current | |
472 // (touchmove) event, great, otherwise send it immediately but separately. Its | |
473 // ack will trigger forwarding of the original |touch| event. | |
474 if (pending_async_touch_move_) { | |
475 if (pending_async_touch_move_->CanCoalesceWith(touch)) { | |
476 pending_async_touch_move_->CoalesceWith(touch); | |
477 pending_async_touch_move_->event.cancelable = !async_touch_moves_; | |
478 touch = *pending_async_touch_move_.Pass(); | |
479 } else { | |
480 scoped_ptr<TouchEventWithLatencyInfo> async_move = | |
481 pending_async_touch_move_.Pass(); | |
482 async_move->event.cancelable = false; | |
483 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true)); | |
484 SendTouchEventImmediately(*async_move); | |
485 return; | |
486 } | |
487 } | |
488 | |
489 if (async_touch_moves_) | |
490 touch.event.cancelable = false; | |
Rick Byers
2014/04/23 21:56:17
So you're intentionally making new touchstart even
jdduke (slow)
2014/04/23 22:56:48
Yeah, I think that makes the most sense at this po
| |
491 | |
415 // A synchronous ack will reset |dispatching_touch_|, in which case | 492 // A synchronous ack will reset |dispatching_touch_|, in which case |
416 // the touch timeout should not be started. | 493 // the touch timeout should not be started. |
417 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); | 494 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); |
418 client_->SendTouchEventImmediately(touch); | 495 SendTouchEventImmediately(touch); |
419 if (dispatching_touch_ && | 496 if (dispatching_touch_ && |
420 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && | 497 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && |
421 ShouldTouchTypeTriggerTimeout(touch.event.type)) { | 498 ShouldTouchTriggerTimeout(touch.event)) { |
422 DCHECK(timeout_handler_); | 499 DCHECK(timeout_handler_); |
423 timeout_handler_->Start(touch); | 500 timeout_handler_->Start(touch); |
424 } | 501 } |
425 } | 502 } |
426 | 503 |
427 void TouchEventQueue::OnGestureScrollEvent( | 504 void TouchEventQueue::OnGestureScrollEvent( |
428 const GestureEventWithLatencyInfo& gesture_event) { | 505 const GestureEventWithLatencyInfo& gesture_event) { |
429 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) | 506 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) |
430 return; | 507 return; |
431 | 508 |
432 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) | 509 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) { |
433 absorbing_touch_moves_ = true; | 510 pending_async_touch_move_.reset(); |
511 async_touch_moves_ = true; | |
512 needs_async_touch_move_for_outer_slop_region_ = true; | |
513 return; | |
514 } | |
434 | 515 |
435 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) | 516 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) |
436 return; | 517 return; |
437 | 518 |
438 // We assume that scroll events are generated synchronously from | 519 // We assume that scroll events are generated synchronously from |
439 // dispatching a touch event ack. This allows us to generate a synthetic | 520 // dispatching a touch event ack. This allows us to generate a synthetic |
440 // cancel event that has the same touch ids as the touch event that | 521 // cancel event that has the same touch ids as the touch event that |
441 // is being acked. Otherwise, we don't perform the touch-cancel optimization. | 522 // is being acked. Otherwise, we don't perform the touch-cancel optimization. |
442 if (!dispatching_touch_ack_) | 523 if (!dispatching_touch_ack_) |
443 return; | 524 return; |
(...skipping 10 matching lines...) Expand all Loading... | |
454 // in the queue is waiting for ack from renderer. So we can just insert | 535 // in the queue is waiting for ack from renderer. So we can just insert |
455 // the touch cancel at the beginning of the queue. | 536 // the touch cancel at the beginning of the queue. |
456 touch_queue_.push_front(new CoalescedWebTouchEvent( | 537 touch_queue_.push_front(new CoalescedWebTouchEvent( |
457 ObtainCancelEventForTouchEvent( | 538 ObtainCancelEventForTouchEvent( |
458 dispatching_touch_ack_->coalesced_event()), true)); | 539 dispatching_touch_ack_->coalesced_event()), true)); |
459 } | 540 } |
460 | 541 |
461 void TouchEventQueue::OnGestureEventAck( | 542 void TouchEventQueue::OnGestureEventAck( |
462 const GestureEventWithLatencyInfo& event, | 543 const GestureEventWithLatencyInfo& event, |
463 InputEventAckState ack_result) { | 544 InputEventAckState ack_result) { |
464 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) | 545 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) |
465 return; | 546 return; |
466 | 547 |
467 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) | 548 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) |
468 return; | 549 return; |
469 | 550 |
470 // Suspend sending touchmove events as long as the scroll events are handled. | 551 // Throttle sending touchmove events as long as the scroll events are handled. |
471 // Note that there's no guarantee that this ACK is for the most recent | 552 // Note that there's no guarantee that this ACK is for the most recent |
472 // gesture event (or even part of the current sequence). Worst case, the | 553 // gesture event (or even part of the current sequence). Worst case, the |
473 // delay in updating the absorption state should only result in minor UI | 554 // delay in updating the absorption state will result in minor UI glitches. |
474 // glitches. | 555 // A valid |pending_async_touch_move_| will be flushed when the next event is |
475 absorbing_touch_moves_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | 556 // forwarded. |
557 async_touch_moves_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); | |
558 if (!async_touch_moves_) | |
559 needs_async_touch_move_for_outer_slop_region_ = false; | |
476 } | 560 } |
477 | 561 |
478 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { | 562 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { |
479 DCHECK(!dispatching_touch_ack_); | 563 DCHECK(!dispatching_touch_ack_); |
480 DCHECK(!dispatching_touch_); | 564 DCHECK(!dispatching_touch_); |
481 | 565 |
482 if (has_handlers) { | 566 if (has_handlers) { |
483 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { | 567 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { |
484 // If no touch handler was previously registered, ensure that we don't | 568 // If no touch handler was previously registered, ensure that we don't |
485 // send a partial touch sequence to the renderer. | 569 // send a partial touch sequence to the renderer. |
486 DCHECK(touch_queue_.empty()); | 570 DCHECK(touch_queue_.empty()); |
487 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 571 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
488 } | 572 } |
489 } else { | 573 } else { |
490 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch | 574 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch |
491 // state tracking (e.g., if the touch handler was removed mid-sequence). | 575 // state tracking (e.g., if the touch handler was removed mid-sequence). |
492 touch_filtering_state_ = DROP_ALL_TOUCHES; | 576 touch_filtering_state_ = DROP_ALL_TOUCHES; |
577 pending_async_touch_move_.reset(); | |
493 if (timeout_handler_) | 578 if (timeout_handler_) |
494 timeout_handler_->Reset(); | 579 timeout_handler_->Reset(); |
495 if (!touch_queue_.empty()) | 580 if (!touch_queue_.empty()) |
496 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); | 581 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); |
497 // As there is no touch handler, ack'ing the event should flush the queue. | 582 // As there is no touch handler, ack'ing the event should flush the queue. |
498 DCHECK(touch_queue_.empty()); | 583 DCHECK(touch_queue_.empty()); |
499 } | 584 } |
500 } | 585 } |
501 | 586 |
502 bool TouchEventQueue::IsPendingAckTouchStart() const { | 587 bool TouchEventQueue::IsPendingAckTouchStart() const { |
(...skipping 20 matching lines...) Expand all Loading... | |
523 return; | 608 return; |
524 } | 609 } |
525 | 610 |
526 ack_timeout_enabled_ = true; | 611 ack_timeout_enabled_ = true; |
527 if (!timeout_handler_) | 612 if (!timeout_handler_) |
528 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay)); | 613 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay)); |
529 else | 614 else |
530 timeout_handler_->set_timeout_delay(ack_timeout_delay); | 615 timeout_handler_->set_timeout_delay(ack_timeout_delay); |
531 } | 616 } |
532 | 617 |
618 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const { | |
619 return pending_async_touch_move_; | |
620 } | |
621 | |
533 bool TouchEventQueue::IsTimeoutRunningForTesting() const { | 622 bool TouchEventQueue::IsTimeoutRunningForTesting() const { |
534 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); | 623 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); |
535 } | 624 } |
536 | 625 |
537 const TouchEventWithLatencyInfo& | 626 const TouchEventWithLatencyInfo& |
538 TouchEventQueue::GetLatestEventForTesting() const { | 627 TouchEventQueue::GetLatestEventForTesting() const { |
539 return touch_queue_.back()->coalesced_event(); | 628 return touch_queue_.back()->coalesced_event(); |
540 } | 629 } |
541 | 630 |
542 void TouchEventQueue::FlushQueue() { | 631 void TouchEventQueue::FlushQueue() { |
543 DCHECK(!dispatching_touch_ack_); | 632 DCHECK(!dispatching_touch_ack_); |
544 DCHECK(!dispatching_touch_); | 633 DCHECK(!dispatching_touch_); |
634 pending_async_touch_move_.reset(); | |
545 if (touch_filtering_state_ != DROP_ALL_TOUCHES) | 635 if (touch_filtering_state_ != DROP_ALL_TOUCHES) |
546 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; | 636 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; |
547 while (!touch_queue_.empty()) { | 637 while (!touch_queue_.empty()) |
548 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, | 638 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); |
549 LatencyInfo()); | 639 } |
550 } | 640 |
641 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) { | |
642 AckTouchEventToClient(ack_result, PopTouchEvent()); | |
551 } | 643 } |
552 | 644 |
553 void TouchEventQueue::PopTouchEventToClient( | 645 void TouchEventQueue::PopTouchEventToClient( |
554 InputEventAckState ack_result, | 646 InputEventAckState ack_result, |
555 const LatencyInfo& renderer_latency_info) { | 647 const LatencyInfo& renderer_latency_info) { |
648 scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent(); | |
649 acked_event->UpdateLatencyInfo(renderer_latency_info); | |
650 AckTouchEventToClient(ack_result, acked_event.Pass()); | |
651 } | |
652 | |
653 void TouchEventQueue::AckTouchEventToClient( | |
654 InputEventAckState ack_result, | |
655 scoped_ptr<CoalescedWebTouchEvent> acked_event) { | |
656 DCHECK(acked_event); | |
556 DCHECK(!dispatching_touch_ack_); | 657 DCHECK(!dispatching_touch_ack_); |
557 if (touch_queue_.empty()) | 658 UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result); |
558 return; | |
559 scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front()); | |
560 touch_queue_.pop_front(); | |
561 | |
562 if (acked_event->ignore_ack()) | |
563 return; | |
564 | 659 |
565 // Note that acking the touch-event may result in multiple gestures being sent | 660 // Note that acking the touch-event may result in multiple gestures being sent |
566 // to the renderer, or touch-events being queued. | 661 // to the renderer, or touch-events being queued. |
567 base::AutoReset<CoalescedWebTouchEvent*> | 662 base::AutoReset<const CoalescedWebTouchEvent*> |
568 dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get()); | 663 dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get()); |
664 acked_event->DispatchAckToClient(ack_result, client_); | |
665 } | |
569 | 666 |
570 for (WebTouchEventWithLatencyList::iterator iter = acked_event->begin(), | 667 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() { |
571 end = acked_event->end(); | 668 DCHECK(!touch_queue_.empty()); |
572 iter != end; ++iter) { | 669 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front()); |
573 iter->latency.AddNewLatencyFrom(renderer_latency_info); | 670 touch_queue_.pop_front(); |
574 client_->OnTouchEventAck((*iter), ack_result); | 671 return event.Pass(); |
672 } | |
673 | |
674 void TouchEventQueue::SendTouchEventImmediately( | |
675 const TouchEventWithLatencyInfo& touch) { | |
676 if (needs_async_touch_move_for_outer_slop_region_) { | |
677 // Any event other than a touchmove (e.g., touchcancel or secondary | |
678 // touchstart) after a scroll has started will interrupt the need to send a | |
679 // an outer slop-region exceeding touchmove. | |
680 if (touch.event.type != WebInputEvent::TouchMove || | |
681 OutsideOuterSlopRegion(touch.event, touch_sequence_start_position_)) | |
682 needs_async_touch_move_for_outer_slop_region_ = false; | |
575 } | 683 } |
684 | |
685 client_->SendTouchEventImmediately(touch); | |
576 } | 686 } |
577 | 687 |
578 TouchEventQueue::PreFilterResult | 688 TouchEventQueue::PreFilterResult |
579 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { | 689 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { |
580 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) | 690 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) |
581 return ACK_WITH_NO_CONSUMER_EXISTS; | 691 return ACK_WITH_NO_CONSUMER_EXISTS; |
582 | 692 |
583 if (touchmove_slop_suppressor_->FilterEvent(event)) | 693 if (touchmove_slop_suppressor_->FilterEvent(event)) |
584 return ACK_WITH_NOT_CONSUMED; | 694 return ACK_WITH_NOT_CONSUMED; |
585 | 695 |
586 if (touch_filtering_state_ == DROP_ALL_TOUCHES) | 696 if (touch_filtering_state_ == DROP_ALL_TOUCHES) |
587 return ACK_WITH_NO_CONSUMER_EXISTS; | 697 return ACK_WITH_NO_CONSUMER_EXISTS; |
588 | 698 |
589 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && | 699 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && |
590 event.type != WebInputEvent::TouchCancel) { | 700 event.type != WebInputEvent::TouchCancel) { |
591 if (WebTouchEventTraits::IsTouchSequenceStart(event)) | 701 if (WebTouchEventTraits::IsTouchSequenceStart(event)) |
592 return FORWARD_TO_RENDERER; | 702 return FORWARD_TO_RENDERER; |
593 return ACK_WITH_NOT_CONSUMED; | 703 return ACK_WITH_NOT_CONSUMED; |
594 } | 704 } |
595 | 705 |
596 if (absorbing_touch_moves_ && event.type == WebInputEvent::TouchMove) | |
597 return ACK_WITH_NOT_CONSUMED; | |
598 | |
599 // Touch press events should always be forwarded to the renderer. | 706 // Touch press events should always be forwarded to the renderer. |
600 if (event.type == WebInputEvent::TouchStart) | 707 if (event.type == WebInputEvent::TouchStart) |
601 return FORWARD_TO_RENDERER; | 708 return FORWARD_TO_RENDERER; |
602 | 709 |
603 for (unsigned int i = 0; i < event.touchesLength; ++i) { | 710 for (unsigned int i = 0; i < event.touchesLength; ++i) { |
604 const WebTouchPoint& point = event.touches[i]; | 711 const WebTouchPoint& point = event.touches[i]; |
605 // If a point has been stationary, then don't take it into account. | 712 // If a point has been stationary, then don't take it into account. |
606 if (point.state == WebTouchPoint::StateStationary) | 713 if (point.state == WebTouchPoint::StateStationary) |
607 continue; | 714 continue; |
608 | 715 |
(...skipping 26 matching lines...) Expand all Loading... | |
635 } else if (event.type == WebInputEvent::TouchStart) { | 742 } else if (event.type == WebInputEvent::TouchStart) { |
636 for (unsigned i = 0; i < event.touchesLength; ++i) { | 743 for (unsigned i = 0; i < event.touchesLength; ++i) { |
637 const WebTouchPoint& point = event.touches[i]; | 744 const WebTouchPoint& point = event.touches[i]; |
638 if (point.state == WebTouchPoint::StatePressed) | 745 if (point.state == WebTouchPoint::StatePressed) |
639 touch_ack_states_[point.id] = ack_result; | 746 touch_ack_states_[point.id] = ack_result; |
640 } | 747 } |
641 } | 748 } |
642 } | 749 } |
643 | 750 |
644 } // namespace content | 751 } // namespace content |
OLD | NEW |