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

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

Issue 245833002: Implement async touchmove dispatch during scroll (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: More testing Created 6 years, 8 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/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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698