summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordtapuska <dtapuska@chromium.org>2016-03-11 07:24:59 -0800
committerCommit bot <commit-bot@chromium.org>2016-03-11 15:26:04 +0000
commit1827dd232f84cc1143914fe89e375eb33c7d12d6 (patch)
treec26302973c8a55e79cbcd5adfe6c1946cc65f697
parent73efc04ee2629e8fb0f509543bcab549ec660e75 (diff)
downloadchromium_src-1827dd232f84cc1143914fe89e375eb33c7d12d6.zip
chromium_src-1827dd232f84cc1143914fe89e375eb33c7d12d6.tar.gz
chromium_src-1827dd232f84cc1143914fe89e375eb33c7d12d6.tar.bz2
Implement Wheel Gesture Scrolling on OSX.
Create wheel based gestures from mouse wheel event phases when available. Pass overscroll into the input elasticity controller to handle the elastic overflow scroll on OSX. Transition the FrameWatcher to be a global IPC Message filter so that it gets first crack at the messages (it is a passive listener); because the RenderBrowserFilter was consuming the FrameSwap messages causing the browser test to always fail on Mac. BUG=587979 CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel Review URL: https://codereview.chromium.org/1749343004 Cr-Commit-Position: refs/heads/master@{#380639}
-rw-r--r--cc/input/input_handler.h18
-rw-r--r--cc/trees/layer_tree_host_impl.cc56
-rw-r--r--cc/trees/layer_tree_host_impl.h1
-rw-r--r--content/browser/renderer_host/input/composited_scrolling_browsertest.cc2
-rw-r--r--content/browser/renderer_host/input/mouse_wheel_event_queue.cc159
-rw-r--r--content/browser/renderer_host/input/mouse_wheel_event_queue.h18
-rw-r--r--content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc256
-rw-r--r--content/browser/renderer_host/input/non_blocking_event_browsertest.cc10
-rw-r--r--content/browser/renderer_host/input/touch_action_browsertest.cc2
-rw-r--r--content/browser/web_contents/web_contents_view_aura_browsertest.cc2
-rw-r--r--content/public/test/browser_test_utils.cc6
-rw-r--r--content/public/test/browser_test_utils.h3
-rw-r--r--content/renderer/input/input_handler_manager.cc27
-rw-r--r--content/renderer/input/input_handler_manager.h10
-rw-r--r--content/renderer/input/render_widget_input_handler.cc12
-rw-r--r--content/renderer/input/render_widget_input_handler_delegate.h7
-rw-r--r--content/renderer/mus/render_widget_mus_connection.cc7
-rw-r--r--content/renderer/mus/render_widget_mus_connection.h3
-rw-r--r--content/renderer/render_widget.cc21
-rw-r--r--content/renderer/render_widget.h4
-rw-r--r--third_party/WebKit/public/web/WebInputEvent.h18
-rw-r--r--ui/events/blink/input_handler_proxy.cc98
-rw-r--r--ui/events/blink/input_handler_proxy.h7
-rw-r--r--ui/events/blink/input_handler_proxy_unittest.cc5
-rw-r--r--ui/events/blink/input_scroll_elasticity_controller.cc65
-rw-r--r--ui/events/blink/input_scroll_elasticity_controller.h9
-rw-r--r--ui/events/blink/input_scroll_elasticity_controller_unittest.cc308
27 files changed, 979 insertions, 155 deletions
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h
index 9a39562..b81ee99 100644
--- a/cc/input/input_handler.h
+++ b/cc/input/input_handler.h
@@ -99,7 +99,15 @@ class CC_EXPORT InputHandler {
uint32_t main_thread_scrolling_reasons;
};
- enum ScrollInputType { GESTURE, WHEEL, ANIMATED_WHEEL, NON_BUBBLING_GESTURE };
+ enum ScrollInputType {
+ // TODO(dtapuska): crbug.com/593017; Remove GESTURE and just use
+ // TOUCHSCREEN.
+ GESTURE,
+ TOUCHSCREEN = GESTURE,
+ WHEEL,
+ ANIMATED_WHEEL,
+ NON_BUBBLING_GESTURE
+ };
// Binds a client to this handler to receive notifications. Only one client
// can be bound to an InputHandler. The client must live at least until the
@@ -118,6 +126,12 @@ class CC_EXPORT InputHandler {
// targets at the root layer.
virtual ScrollStatus RootScrollBegin(ScrollState* scroll_state,
ScrollInputType type) = 0;
+
+ // Returns SCROLL_ON_IMPL_THREAD if a layer is actively being scrolled or
+ // a subsequent call to ScrollAnimated can begin on the impl thread.
+ virtual ScrollStatus ScrollAnimatedBegin(
+ const gfx::Point& viewport_point) = 0;
+
virtual ScrollStatus ScrollAnimated(const gfx::Point& viewport_point,
const gfx::Vector2dF& scroll_delta) = 0;
@@ -137,7 +151,7 @@ class CC_EXPORT InputHandler {
virtual bool ScrollVerticallyByPage(const gfx::Point& viewport_point,
ScrollDirection direction) = 0;
- // Returns SCROLL_STARTED if a layer was being actively being scrolled,
+ // Returns SCROLL_STARTED if a layer was actively being scrolled,
// SCROLL_IGNORED if not.
virtual ScrollStatus FlingScrollBegin() = 0;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 7bda114..6e2dd2d 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -139,12 +139,16 @@ void DidVisibilityChange(LayerTreeHostImpl* id, bool visible) {
TRACE_EVENT_ASYNC_END0("cc", "LayerTreeHostImpl::SetVisible", id);
}
+bool IsWheelBasedScroll(InputHandler::ScrollInputType type) {
+ return type == InputHandler::WHEEL || type == InputHandler::ANIMATED_WHEEL;
+}
+
enum ScrollThread { MAIN_THREAD, CC_THREAD };
void RecordCompositorSlowScrollMetric(InputHandler::ScrollInputType type,
ScrollThread scroll_thread) {
bool scroll_on_main_thread = (scroll_thread == MAIN_THREAD);
- if (type == InputHandler::WHEEL || type == InputHandler::ANIMATED_WHEEL) {
+ if (IsWheelBasedScroll(type)) {
UMA_HISTOGRAM_BOOLEAN("Renderer4.CompositorWheelScrollUpdateThread",
scroll_on_main_thread);
} else {
@@ -2475,14 +2479,12 @@ InputHandler::ScrollStatus LayerTreeHostImpl::TryScroll(
}
}
- if (type == InputHandler::WHEEL || type == InputHandler::ANIMATED_WHEEL) {
+ if (IsWheelBasedScroll(type) &&
+ !active_tree()->settings().use_mouse_wheel_gestures) {
EventListenerProperties event_properties =
active_tree()->event_listener_properties(
EventListenerClass::kMouseWheel);
- if (event_properties == EventListenerProperties::kBlocking ||
- event_properties == EventListenerProperties::kBlockingAndPassive ||
- (!active_tree()->settings().use_mouse_wheel_gestures &&
- event_properties == EventListenerProperties::kPassive)) {
+ if (event_properties != EventListenerProperties::kNone) {
TRACE_EVENT0("cc", "LayerImpl::tryScroll: Failed WheelEventHandlers");
scroll_status.thread = InputHandler::SCROLL_ON_MAIN_THREAD;
scroll_status.main_thread_scrolling_reasons =
@@ -2620,7 +2622,7 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBeginImpl(
active_tree_->SetCurrentlyScrollingLayer(scrolling_layer_impl);
// TODO(majidvp): get rid of wheel_scrolling_ and set is_direct_manipulation
// in input_handler_proxy instead.
- wheel_scrolling_ = (type == WHEEL || type == ANIMATED_WHEEL);
+ wheel_scrolling_ = IsWheelBasedScroll(type);
scroll_state->set_is_direct_manipulation(!wheel_scrolling_);
// Invoke |DistributeScrollDelta| even with zero delta and velocity to ensure
// scroll customization callbacks are invoked.
@@ -2692,6 +2694,46 @@ InputHandler::ScrollStatus LayerTreeHostImpl::ScrollBegin(
return ScrollBeginImpl(scroll_state, scrolling_layer_impl, type);
}
+InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimatedBegin(
+ const gfx::Point& viewport_point) {
+ InputHandler::ScrollStatus scroll_status;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollingOnMain;
+ ScrollTree& scroll_tree = active_tree_->property_trees()->scroll_tree;
+ ScrollNode* scroll_node = scroll_tree.CurrentlyScrollingNode();
+ if (scroll_node) {
+ gfx::Vector2dF delta;
+
+ if (ScrollAnimationUpdateTarget(scroll_node, delta)) {
+ scroll_status.thread = SCROLL_ON_IMPL_THREAD;
+ } else {
+ scroll_status.thread = SCROLL_IGNORED;
+ scroll_status.main_thread_scrolling_reasons =
+ MainThreadScrollingReason::kNotScrollable;
+ }
+ return scroll_status;
+ }
+ ScrollStateData scroll_state_data;
+ scroll_state_data.start_position_x = viewport_point.x();
+ scroll_state_data.start_position_y = viewport_point.y();
+ scroll_state_data.is_in_inertial_phase = true;
+ ScrollState scroll_state(scroll_state_data);
+
+ // ScrollAnimated is used for animated wheel scrolls. We find the first layer
+ // that can scroll and set up an animation of its scroll offset. Note that
+ // this does not currently go through the scroll customization and viewport
+ // machinery that ScrollBy uses for non-animated wheel scrolls.
+ scroll_status = ScrollBegin(&scroll_state, ANIMATED_WHEEL);
+ scroll_node = scroll_tree.CurrentlyScrollingNode();
+ if (scroll_status.thread == SCROLL_ON_IMPL_THREAD) {
+ ScrollStateData scroll_state_end_data;
+ scroll_state_end_data.is_ending = true;
+ ScrollState scroll_state_end(scroll_state_end_data);
+ ScrollEnd(&scroll_state_end);
+ }
+ return scroll_status;
+}
+
InputHandler::ScrollStatus LayerTreeHostImpl::ScrollAnimated(
const gfx::Point& viewport_point,
const gfx::Vector2dF& scroll_delta) {
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 359a436..7a8aca72 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -166,6 +166,7 @@ class CC_EXPORT LayerTreeHostImpl
InputHandler::ScrollStatus RootScrollBegin(
ScrollState* scroll_state,
InputHandler::ScrollInputType type) override;
+ ScrollStatus ScrollAnimatedBegin(const gfx::Point& viewport_point) override;
InputHandler::ScrollStatus ScrollAnimated(
const gfx::Point& viewport_point,
const gfx::Vector2dF& scroll_delta) override;
diff --git a/content/browser/renderer_host/input/composited_scrolling_browsertest.cc b/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
index cbf48e8..fdca6bc 100644
--- a/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
+++ b/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
@@ -84,7 +84,7 @@ class CompositedScrollingBrowserTest : public ContentBrowserTest {
RenderWidgetHostImpl* host = GetWidgetHost();
scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
- host->GetProcess()->AddFilter(frame_watcher.get());
+ frame_watcher->AttachTo(shell()->web_contents());
host->GetView()->SetSize(gfx::Size(400, 400));
base::string16 ready_title(base::ASCIIToUTF16("ready"));
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue.cc b/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
index 48dbad2..5e56e9b 100644
--- a/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
+++ b/content/browser/renderer_host/input/mouse_wheel_event_queue.cc
@@ -37,6 +37,7 @@ MouseWheelEventQueue::MouseWheelEventQueue(MouseWheelEventQueueClient* client,
int64_t scroll_transaction_ms)
: client_(client),
needs_scroll_begin_(true),
+ needs_scroll_end_(false),
send_gestures_(send_gestures),
scroll_transaction_ms_(scroll_transaction_ms),
scrolling_device_(blink::WebGestureDeviceUninitialized) {
@@ -85,6 +86,9 @@ void MouseWheelEventQueue::ProcessMouseWheelAck(
(scrolling_device_ == blink::WebGestureDeviceUninitialized ||
scrolling_device_ == blink::WebGestureDeviceTouchpad)) {
GestureEventWithLatencyInfo scroll_update;
+ scroll_update.event.timeStampSeconds =
+ event_sent_for_gesture_ack_->event.timeStampSeconds;
+
scroll_update.event.x = event_sent_for_gesture_ack_->event.x;
scroll_update.event.y = event_sent_for_gesture_ack_->event.y;
scroll_update.event.globalX = event_sent_for_gesture_ack_->event.globalX;
@@ -96,6 +100,11 @@ void MouseWheelEventQueue::ProcessMouseWheelAck(
event_sent_for_gesture_ack_->event.deltaX;
scroll_update.event.data.scrollUpdate.deltaY =
event_sent_for_gesture_ack_->event.deltaY;
+ // Only OSX populates the momentumPhase; so expect this to
+ // always be PhaseNone on all other platforms.
+ scroll_update.event.data.scrollUpdate.inertial =
+ event_sent_for_gesture_ack_->event.momentumPhase !=
+ blink::WebMouseWheelEvent::PhaseNone;
if (event_sent_for_gesture_ack_->event.scrollByPage) {
scroll_update.event.data.scrollUpdate.deltaUnits =
blink::WebGestureEvent::Page;
@@ -114,7 +123,67 @@ void MouseWheelEventQueue::ProcessMouseWheelAck(
? blink::WebGestureEvent::PrecisePixels
: blink::WebGestureEvent::Pixels;
}
- SendGesture(scroll_update);
+
+ bool current_phase_ended = false;
+ bool has_phase_info = false;
+
+ if (event_sent_for_gesture_ack_->event.phase !=
+ blink::WebMouseWheelEvent::PhaseNone ||
+ event_sent_for_gesture_ack_->event.momentumPhase !=
+ blink::WebMouseWheelEvent::PhaseNone) {
+ has_phase_info = true;
+ current_phase_ended = event_sent_for_gesture_ack_->event.phase ==
+ blink::WebMouseWheelEvent::PhaseEnded ||
+ event_sent_for_gesture_ack_->event.phase ==
+ blink::WebMouseWheelEvent::PhaseCancelled ||
+ event_sent_for_gesture_ack_->event.momentumPhase ==
+ blink::WebMouseWheelEvent::PhaseEnded ||
+ event_sent_for_gesture_ack_->event.momentumPhase ==
+ blink::WebMouseWheelEvent::PhaseCancelled;
+ }
+
+ bool needs_update = scroll_update.event.data.scrollUpdate.deltaX != 0 ||
+ scroll_update.event.data.scrollUpdate.deltaY != 0;
+
+ // If there is no update to send and the current phase is ended yet a GSB
+ // needs to be sent, this event sequence doesn't need to be generated
+ // because the events generated will be a GSB (non-synthetic) and GSE
+ // (non-synthetic). This situation arises when OSX generates double
+ // phase end information.
+ bool empty_sequence =
+ !needs_update && needs_scroll_begin_ && current_phase_ended;
+
+ if (needs_update || !empty_sequence) {
+ if (needs_scroll_begin_) {
+ // If no GSB has been sent, it will be a non-synthetic GSB.
+ SendScrollBegin(scroll_update, false);
+ } else if (has_phase_info) {
+ // If a GSB has been sent, generate a synthetic GSB if we have phase
+ // information. This should be removed once crbug.com/526463 is fully
+ // implemented.
+ SendScrollBegin(scroll_update, true);
+ }
+
+ if (needs_update)
+ client_->SendGestureEvent(scroll_update);
+
+ if (current_phase_ended) {
+ // Non-synthetic GSEs are sent when the current phase is canceled or
+ // ended.
+ SendScrollEnd(scroll_update.event, false);
+ } else if (has_phase_info) {
+ // Generate a synthetic GSE for every update to force hit testing so
+ // that the non-latching behavior is preserved. Remove once
+ // crbug.com/526463 is fully implemented.
+ SendScrollEnd(scroll_update.event, true);
+ } else {
+ scroll_end_timer_.Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(scroll_transaction_ms_),
+ base::Bind(&MouseWheelEventQueue::SendScrollEnd,
+ base::Unretained(this), scroll_update.event, false));
+ }
+ }
}
event_sent_for_gesture_ack_.reset();
@@ -158,62 +227,52 @@ void MouseWheelEventQueue::TryForwardNextEventToRenderer() {
client_->SendMouseWheelEventImmediately(send_event);
}
-void MouseWheelEventQueue::SendScrollEnd(blink::WebGestureEvent update_event) {
- GestureEventWithLatencyInfo scroll_end;
+void MouseWheelEventQueue::SendScrollEnd(blink::WebGestureEvent update_event,
+ bool synthetic) {
+ DCHECK((synthetic && !needs_scroll_end_) || needs_scroll_end_);
+
+ GestureEventWithLatencyInfo scroll_end(update_event);
+ scroll_end.event.timeStampSeconds =
+ (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF();
scroll_end.event.type = WebInputEvent::GestureScrollEnd;
- scroll_end.event.sourceDevice = blink::WebGestureDeviceTouchpad;
scroll_end.event.resendingPluginId = -1;
+ scroll_end.event.data.scrollEnd.synthetic = synthetic;
+ scroll_end.event.data.scrollEnd.inertial =
+ update_event.data.scrollUpdate.inertial;
scroll_end.event.data.scrollEnd.deltaUnits =
update_event.data.scrollUpdate.deltaUnits;
- scroll_end.event.x = update_event.x;
- scroll_end.event.y = update_event.y;
- scroll_end.event.globalX = update_event.globalX;
- scroll_end.event.globalY = update_event.globalY;
- SendGesture(scroll_end);
-}
+ if (!synthetic) {
+ needs_scroll_begin_ = true;
+ needs_scroll_end_ = false;
-void MouseWheelEventQueue::SendGesture(
- const GestureEventWithLatencyInfo& gesture) {
- switch (gesture.event.type) {
- case WebInputEvent::GestureScrollUpdate:
- if (needs_scroll_begin_) {
- GestureEventWithLatencyInfo scroll_begin(gesture);
- scroll_begin.event.x = gesture.event.x;
- scroll_begin.event.y = gesture.event.y;
- scroll_begin.event.globalX = gesture.event.globalX;
- scroll_begin.event.globalY = gesture.event.globalY;
- scroll_begin.event.type = WebInputEvent::GestureScrollBegin;
- scroll_begin.event.data.scrollBegin.deltaXHint =
- gesture.event.data.scrollUpdate.deltaX;
- scroll_begin.event.data.scrollBegin.deltaYHint =
- gesture.event.data.scrollUpdate.deltaY;
- scroll_begin.event.data.scrollBegin.targetViewport = false;
- scroll_begin.event.data.scrollBegin.deltaHintUnits =
- gesture.event.data.scrollUpdate.deltaUnits;
-
- SendGesture(scroll_begin);
- }
- if (scroll_end_timer_.IsRunning()) {
- scroll_end_timer_.Reset();
- } else {
- scroll_end_timer_.Start(
- FROM_HERE,
- base::TimeDelta::FromMilliseconds(scroll_transaction_ms_),
- base::Bind(&MouseWheelEventQueue::SendScrollEnd,
- base::Unretained(this), gesture.event));
- }
- break;
- case WebInputEvent::GestureScrollEnd:
- needs_scroll_begin_ = true;
- break;
- case WebInputEvent::GestureScrollBegin:
- needs_scroll_begin_ = false;
- break;
- default:
- return;
+ if (scroll_end_timer_.IsRunning())
+ scroll_end_timer_.Reset();
}
- client_->SendGestureEvent(gesture);
+ client_->SendGestureEvent(scroll_end);
+}
+
+void MouseWheelEventQueue::SendScrollBegin(
+ const GestureEventWithLatencyInfo& gesture_update,
+ bool synthetic) {
+ DCHECK((synthetic && !needs_scroll_begin_) || needs_scroll_begin_);
+
+ GestureEventWithLatencyInfo scroll_begin(gesture_update);
+ scroll_begin.event.type = WebInputEvent::GestureScrollBegin;
+ scroll_begin.event.data.scrollBegin.synthetic = synthetic;
+ scroll_begin.event.data.scrollBegin.inertial =
+ gesture_update.event.data.scrollUpdate.inertial;
+ scroll_begin.event.data.scrollBegin.deltaXHint =
+ gesture_update.event.data.scrollUpdate.deltaX;
+ scroll_begin.event.data.scrollBegin.deltaYHint =
+ gesture_update.event.data.scrollUpdate.deltaY;
+ scroll_begin.event.data.scrollBegin.targetViewport = false;
+ scroll_begin.event.data.scrollBegin.deltaHintUnits =
+ gesture_update.event.data.scrollUpdate.deltaUnits;
+
+ needs_scroll_begin_ = false;
+ needs_scroll_end_ = true;
+ client_->SendGestureEvent(scroll_begin);
}
} // namespace content
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue.h b/content/browser/renderer_host/input/mouse_wheel_event_queue.h
index b5ad7f2..a224b9e 100644
--- a/content/browser/renderer_host/input/mouse_wheel_event_queue.h
+++ b/content/browser/renderer_host/input/mouse_wheel_event_queue.h
@@ -17,7 +17,9 @@ namespace content {
// The duration in which a ScrollEnd will be sent after the last
// ScrollUpdate was sent for wheel based gesture scrolls.
-const int64_t kDefaultWheelScrollTransactionMs = 100;
+// Set the default wheel transaction to 0ms until
+// crbug.com/526463 is fully implemented.
+const int64_t kDefaultWheelScrollTransactionMs = 0; // 100;
class QueuedWebMouseWheelEvent;
@@ -76,16 +78,24 @@ class CONTENT_EXPORT MouseWheelEventQueue {
private:
void TryForwardNextEventToRenderer();
- void SendScrollEnd(blink::WebGestureEvent update_event);
- void SendGesture(const GestureEventWithLatencyInfo& gesture);
+ void SendScrollEnd(blink::WebGestureEvent update_event, bool synthetic);
+ void SendScrollBegin(const GestureEventWithLatencyInfo& gesture_update,
+ bool synthetic);
MouseWheelEventQueueClient* client_;
- bool needs_scroll_begin_;
base::OneShotTimer scroll_end_timer_;
typedef std::deque<QueuedWebMouseWheelEvent*> WheelEventQueue;
WheelEventQueue wheel_queue_;
scoped_ptr<QueuedWebMouseWheelEvent> event_sent_for_gesture_ack_;
+
+ // True if a non-synthetic GSB needs to be sent before a GSU is sent.
+ bool needs_scroll_begin_;
+
+ // True if a non-synthetic GSE needs to be sent because a non-synthetic
+ // GSB has been sent in the past.
+ bool needs_scroll_end_;
+
bool send_gestures_;
int64_t scroll_transaction_ms_;
blink::WebGestureDevice scrolling_device_;
diff --git a/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc b/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
index f9e10a9..f14b6e6 100644
--- a/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
+++ b/content/browser/renderer_host/input/mouse_wheel_event_queue_unittest.cc
@@ -34,6 +34,81 @@ base::TimeDelta DefaultScrollEndTimeoutDelay() {
return base::TimeDelta::FromMilliseconds(kScrollEndTimeoutMs);
}
+#define EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event) \
+ EXPECT_EQ(WebInputEvent::GestureScrollBegin, event->type); \
+ EXPECT_EQ(kWheelScrollX, event->x); \
+ EXPECT_EQ(kWheelScrollY, event->y); \
+ EXPECT_EQ(kWheelScrollGlobalX, event->globalX); \
+ EXPECT_EQ(kWheelScrollGlobalY, event->globalY); \
+ EXPECT_EQ(scroll_units, event->data.scrollBegin.deltaHintUnits);
+
+#define EXPECT_GESTURE_SCROLL_BEGIN(event) \
+ EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
+ EXPECT_FALSE(event->data.scrollBegin.synthetic); \
+ EXPECT_FALSE(event->data.scrollBegin.inertial);
+
+#define EXPECT_SYNTHETIC_GESTURE_SCROLL_BEGIN(event) \
+ EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
+ EXPECT_TRUE(event->data.scrollBegin.synthetic); \
+ EXPECT_FALSE(event->data.scrollBegin.inertial);
+
+#define EXPECT_INERTIAL_GESTURE_SCROLL_BEGIN(event) \
+ EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
+ EXPECT_FALSE(event->data.scrollBegin.synthetic); \
+ EXPECT_TRUE(event->data.scrollBegin.inertial);
+
+#define EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_BEGIN(event) \
+ EXPECT_GESTURE_SCROLL_BEGIN_IMPL(event); \
+ EXPECT_TRUE(event->data.scrollBegin.synthetic); \
+ EXPECT_TRUE(event->data.scrollBegin.inertial);
+
+#define EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event) \
+ EXPECT_EQ(WebInputEvent::GestureScrollUpdate, event->type); \
+ EXPECT_EQ(scroll_units, event->data.scrollUpdate.deltaUnits); \
+ EXPECT_EQ(kWheelScrollX, event->x); \
+ EXPECT_EQ(kWheelScrollY, event->y); \
+ EXPECT_EQ(kWheelScrollGlobalX, event->globalX); \
+ EXPECT_EQ(kWheelScrollGlobalY, event->globalY);
+
+#define EXPECT_GESTURE_SCROLL_UPDATE(event) \
+ EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event); \
+ EXPECT_FALSE(event->data.scrollUpdate.inertial);
+
+#define EXPECT_INERTIAL_GESTURE_SCROLL_UPDATE(event) \
+ EXPECT_GESTURE_SCROLL_UPDATE_IMPL(event); \
+ EXPECT_TRUE(event->data.scrollUpdate.inertial);
+
+#define EXPECT_GESTURE_SCROLL_END_IMPL(event) \
+ EXPECT_EQ(WebInputEvent::GestureScrollEnd, event->type); \
+ EXPECT_EQ(scroll_units, event->data.scrollEnd.deltaUnits); \
+ EXPECT_EQ(kWheelScrollX, event->x); \
+ EXPECT_EQ(kWheelScrollY, event->y); \
+ EXPECT_EQ(kWheelScrollGlobalX, event->globalX); \
+ EXPECT_EQ(kWheelScrollGlobalY, event->globalY);
+
+#define EXPECT_GESTURE_SCROLL_END(event) \
+ EXPECT_GESTURE_SCROLL_END_IMPL(event); \
+ EXPECT_FALSE(event->data.scrollEnd.synthetic); \
+ EXPECT_FALSE(event->data.scrollEnd.inertial);
+
+#define EXPECT_SYNTHETIC_GESTURE_SCROLL_END(event) \
+ EXPECT_GESTURE_SCROLL_END_IMPL(event); \
+ EXPECT_TRUE(event->data.scrollEnd.synthetic); \
+ EXPECT_FALSE(event->data.scrollEnd.inertial);
+
+#define EXPECT_INERTIAL_GESTURE_SCROLL_END(event) \
+ EXPECT_GESTURE_SCROLL_END_IMPL(event); \
+ EXPECT_FALSE(event->data.scrollEnd.synthetic); \
+ EXPECT_TRUE(event->data.scrollEnd.inertial);
+
+#define EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(event) \
+ EXPECT_GESTURE_SCROLL_END_IMPL(event); \
+ EXPECT_TRUE(event->data.scrollEnd.synthetic); \
+ EXPECT_TRUE(event->data.scrollEnd.inertial);
+
+#define EXPECT_MOUSE_WHEEL(event) \
+ EXPECT_EQ(WebInputEvent::MouseWheel, event->type);
+
} // namespace
class MouseWheelEventQueueTest : public testing::Test,
@@ -50,12 +125,17 @@ class MouseWheelEventQueueTest : public testing::Test,
// MouseWheelEventQueueClient
void SendMouseWheelEventImmediately(
const MouseWheelEventWithLatencyInfo& event) override {
- sent_events_.push_back(event.event);
+ WebMouseWheelEvent* cloned_event = new WebMouseWheelEvent();
+ scoped_ptr<WebInputEvent> cloned_event_holder(cloned_event);
+ *cloned_event = event.event;
+ sent_events_.push_back(std::move(cloned_event_holder));
}
void SendGestureEvent(const GestureEventWithLatencyInfo& event) override {
- sent_events_.push_back(event.event);
- sent_gesture_events_.push_back(event.event);
+ WebGestureEvent* cloned_event = new WebGestureEvent();
+ scoped_ptr<WebInputEvent> cloned_event_holder(cloned_event);
+ *cloned_event = event.event;
+ sent_events_.push_back(std::move(cloned_event_holder));
}
void OnMouseWheelEventAck(const MouseWheelEventWithLatencyInfo& event,
@@ -75,10 +155,15 @@ class MouseWheelEventQueueTest : public testing::Test,
bool event_in_flight() const { return queue_->event_in_flight(); }
- std::vector<WebInputEvent>& all_sent_events() { return sent_events_; }
+ std::vector<scoped_ptr<WebInputEvent>>& all_sent_events() {
+ return sent_events_;
+ }
- std::vector<WebGestureEvent>& sent_gesture_events() {
- return sent_gesture_events_;
+ const scoped_ptr<WebInputEvent>& sent_input_event(size_t index) {
+ return sent_events_[index];
+ }
+ const WebGestureEvent* sent_gesture_event(size_t index) {
+ return static_cast<WebGestureEvent*>(sent_events_[index].get());
}
const WebMouseWheelEvent& acked_event() const { return last_acked_event_; }
@@ -86,7 +171,6 @@ class MouseWheelEventQueueTest : public testing::Test,
size_t GetAndResetSentEventCount() {
size_t count = sent_events_.size();
sent_events_.clear();
- sent_gesture_events_.clear();
return count;
}
@@ -113,6 +197,24 @@ class MouseWheelEventQueueTest : public testing::Test,
x, y, global_x, global_y, dX, dY, modifiers, high_precision)));
}
+ void SendMouseWheelWithPhase(
+ float x,
+ float y,
+ float global_x,
+ float global_y,
+ float dX,
+ float dY,
+ int modifiers,
+ bool high_precision,
+ blink::WebMouseWheelEvent::Phase phase,
+ blink::WebMouseWheelEvent::Phase momentum_phase) {
+ WebMouseWheelEvent event = SyntheticWebMouseWheelEventBuilder::Build(
+ x, y, global_x, global_y, dX, dY, modifiers, high_precision);
+ event.phase = phase;
+ event.momentumPhase = momentum_phase;
+ queue_->QueueEvent(MouseWheelEventWithLatencyInfo(event));
+ }
+
void SendGestureEvent(WebInputEvent::Type type) {
WebGestureEvent event;
event.type = type;
@@ -137,7 +239,8 @@ class MouseWheelEventQueueTest : public testing::Test,
EXPECT_TRUE(event_in_flight());
EXPECT_EQ(1U, GetAndResetSentEventCount());
- // The second mouse wheel should not be sent since one is already in queue.
+ // The second mouse wheel should not be sent since one is already in
+ // queue.
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 5, 5, 0, high_precision);
EXPECT_EQ(1U, queued_event_count());
@@ -152,36 +255,104 @@ class MouseWheelEventQueueTest : public testing::Test,
EXPECT_EQ(WebInputEvent::MouseWheel, acked_event().type);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(3U, all_sent_events().size());
- EXPECT_EQ(WebInputEvent::GestureScrollBegin, all_sent_events()[0].type);
- EXPECT_EQ(scroll_units,
- sent_gesture_events()[0].data.scrollBegin.deltaHintUnits);
- EXPECT_EQ(kWheelScrollX, sent_gesture_events()[0].x);
- EXPECT_EQ(kWheelScrollY, sent_gesture_events()[0].y);
- EXPECT_EQ(kWheelScrollGlobalX, sent_gesture_events()[0].globalX);
- EXPECT_EQ(kWheelScrollGlobalY, sent_gesture_events()[0].globalY);
- EXPECT_EQ(WebInputEvent::GestureScrollUpdate, all_sent_events()[1].type);
- EXPECT_EQ(scroll_units,
- sent_gesture_events()[1].data.scrollUpdate.deltaUnits);
- EXPECT_EQ(kWheelScrollX, sent_gesture_events()[1].x);
- EXPECT_EQ(kWheelScrollY, sent_gesture_events()[1].y);
- EXPECT_EQ(kWheelScrollGlobalX, sent_gesture_events()[1].globalX);
- EXPECT_EQ(kWheelScrollGlobalY, sent_gesture_events()[1].globalY);
- EXPECT_EQ(WebInputEvent::MouseWheel, all_sent_events()[2].type);
+ EXPECT_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
+ EXPECT_MOUSE_WHEEL(sent_input_event(2));
EXPECT_EQ(3U, GetAndResetSentEventCount());
RunTasksAndWait(DefaultScrollEndTimeoutDelay() * 2);
EXPECT_EQ(1U, all_sent_events().size());
- EXPECT_EQ(WebInputEvent::GestureScrollEnd, all_sent_events()[0].type);
- EXPECT_EQ(scroll_units, sent_gesture_events()[0].data.scrollEnd.deltaUnits);
- EXPECT_EQ(kWheelScrollX, sent_gesture_events()[0].x);
- EXPECT_EQ(kWheelScrollY, sent_gesture_events()[0].y);
- EXPECT_EQ(kWheelScrollGlobalX, sent_gesture_events()[0].globalX);
- EXPECT_EQ(kWheelScrollGlobalY, sent_gesture_events()[0].globalY);
+ EXPECT_GESTURE_SCROLL_END(sent_gesture_event(0));
+ }
+
+ void PhaseGestureSendingTest(bool high_precision) {
+ const WebGestureEvent::ScrollUnits scroll_units =
+ high_precision ? WebGestureEvent::PrecisePixels
+ : WebGestureEvent::Pixels;
+
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 1, 1, 0, high_precision,
+ WebMouseWheelEvent::PhaseBegan,
+ WebMouseWheelEvent::PhaseNone);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(3U, all_sent_events().size());
+ EXPECT_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
+ EXPECT_SYNTHETIC_GESTURE_SCROLL_END(sent_gesture_event(2));
+ EXPECT_EQ(3U, GetAndResetSentEventCount());
+
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 5, 5, 0, high_precision,
+ WebMouseWheelEvent::PhaseChanged,
+ WebMouseWheelEvent::PhaseNone);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(3U, all_sent_events().size());
+ EXPECT_SYNTHETIC_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
+ EXPECT_SYNTHETIC_GESTURE_SCROLL_END(sent_gesture_event(2));
+ EXPECT_EQ(3U, GetAndResetSentEventCount());
+
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 0, 0, 0, high_precision,
+ WebMouseWheelEvent::PhaseEnded,
+ WebMouseWheelEvent::PhaseNone);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(2U, all_sent_events().size());
+ EXPECT_SYNTHETIC_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_GESTURE_SCROLL_END(sent_gesture_event(1));
+ EXPECT_EQ(2U, GetAndResetSentEventCount());
+
+ // Send a double phase end; OSX does it consistently.
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 0, 0, 0, high_precision,
+ WebMouseWheelEvent::PhaseEnded,
+ WebMouseWheelEvent::PhaseNone);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(0U, all_sent_events().size());
+ EXPECT_EQ(0U, GetAndResetSentEventCount());
+
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 5, 5, 0, high_precision,
+ WebMouseWheelEvent::PhaseNone,
+ WebMouseWheelEvent::PhaseBegan);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(3U, all_sent_events().size());
+ EXPECT_INERTIAL_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_INERTIAL_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
+ EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(sent_gesture_event(2));
+ EXPECT_EQ(3U, GetAndResetSentEventCount());
+
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 5, 5, 0, high_precision,
+ WebMouseWheelEvent::PhaseNone,
+ WebMouseWheelEvent::PhaseChanged);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(3U, all_sent_events().size());
+ EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_INERTIAL_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
+ EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_END(sent_gesture_event(2));
+ EXPECT_EQ(3U, GetAndResetSentEventCount());
+
+ SendMouseWheelWithPhase(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
+ kWheelScrollGlobalY, 0, 0, 0, high_precision,
+ WebMouseWheelEvent::PhaseNone,
+ WebMouseWheelEvent::PhaseEnded);
+ EXPECT_EQ(1U, GetAndResetSentEventCount());
+ SendMouseWheelEventAck(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ EXPECT_EQ(2U, all_sent_events().size());
+ EXPECT_SYNTHETIC_INERTIAL_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_INERTIAL_GESTURE_SCROLL_END(sent_gesture_event(1));
+ EXPECT_EQ(2U, GetAndResetSentEventCount());
}
scoped_ptr<MouseWheelEventQueue> queue_;
- std::vector<WebInputEvent> sent_events_;
- std::vector<WebGestureEvent> sent_gesture_events_;
+ std::vector<scoped_ptr<WebInputEvent>> sent_events_;
size_t acked_event_count_;
InputEventAckState last_acked_event_state_;
base::MessageLoopForUI message_loop_;
@@ -230,8 +401,21 @@ TEST_F(MouseWheelEventQueueTest, GestureSendingPrecisePixels) {
GestureSendingTest(false);
}
+TEST_F(MouseWheelEventQueueTest, GestureSendingWithPhaseInformation) {
+ SetUpForGestureTesting(true);
+ PhaseGestureSendingTest(false);
+}
+
+TEST_F(MouseWheelEventQueueTest,
+ GestureSendingWithPhaseInformationPrecisePixels) {
+ SetUpForGestureTesting(true);
+ PhaseGestureSendingTest(true);
+}
+
TEST_F(MouseWheelEventQueueTest, GestureSendingInterrupted) {
SetUpForGestureTesting(true);
+ const WebGestureEvent::ScrollUnits scroll_units = WebGestureEvent::Pixels;
+
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
kWheelScrollGlobalY, 1, 1, 0, false);
EXPECT_EQ(0U, queued_event_count());
@@ -245,14 +429,14 @@ TEST_F(MouseWheelEventQueueTest, GestureSendingInterrupted) {
EXPECT_EQ(WebInputEvent::MouseWheel, acked_event().type);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
- EXPECT_EQ(WebInputEvent::GestureScrollBegin, all_sent_events()[0].type);
- EXPECT_EQ(WebInputEvent::GestureScrollUpdate, all_sent_events()[1].type);
+ EXPECT_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
// Ensure that a gesture scroll begin terminates the current scroll event.
SendGestureEvent(WebInputEvent::GestureScrollBegin);
EXPECT_EQ(1U, all_sent_events().size());
- EXPECT_EQ(WebInputEvent::GestureScrollEnd, all_sent_events()[0].type);
+ EXPECT_GESTURE_SCROLL_END(sent_gesture_event(0));
EXPECT_EQ(1U, GetAndResetSentEventCount());
SendMouseWheel(kWheelScrollX, kWheelScrollY, kWheelScrollGlobalX,
@@ -286,8 +470,8 @@ TEST_F(MouseWheelEventQueueTest, GestureSendingInterrupted) {
EXPECT_EQ(WebInputEvent::MouseWheel, acked_event().type);
EXPECT_EQ(1U, GetAndResetAckedEventCount());
EXPECT_EQ(2U, all_sent_events().size());
- EXPECT_EQ(WebInputEvent::GestureScrollBegin, all_sent_events()[0].type);
- EXPECT_EQ(WebInputEvent::GestureScrollUpdate, all_sent_events()[1].type);
+ EXPECT_GESTURE_SCROLL_BEGIN(sent_gesture_event(0));
+ EXPECT_GESTURE_SCROLL_UPDATE(sent_gesture_event(1));
EXPECT_EQ(2U, GetAndResetSentEventCount());
}
diff --git a/content/browser/renderer_host/input/non_blocking_event_browsertest.cc b/content/browser/renderer_host/input/non_blocking_event_browsertest.cc
index c681914..abed148 100644
--- a/content/browser/renderer_host/input/non_blocking_event_browsertest.cc
+++ b/content/browser/renderer_host/input/non_blocking_event_browsertest.cc
@@ -113,7 +113,7 @@ class NonBlockingEventBrowserTest : public ContentBrowserTest {
EXPECT_EQ(1000, scrollHeight);
scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
- GetWidgetHost()->GetProcess()->AddFilter(frame_watcher.get());
+ frame_watcher->AttachTo(shell()->web_contents());
scoped_refptr<InputMsgWatcher> input_msg_watcher(
new InputMsgWatcher(GetWidgetHost(), blink::WebInputEvent::MouseWheel));
@@ -138,7 +138,7 @@ class NonBlockingEventBrowserTest : public ContentBrowserTest {
EXPECT_EQ(1000, scrollHeight);
scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
- GetWidgetHost()->GetProcess()->AddFilter(frame_watcher.get());
+ frame_watcher->AttachTo(shell()->web_contents());
SyntheticSmoothScrollGestureParams params;
params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
@@ -162,18 +162,12 @@ class NonBlockingEventBrowserTest : public ContentBrowserTest {
DISALLOW_COPY_AND_ASSIGN(NonBlockingEventBrowserTest);
};
-// Disabled on MacOS because it doesn't support wheel gestures
-// just yet.
-#if defined(OS_MACOSX)
-#define MAYBE_MouseWheel DISABLED_MouseWheel
-#else
// Also appears to be flaky under TSan. crbug.com/588199
#if defined(THREAD_SANITIZER)
#define MAYBE_MouseWheel DISABLED_MouseWheel
#else
#define MAYBE_MouseWheel MouseWheel
#endif
-#endif
IN_PROC_BROWSER_TEST_F(NonBlockingEventBrowserTest, MAYBE_MouseWheel) {
LoadURL();
DoWheelScroll();
diff --git a/content/browser/renderer_host/input/touch_action_browsertest.cc b/content/browser/renderer_host/input/touch_action_browsertest.cc
index 72c8bc9..a1a9e83 100644
--- a/content/browser/renderer_host/input/touch_action_browsertest.cc
+++ b/content/browser/renderer_host/input/touch_action_browsertest.cc
@@ -94,7 +94,7 @@ class TouchActionBrowserTest : public ContentBrowserTest {
RenderWidgetHostImpl* host = GetWidgetHost();
scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
- host->GetProcess()->AddFilter(frame_watcher.get());
+ frame_watcher->AttachTo(shell()->web_contents());
host->GetView()->SetSize(gfx::Size(400, 400));
base::string16 ready_title(base::ASCIIToUTF16("ready"));
diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
index 8d08134..b896035 100644
--- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc
+++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc
@@ -272,7 +272,7 @@ class WebContentsViewAuraTest : public ContentBrowserTest {
controller->SetScreenshotManager(make_scoped_ptr(screenshot_manager_));
frame_watcher_ = new FrameWatcher();
- GetRenderWidgetHost()->GetProcess()->AddFilter(frame_watcher_.get());
+ frame_watcher_->AttachTo(shell()->web_contents());
}
void SetUpCommandLine(base::CommandLine* cmd) override {
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 51ac846..1761a9c 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -1067,9 +1067,7 @@ bool RequestFrame(WebContents* web_contents) {
->ScheduleComposite();
}
-FrameWatcher::FrameWatcher()
- : BrowserMessageFilter(ViewMsgStart), frames_to_wait_(0) {
-}
+FrameWatcher::FrameWatcher() : MessageFilter(), frames_to_wait_(0) {}
FrameWatcher::~FrameWatcher() {
}
@@ -1100,7 +1098,7 @@ void FrameWatcher::AttachTo(WebContents* web_contents) {
DCHECK(web_contents);
RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From(
web_contents->GetRenderViewHost()->GetWidget());
- widget_host->GetProcess()->AddFilter(this);
+ widget_host->GetProcess()->GetChannel()->AddFilter(this);
}
void FrameWatcher::WaitFrames(int frames_to_wait) {
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 1793726..203c6f80 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -24,6 +24,7 @@
#include "content/public/browser/render_process_host_observer.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/page_type.h"
+#include "ipc/message_filter.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "url/gurl.h"
@@ -416,7 +417,7 @@ bool RequestFrame(WebContents* web_contents);
// Watches compositor frame changes, blocking until a frame has been
// composited. This class is intended to be run on the main thread; to
// synchronize the main thread against the impl thread.
-class FrameWatcher : public BrowserMessageFilter {
+class FrameWatcher : public IPC::MessageFilter {
public:
FrameWatcher();
diff --git a/content/renderer/input/input_handler_manager.cc b/content/renderer/input/input_handler_manager.cc
index 5f1bfe0..b249a64 100644
--- a/content/renderer/input/input_handler_manager.cc
+++ b/content/renderer/input/input_handler_manager.cc
@@ -144,6 +144,7 @@ void InputHandlerManager::ObserveWheelEventAndResultOnCompositorThread(
int routing_id,
const blink::WebMouseWheelEvent& wheel_event,
const cc::InputHandlerScrollResult& scroll_result) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
auto it = input_handlers_.find(routing_id);
if (it == input_handlers_.end())
return;
@@ -154,6 +155,32 @@ void InputHandlerManager::ObserveWheelEventAndResultOnCompositorThread(
wheel_event, scroll_result);
}
+void InputHandlerManager::ObserveGestureEventAndResultOnMainThread(
+ int routing_id,
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &InputHandlerManager::ObserveGestureEventAndResultOnCompositorThread,
+ base::Unretained(this), routing_id, gesture_event, scroll_result));
+}
+
+void InputHandlerManager::ObserveGestureEventAndResultOnCompositorThread(
+ int routing_id,
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ auto it = input_handlers_.find(routing_id);
+ if (it == input_handlers_.end())
+ return;
+
+ InputHandlerProxy* proxy = it->second->input_handler_proxy();
+ DCHECK(proxy->scroll_elasticity_controller());
+ proxy->scroll_elasticity_controller()->ObserveGestureEventAndResult(
+ gesture_event, scroll_result);
+}
+
void InputHandlerManager::NonBlockingInputEventHandledOnMainThread(
int routing_id,
blink::WebInputEvent::Type type) {
diff --git a/content/renderer/input/input_handler_manager.h b/content/renderer/input/input_handler_manager.h
index 3211a1f..e9165d5 100644
--- a/content/renderer/input/input_handler_manager.h
+++ b/content/renderer/input/input_handler_manager.h
@@ -64,6 +64,11 @@ class CONTENT_EXPORT InputHandlerManager {
const blink::WebMouseWheelEvent& wheel_event,
const cc::InputHandlerScrollResult& scroll_result);
+ void ObserveGestureEventAndResultOnMainThread(
+ int routing_id,
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result);
+
void NonBlockingInputEventHandledOnMainThread(int routing_id,
blink::WebInputEvent::Type);
@@ -99,6 +104,11 @@ class CONTENT_EXPORT InputHandlerManager {
const blink::WebMouseWheelEvent& wheel_event,
const cc::InputHandlerScrollResult& scroll_result);
+ void ObserveGestureEventAndResultOnCompositorThread(
+ int routing_id,
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result);
+
void NonBlockingInputEventHandledOnCompositorThread(
int routing_id,
blink::WebInputEvent::Type);
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 215f1f4..6a0a652 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -364,6 +364,18 @@ void RenderWidgetInputHandler::HandleInputEvent(
: gfx::Vector2dF(),
processed != WebInputEventResult::NotHandled);
}
+ } else if (input_event.type == WebInputEvent::GestureScrollBegin ||
+ input_event.type == WebInputEvent::GestureScrollEnd ||
+ input_event.type == WebInputEvent::GestureScrollUpdate) {
+ const WebGestureEvent& gesture_event =
+ static_cast<const WebGestureEvent&>(input_event);
+ if (gesture_event.sourceDevice == blink::WebGestureDeviceTouchpad) {
+ delegate_->ObserveGestureEventAndResult(
+ gesture_event,
+ event_overscroll ? event_overscroll->latest_overscroll_delta
+ : gfx::Vector2dF(),
+ processed != WebInputEventResult::NotHandled);
+ }
}
bool frame_pending =
diff --git a/content/renderer/input/render_widget_input_handler_delegate.h b/content/renderer/input/render_widget_input_handler_delegate.h
index 7a7f544..0676b50 100644
--- a/content/renderer/input/render_widget_input_handler_delegate.h
+++ b/content/renderer/input/render_widget_input_handler_delegate.h
@@ -49,6 +49,13 @@ class CONTENT_EXPORT RenderWidgetInputHandlerDelegate {
const gfx::Vector2dF& wheel_unused_delta,
bool event_processed) = 0;
+ // Called to forward a gesture event to the compositor thread, to effect
+ // the elastic overscroll effect.
+ virtual void ObserveGestureEventAndResult(
+ const blink::WebGestureEvent& gesture_event,
+ const gfx::Vector2dF& unused_delta,
+ bool event_processed) = 0;
+
// Notifies that a key event was just handled.
virtual void OnDidHandleKeyEvent() = 0;
diff --git a/content/renderer/mus/render_widget_mus_connection.cc b/content/renderer/mus/render_widget_mus_connection.cc
index c2dc1d1..73f17f6 100644
--- a/content/renderer/mus/render_widget_mus_connection.cc
+++ b/content/renderer/mus/render_widget_mus_connection.cc
@@ -106,6 +106,13 @@ void RenderWidgetMusConnection::ObserveWheelEventAndResult(
NOTIMPLEMENTED();
}
+void RenderWidgetMusConnection::ObserveGestureEventAndResult(
+ const blink::WebGestureEvent& gesture_event,
+ const gfx::Vector2dF& wheel_unused_delta,
+ bool event_processed) {
+ NOTIMPLEMENTED();
+}
+
void RenderWidgetMusConnection::OnDidHandleKeyEvent() {
NOTIMPLEMENTED();
}
diff --git a/content/renderer/mus/render_widget_mus_connection.h b/content/renderer/mus/render_widget_mus_connection.h
index fea8db3..bd8572b 100644
--- a/content/renderer/mus/render_widget_mus_connection.h
+++ b/content/renderer/mus/render_widget_mus_connection.h
@@ -45,6 +45,9 @@ class CONTENT_EXPORT RenderWidgetMusConnection
void ObserveWheelEventAndResult(const blink::WebMouseWheelEvent& wheel_event,
const gfx::Vector2dF& wheel_unused_delta,
bool event_processed) override;
+ void ObserveGestureEventAndResult(const blink::WebGestureEvent& gesture_event,
+ const gfx::Vector2dF& gesture_unused_delta,
+ bool event_processed) override;
void OnDidHandleKeyEvent() override;
void OnDidOverscroll(const DidOverscrollParams& params) override;
void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index b8b4ad4..50a882f 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -959,6 +959,27 @@ void RenderWidget::ObserveWheelEventAndResult(
}
}
+void RenderWidget::ObserveGestureEventAndResult(
+ const blink::WebGestureEvent& gesture_event,
+ const gfx::Vector2dF& unused_delta,
+ bool event_processed) {
+ if (!compositor_deps_->IsElasticOverscrollEnabled())
+ return;
+
+ cc::InputHandlerScrollResult scroll_result;
+ scroll_result.did_scroll = event_processed;
+ scroll_result.did_overscroll_root = !unused_delta.IsZero();
+ scroll_result.unused_scroll_delta = unused_delta;
+
+ RenderThreadImpl* render_thread = RenderThreadImpl::current();
+ InputHandlerManager* input_handler_manager =
+ render_thread ? render_thread->input_handler_manager() : NULL;
+ if (input_handler_manager) {
+ input_handler_manager->ObserveGestureEventAndResultOnMainThread(
+ routing_id_, gesture_event, scroll_result);
+ }
+}
+
void RenderWidget::OnDidHandleKeyEvent() {
if (owner_delegate_)
owner_delegate_->RenderWidgetDidHandleKeyEvent();
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index a239554..fb2e39f 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -213,6 +213,10 @@ class CONTENT_EXPORT RenderWidget
void ObserveWheelEventAndResult(const blink::WebMouseWheelEvent& wheel_event,
const gfx::Vector2dF& wheel_unused_delta,
bool event_processed) override;
+ void ObserveGestureEventAndResult(const blink::WebGestureEvent& gesture_event,
+ const gfx::Vector2dF& unused_delta,
+ bool event_processed) override;
+
void OnDidHandleKeyEvent() override;
void OnDidOverscroll(const DidOverscrollParams& params) override;
void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) override;
diff --git a/third_party/WebKit/public/web/WebInputEvent.h b/third_party/WebKit/public/web/WebInputEvent.h
index 3be5b18e..d2e24f8 100644
--- a/third_party/WebKit/public/web/WebInputEvent.h
+++ b/third_party/WebKit/public/web/WebInputEvent.h
@@ -513,6 +513,14 @@ public:
// If true, this event will skip hit testing to find a scroll
// target and instead just scroll the viewport.
bool targetViewport;
+ // If true, this event comes after a non-inertial gesture
+ // scroll sequence; OSX has unique phases for normal and
+ // momentum scroll events. Should always be false for touch based
+ // input as it generates GestureFlingStart instead.
+ bool inertial;
+ // True if this event was synthesized in order to force a hit test; avoiding scroll
+ // latching behavior until crbug.com/526463 is fully implemented.
+ bool synthetic;
} scrollBegin;
struct {
@@ -536,6 +544,16 @@ public:
// The original delta units the scrollBegin and scrollUpdates
// were sent as.
ScrollUnits deltaUnits;
+ // If true, this event comes after an inertial gesture
+ // scroll sequence; OSX has unique phases for normal and
+ // momentum scroll events. Should always be false for touch based
+ // input as it generates GestureFlingStart instead.
+ bool inertial;
+ // True if this event was synthesized in order to generate the proper
+ // GSB/GSU/GSE matching sequences. This is a temporary so that a future
+ // GSB will generate a hit test so latching behavior is avoided
+ // until crbug.com/526463 is fully implemented.
+ bool synthetic;
} scrollEnd;
struct {
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 187c9ac..1889a89 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -208,6 +208,13 @@ void ReportInputEventLatencyUma(const WebInputEvent& event,
}
}
+cc::InputHandler::ScrollInputType GestureScrollInputType(
+ blink::WebGestureDevice device) {
+ return device == blink::WebGestureDeviceTouchpad
+ ? cc::InputHandler::WHEEL
+ : cc::InputHandler::TOUCHSCREEN;
+}
+
} // namespace
namespace ui {
@@ -424,13 +431,12 @@ void RecordMainThreadScrollingReasons(WebInputEvent::Type type,
}
}
-bool InputHandlerProxy::ShouldAnimate(
- const blink::WebMouseWheelEvent& event) const {
+bool InputHandlerProxy::ShouldAnimate(bool has_precise_scroll_deltas) const {
#if defined(OS_MACOSX)
// Mac does not smooth scroll wheel events (crbug.com/574283).
return false;
#else
- return smooth_scroll_enabled_ && !event.hasPreciseScrollingDeltas;
+ return smooth_scroll_enabled_ && !has_precise_scroll_deltas;
#endif
}
@@ -487,7 +493,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::ScrollByMouseWheel(
// Wheel events with |canScroll| == false will not trigger scrolling,
// only event handlers. Forward to the main thread.
result = DID_NOT_HANDLE;
- } else if (ShouldAnimate(wheel_event)) {
+ } else if (ShouldAnimate(wheel_event.hasPreciseScrollingDeltas)) {
cc::InputHandler::ScrollStatus scroll_status =
input_handler_->ScrollAnimated(gfx::Point(wheel_event.x, wheel_event.y),
scroll_delta);
@@ -592,26 +598,15 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
scroll_status.main_thread_scrolling_reasons =
cc::MainThreadScrollingReason::kContinuingMainThreadScroll;
} else if (gesture_event.data.scrollBegin.targetViewport) {
- scroll_status = input_handler_->RootScrollBegin(&scroll_state,
- cc::InputHandler::GESTURE);
- } else if (smooth_scroll_enabled_ &&
- gesture_event.data.scrollBegin.deltaHintUnits ==
- blink::WebGestureEvent::ScrollUnits::Pixels) {
- // Generate a scroll begin/end combination to determine if
- // this can actually be handled by the impl thread or not. But
- // don't generate any scroll yet; GestureScrollUpdate will generate
- // the scroll animation.
- scroll_status = input_handler_->ScrollBegin(
- &scroll_state, cc::InputHandler::ANIMATED_WHEEL);
- if (scroll_status.thread == cc::InputHandler::SCROLL_ON_IMPL_THREAD) {
- cc::ScrollStateData scroll_state_end_data;
- scroll_state_end_data.is_ending = true;
- cc::ScrollState scroll_state_end(scroll_state_end_data);
- input_handler_->ScrollEnd(&scroll_state_end);
- }
+ scroll_status = input_handler_->RootScrollBegin(
+ &scroll_state, GestureScrollInputType(gesture_event.sourceDevice));
+ } else if (ShouldAnimate(gesture_event.data.scrollBegin.deltaHintUnits !=
+ blink::WebGestureEvent::ScrollUnits::Pixels)) {
+ gfx::Point scroll_point(gesture_event.x, gesture_event.y);
+ scroll_status = input_handler_->ScrollAnimatedBegin(scroll_point);
} else {
- scroll_status =
- input_handler_->ScrollBegin(&scroll_state, cc::InputHandler::GESTURE);
+ scroll_status = input_handler_->ScrollBegin(
+ &scroll_state, GestureScrollInputType(gesture_event.sourceDevice));
}
UMA_HISTOGRAM_ENUMERATION("Renderer4.CompositorScrollHitTestResult",
scroll_status.thread,
@@ -620,20 +615,28 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin(
RecordMainThreadScrollingReasons(gesture_event.type,
scroll_status.main_thread_scrolling_reasons);
+ InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE;
switch (scroll_status.thread) {
case cc::InputHandler::SCROLL_ON_IMPL_THREAD:
TRACE_EVENT_INSTANT0("input",
"InputHandlerProxy::handle_input gesture scroll",
TRACE_EVENT_SCOPE_THREAD);
gesture_scroll_on_impl_thread_ = true;
- return DID_HANDLE;
+ result = DID_HANDLE;
+ break;
case cc::InputHandler::SCROLL_UNKNOWN:
case cc::InputHandler::SCROLL_ON_MAIN_THREAD:
- return DID_NOT_HANDLE;
+ result = DID_NOT_HANDLE;
+ break;
case cc::InputHandler::SCROLL_IGNORED:
- return DROP_EVENT;
+ result = DROP_EVENT;
+ break;
}
- return DID_NOT_HANDLE;
+ if (scroll_elasticity_controller_ && result != DID_NOT_HANDLE)
+ HandleScrollElasticityOverscroll(gesture_event,
+ cc::InputHandlerScrollResult());
+
+ return result;
}
InputHandlerProxy::EventDisposition
@@ -650,9 +653,8 @@ InputHandlerProxy::HandleGestureScrollUpdate(
gfx::Vector2dF scroll_delta(-gesture_event.data.scrollUpdate.deltaX,
-gesture_event.data.scrollUpdate.deltaY);
- if (smooth_scroll_enabled_ &&
- gesture_event.data.scrollUpdate.deltaUnits ==
- blink::WebGestureEvent::ScrollUnits::Pixels) {
+ if (ShouldAnimate(gesture_event.data.scrollUpdate.deltaUnits !=
+ blink::WebGestureEvent::ScrollUnits::Pixels)) {
switch (input_handler_->ScrollAnimated(scroll_point, scroll_delta).thread) {
case cc::InputHandler::SCROLL_ON_IMPL_THREAD:
return DID_HANDLE;
@@ -665,6 +667,10 @@ InputHandlerProxy::HandleGestureScrollUpdate(
cc::InputHandlerScrollResult scroll_result =
input_handler_->ScrollBy(&scroll_state);
HandleOverscroll(scroll_point, scroll_result);
+
+ if (scroll_elasticity_controller_)
+ HandleScrollElasticityOverscroll(gesture_event, scroll_result);
+
return scroll_result.did_scroll ? DID_HANDLE : DROP_EVENT;
}
@@ -674,10 +680,21 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollEnd(
DCHECK(expect_scroll_update_end_);
expect_scroll_update_end_ = false;
#endif
- cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
- input_handler_->ScrollEnd(&scroll_state);
+ if (ShouldAnimate(gesture_event.data.scrollEnd.deltaUnits !=
+ blink::WebGestureEvent::ScrollUnits::Pixels)) {
+ // Do nothing if the scroll is being animated; the scroll animation will
+ // generate the ScrollEnd when it is done.
+ } else {
+ cc::ScrollState scroll_state = CreateScrollStateForGesture(gesture_event);
+ input_handler_->ScrollEnd(&scroll_state);
+ }
if (!gesture_scroll_on_impl_thread_)
return DID_NOT_HANDLE;
+
+ if (scroll_elasticity_controller_)
+ HandleScrollElasticityOverscroll(gesture_event,
+ cc::InputHandlerScrollResult());
+
gesture_scroll_on_impl_thread_ = false;
return DID_HANDLE;
}
@@ -890,7 +907,7 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting(
gfx::Point(gesture_event.x, gesture_event.y),
fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad
? cc::InputHandler::NON_BUBBLING_GESTURE
- : cc::InputHandler::GESTURE)) {
+ : cc::InputHandler::TOUCHSCREEN)) {
CancelCurrentFling();
return false;
}
@@ -1310,4 +1327,19 @@ bool InputHandlerProxy::scrollBy(const WebFloatSize& increment,
return did_scroll;
}
+void InputHandlerProxy::HandleScrollElasticityOverscroll(
+ const WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result) {
+ DCHECK(scroll_elasticity_controller_);
+ // Send the event and its disposition to the elasticity controller to update
+ // the over-scroll animation. Note that the call to the elasticity controller
+ // is made asynchronously, to minimize divergence between main thread and
+ // impl thread event handling paths.
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&InputScrollElasticityController::ObserveGestureEventAndResult,
+ scroll_elasticity_controller_->GetWeakPtr(), gesture_event,
+ scroll_result));
+}
+
} // namespace ui
diff --git a/ui/events/blink/input_handler_proxy.h b/ui/events/blink/input_handler_proxy.h
index 32c0efc..2da4bc9 100644
--- a/ui/events/blink/input_handler_proxy.h
+++ b/ui/events/blink/input_handler_proxy.h
@@ -138,7 +138,12 @@ class InputHandlerProxy
const cc::InputHandlerScrollResult& scroll_result);
// Whether to use a smooth scroll animation for this event.
- bool ShouldAnimate(const blink::WebMouseWheelEvent& event) const;
+ bool ShouldAnimate(bool has_precise_scroll_deltas) const;
+
+ // Update the elastic overscroll controller with |gesture_event|.
+ void HandleScrollElasticityOverscroll(
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result);
scoped_ptr<blink::WebGestureCurve> fling_curve_;
// Parameters for the active fling animation, stored in case we need to
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 6723a31..b798dc9 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -104,6 +104,8 @@ class MockInputHandler : public cc::InputHandler {
MOCK_METHOD2(RootScrollBegin,
ScrollStatus(cc::ScrollState*,
cc::InputHandler::ScrollInputType type));
+ MOCK_METHOD1(ScrollAnimatedBegin,
+ ScrollStatus(const gfx::Point& viewport_point));
MOCK_METHOD2(ScrollAnimated,
ScrollStatus(const gfx::Point& viewport_point,
const gfx::Vector2dF& scroll_delta));
@@ -639,9 +641,8 @@ TEST_P(InputHandlerProxyTest, DISABLED_GestureScrollByCoarsePixels) {
gesture_.type = WebInputEvent::GestureScrollBegin;
gesture_.data.scrollBegin.deltaHintUnits =
WebGestureEvent::ScrollUnits::Pixels;
- EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+ EXPECT_CALL(mock_input_handler_, ScrollAnimatedBegin(::testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
- EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
gesture_.type = WebInputEvent::GestureScrollUpdate;
diff --git a/ui/events/blink/input_scroll_elasticity_controller.cc b/ui/events/blink/input_scroll_elasticity_controller.cc
index d063093..dedf7de 100644
--- a/ui/events/blink/input_scroll_elasticity_controller.cc
+++ b/ui/events/blink/input_scroll_elasticity_controller.cc
@@ -167,6 +167,71 @@ void InputScrollElasticityController::ObserveWheelEventAndResult(
}
}
+void InputScrollElasticityController::ObserveGestureEventAndResult(
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result) {
+ base::TimeTicks event_timestamp =
+ base::TimeTicks() +
+ base::TimeDelta::FromSecondsD(gesture_event.timeStampSeconds);
+
+ switch (gesture_event.type) {
+ case blink::WebInputEvent::GestureScrollBegin: {
+ if (gesture_event.data.scrollBegin.synthetic)
+ return;
+ if (gesture_event.data.scrollBegin.inertial) {
+ if (state_ == kStateInactive)
+ state_ = kStateMomentumScroll;
+ } else if (gesture_event.data.scrollBegin.deltaHintUnits ==
+ blink::WebGestureEvent::PrecisePixels) {
+ scroll_velocity = gfx::Vector2dF();
+ last_scroll_event_timestamp_ = base::TimeTicks();
+ state_ = kStateActiveScroll;
+ pending_overscroll_delta_ = gfx::Vector2dF();
+ }
+ break;
+ }
+ case blink::WebInputEvent::GestureScrollUpdate: {
+ gfx::Vector2dF event_delta(-gesture_event.data.scrollUpdate.deltaX,
+ -gesture_event.data.scrollUpdate.deltaY);
+ switch (state_) {
+ case kStateMomentumAnimated:
+ case kStateInactive:
+ break;
+ case kStateActiveScroll:
+ case kStateMomentumScroll:
+ UpdateVelocity(event_delta, event_timestamp);
+ Overscroll(event_delta, scroll_result.unused_scroll_delta);
+ if (gesture_event.data.scrollUpdate.inertial &&
+ !helper_->StretchAmount().IsZero()) {
+ EnterStateMomentumAnimated(event_timestamp);
+ }
+ break;
+ }
+ break;
+ }
+ case blink::WebInputEvent::GestureScrollEnd: {
+ if (gesture_event.data.scrollEnd.synthetic)
+ return;
+ switch (state_) {
+ case kStateMomentumAnimated:
+ case kStateInactive:
+ break;
+ case kStateActiveScroll:
+ case kStateMomentumScroll:
+ if (helper_->StretchAmount().IsZero()) {
+ EnterStateInactive();
+ } else {
+ EnterStateMomentumAnimated(event_timestamp);
+ }
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
void InputScrollElasticityController::UpdateVelocity(
const gfx::Vector2dF& event_delta,
const base::TimeTicks& event_timestamp) {
diff --git a/ui/events/blink/input_scroll_elasticity_controller.h b/ui/events/blink/input_scroll_elasticity_controller.h
index 8c64681..915b2e8 100644
--- a/ui/events/blink/input_scroll_elasticity_controller.h
+++ b/ui/events/blink/input_scroll_elasticity_controller.h
@@ -58,6 +58,15 @@ class InputScrollElasticityController {
void ObserveWheelEventAndResult(
const blink::WebMouseWheelEvent& wheel_event,
const cc::InputHandlerScrollResult& scroll_result);
+ // Update the overscroll state based a gesture event that has been processed.
+ // Note that this assumes that all events are coming from a single input
+ // device. If the user simultaneously uses multiple input devices, Cocoa may
+ // not correctly pass all the gesture begin and end events. In this case,
+ // this class may disregard some scrolls that come in at unexpected times.
+ void ObserveGestureEventAndResult(
+ const blink::WebGestureEvent& gesture_event,
+ const cc::InputHandlerScrollResult& scroll_result);
+
void Animate(base::TimeTicks time);
void ReconcileStretchAndScroll();
diff --git a/ui/events/blink/input_scroll_elasticity_controller_unittest.cc b/ui/events/blink/input_scroll_elasticity_controller_unittest.cc
index 82b9434..4ce7b94 100644
--- a/ui/events/blink/input_scroll_elasticity_controller_unittest.cc
+++ b/ui/events/blink/input_scroll_elasticity_controller_unittest.cc
@@ -99,6 +99,53 @@ class ScrollElasticityControllerTest : public testing::Test {
input_event_count_ += 1;
}
+ void SendGestureScrollBegin(bool inertial) {
+ blink::WebGestureEvent event;
+ event.sourceDevice = blink::WebGestureDeviceTouchpad;
+ event.type = blink::WebInputEvent::GestureScrollBegin;
+ event.data.scrollBegin.inertial = inertial;
+ TickCurrentTime();
+ event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF();
+
+ controller_.ObserveGestureEventAndResult(event,
+ cc::InputHandlerScrollResult());
+ input_event_count_ += 1;
+ }
+
+ void SendGestureScrollUpdate(
+ bool inertial,
+ const gfx::Vector2dF& event_delta = gfx::Vector2dF(),
+ const gfx::Vector2dF& overscroll_delta = gfx::Vector2dF()) {
+ blink::WebGestureEvent event;
+ event.sourceDevice = blink::WebGestureDeviceTouchpad;
+ event.type = blink::WebInputEvent::GestureScrollUpdate;
+ event.data.scrollUpdate.inertial = inertial;
+ event.data.scrollUpdate.deltaX = -event_delta.x();
+ event.data.scrollUpdate.deltaY = -event_delta.y();
+ TickCurrentTime();
+ event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF();
+
+ cc::InputHandlerScrollResult scroll_result;
+ scroll_result.did_overscroll_root = !overscroll_delta.IsZero();
+ scroll_result.unused_scroll_delta = overscroll_delta;
+
+ controller_.ObserveGestureEventAndResult(event, scroll_result);
+ input_event_count_ += 1;
+ }
+
+ void SendGestureScrollEnd() {
+ blink::WebGestureEvent event;
+ event.sourceDevice = blink::WebGestureDeviceTouchpad;
+ event.type = blink::WebInputEvent::GestureScrollEnd;
+
+ TickCurrentTime();
+ event.timeStampSeconds = (current_time_ - base::TimeTicks()).InSecondsF();
+
+ controller_.ObserveGestureEventAndResult(event,
+ cc::InputHandlerScrollResult());
+ input_event_count_ += 1;
+ }
+
const base::TimeTicks& TickCurrentTime() {
current_time_ += base::TimeDelta::FromSecondsD(1 / 60.f);
return current_time_;
@@ -148,6 +195,40 @@ TEST_F(ScrollElasticityControllerTest, Axis) {
EXPECT_EQ(0, helper_.request_begin_frame_count());
}
+// Verify that stretching only occurs in one axis at a time, and that it
+// is biased to the Y axis.
+TEST_F(ScrollElasticityControllerTest, GestureBased_Axis) {
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0),
+ gfx::ScrollOffset(0, 0));
+
+ // If we push equally in the X and Y directions, we should see a stretch only
+ // in the Y direction.
+ SendGestureScrollBegin(false);
+ SendGestureScrollUpdate(false, gfx::Vector2dF(10, 10),
+ gfx::Vector2dF(10, 10));
+ EXPECT_EQ(1, helper_.set_stretch_amount_count());
+ EXPECT_EQ(0.f, helper_.StretchAmount().x());
+ EXPECT_LT(0.f, helper_.StretchAmount().y());
+ helper_.SetStretchAmount(gfx::Vector2dF());
+ EXPECT_EQ(2, helper_.set_stretch_amount_count());
+ SendGestureScrollEnd();
+ EXPECT_EQ(0, helper_.request_begin_frame_count());
+
+ // If we push more in the X direction than the Y direction, we should see a
+ // stretch only in the X direction. This decision should be based on the
+ // input delta, not the actual overscroll delta.
+ SendGestureScrollBegin(false);
+ SendGestureScrollUpdate(false, gfx::Vector2dF(-25, 10),
+ gfx::Vector2dF(-25, 40));
+ EXPECT_EQ(3, helper_.set_stretch_amount_count());
+ EXPECT_GT(0.f, helper_.StretchAmount().x());
+ EXPECT_EQ(0.f, helper_.StretchAmount().y());
+ helper_.SetStretchAmount(gfx::Vector2dF());
+ EXPECT_EQ(4, helper_.set_stretch_amount_count());
+ SendGestureScrollEnd();
+ EXPECT_EQ(0, helper_.request_begin_frame_count());
+}
+
// Verify that we need a total overscroll delta of at least 10 in a pinned
// direction before we start stretching.
TEST_F(ScrollElasticityControllerTest, MinimumDeltaBeforeStretch) {
@@ -201,7 +282,54 @@ TEST_F(ScrollElasticityControllerTest, MinimumDeltaBeforeStretch) {
EXPECT_EQ(1, helper_.request_begin_frame_count());
}
-// Verify that an stretch caused by a momentum scroll will switch to the
+// Verify that we need a total overscroll delta of at least 10 in a pinned
+// direction before we start stretching.
+TEST_F(ScrollElasticityControllerTest, GestureBased_MinimumDeltaBeforeStretch) {
+ // We should not start stretching while we are not pinned in the direction
+ // of the scroll (even if there is an overscroll delta). We have to wait for
+ // the regular scroll to eat all of the events.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5),
+ gfx::ScrollOffset(10, 10));
+ SendGestureScrollBegin(false);
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 10));
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 10));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+ // Now pin the -X and +Y direction. The first event will not generate a
+ // stretch
+ // because it is below the delta threshold of 10.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 10),
+ gfx::ScrollOffset(10, 10));
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 8));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+ // Make the next scroll be in the -X direction more than the +Y direction,
+ // which will erase the memory of the previous unused delta of 8.
+ SendGestureScrollUpdate(false, gfx::Vector2dF(-10, 5),
+ gfx::Vector2dF(-8, 10));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+ // Now push against the pinned +Y direction again by 8. We reset the
+ // previous delta, so this will not generate a stretch.
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 8));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+ // Push against +Y by another 8. This gets us above the delta threshold of
+ // 10, so we should now have had the stretch set, and it should be in the
+ // +Y direction. The scroll in the -X direction should have been forgotten.
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, 10), gfx::Vector2dF(0, 8));
+ EXPECT_EQ(1, helper_.set_stretch_amount_count());
+ EXPECT_EQ(0.f, helper_.StretchAmount().x());
+ EXPECT_LT(0.f, helper_.StretchAmount().y());
+
+ // End the gesture. Because there is a non-zero stretch, we should be in the
+ // animated state, and should have had a frame requested.
+ EXPECT_EQ(0, helper_.request_begin_frame_count());
+ SendGestureScrollEnd();
+ EXPECT_EQ(1, helper_.request_begin_frame_count());
+}
+
+// Verify that a stretch caused by a momentum scroll will switch to the
// animating mode, where input events are ignored, and the stretch is updated
// while animating.
TEST_F(ScrollElasticityControllerTest, MomentumAnimate) {
@@ -297,7 +425,93 @@ TEST_F(ScrollElasticityControllerTest, MomentumAnimate) {
EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count());
}
-// Verify that an stretch opposing a scroll is correctly resolved.
+// Verify that a stretch caused by a momentum scroll will switch to the
+// animating mode, where input events are ignored, and the stretch is updated
+// while animating.
+TEST_F(ScrollElasticityControllerTest, GestureBased_MomentumAnimate) {
+ // Do an active scroll, then switch to the momentum phase and scroll for a
+ // bit.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5),
+ gfx::ScrollOffset(10, 10));
+ SendGestureScrollBegin(false);
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0));
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0));
+ SendGestureScrollUpdate(false, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0));
+ SendGestureScrollEnd();
+ SendGestureScrollBegin(true);
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0));
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0));
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, 0));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+ // Hit the -Y edge and overscroll slightly, but not enough to go over the
+ // threshold to cause a stretch.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 0),
+ gfx::ScrollOffset(10, 10));
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -8));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+ EXPECT_EQ(0, helper_.request_begin_frame_count());
+
+ // Take another step, this time going over the threshold. This should update
+ // the stretch amount, and then switch to the animating mode.
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80));
+ EXPECT_EQ(1, helper_.set_stretch_amount_count());
+ EXPECT_EQ(1, helper_.request_begin_frame_count());
+ EXPECT_GT(-1.f, helper_.StretchAmount().y());
+
+ // Subsequent momentum events should do nothing.
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80));
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80));
+ SendGestureScrollUpdate(true, gfx::Vector2dF(0, -80), gfx::Vector2dF(0, -80));
+ SendGestureScrollEnd();
+ EXPECT_EQ(1, helper_.set_stretch_amount_count());
+ EXPECT_EQ(1, helper_.request_begin_frame_count());
+
+ // Subsequent animate events should update the stretch amount and request
+ // another frame.
+ TickCurrentTimeAndAnimate();
+ EXPECT_EQ(2, helper_.set_stretch_amount_count());
+ EXPECT_EQ(2, helper_.request_begin_frame_count());
+ EXPECT_GT(-1.f, helper_.StretchAmount().y());
+
+ // Touching the trackpad (a PhaseMayBegin event) should disable animation.
+ SendGestureScrollBegin(false);
+ TickCurrentTimeAndAnimate();
+ EXPECT_EQ(2, helper_.set_stretch_amount_count());
+ EXPECT_EQ(2, helper_.request_begin_frame_count());
+
+ // Releasing the trackpad should re-enable animation.
+ SendGestureScrollEnd();
+ EXPECT_EQ(2, helper_.set_stretch_amount_count());
+ EXPECT_EQ(3, helper_.request_begin_frame_count());
+ TickCurrentTimeAndAnimate();
+ EXPECT_EQ(3, helper_.set_stretch_amount_count());
+ EXPECT_EQ(4, helper_.request_begin_frame_count());
+
+ // Keep animating frames until the stretch returns to rest.
+ int stretch_count = 3;
+ int begin_frame_count = 4;
+ while (1) {
+ TickCurrentTimeAndAnimate();
+ if (helper_.StretchAmount().IsZero()) {
+ stretch_count += 1;
+ EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count());
+ EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count());
+ break;
+ }
+ stretch_count += 1;
+ begin_frame_count += 1;
+ EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count());
+ EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count());
+ }
+
+ // After coming to rest, no subsequent animate calls change anything.
+ TickCurrentTimeAndAnimate();
+ EXPECT_EQ(stretch_count, helper_.set_stretch_amount_count());
+ EXPECT_EQ(begin_frame_count, helper_.request_begin_frame_count());
+}
+
+// Verify that a stretch opposing a scroll is correctly resolved.
TEST_F(ScrollElasticityControllerTest, ReconcileStretchAndScroll) {
SendMouseWheelEvent(PhaseBegan, PhaseNone);
@@ -334,14 +548,50 @@ TEST_F(ScrollElasticityControllerTest, ReconcileStretchAndScroll) {
EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(7, 8));
}
+// Verify that a stretch opposing a scroll is correctly resolved.
+TEST_F(ScrollElasticityControllerTest, GestureBased_ReconcileStretchAndScroll) {
+ SendGestureScrollBegin(false);
+
+ // Verify completely knocking out the scroll in the -Y direction.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5),
+ gfx::ScrollOffset(10, 10));
+ helper_.SetStretchAmount(gfx::Vector2dF(0, -10));
+ controller_.ReconcileStretchAndScroll();
+ EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, -5));
+ EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(5, 0));
+
+ // Verify partially knocking out the scroll in the -Y direction.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 8),
+ gfx::ScrollOffset(10, 10));
+ helper_.SetStretchAmount(gfx::Vector2dF(0, -5));
+ controller_.ReconcileStretchAndScroll();
+ EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+ EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(5, 3));
+
+ // Verify completely knocking out the scroll in the +X direction.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(5, 5),
+ gfx::ScrollOffset(10, 10));
+ helper_.SetStretchAmount(gfx::Vector2dF(10, 0));
+ controller_.ReconcileStretchAndScroll();
+ EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(5, 0));
+ EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(10, 5));
+
+ // Verify partially knocking out the scroll in the +X and +Y directions.
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(2, 3),
+ gfx::ScrollOffset(10, 10));
+ helper_.SetStretchAmount(gfx::Vector2dF(5, 5));
+ controller_.ReconcileStretchAndScroll();
+ EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+ EXPECT_EQ(helper_.ScrollOffset(), gfx::ScrollOffset(7, 8));
+}
+
// Verify that stretching only happens when the area is user scrollable.
TEST_F(ScrollElasticityControllerTest, UserScrollableRequiredForStretch) {
helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0),
gfx::ScrollOffset(10, 10));
gfx::Vector2dF delta(0, -15);
- // Do an active scroll, and ensure that the stretch amount doesn't change,
- // and also that the stretch amount isn't even ever changed.
+ // Do an active scroll, and ensure that the stretch amount doesn't change.
helper_.SetUserScrollable(false);
SendMouseWheelEvent(PhaseBegan, PhaseNone);
SendMouseWheelEvent(PhaseChanged, PhaseNone, delta, delta);
@@ -384,5 +634,55 @@ TEST_F(ScrollElasticityControllerTest, UserScrollableRequiredForStretch) {
EXPECT_GT(ticks_to_zero, 3);
}
+// Verify that stretching only happens when the area is user scrollable.
+TEST_F(ScrollElasticityControllerTest,
+ GestureBased_UserScrollableRequiredForStretch) {
+ helper_.SetScrollOffsetAndMaxScrollOffset(gfx::ScrollOffset(0, 0),
+ gfx::ScrollOffset(10, 10));
+ gfx::Vector2dF delta(0, -15);
+
+ // Do an active scroll, and ensure that the stretch amount doesn't change.
+ helper_.SetUserScrollable(false);
+ SendGestureScrollBegin(false);
+ SendGestureScrollUpdate(false, delta, delta);
+ SendGestureScrollUpdate(false, delta, delta);
+ SendGestureScrollEnd();
+ EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+ SendGestureScrollBegin(true);
+ SendGestureScrollUpdate(true, delta, delta);
+ SendGestureScrollUpdate(true, delta, delta);
+ SendGestureScrollEnd();
+ EXPECT_EQ(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+ EXPECT_EQ(0, helper_.set_stretch_amount_count());
+
+ // Re-enable user scrolling and ensure that stretching is re-enabled.
+ helper_.SetUserScrollable(true);
+ SendGestureScrollBegin(false);
+ SendGestureScrollUpdate(false, delta, delta);
+ SendGestureScrollUpdate(false, delta, delta);
+ SendGestureScrollEnd();
+ EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+ EXPECT_GT(helper_.set_stretch_amount_count(), 0);
+ SendGestureScrollBegin(true);
+ SendGestureScrollUpdate(true, delta, delta);
+ SendGestureScrollUpdate(true, delta, delta);
+ SendGestureScrollEnd();
+ EXPECT_NE(helper_.StretchAmount(), gfx::Vector2dF(0, 0));
+ EXPECT_GT(helper_.set_stretch_amount_count(), 0);
+
+ // Disable user scrolling and tick the timer until the stretch goes back
+ // to zero. Ensure that the return to zero doesn't happen immediately.
+ helper_.SetUserScrollable(false);
+ int ticks_to_zero = 0;
+ while (1) {
+ TickCurrentTimeAndAnimate();
+ if (helper_.StretchAmount().IsZero())
+ break;
+ ticks_to_zero += 1;
+ }
+ EXPECT_GT(ticks_to_zero, 3);
+}
+
} // namespace
} // namespace ui