Home | History | Annotate | Download | only in input
      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
      3 // found in the LICENSE file.
      4 
      5 #ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
      6 #define CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
      7 
      8 #include <deque>
      9 #include <map>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/time/time.h"
     13 #include "content/browser/renderer_host/event_with_latency_info.h"
     14 #include "content/common/content_export.h"
     15 #include "content/common/input/input_event_ack_state.h"
     16 #include "third_party/WebKit/public/web/WebInputEvent.h"
     17 #include "ui/gfx/geometry/point_f.h"
     18 
     19 namespace content {
     20 
     21 class CoalescedWebTouchEvent;
     22 
     23 // Interface with which TouchEventQueue can forward touch events, and dispatch
     24 // touch event responses.
     25 class CONTENT_EXPORT TouchEventQueueClient {
     26  public:
     27   virtual ~TouchEventQueueClient() {}
     28 
     29   virtual void SendTouchEventImmediately(
     30       const TouchEventWithLatencyInfo& event) = 0;
     31 
     32   virtual void OnTouchEventAck(
     33       const TouchEventWithLatencyInfo& event,
     34       InputEventAckState ack_result) = 0;
     35 };
     36 
     37 // A queue for throttling and coalescing touch-events.
     38 class CONTENT_EXPORT TouchEventQueue {
     39  public:
     40   // Different ways of dealing with touch events during scrolling.
     41   // TODO(rbyers): Remove this once we're confident that touch move absorption
     42   // is OK. http://crbug.com/350430
     43   enum TouchScrollingMode {
     44     // Send a touchcancel on scroll start and no further touch events for the
     45     // duration of the scroll.  Chrome Android's traditional behavior.
     46     TOUCH_SCROLLING_MODE_TOUCHCANCEL,
     47     // Send touchmove events throughout a scroll, blocking on each ACK and
     48     // using the disposition to determine whether a scroll update should be
     49     // sent.  Mobile Safari's default overflow scroll behavior.
     50     TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
     51     // Send touchmove events throughout a scroll, but throttle sending and
     52     // ignore the ACK as long as scrolling remains possible.  Unconsumed scroll
     53     // events return touchmove events to being dispatched synchronously.
     54     TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE,
     55     TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE
     56   };
     57 
     58   struct CONTENT_EXPORT Config {
     59     Config();
     60 
     61     // Determines the bounds of the (square) touchmove slop suppression region.
     62     // Defaults to 0 (disabled).
     63     double touchmove_slop_suppression_length_dips;
     64 
     65     // Whether the touchmove slop suppression region is boundary inclusive.
     66     // Defaults to true.
     67     // TODO(jdduke): Remove when unified GR enabled, crbug.com/332418.
     68     bool touchmove_slop_suppression_region_includes_boundary;
     69 
     70     // Determines the type of touch scrolling.
     71     // Defaults to TouchEventQueue:::TOUCH_SCROLLING_MODE_DEFAULT.
     72     TouchEventQueue::TouchScrollingMode touch_scrolling_mode;
     73 
     74     // Controls whether touch ack timeouts will trigger touch cancellation.
     75     // Defaults to 200ms.
     76     base::TimeDelta touch_ack_timeout_delay;
     77 
     78     // Whether the platform supports touch ack timeout behavior.
     79     // Defaults to false (disabled).
     80     bool touch_ack_timeout_supported;
     81   };
     82 
     83   // The |client| must outlive the TouchEventQueue.
     84   TouchEventQueue(TouchEventQueueClient* client, const Config& config);
     85 
     86   ~TouchEventQueue();
     87 
     88   // Adds an event to the queue. The event may be coalesced with previously
     89   // queued events (e.g. consecutive touch-move events can be coalesced into a
     90   // single touch-move event). The event may also be immediately forwarded to
     91   // the renderer (e.g. when there are no other queued touch event).
     92   void QueueEvent(const TouchEventWithLatencyInfo& event);
     93 
     94   // Notifies the queue that a touch-event has been processed by the renderer.
     95   // At this point, the queue may send one or more gesture events and/or
     96   // additional queued touch-events to the renderer.
     97   void ProcessTouchAck(InputEventAckState ack_result,
     98                        const ui::LatencyInfo& latency_info);
     99 
    100   // When GestureScrollBegin is received, we send a touch cancel to renderer,
    101   // route all the following touch events directly to client, and ignore the
    102   // ack for the touch cancel. When Gesture{ScrollEnd,FlingStart} is received,
    103   // resume the normal flow of sending touch events to the renderer.
    104   void OnGestureScrollEvent(const GestureEventWithLatencyInfo& gesture_event);
    105 
    106   void OnGestureEventAck(
    107       const GestureEventWithLatencyInfo& event,
    108       InputEventAckState ack_result);
    109 
    110   // Notifies the queue whether the renderer has at least one touch handler.
    111   void OnHasTouchEventHandlers(bool has_handlers);
    112 
    113   // Returns whether the currently pending touch event (waiting ACK) is for
    114   // a touch start event.
    115   bool IsPendingAckTouchStart() const;
    116 
    117   // Sets whether a delayed touch ack will cancel and flush the current
    118   // touch sequence. Note that, if the timeout was previously disabled, enabling
    119   // it will take effect only for the following touch sequence.
    120   void SetAckTimeoutEnabled(bool enabled);
    121 
    122   bool empty() const WARN_UNUSED_RESULT {
    123     return touch_queue_.empty();
    124   }
    125 
    126   size_t size() const {
    127     return touch_queue_.size();
    128   }
    129 
    130   bool ack_timeout_enabled() const {
    131     return ack_timeout_enabled_;
    132   }
    133 
    134   bool has_handlers() const {
    135     return touch_filtering_state_ != DROP_ALL_TOUCHES;
    136   }
    137 
    138  private:
    139   class TouchTimeoutHandler;
    140   class TouchMoveSlopSuppressor;
    141   friend class TouchTimeoutHandler;
    142   friend class TouchEventQueueTest;
    143 
    144   bool HasPendingAsyncTouchMoveForTesting() const;
    145   bool IsTimeoutRunningForTesting() const;
    146   const TouchEventWithLatencyInfo& GetLatestEventForTesting() const;
    147 
    148   // Empties the queue of touch events. This may result in any number of gesture
    149   // events being sent to the renderer.
    150   void FlushQueue();
    151 
    152   // Walks the queue, checking each event with |FilterBeforeForwarding()|.
    153   // If allowed, forwards the touch event and stops processing further events.
    154   // Otherwise, acks the event with |INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS|.
    155   void TryForwardNextEventToRenderer();
    156 
    157   // Forwards the event at the head of the queue to the renderer.
    158   void ForwardNextEventToRenderer();
    159 
    160   // Pops the touch-event from the head of the queue and acks it to the client.
    161   void PopTouchEventToClient(InputEventAckState ack_result);
    162 
    163   // Pops the touch-event from the top of the queue and acks it to the client,
    164   // updating the event with |renderer_latency_info|.
    165   void PopTouchEventToClient(InputEventAckState ack_result,
    166                              const ui::LatencyInfo& renderer_latency_info);
    167 
    168   // Ack all coalesced events in |acked_event| to the client with |ack_result|.
    169   void AckTouchEventToClient(InputEventAckState ack_result,
    170                              scoped_ptr<CoalescedWebTouchEvent> acked_event);
    171 
    172   // Safely pop the head of the queue.
    173   scoped_ptr<CoalescedWebTouchEvent> PopTouchEvent();
    174 
    175   // Dispatch |touch| to the client.
    176   void SendTouchEventImmediately(const TouchEventWithLatencyInfo& touch);
    177 
    178   enum PreFilterResult {
    179     ACK_WITH_NO_CONSUMER_EXISTS,
    180     ACK_WITH_NOT_CONSUMED,
    181     FORWARD_TO_RENDERER,
    182   };
    183   // Filter touches prior to forwarding to the renderer, e.g., if the renderer
    184   // has no touch handler.
    185   PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
    186   void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
    187   void UpdateTouchAckStates(const blink::WebTouchEvent& event,
    188                             InputEventAckState ack_result);
    189   bool AllTouchAckStatesHaveState(InputEventAckState ack_state) const;
    190 
    191 
    192   // Handles touch event forwarding and ack'ed event dispatch.
    193   TouchEventQueueClient* client_;
    194 
    195   typedef std::deque<CoalescedWebTouchEvent*> TouchQueue;
    196   TouchQueue touch_queue_;
    197 
    198   // Maintain the ACK status for each touch point.
    199   typedef std::map<int, InputEventAckState> TouchPointAckStates;
    200   TouchPointAckStates touch_ack_states_;
    201 
    202   // Position of the first touch in the most recent sequence forwarded to the
    203   // client.
    204   gfx::PointF touch_sequence_start_position_;
    205 
    206   // Used to defer touch forwarding when ack dispatch triggers |QueueEvent()|.
    207   // If not NULL, |dispatching_touch_ack_| is the touch event of which the ack
    208   // is being dispatched.
    209   const CoalescedWebTouchEvent* dispatching_touch_ack_;
    210 
    211   // Used to prevent touch timeout scheduling if we receive a synchronous
    212   // ack after forwarding a touch event to the client.
    213   bool dispatching_touch_;
    214 
    215   enum TouchFilteringState {
    216     FORWARD_ALL_TOUCHES,           // Don't filter at all - the default.
    217     FORWARD_TOUCHES_UNTIL_TIMEOUT, // Don't filter unless we get an ACK timeout.
    218     DROP_TOUCHES_IN_SEQUENCE,      // Filter all events until a new touch
    219                                    // sequence is received.
    220     DROP_ALL_TOUCHES,              // Filter all events, e.g., no touch handler.
    221     TOUCH_FILTERING_STATE_DEFAULT = FORWARD_ALL_TOUCHES,
    222   };
    223   TouchFilteringState touch_filtering_state_;
    224 
    225   // Optional handler for timed-out touch event acks.
    226   bool ack_timeout_enabled_;
    227   scoped_ptr<TouchTimeoutHandler> timeout_handler_;
    228 
    229   // Suppression of TouchMove's within a slop region when a sequence has not yet
    230   // been preventDefaulted.
    231   scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
    232 
    233   // Whether touch events should remain buffered and dispatched asynchronously
    234   // while a scroll sequence is active.  In this mode, touchmove's are throttled
    235   // and ack'ed immediately, but remain buffered in |pending_async_touchmove_|
    236   // until a sufficient time period has elapsed since the last sent touch event.
    237   // For details see the design doc at http://goo.gl/lVyJAa.
    238   bool send_touch_events_async_;
    239   bool needs_async_touchmove_for_outer_slop_region_;
    240   scoped_ptr<TouchEventWithLatencyInfo> pending_async_touchmove_;
    241   double last_sent_touch_timestamp_sec_;
    242 
    243   // How touch events are handled during scrolling.  For now this is a global
    244   // setting for experimentation, but we may evolve it into an app-controlled
    245   // mode.
    246   const TouchScrollingMode touch_scrolling_mode_;
    247 
    248   DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
    249 };
    250 
    251 }  // namespace content
    252 
    253 #endif  // CONTENT_BROWSER_RENDERER_HOST_INPUT_TOUCH_EVENT_QUEUE_H_
    254