#include <map>
#include "base/basictypes.h"
+#include "base/time/time.h"
#include "content/common/content_export.h"
#include "content/port/browser/event_with_latency_info.h"
#include "content/port/common/input_event_ack_state.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/gfx/geometry/point_f.h"
namespace content {
// A queue for throttling and coalescing touch-events.
class CONTENT_EXPORT TouchEventQueue {
public:
+ // Different ways of dealing with touch events during scrolling.
+ // TODO(rbyers): Remove this once we're confident that touch move absorption
+ // is OK. http://crbug.com/350430
+ enum TouchScrollingMode {
+ // Send a touchcancel on scroll start and no further touch events for the
+ // duration of the scroll. Chrome Android's traditional behavior.
+ TOUCH_SCROLLING_MODE_TOUCHCANCEL,
+ // Send touchmove events throughout a scroll, blocking on each ACK and
+ // using the disposition to determine whether a scroll update should be
+ // sent. Mobile Safari's default overflow scroll behavior.
+ TOUCH_SCROLLING_MODE_SYNC_TOUCHMOVE,
+ // Like sync, except that consumed scroll events cause subsequent touchmove
+ // events to be suppressed. Unconsumed scroll events return touchmove
+ // events to being dispatched synchronously (so scrolling may be hijacked
+ // when a scroll limit is reached, and later resumed). http://goo.gl/RShsdN
+ TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE,
+ TOUCH_SCROLLING_MODE_DEFAULT = TOUCH_SCROLLING_MODE_TOUCHCANCEL
+ };
- // The |client| must outlive the TouchEventQueue.
- explicit TouchEventQueue(TouchEventQueueClient* client);
+ // The |client| must outlive the TouchEventQueue. If
+ // |touchmove_suppression_length_dips| <= 0, touch move suppression is
+ // disabled.
+ TouchEventQueue(TouchEventQueueClient* client,
+ TouchScrollingMode mode,
+ double touchmove_suppression_length_dips);
~TouchEventQueue();
// Adds an event to the queue. The event may be coalesced with previously
// resume the normal flow of sending touch events to the renderer.
void OnGestureScrollEvent(const GestureEventWithLatencyInfo& gesture_event);
+ void OnGestureEventAck(
+ const GestureEventWithLatencyInfo& event,
+ InputEventAckState ack_result);
+
// Notifies the queue whether the renderer has at least one touch handler.
void OnHasTouchEventHandlers(bool has_handlers);
bool IsPendingAckTouchStart() const;
// Sets whether a delayed touch ack will cancel and flush the current
- // touch sequence.
- void SetAckTimeoutEnabled(bool enabled, size_t ack_timeout_delay_ms);
+ // touch sequence. Note that, if the timeout was previously disabled, enabling
+ // it will take effect only for the following touch sequence.
+ void SetAckTimeoutEnabled(bool enabled, base::TimeDelta ack_timeout_delay);
bool empty() const WARN_UNUSED_RESULT {
return touch_queue_.empty();
private:
class TouchTimeoutHandler;
+ class TouchMoveSlopSuppressor;
friend class TouchTimeoutHandler;
friend class TouchEventQueueTest;
void PopTouchEventToClient(InputEventAckState ack_result,
const ui::LatencyInfo& renderer_latency_info);
- bool ShouldForwardToRenderer(const blink::WebTouchEvent& event) const;
+ enum PreFilterResult {
+ ACK_WITH_NO_CONSUMER_EXISTS,
+ ACK_WITH_NOT_CONSUMED,
+ FORWARD_TO_RENDERER,
+ };
+ // Filter touches prior to forwarding to the renderer, e.g., if the renderer
+ // has no touch handler.
+ PreFilterResult FilterBeforeForwarding(const blink::WebTouchEvent& event);
void ForwardToRenderer(const TouchEventWithLatencyInfo& event);
void UpdateTouchAckStates(const blink::WebTouchEvent& event,
InputEventAckState ack_result);
bool ack_timeout_enabled_;
scoped_ptr<TouchTimeoutHandler> timeout_handler_;
+ // Suppression of TouchMove's within a slop region when a sequence has not yet
+ // been preventDefaulted.
+ scoped_ptr<TouchMoveSlopSuppressor> touchmove_slop_suppressor_;
+
+ // Whether touchmove events should be dropped due to the
+ // TOUCH_SCROLLING_MODE_ABSORB_TOUCHMOVE mode. Note that we can't use
+ // touch_filtering_state_ for this (without adding a few new states and
+ // complicating the code significantly) because it can occur with and without
+ // timeout, and shouldn't cause touchend to be dropped.
+ bool absorbing_touch_moves_;
+
+ // How touch events are handled during scrolling. For now this is a global
+ // setting for experimentation, but we may evolve it into an app-controlled
+ // mode.
+ const TouchScrollingMode touch_scrolling_mode_;
+
DISALLOW_COPY_AND_ASSIGN(TouchEventQueue);
};