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

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

Issue 254803012: Reland "Implement async touchmove dispatch during scroll" (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 7 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
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/common/input/web_touch_event_traits.h" 12 #include "content/common/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 web applications. When
29 // touchmove's are being sent asynchronously, movement outside this region will
30 // trigger an immediate async touchmove to cancel potential tap-related logic.
31 const double kApplicationSlopRegionLengthDipsSqared = 15. * 15.;
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 WebTouchEventTraits::ResetTypeAndTouchStates( 43 WebTouchEventTraits::ResetTypeAndTouchStates(
35 WebInputEvent::TouchCancel, 44 WebInputEvent::TouchCancel,
36 // TODO(rbyers): Shouldn't we use a fresh timestamp? 45 // TODO(rbyers): Shouldn't we use a fresh timestamp?
37 event.event.timeStampSeconds, 46 event.event.timeStampSeconds,
38 &event.event); 47 &event.event);
39 return event; 48 return event;
40 } 49 }
41 50
42 bool ShouldTouchTypeTriggerTimeout(WebInputEvent::Type type) { 51 bool ShouldTouchTriggerTimeout(const WebTouchEvent& event) {
43 return type == WebInputEvent::TouchStart || 52 return (event.type == WebInputEvent::TouchStart ||
44 type == WebInputEvent::TouchMove; 53 event.type == WebInputEvent::TouchMove) &&
54 !WebInputEventTraits::IgnoresAckDisposition(event);
55 }
56
57 bool OutsideApplicationSlopRegion(const WebTouchEvent& event,
58 const gfx::PointF& anchor) {
59 return (gfx::PointF(event.touches[0].position) - anchor).LengthSquared() >
60 kApplicationSlopRegionLengthDipsSqared;
45 } 61 }
46 62
47 } // namespace 63 } // namespace
48 64
49 65
50 // Cancels a touch sequence if a touchstart or touchmove ack response is 66 // Cancels a touch sequence if a touchstart or touchmove ack response is
51 // sufficiently delayed. 67 // sufficiently delayed.
52 class TouchEventQueue::TouchTimeoutHandler { 68 class TouchEventQueue::TouchTimeoutHandler {
53 public: 69 public:
54 TouchTimeoutHandler(TouchEventQueue* touch_queue, 70 TouchTimeoutHandler(TouchEventQueue* touch_queue,
55 base::TimeDelta timeout_delay) 71 base::TimeDelta timeout_delay)
56 : touch_queue_(touch_queue), 72 : touch_queue_(touch_queue),
57 timeout_delay_(timeout_delay), 73 timeout_delay_(timeout_delay),
58 pending_ack_state_(PENDING_ACK_NONE), 74 pending_ack_state_(PENDING_ACK_NONE),
59 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut, 75 timeout_monitor_(base::Bind(&TouchTimeoutHandler::OnTimeOut,
60 base::Unretained(this))) {} 76 base::Unretained(this))) {}
61 77
62 ~TouchTimeoutHandler() {} 78 ~TouchTimeoutHandler() {}
63 79
64 void Start(const TouchEventWithLatencyInfo& event) { 80 void Start(const TouchEventWithLatencyInfo& event) {
65 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE); 81 DCHECK_EQ(pending_ack_state_, PENDING_ACK_NONE);
66 DCHECK(ShouldTouchTypeTriggerTimeout(event.event.type)); 82 DCHECK(ShouldTouchTriggerTimeout(event.event));
67 timeout_event_ = event; 83 timeout_event_ = event;
68 timeout_monitor_.Restart(timeout_delay_); 84 timeout_monitor_.Restart(timeout_delay_);
69 } 85 }
70 86
71 bool ConfirmTouchEvent(InputEventAckState ack_result) { 87 bool ConfirmTouchEvent(InputEventAckState ack_result) {
72 switch (pending_ack_state_) { 88 switch (pending_ack_state_) {
73 case PENDING_ACK_NONE: 89 case PENDING_ACK_NONE:
74 timeout_monitor_.Stop(); 90 timeout_monitor_.Stop();
75 return false; 91 return false;
76 case PENDING_ACK_ORIGINAL_EVENT: 92 case PENDING_ACK_ORIGINAL_EVENT:
77 if (AckedTimeoutEventRequiresCancel(ack_result)) { 93 if (AckedTimeoutEventRequiresCancel(ack_result)) {
78 SetPendingAckState(PENDING_ACK_CANCEL_EVENT); 94 SetPendingAckState(PENDING_ACK_CANCEL_EVENT);
79 TouchEventWithLatencyInfo cancel_event = 95 TouchEventWithLatencyInfo cancel_event =
80 ObtainCancelEventForTouchEvent(timeout_event_); 96 ObtainCancelEventForTouchEvent(timeout_event_);
81 touch_queue_->client_->SendTouchEventImmediately(cancel_event); 97 touch_queue_->SendTouchEventImmediately(cancel_event);
82 } else { 98 } else {
83 SetPendingAckState(PENDING_ACK_NONE); 99 SetPendingAckState(PENDING_ACK_NONE);
84 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result); 100 touch_queue_->UpdateTouchAckStates(timeout_event_.event, ack_result);
85 } 101 }
86 return true; 102 return true;
87 case PENDING_ACK_CANCEL_EVENT: 103 case PENDING_ACK_CANCEL_EVENT:
88 SetPendingAckState(PENDING_ACK_NONE); 104 SetPendingAckState(PENDING_ACK_NONE);
89 return true; 105 return true;
90 } 106 }
91 return false; 107 return false;
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor); 237 DISALLOW_COPY_AND_ASSIGN(TouchMoveSlopSuppressor);
222 }; 238 };
223 239
224 // This class represents a single coalesced touch event. However, it also keeps 240 // This class represents a single coalesced touch event. However, it also keeps
225 // track of all the original touch-events that were coalesced into a single 241 // track of all the original touch-events that were coalesced into a single
226 // event. The coalesced event is forwarded to the renderer, while the original 242 // event. The coalesced event is forwarded to the renderer, while the original
227 // touch-events are sent to the Client (on ACK for the coalesced event) so that 243 // touch-events are sent to the Client (on ACK for the coalesced event) so that
228 // the Client receives the event with their original timestamp. 244 // the Client receives the event with their original timestamp.
229 class CoalescedWebTouchEvent { 245 class CoalescedWebTouchEvent {
230 public: 246 public:
231 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, 247 // Events for which |async| is true will not be ack'ed to the client after the
232 bool ignore_ack) 248 // corresponding ack is received following dispatch.
233 : coalesced_event_(event), 249 CoalescedWebTouchEvent(const TouchEventWithLatencyInfo& event, bool async)
234 ignore_ack_(ignore_ack) { 250 : coalesced_event_(event) {
235 events_.push_back(event); 251 if (async)
236 TRACE_EVENT_ASYNC_BEGIN0( 252 coalesced_event_.event.cancelable = false;
237 "input", "TouchEventQueue::QueueEvent", this); 253 else
254 events_to_ack_.push_back(event);
255
256 TRACE_EVENT_ASYNC_BEGIN0("input", "TouchEventQueue::QueueEvent", this);
238 } 257 }
239 258
240 ~CoalescedWebTouchEvent() { 259 ~CoalescedWebTouchEvent() {
241 TRACE_EVENT_ASYNC_END0( 260 TRACE_EVENT_ASYNC_END0("input", "TouchEventQueue::QueueEvent", this);
242 "input", "TouchEventQueue::QueueEvent", this);
243 } 261 }
244 262
245 // Coalesces the event with the existing event if possible. Returns whether 263 // Coalesces the event with the existing event if possible. Returns whether
246 // the event was coalesced. 264 // the event was coalesced.
247 bool CoalesceEventIfPossible( 265 bool CoalesceEventIfPossible(
248 const TouchEventWithLatencyInfo& event_with_latency) { 266 const TouchEventWithLatencyInfo& event_with_latency) {
249 if (ignore_ack_) 267 if (!WillDispatchAckToClient())
250 return false; 268 return false;
251 269
252 if (!coalesced_event_.CanCoalesceWith(event_with_latency)) 270 if (!coalesced_event_.CanCoalesceWith(event_with_latency))
253 return false; 271 return false;
254 272
255 TRACE_EVENT_INSTANT0( 273 TRACE_EVENT_INSTANT0(
256 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD); 274 "input", "TouchEventQueue::MoveCoalesced", TRACE_EVENT_SCOPE_THREAD);
257 coalesced_event_.CoalesceWith(event_with_latency); 275 coalesced_event_.CoalesceWith(event_with_latency);
258 events_.push_back(event_with_latency); 276 events_to_ack_.push_back(event_with_latency);
259 return true; 277 return true;
260 } 278 }
261 279
280 void UpdateLatencyInfoForAck(const ui::LatencyInfo& renderer_latency_info) {
281 if (!WillDispatchAckToClient())
282 return;
283
284 for (WebTouchEventWithLatencyList::iterator iter = events_to_ack_.begin(),
285 end = events_to_ack_.end();
286 iter != end;
287 ++iter) {
288 iter->latency.AddNewLatencyFrom(renderer_latency_info);
289 }
290 }
291
292 void DispatchAckToClient(InputEventAckState ack_result,
293 TouchEventQueueClient* client) {
294 DCHECK(client);
295 if (!WillDispatchAckToClient())
296 return;
297
298 for (WebTouchEventWithLatencyList::const_iterator
299 iter = events_to_ack_.begin(),
300 end = events_to_ack_.end();
301 iter != end;
302 ++iter) {
303 client->OnTouchEventAck(*iter, ack_result);
304 }
305 }
306
262 const TouchEventWithLatencyInfo& coalesced_event() const { 307 const TouchEventWithLatencyInfo& coalesced_event() const {
263 return coalesced_event_; 308 return coalesced_event_;
264 } 309 }
265 310
266 WebTouchEventWithLatencyList::iterator begin() { 311 private:
267 return events_.begin(); 312 bool WillDispatchAckToClient() const { return !events_to_ack_.empty(); }
268 }
269 313
270 WebTouchEventWithLatencyList::iterator end() {
271 return events_.end();
272 }
273
274 size_t size() const { return events_.size(); }
275
276 bool ignore_ack() const { return ignore_ack_; }
277
278 private:
279 // This is the event that is forwarded to the renderer. 314 // This is the event that is forwarded to the renderer.
280 TouchEventWithLatencyInfo coalesced_event_; 315 TouchEventWithLatencyInfo coalesced_event_;
281 316
282 // This is the list of the original events that were coalesced. 317 // This is the list of the original events that were coalesced, each requiring
283 WebTouchEventWithLatencyList events_; 318 // future ack dispatch to the client.
284 319 WebTouchEventWithLatencyList events_to_ack_;
285 // If |ignore_ack_| is true, don't send this touch event to client
286 // when the event is acked.
287 bool ignore_ack_;
288 320
289 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent); 321 DISALLOW_COPY_AND_ASSIGN(CoalescedWebTouchEvent);
290 }; 322 };
291 323
292 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client, 324 TouchEventQueue::TouchEventQueue(TouchEventQueueClient* client,
293 TouchScrollingMode mode, 325 TouchScrollingMode mode,
294 double touchmove_suppression_length_dips) 326 double touchmove_suppression_length_dips)
295 : client_(client), 327 : client_(client),
296 dispatching_touch_ack_(NULL), 328 dispatching_touch_ack_(NULL),
297 dispatching_touch_(false), 329 dispatching_touch_(false),
298 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT), 330 touch_filtering_state_(TOUCH_FILTERING_STATE_DEFAULT),
299 ack_timeout_enabled_(false), 331 ack_timeout_enabled_(false),
300 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor( 332 touchmove_slop_suppressor_(new TouchMoveSlopSuppressor(
301 touchmove_suppression_length_dips + kSlopEpsilon)), 333 touchmove_suppression_length_dips + kSlopEpsilon)),
302 absorbing_touch_moves_(false), 334 send_touch_events_async_(false),
335 needs_async_touch_move_for_outer_slop_region_(false),
336 last_sent_touch_timestamp_sec_(0),
303 touch_scrolling_mode_(mode) { 337 touch_scrolling_mode_(mode) {
304 DCHECK(client); 338 DCHECK(client);
305 } 339 }
306 340
307 TouchEventQueue::~TouchEventQueue() { 341 TouchEventQueue::~TouchEventQueue() {
308 if (!touch_queue_.empty()) 342 if (!touch_queue_.empty())
309 STLDeleteElements(&touch_queue_); 343 STLDeleteElements(&touch_queue_);
310 } 344 }
311 345
312 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) { 346 void TouchEventQueue::QueueEvent(const TouchEventWithLatencyInfo& event) {
313 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent"); 347 TRACE_EVENT0("input", "TouchEventQueue::QueueEvent");
314 348
315 // If the queueing of |event| was triggered by an ack dispatch, defer 349 // If the queueing of |event| was triggered by an ack dispatch, defer
316 // processing the event until the dispatch has finished. 350 // processing the event until the dispatch has finished.
317 if (touch_queue_.empty() && !dispatching_touch_ack_) { 351 if (touch_queue_.empty() && !dispatching_touch_ack_) {
318 // Optimization of the case without touch handlers. Removing this path 352 // Optimization of the case without touch handlers. Removing this path
319 // yields identical results, but this avoids unnecessary allocations. 353 // yields identical results, but this avoids unnecessary allocations.
320 if (touch_filtering_state_ == DROP_ALL_TOUCHES || 354 PreFilterResult filter_result = FilterBeforeForwarding(event.event);
321 (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && 355 if (filter_result != FORWARD_TO_RENDERER) {
322 !WebTouchEventTraits::IsTouchSequenceStart(event.event))) { 356 client_->OnTouchEventAck(event,
323 client_->OnTouchEventAck(event, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS); 357 filter_result == ACK_WITH_NO_CONSUMER_EXISTS
358 ? INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS
359 : INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
324 return; 360 return;
325 } 361 }
326 362
327 // There is no touch event in the queue. Forward it to the renderer 363 // There is no touch event in the queue. Forward it to the renderer
328 // immediately. 364 // immediately.
329 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false)); 365 touch_queue_.push_back(new CoalescedWebTouchEvent(event, false));
330 TryForwardNextEventToRenderer(); 366 ForwardNextEventToRenderer();
331 return; 367 return;
332 } 368 }
333 369
334 // If the last queued touch-event was a touch-move, and the current event is 370 // If the last queued touch-event was a touch-move, and the current event is
335 // also a touch-move, then the events can be coalesced into a single event. 371 // also a touch-move, then the events can be coalesced into a single event.
336 if (touch_queue_.size() > 1) { 372 if (touch_queue_.size() > 1) {
337 CoalescedWebTouchEvent* last_event = touch_queue_.back(); 373 CoalescedWebTouchEvent* last_event = touch_queue_.back();
338 if (last_event->CoalesceEventIfPossible(event)) 374 if (last_event->CoalesceEventIfPossible(event))
339 return; 375 return;
340 } 376 }
(...skipping 22 matching lines...) Expand all
363 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) { 399 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT) {
364 touch_filtering_state_ = FORWARD_ALL_TOUCHES; 400 touch_filtering_state_ = FORWARD_ALL_TOUCHES;
365 } 401 }
366 402
367 if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS && 403 if (ack_result == INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS &&
368 touch_filtering_state_ != DROP_ALL_TOUCHES && 404 touch_filtering_state_ != DROP_ALL_TOUCHES &&
369 WebTouchEventTraits::IsTouchSequenceStart(acked_event)) { 405 WebTouchEventTraits::IsTouchSequenceStart(acked_event)) {
370 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; 406 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
371 } 407 }
372 408
373 UpdateTouchAckStates(acked_event, ack_result);
374 PopTouchEventToClient(ack_result, latency_info); 409 PopTouchEventToClient(ack_result, latency_info);
375 TryForwardNextEventToRenderer(); 410 TryForwardNextEventToRenderer();
376 } 411 }
377 412
378 void TouchEventQueue::TryForwardNextEventToRenderer() { 413 void TouchEventQueue::TryForwardNextEventToRenderer() {
379 DCHECK(!dispatching_touch_ack_); 414 DCHECK(!dispatching_touch_ack_);
380 // If there are queued touch events, then try to forward them to the renderer 415 // If there are queued touch events, then try to forward them to the renderer
381 // immediately, or ACK the events back to the client if appropriate. 416 // immediately, or ACK the events back to the client if appropriate.
382 while (!touch_queue_.empty()) { 417 while (!touch_queue_.empty()) {
383 const TouchEventWithLatencyInfo& touch = 418 PreFilterResult filter_result =
384 touch_queue_.front()->coalesced_event(); 419 FilterBeforeForwarding(touch_queue_.front()->coalesced_event().event);
385 PreFilterResult result = FilterBeforeForwarding(touch.event); 420 switch (filter_result) {
386 switch (result) {
387 case ACK_WITH_NO_CONSUMER_EXISTS: 421 case ACK_WITH_NO_CONSUMER_EXISTS:
388 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, 422 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
389 LatencyInfo());
390 break; 423 break;
391 case ACK_WITH_NOT_CONSUMED: 424 case ACK_WITH_NOT_CONSUMED:
392 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, 425 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
393 LatencyInfo());
394 break; 426 break;
395 case FORWARD_TO_RENDERER: 427 case FORWARD_TO_RENDERER:
396 ForwardToRenderer(touch); 428 ForwardNextEventToRenderer();
397 return; 429 return;
398 } 430 }
399 } 431 }
400 } 432 }
401 433
402 void TouchEventQueue::ForwardToRenderer( 434 void TouchEventQueue::ForwardNextEventToRenderer() {
403 const TouchEventWithLatencyInfo& touch) { 435 TRACE_EVENT0("input", "TouchEventQueue::ForwardNextEventToRenderer");
404 TRACE_EVENT0("input", "TouchEventQueue::ForwardToRenderer");
405 436
437 DCHECK(!empty());
406 DCHECK(!dispatching_touch_); 438 DCHECK(!dispatching_touch_);
407 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES); 439 DCHECK_NE(touch_filtering_state_, DROP_ALL_TOUCHES);
440 TouchEventWithLatencyInfo touch = touch_queue_.front()->coalesced_event();
408 441
409 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) { 442 if (WebTouchEventTraits::IsTouchSequenceStart(touch.event)) {
410 touch_filtering_state_ = 443 touch_filtering_state_ =
411 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT 444 ack_timeout_enabled_ ? FORWARD_TOUCHES_UNTIL_TIMEOUT
412 : FORWARD_ALL_TOUCHES; 445 : FORWARD_ALL_TOUCHES;
413 touch_ack_states_.clear(); 446 touch_ack_states_.clear();
414 absorbing_touch_moves_ = false; 447 send_touch_events_async_ = false;
448 touch_sequence_start_position_ =
449 gfx::PointF(touch.event.touches[0].position);
415 } 450 }
416 451
452 if (send_touch_events_async_ &&
453 touch.event.type == WebInputEvent::TouchMove) {
454 // Throttling touchmove's in a continuous touchmove stream while scrolling
455 // reduces the risk of jank. However, it's still important that the web
456 // application be sent touches at key points in the gesture stream,
457 // e.g., when the application slop region is exceeded or touchmove
458 // coalescing fails because of different modifiers.
459 const bool send_touch_move_now =
460 size() > 1 ||
461 (touch.event.timeStampSeconds >=
462 last_sent_touch_timestamp_sec_ + kAsyncTouchMoveIntervalSec) ||
463 (needs_async_touch_move_for_outer_slop_region_ &&
464 OutsideApplicationSlopRegion(touch.event,
465 touch_sequence_start_position_)) ||
466 (pending_async_touch_move_ &&
467 !pending_async_touch_move_->CanCoalesceWith(touch));
468
469 if (!send_touch_move_now) {
470 if (!pending_async_touch_move_) {
471 pending_async_touch_move_.reset(new TouchEventWithLatencyInfo(touch));
472 } else {
473 DCHECK(pending_async_touch_move_->CanCoalesceWith(touch));
474 pending_async_touch_move_->CoalesceWith(touch);
475 }
476 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
477 return;
478 }
479 }
480
481 last_sent_touch_timestamp_sec_ = touch.event.timeStampSeconds;
482
483 // Flush any pending async touch move. If it can be combined with the current
484 // (touchmove) event, great, otherwise send it immediately but separately. Its
485 // ack will trigger forwarding of the original |touch| event.
486 if (pending_async_touch_move_) {
487 if (pending_async_touch_move_->CanCoalesceWith(touch)) {
488 pending_async_touch_move_->CoalesceWith(touch);
489 pending_async_touch_move_->event.cancelable = !send_touch_events_async_;
490 touch = *pending_async_touch_move_.Pass();
491 } else {
492 scoped_ptr<TouchEventWithLatencyInfo> async_move =
493 pending_async_touch_move_.Pass();
494 async_move->event.cancelable = false;
495 touch_queue_.push_front(new CoalescedWebTouchEvent(*async_move, true));
496 SendTouchEventImmediately(*async_move);
497 return;
498 }
499 }
500
501 // Note: Marking touchstart events as not-cancelable prevents them from
502 // blocking subsequent gestures, but it may not be the best long term solution
503 // for tracking touch point dispatch.
504 if (send_touch_events_async_)
505 touch.event.cancelable = false;
506
417 // A synchronous ack will reset |dispatching_touch_|, in which case 507 // A synchronous ack will reset |dispatching_touch_|, in which case
418 // the touch timeout should not be started. 508 // the touch timeout should not be started.
419 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true); 509 base::AutoReset<bool> dispatching_touch(&dispatching_touch_, true);
420 client_->SendTouchEventImmediately(touch); 510 SendTouchEventImmediately(touch);
421 if (dispatching_touch_ && 511 if (dispatching_touch_ &&
422 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT && 512 touch_filtering_state_ == FORWARD_TOUCHES_UNTIL_TIMEOUT &&
423 ShouldTouchTypeTriggerTimeout(touch.event.type)) { 513 ShouldTouchTriggerTimeout(touch.event)) {
424 DCHECK(timeout_handler_); 514 DCHECK(timeout_handler_);
425 timeout_handler_->Start(touch); 515 timeout_handler_->Start(touch);
426 } 516 }
427 } 517 }
428 518
429 void TouchEventQueue::OnGestureScrollEvent( 519 void TouchEventQueue::OnGestureScrollEvent(
430 const GestureEventWithLatencyInfo& gesture_event) { 520 const GestureEventWithLatencyInfo& gesture_event) {
431 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin) 521 if (gesture_event.event.type != blink::WebInputEvent::GestureScrollBegin)
432 return; 522 return;
433 523
434 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) 524 if (touch_scrolling_mode_ == TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE) {
435 absorbing_touch_moves_ = true; 525 pending_async_touch_move_.reset();
526 send_touch_events_async_ = true;
527 needs_async_touch_move_for_outer_slop_region_ = true;
528 return;
529 }
436 530
437 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL) 531 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_TOUCHCANCEL)
438 return; 532 return;
439 533
440 // We assume that scroll events are generated synchronously from 534 // We assume that scroll events are generated synchronously from
441 // dispatching a touch event ack. This allows us to generate a synthetic 535 // dispatching a touch event ack. This allows us to generate a synthetic
442 // cancel event that has the same touch ids as the touch event that 536 // cancel event that has the same touch ids as the touch event that
443 // is being acked. Otherwise, we don't perform the touch-cancel optimization. 537 // is being acked. Otherwise, we don't perform the touch-cancel optimization.
444 if (!dispatching_touch_ack_) 538 if (!dispatching_touch_ack_)
445 return; 539 return;
(...skipping 10 matching lines...) Expand all
456 // in the queue is waiting for ack from renderer. So we can just insert 550 // in the queue is waiting for ack from renderer. So we can just insert
457 // the touch cancel at the beginning of the queue. 551 // the touch cancel at the beginning of the queue.
458 touch_queue_.push_front(new CoalescedWebTouchEvent( 552 touch_queue_.push_front(new CoalescedWebTouchEvent(
459 ObtainCancelEventForTouchEvent( 553 ObtainCancelEventForTouchEvent(
460 dispatching_touch_ack_->coalesced_event()), true)); 554 dispatching_touch_ack_->coalesced_event()), true));
461 } 555 }
462 556
463 void TouchEventQueue::OnGestureEventAck( 557 void TouchEventQueue::OnGestureEventAck(
464 const GestureEventWithLatencyInfo& event, 558 const GestureEventWithLatencyInfo& event,
465 InputEventAckState ack_result) { 559 InputEventAckState ack_result) {
466 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE) 560 if (touch_scrolling_mode_ != TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE)
467 return; 561 return;
468 562
469 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate) 563 if (event.event.type != blink::WebInputEvent::GestureScrollUpdate)
470 return; 564 return;
471 565
472 // Suspend sending touchmove events as long as the scroll events are handled. 566 // Throttle sending touchmove events as long as the scroll events are handled.
473 // Note that there's no guarantee that this ACK is for the most recent 567 // Note that there's no guarantee that this ACK is for the most recent
474 // gesture event (or even part of the current sequence). Worst case, the 568 // gesture event (or even part of the current sequence). Worst case, the
475 // delay in updating the absorption state should only result in minor UI 569 // delay in updating the absorption state will result in minor UI glitches.
476 // glitches. 570 // A valid |pending_async_touch_move_| will be flushed when the next event is
477 absorbing_touch_moves_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED); 571 // forwarded.
572 send_touch_events_async_ = (ack_result == INPUT_EVENT_ACK_STATE_CONSUMED);
573 if (!send_touch_events_async_)
574 needs_async_touch_move_for_outer_slop_region_ = false;
478 } 575 }
479 576
480 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) { 577 void TouchEventQueue::OnHasTouchEventHandlers(bool has_handlers) {
481 DCHECK(!dispatching_touch_ack_); 578 DCHECK(!dispatching_touch_ack_);
482 DCHECK(!dispatching_touch_); 579 DCHECK(!dispatching_touch_);
483 580
484 if (has_handlers) { 581 if (has_handlers) {
485 if (touch_filtering_state_ == DROP_ALL_TOUCHES) { 582 if (touch_filtering_state_ == DROP_ALL_TOUCHES) {
486 // If no touch handler was previously registered, ensure that we don't 583 // If no touch handler was previously registered, ensure that we don't
487 // send a partial touch sequence to the renderer. 584 // send a partial touch sequence to the renderer.
488 DCHECK(touch_queue_.empty()); 585 DCHECK(touch_queue_.empty());
489 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; 586 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
490 } 587 }
491 } else { 588 } else {
492 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch 589 // TODO(jdduke): Synthesize a TouchCancel if necessary to update Blink touch
493 // state tracking (e.g., if the touch handler was removed mid-sequence). 590 // state tracking (e.g., if the touch handler was removed mid-sequence).
494 touch_filtering_state_ = DROP_ALL_TOUCHES; 591 touch_filtering_state_ = DROP_ALL_TOUCHES;
592 pending_async_touch_move_.reset();
495 if (timeout_handler_) 593 if (timeout_handler_)
496 timeout_handler_->Reset(); 594 timeout_handler_->Reset();
497 if (!touch_queue_.empty()) 595 if (!touch_queue_.empty())
498 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo()); 596 ProcessTouchAck(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, LatencyInfo());
499 // As there is no touch handler, ack'ing the event should flush the queue. 597 // As there is no touch handler, ack'ing the event should flush the queue.
500 DCHECK(touch_queue_.empty()); 598 DCHECK(touch_queue_.empty());
501 } 599 }
502 } 600 }
503 601
504 bool TouchEventQueue::IsPendingAckTouchStart() const { 602 bool TouchEventQueue::IsPendingAckTouchStart() const {
(...skipping 20 matching lines...) Expand all
525 return; 623 return;
526 } 624 }
527 625
528 ack_timeout_enabled_ = true; 626 ack_timeout_enabled_ = true;
529 if (!timeout_handler_) 627 if (!timeout_handler_)
530 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay)); 628 timeout_handler_.reset(new TouchTimeoutHandler(this, ack_timeout_delay));
531 else 629 else
532 timeout_handler_->set_timeout_delay(ack_timeout_delay); 630 timeout_handler_->set_timeout_delay(ack_timeout_delay);
533 } 631 }
534 632
633 bool TouchEventQueue::HasPendingAsyncTouchMoveForTesting() const {
634 return pending_async_touch_move_;
635 }
636
535 bool TouchEventQueue::IsTimeoutRunningForTesting() const { 637 bool TouchEventQueue::IsTimeoutRunningForTesting() const {
536 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning(); 638 return timeout_handler_ && timeout_handler_->IsTimeoutTimerRunning();
537 } 639 }
538 640
539 const TouchEventWithLatencyInfo& 641 const TouchEventWithLatencyInfo&
540 TouchEventQueue::GetLatestEventForTesting() const { 642 TouchEventQueue::GetLatestEventForTesting() const {
541 return touch_queue_.back()->coalesced_event(); 643 return touch_queue_.back()->coalesced_event();
542 } 644 }
543 645
544 void TouchEventQueue::FlushQueue() { 646 void TouchEventQueue::FlushQueue() {
545 DCHECK(!dispatching_touch_ack_); 647 DCHECK(!dispatching_touch_ack_);
546 DCHECK(!dispatching_touch_); 648 DCHECK(!dispatching_touch_);
649 pending_async_touch_move_.reset();
547 if (touch_filtering_state_ != DROP_ALL_TOUCHES) 650 if (touch_filtering_state_ != DROP_ALL_TOUCHES)
548 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE; 651 touch_filtering_state_ = DROP_TOUCHES_IN_SEQUENCE;
549 while (!touch_queue_.empty()) { 652 while (!touch_queue_.empty())
550 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, 653 PopTouchEventToClient(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
551 LatencyInfo()); 654 }
552 } 655
656 void TouchEventQueue::PopTouchEventToClient(InputEventAckState ack_result) {
657 AckTouchEventToClient(ack_result, PopTouchEvent());
553 } 658 }
554 659
555 void TouchEventQueue::PopTouchEventToClient( 660 void TouchEventQueue::PopTouchEventToClient(
556 InputEventAckState ack_result, 661 InputEventAckState ack_result,
557 const LatencyInfo& renderer_latency_info) { 662 const LatencyInfo& renderer_latency_info) {
663 scoped_ptr<CoalescedWebTouchEvent> acked_event = PopTouchEvent();
664 acked_event->UpdateLatencyInfoForAck(renderer_latency_info);
665 AckTouchEventToClient(ack_result, acked_event.Pass());
666 }
667
668 void TouchEventQueue::AckTouchEventToClient(
669 InputEventAckState ack_result,
670 scoped_ptr<CoalescedWebTouchEvent> acked_event) {
671 DCHECK(acked_event);
558 DCHECK(!dispatching_touch_ack_); 672 DCHECK(!dispatching_touch_ack_);
559 if (touch_queue_.empty()) 673 UpdateTouchAckStates(acked_event->coalesced_event().event, ack_result);
560 return;
561 scoped_ptr<CoalescedWebTouchEvent> acked_event(touch_queue_.front());
562 touch_queue_.pop_front();
563
564 if (acked_event->ignore_ack())
565 return;
566 674
567 // Note that acking the touch-event may result in multiple gestures being sent 675 // Note that acking the touch-event may result in multiple gestures being sent
568 // to the renderer, or touch-events being queued. 676 // to the renderer, or touch-events being queued.
569 base::AutoReset<CoalescedWebTouchEvent*> 677 base::AutoReset<const CoalescedWebTouchEvent*> dispatching_touch_ack(
570 dispatching_touch_ack(&dispatching_touch_ack_, acked_event.get()); 678 &dispatching_touch_ack_, acked_event.get());
679 acked_event->DispatchAckToClient(ack_result, client_);
680 }
571 681
572 for (WebTouchEventWithLatencyList::iterator iter = acked_event->begin(), 682 scoped_ptr<CoalescedWebTouchEvent> TouchEventQueue::PopTouchEvent() {
573 end = acked_event->end(); 683 DCHECK(!touch_queue_.empty());
574 iter != end; ++iter) { 684 scoped_ptr<CoalescedWebTouchEvent> event(touch_queue_.front());
575 iter->latency.AddNewLatencyFrom(renderer_latency_info); 685 touch_queue_.pop_front();
576 client_->OnTouchEventAck((*iter), ack_result); 686 return event.Pass();
687 }
688
689 void TouchEventQueue::SendTouchEventImmediately(
690 const TouchEventWithLatencyInfo& touch) {
691 if (needs_async_touch_move_for_outer_slop_region_) {
692 // Any event other than a touchmove (e.g., touchcancel or secondary
693 // touchstart) after a scroll has started will interrupt the need to send a
694 // an outer slop-region exceeding touchmove.
695 if (touch.event.type != WebInputEvent::TouchMove ||
696 OutsideApplicationSlopRegion(touch.event,
697 touch_sequence_start_position_))
698 needs_async_touch_move_for_outer_slop_region_ = false;
577 } 699 }
700
701 client_->SendTouchEventImmediately(touch);
578 } 702 }
579 703
580 TouchEventQueue::PreFilterResult 704 TouchEventQueue::PreFilterResult
581 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) { 705 TouchEventQueue::FilterBeforeForwarding(const WebTouchEvent& event) {
582 if (timeout_handler_ && timeout_handler_->FilterEvent(event)) 706 if (timeout_handler_ && timeout_handler_->FilterEvent(event))
583 return ACK_WITH_NO_CONSUMER_EXISTS; 707 return ACK_WITH_NO_CONSUMER_EXISTS;
584 708
585 if (touchmove_slop_suppressor_->FilterEvent(event)) 709 if (touchmove_slop_suppressor_->FilterEvent(event))
586 return ACK_WITH_NOT_CONSUMED; 710 return ACK_WITH_NOT_CONSUMED;
587 711
588 if (touch_filtering_state_ == DROP_ALL_TOUCHES) 712 if (touch_filtering_state_ == DROP_ALL_TOUCHES)
589 return ACK_WITH_NO_CONSUMER_EXISTS; 713 return ACK_WITH_NO_CONSUMER_EXISTS;
590 714
591 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE && 715 if (touch_filtering_state_ == DROP_TOUCHES_IN_SEQUENCE &&
592 event.type != WebInputEvent::TouchCancel) { 716 event.type != WebInputEvent::TouchCancel) {
593 if (WebTouchEventTraits::IsTouchSequenceStart(event)) 717 if (WebTouchEventTraits::IsTouchSequenceStart(event))
594 return FORWARD_TO_RENDERER; 718 return FORWARD_TO_RENDERER;
595 return ACK_WITH_NOT_CONSUMED; 719 return ACK_WITH_NO_CONSUMER_EXISTS;
596 } 720 }
597 721
598 if (absorbing_touch_moves_ && event.type == WebInputEvent::TouchMove)
599 return ACK_WITH_NOT_CONSUMED;
600
601 // Touch press events should always be forwarded to the renderer. 722 // Touch press events should always be forwarded to the renderer.
602 if (event.type == WebInputEvent::TouchStart) 723 if (event.type == WebInputEvent::TouchStart)
603 return FORWARD_TO_RENDERER; 724 return FORWARD_TO_RENDERER;
604 725
605 for (unsigned int i = 0; i < event.touchesLength; ++i) { 726 for (unsigned int i = 0; i < event.touchesLength; ++i) {
606 const WebTouchPoint& point = event.touches[i]; 727 const WebTouchPoint& point = event.touches[i];
607 // If a point has been stationary, then don't take it into account. 728 // If a point has been stationary, then don't take it into account.
608 if (point.state == WebTouchPoint::StateStationary) 729 if (point.state == WebTouchPoint::StateStationary)
609 continue; 730 continue;
610 731
(...skipping 26 matching lines...) Expand all
637 } else if (event.type == WebInputEvent::TouchStart) { 758 } else if (event.type == WebInputEvent::TouchStart) {
638 for (unsigned i = 0; i < event.touchesLength; ++i) { 759 for (unsigned i = 0; i < event.touchesLength; ++i) {
639 const WebTouchPoint& point = event.touches[i]; 760 const WebTouchPoint& point = event.touches[i];
640 if (point.state == WebTouchPoint::StatePressed) 761 if (point.state == WebTouchPoint::StatePressed)
641 touch_ack_states_[point.id] = ack_result; 762 touch_ack_states_[point.id] = ack_result;
642 } 763 }
643 } 764 }
644 } 765 }
645 766
646 } // namespace content 767 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698