summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--blimp/client/feature/compositor/blimp_input_handler_wrapper.cc1
-rw-r--r--content/browser/android/in_process/synchronous_input_event_filter.cc4
-rw-r--r--content/browser/android/in_process/synchronous_input_event_filter.h2
-rw-r--r--content/browser/renderer_host/input/input_router_impl.cc7
-rw-r--r--content/browser/renderer_host/input/non_blocking_event_browsertest.cc188
-rw-r--r--content/browser/renderer_host/input/touch_input_browsertest.cc101
-rw-r--r--content/browser/renderer_host/render_widget_host_view_mac_unittest.mm4
-rw-r--r--content/common/input/input_event_ack_state.h3
-rw-r--r--content/common/input/input_event_dispatch_type.h18
-rw-r--r--content/common/input/web_input_event_queue.h79
-rw-r--r--content/common/input_messages.h8
-rw-r--r--content/content_common.gypi2
-rw-r--r--content/content_renderer.gypi2
-rw-r--r--content/content_tests.gypi2
-rw-r--r--content/public/test/browser_test_utils.cc63
-rw-r--r--content/public/test/browser_test_utils.h33
-rw-r--r--content/public/test/render_view_test.cc21
-rw-r--r--content/renderer/android/synchronous_compositor_filter.cc4
-rw-r--r--content/renderer/android/synchronous_compositor_filter.h2
-rw-r--r--content/renderer/idle_user_detector.cc6
-rw-r--r--content/renderer/idle_user_detector.h4
-rw-r--r--content/renderer/input/input_event_filter.cc43
-rw-r--r--content/renderer/input/input_event_filter.h16
-rw-r--r--content/renderer/input/input_event_filter_unittest.cc184
-rw-r--r--content/renderer/input/input_handler_manager.cc42
-rw-r--r--content/renderer/input/input_handler_manager.h20
-rw-r--r--content/renderer/input/input_handler_manager_client.h8
-rw-r--r--content/renderer/input/input_handler_wrapper.cc5
-rw-r--r--content/renderer/input/input_handler_wrapper.h3
-rw-r--r--content/renderer/input/non_blocking_event_queue.cc60
-rw-r--r--content/renderer/input/non_blocking_event_queue.h62
-rw-r--r--content/renderer/input/non_blocking_event_queue_unittest.cc120
-rw-r--r--content/renderer/input/render_widget_input_handler.cc36
-rw-r--r--content/renderer/input/render_widget_input_handler.h4
-rw-r--r--content/renderer/input/render_widget_input_handler_delegate.h5
-rw-r--r--content/renderer/mus/render_widget_mus_connection.cc8
-rw-r--r--content/renderer/mus/render_widget_mus_connection.h2
-rw-r--r--content/renderer/render_view_impl.cc4
-rw-r--r--content/renderer/render_widget.cc16
-rw-r--r--content/renderer/render_widget.h5
-rw-r--r--content/renderer/render_widget_unittest.cc3
-rw-r--r--ui/events/blink/input_handler_proxy.cc141
-rw-r--r--ui/events/blink/input_handler_proxy.h17
-rw-r--r--ui/events/blink/input_handler_proxy_unittest.cc196
44 files changed, 1374 insertions, 180 deletions
diff --git a/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc b/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc
index 4716b58..5e38f33 100644
--- a/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc
+++ b/blimp/client/feature/compositor/blimp_input_handler_wrapper.cc
@@ -51,6 +51,7 @@ void BlimpInputHandlerWrapper::HandleWebGestureEvent(
case ui::InputHandlerProxy::EventDisposition::DROP_EVENT:
consumed = true;
break;
+ case ui::InputHandlerProxy::EventDisposition::DID_HANDLE_NON_BLOCKING:
case ui::InputHandlerProxy::EventDisposition::DID_NOT_HANDLE:
consumed = false;
break;
diff --git a/content/browser/android/in_process/synchronous_input_event_filter.cc b/content/browser/android/in_process/synchronous_input_event_filter.cc
index 3ce1c61..80f1025 100644
--- a/content/browser/android/in_process/synchronous_input_event_filter.cc
+++ b/content/browser/android/in_process/synchronous_input_event_filter.cc
@@ -80,4 +80,8 @@ void SynchronousInputEventFilter::DidStopFlinging(int routing_id) {
compositor->DidStopFlinging();
}
+void SynchronousInputEventFilter::NonBlockingInputEventHandled(
+ int routing_id,
+ blink::WebInputEvent::Type type) {}
+
} // namespace content
diff --git a/content/browser/android/in_process/synchronous_input_event_filter.h b/content/browser/android/in_process/synchronous_input_event_filter.h
index 98375d7..32b6ecb 100644
--- a/content/browser/android/in_process/synchronous_input_event_filter.h
+++ b/content/browser/android/in_process/synchronous_input_event_filter.h
@@ -44,6 +44,8 @@ class SynchronousInputEventFilter : public InputHandlerManagerClient {
void DidOverscroll(int routing_id,
const DidOverscrollParams& params) override;
void DidStopFlinging(int routing_id) override;
+ void NonBlockingInputEventHandled(int routing_id,
+ blink::WebInputEvent::Type type) override;
private:
void SetBoundHandlerOnUIThread(const Handler& handler);
diff --git a/content/browser/renderer_host/input/input_router_impl.cc b/content/browser/renderer_host/input/input_router_impl.cc
index 8ba9287..9f79fb4 100644
--- a/content/browser/renderer_host/input/input_router_impl.cc
+++ b/content/browser/renderer_host/input/input_router_impl.cc
@@ -53,6 +53,8 @@ const char* GetEventAckName(InputEventAckState ack_result) {
case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: return "NOT_CONSUMED";
case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: return "NO_CONSUMER_EXISTS";
case INPUT_EVENT_ACK_STATE_IGNORED: return "IGNORED";
+ case INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING:
+ return "SET_NON_BLOCKING";
}
DLOG(WARNING) << "Unhandled InputEventAckState in GetEventAckName.";
return "";
@@ -410,8 +412,9 @@ bool InputRouterImpl::OfferToRenderer(const WebInputEvent& input_event,
const WebInputEvent* event_to_send =
event_in_viewport ? event_in_viewport.get() : &input_event;
- if (Send(new InputMsg_HandleInputEvent(routing_id(), event_to_send,
- latency_info))) {
+ if (Send(new InputMsg_HandleInputEvent(
+ routing_id(), event_to_send, latency_info,
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL))) {
// Ack messages for ignored ack event types should never be sent by the
// renderer. Consequently, such event types should not affect event time
// or in-flight event count metrics.
diff --git a/content/browser/renderer_host/input/non_blocking_event_browsertest.cc b/content/browser/renderer_host/input/non_blocking_event_browsertest.cc
new file mode 100644
index 0000000..6cc60c6
--- /dev/null
+++ b/content/browser/renderer_host/input/non_blocking_event_browsertest.cc
@@ -0,0 +1,188 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <utility>
+
+#include "base/auto_reset.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "content/browser/renderer_host/input/synthetic_smooth_scroll_gesture.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/browser/renderer_host/render_widget_host_view_base.h"
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/input/synthetic_web_input_event_builders.h"
+#include "content/common/input_messages.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/test_utils.h"
+#include "content/shell/browser/shell.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/event_switches.h"
+#include "ui/events/latency_info.h"
+
+using blink::WebInputEvent;
+
+namespace {
+
+const char kNonBlockingEventDataURL[] =
+ "data:text/html;charset=utf-8,"
+ "<!DOCTYPE html>"
+ "<meta name='viewport' content='width=device-width'/>"
+ "<style>"
+ "html, body {"
+ " margin: 0;"
+ "}"
+ ".spacer { height: 1000px; }"
+ "</style>"
+ "<div class=spacer></div>"
+ "<script>"
+ " document.addEventListener('wheel', function(e) { while(true) {} }, "
+ "{'passive': true});"
+ " document.addEventListener('touchstart', function(e) { while(true) {} }, "
+ "{'passive': true});"
+ " document.title='ready';"
+ "</script>";
+
+} // namespace
+
+namespace content {
+
+class NonBlockingEventBrowserTest : public ContentBrowserTest {
+ public:
+ NonBlockingEventBrowserTest() {}
+ ~NonBlockingEventBrowserTest() override {}
+
+ RenderWidgetHostImpl* GetWidgetHost() {
+ return RenderWidgetHostImpl::From(
+ shell()->web_contents()->GetRenderViewHost()->GetWidget());
+ }
+
+ void OnSyntheticGestureCompleted(SyntheticGesture::Result result) {
+ EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+ }
+
+ protected:
+ void LoadURL() {
+ const GURL data_url(kNonBlockingEventDataURL);
+ NavigateToURL(shell(), data_url);
+
+ RenderWidgetHostImpl* host = GetWidgetHost();
+ host->GetView()->SetSize(gfx::Size(400, 400));
+
+ base::string16 ready_title(base::ASCIIToUTF16("ready"));
+ TitleWatcher watcher(shell()->web_contents(), ready_title);
+ ignore_result(watcher.WaitAndGetTitle());
+
+ MainThreadFrameObserver main_thread_sync(host);
+ main_thread_sync.Wait();
+ }
+
+ // ContentBrowserTest:
+ void SetUpCommandLine(base::CommandLine* cmd) override {
+ // TODO(dtapuska): Remove this switch once wheel-gestures ships.
+ cmd->AppendSwitch(switches::kEnableExperimentalWebPlatformFeatures);
+ cmd->AppendSwitch(switches::kEnableWheelGestures);
+ }
+
+ int ExecuteScriptAndExtractInt(const std::string& script) {
+ int value = 0;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractInt(
+ shell()->web_contents(), "domAutomationController.send(" + script + ")",
+ &value));
+ return value;
+ }
+
+ int GetScrollTop() {
+ return ExecuteScriptAndExtractInt("document.scrollingElement.scrollTop");
+ }
+
+ void DoWheelScroll() {
+ EXPECT_EQ(0, GetScrollTop());
+
+ int scrollHeight =
+ ExecuteScriptAndExtractInt("document.documentElement.scrollHeight");
+ EXPECT_EQ(1000, scrollHeight);
+
+ scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
+ GetWidgetHost()->GetProcess()->AddFilter(frame_watcher.get());
+ scoped_refptr<InputMsgWatcher> input_msg_watcher(
+ new InputMsgWatcher(GetWidgetHost(), blink::WebInputEvent::MouseWheel));
+
+ GetWidgetHost()->ForwardWheelEvent(
+ SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, -53, 0, true));
+
+ // Runs until we get the InputMsgAck callback
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING,
+ input_msg_watcher->WaitForAck());
+ frame_watcher->WaitFrames(1);
+
+ // Expect that the compositor scrolled at least one pixel while the
+ // main thread was in a busy loop.
+ EXPECT_LT(0, frame_watcher->LastMetadata().root_scroll_offset.y());
+ }
+
+ void DoTouchScroll() {
+ EXPECT_EQ(0, GetScrollTop());
+
+ int scrollHeight =
+ ExecuteScriptAndExtractInt("document.documentElement.scrollHeight");
+ EXPECT_EQ(1000, scrollHeight);
+
+ scoped_refptr<FrameWatcher> frame_watcher(new FrameWatcher());
+ GetWidgetHost()->GetProcess()->AddFilter(frame_watcher.get());
+
+ SyntheticSmoothScrollGestureParams params;
+ params.gesture_source_type = SyntheticGestureParams::TOUCH_INPUT;
+ params.anchor = gfx::PointF(50, 50);
+ params.distances.push_back(gfx::Vector2d(0, -45));
+
+ scoped_ptr<SyntheticSmoothScrollGesture> gesture(
+ new SyntheticSmoothScrollGesture(params));
+ GetWidgetHost()->QueueSyntheticGesture(
+ std::move(gesture),
+ base::Bind(&NonBlockingEventBrowserTest::OnSyntheticGestureCompleted,
+ base::Unretained(this)));
+
+ // Expect that the compositor scrolled at least one pixel while the
+ // main thread was in a busy loop.
+ while (frame_watcher->LastMetadata().root_scroll_offset.y() <= 0)
+ frame_watcher->WaitFrames(1);
+ }
+
+ private:
+ 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
+#define MAYBE_MouseWheel MouseWheel
+#endif
+IN_PROC_BROWSER_TEST_F(NonBlockingEventBrowserTest, MAYBE_MouseWheel) {
+ LoadURL();
+ DoWheelScroll();
+}
+
+// Disabled on MacOS because it doesn't support touch input.
+#if defined(OS_MACOSX)
+#define MAYBE_TouchStart DISABLED_TouchStart
+#else
+#define MAYBE_TouchStart TouchStart
+#endif
+IN_PROC_BROWSER_TEST_F(NonBlockingEventBrowserTest, MAYBE_TouchStart) {
+ LoadURL();
+ DoTouchScroll();
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/touch_input_browsertest.cc b/content/browser/renderer_host/input/touch_input_browsertest.cc
index 2c68ac7..7ff1dd0 100644
--- a/content/browser/renderer_host/input/touch_input_browsertest.cc
+++ b/content/browser/renderer_host/input/touch_input_browsertest.cc
@@ -19,6 +19,7 @@
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.h"
+#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
@@ -88,54 +89,6 @@ const char kTouchEventDataURL[] =
namespace content {
-class InputEventMessageFilter : public BrowserMessageFilter {
- public:
- InputEventMessageFilter()
- : BrowserMessageFilter(InputMsgStart),
- type_(WebInputEvent::Undefined),
- state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
-
- void WaitForAck(WebInputEvent::Type type) {
- base::RunLoop run_loop;
- base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure());
- base::AutoReset<WebInputEvent::Type> reset_type(&type_, type);
- run_loop.Run();
- }
-
- InputEventAckState last_ack_state() const { return state_; }
-
- protected:
- ~InputEventMessageFilter() override {}
-
- private:
- void ReceivedEventAck(WebInputEvent::Type type, InputEventAckState state) {
- if (type_ == type) {
- state_ = state;
- quit_.Run();
- }
- }
-
- // BrowserMessageFilter:
- bool OnMessageReceived(const IPC::Message& message) override {
- if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) {
- InputHostMsg_HandleInputEvent_ACK::Param params;
- InputHostMsg_HandleInputEvent_ACK::Read(&message, &params);
- WebInputEvent::Type type = base::get<0>(params).type;
- InputEventAckState ack = base::get<0>(params).state;
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&InputEventMessageFilter::ReceivedEventAck,
- this, type, ack));
- }
- return false;
- }
-
- base::Closure quit_;
- WebInputEvent::Type type_;
- InputEventAckState state_;
-
- DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilter);
-};
-
class TouchInputBrowserTest : public ContentBrowserTest {
public:
TouchInputBrowserTest() {}
@@ -146,33 +99,27 @@ class TouchInputBrowserTest : public ContentBrowserTest {
shell()->web_contents()->GetRenderViewHost()->GetWidget());
}
- InputEventMessageFilter* filter() { return filter_.get(); }
+ scoped_refptr<InputMsgWatcher> AddFilter(blink::WebInputEvent::Type type) {
+ return new InputMsgWatcher(GetWidgetHost(), type);
+ }
protected:
- void LoadURLAndAddFilter() {
+ void LoadURL() {
const GURL data_url(kTouchEventDataURL);
NavigateToURL(shell(), data_url);
- WebContentsImpl* web_contents =
- static_cast<WebContentsImpl*>(shell()->web_contents());
- RenderWidgetHostImpl* host = RenderWidgetHostImpl::From(
- web_contents->GetRenderViewHost()->GetWidget());
+ RenderWidgetHostImpl* host = GetWidgetHost();
host->GetView()->SetSize(gfx::Size(400, 400));
// The page is loaded in the renderer, wait for a new frame to arrive.
while (!host->ScheduleComposite())
GiveItSomeTime();
-
- filter_ = new InputEventMessageFilter();
- host->GetProcess()->AddFilter(filter_.get());
}
void SetUpCommandLine(base::CommandLine* cmd) override {
cmd->AppendSwitchASCII(switches::kTouchEvents,
switches::kTouchEventsEnabled);
}
-
- scoped_refptr<InputEventMessageFilter> filter_;
};
#if defined(OS_MACOSX)
@@ -182,17 +129,16 @@ class TouchInputBrowserTest : public ContentBrowserTest {
#define MAYBE_TouchNoHandler TouchNoHandler
#endif
IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
- LoadURLAndAddFilter();
+ LoadURL();
SyntheticWebTouchEvent touch;
// A press on |first| should be acked with NO_CONSUMER_EXISTS since there is
// no touch-handler on it.
touch.PressPoint(25, 25);
+ scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchStart);
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
- filter()->last_ack_state());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, filter->WaitForAck());
// If a touch-press is acked with NO_CONSUMER_EXISTS, then subsequent
// touch-points don't need to be dispatched until the touch point is released.
@@ -208,20 +154,21 @@ IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchNoHandler) {
#define MAYBE_TouchHandlerNoConsume TouchHandlerNoConsume
#endif
IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchHandlerNoConsume) {
- LoadURLAndAddFilter();
+ LoadURL();
SyntheticWebTouchEvent touch;
// Press on |second| should be acked with NOT_CONSUMED since there is a
// touch-handler on |second|, but it doesn't consume the event.
touch.PressPoint(125, 25);
+ scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchStart);
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter->WaitForAck());
+ filter = AddFilter(WebInputEvent::TouchEnd);
touch.ReleasePoint(0);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchEnd);
touch.ResetPoints();
+ filter->WaitForAck();
}
#if defined(OS_CHROMEOS)
@@ -231,19 +178,20 @@ IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchHandlerNoConsume) {
#define MAYBE_TouchHandlerConsume TouchHandlerConsume
#endif
IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchHandlerConsume) {
- LoadURLAndAddFilter();
+ LoadURL();
SyntheticWebTouchEvent touch;
// Press on |third| should be acked with CONSUMED since the touch-handler on
// |third| consimes the event.
touch.PressPoint(25, 125);
+ scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchStart);
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter->WaitForAck());
touch.ReleasePoint(0);
+ filter = AddFilter(WebInputEvent::TouchEnd);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchEnd);
+ filter->WaitForAck();
}
#if defined(OS_CHROMEOS)
@@ -256,21 +204,20 @@ IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_TouchHandlerConsume) {
#define MAYBE_MultiPointTouchPress MultiPointTouchPress
#endif
IN_PROC_BROWSER_TEST_F(TouchInputBrowserTest, MAYBE_MultiPointTouchPress) {
- LoadURLAndAddFilter();
+ LoadURL();
SyntheticWebTouchEvent touch;
// Press on |first|, which sould be acked with NO_CONSUMER_EXISTS. Then press
// on |third|. That point should be acked with CONSUMED.
touch.PressPoint(25, 25);
+ scoped_refptr<InputMsgWatcher> filter = AddFilter(WebInputEvent::TouchStart);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchStart);
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
- filter()->last_ack_state());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS, filter->WaitForAck());
touch.PressPoint(25, 125);
+ filter = AddFilter(WebInputEvent::TouchStart);
GetWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo());
- filter()->WaitForAck(WebInputEvent::TouchStart);
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter()->last_ack_state());
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED, filter->WaitForAck());
}
} // namespace content
diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
index 5178d8c..e7a7ace 100644
--- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
+++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm
@@ -1000,7 +1000,9 @@ class RenderWidgetHostViewMacPinchTest : public RenderWidgetHostViewMacTest {
break;
}
DCHECK(message);
- base::Tuple<IPC::WebInputEventPointer, ui::LatencyInfo> data;
+ base::Tuple<IPC::WebInputEventPointer, ui::LatencyInfo,
+ InputEventDispatchType>
+ data;
InputMsg_HandleInputEvent::Read(message, &data);
IPC::WebInputEventPointer ipc_event = base::get<0>(data);
const blink::WebGestureEvent* gesture_event =
diff --git a/content/common/input/input_event_ack_state.h b/content/common/input/input_event_ack_state.h
index 5b9c706..c8e8f83 100644
--- a/content/common/input/input_event_ack_state.h
+++ b/content/common/input/input_event_ack_state.h
@@ -14,7 +14,8 @@ enum InputEventAckState {
INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS,
INPUT_EVENT_ACK_STATE_IGNORED,
- INPUT_EVENT_ACK_STATE_MAX = INPUT_EVENT_ACK_STATE_IGNORED
+ INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING,
+ INPUT_EVENT_ACK_STATE_MAX = INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING
};
} // namespace content
diff --git a/content/common/input/input_event_dispatch_type.h b/content/common/input/input_event_dispatch_type.h
new file mode 100644
index 0000000..e4acb4c
--- /dev/null
+++ b/content/common/input/input_event_dispatch_type.h
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_INPUT_INPUT_EVENT_DISPATCH_TYPE_H_
+#define CONTENT_COMMON_INPUT_INPUT_EVENT_DISPATCH_TYPE_H_
+
+namespace content {
+
+enum InputEventDispatchType {
+ DISPATCH_TYPE_NORMAL, // Dispatch a normal event.
+ DISPATCH_TYPE_NON_BLOCKING, // Dispatch a non-blocking event.
+ DISPATCH_TYPE_MAX = DISPATCH_TYPE_NON_BLOCKING
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_INPUT_INPUT_EVENT_DISPATCH_TYPE_H_
diff --git a/content/common/input/web_input_event_queue.h b/content/common/input/web_input_event_queue.h
new file mode 100644
index 0000000..fa85bd4
--- /dev/null
+++ b/content/common/input/web_input_event_queue.h
@@ -0,0 +1,79 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_COMMON_INPUT_WEB_INPUT_EVENT_QUEUE_H_
+#define CONTENT_COMMON_INPUT_WEB_INPUT_EVENT_QUEUE_H_
+
+#include <deque>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace content {
+
+enum class WebInputEventQueueState { ITEM_PENDING, ITEM_NOT_PENDING };
+
+// WebInputEventQueue is a coalescing queue with the addition of a state
+// variable that represents whether an item is pending to be processed.
+// The desired usage sending with this queue is:
+// if (queue.state() == WebInputEventQueueState::ITEM_PENDING) {
+// queue.Queue(T);
+// } else {
+// send T
+// queue.set_state(WebInputEventQueueState::ITEM_PENDING);
+// }
+//
+// Processing the event response:
+// if (!queue.empty()) {
+// T = queue.Pop();
+// send T now
+// } else {
+// queue.set_state(WebInputEventQueueState::ITEM_NOT_PENDING);
+// }
+//
+template <typename T>
+class WebInputEventQueue {
+ public:
+ WebInputEventQueue() : state_(WebInputEventQueueState::ITEM_NOT_PENDING) {}
+
+ // Adds an event to the queue. The event may be coalesced with previously
+ // queued events.
+ void Queue(const T& event) {
+ if (!queue_.empty()) {
+ scoped_ptr<T>& last_event = queue_.back();
+ if (last_event->CanCoalesceWith(event)) {
+ last_event->CoalesceWith(event);
+ return;
+ }
+ }
+ queue_.emplace_back(scoped_ptr<T>(new T(event)));
+ }
+
+ scoped_ptr<T> Pop() {
+ scoped_ptr<T> result;
+ if (!queue_.empty()) {
+ result.reset(queue_.front().release());
+ queue_.pop_front();
+ }
+ return result;
+ }
+
+ bool empty() const { return queue_.empty(); }
+
+ size_t size() const { return queue_.size(); }
+
+ void set_state(WebInputEventQueueState state) { state_ = state; }
+
+ WebInputEventQueueState state() const WARN_UNUSED_RESULT { return state_; }
+
+ private:
+ typedef std::deque<scoped_ptr<T>> EventQueue;
+ EventQueue queue_;
+ WebInputEventQueueState state_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebInputEventQueue);
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_INPUT_WEB_INPUT_EVENT_QUEUE_H_
diff --git a/content/common/input_messages.h b/content/common/input_messages.h
index 7f05b9f..ed0f5f2 100644
--- a/content/common/input_messages.h
+++ b/content/common/input_messages.h
@@ -15,6 +15,7 @@
#include "content/common/input/input_event.h"
#include "content/common/input/input_event_ack.h"
#include "content/common/input/input_event_ack_state.h"
+#include "content/common/input/input_event_dispatch_type.h"
#include "content/common/input/input_param_traits.h"
#include "content/common/input/synthetic_gesture_packet.h"
#include "content/common/input/synthetic_gesture_params.h"
@@ -48,6 +49,8 @@ IPC_ENUM_TRAITS_MAX_VALUE(
IPC_ENUM_TRAITS_MAX_VALUE(
content::SyntheticGestureParams::GestureType,
content::SyntheticGestureParams::SYNTHETIC_GESTURE_TYPE_MAX)
+IPC_ENUM_TRAITS_MAX_VALUE(content::InputEventDispatchType,
+ content::InputEventDispatchType::DISPATCH_TYPE_MAX)
IPC_ENUM_TRAITS_VALIDATE(content::TouchAction, (
value >= 0 &&
value <= content::TOUCH_ACTION_MAX &&
@@ -114,9 +117,10 @@ IPC_STRUCT_TRAITS_BEGIN(content::InputEventAck)
IPC_STRUCT_TRAITS_END()
// Sends an input event to the render widget.
-IPC_MESSAGE_ROUTED2(InputMsg_HandleInputEvent,
+IPC_MESSAGE_ROUTED3(InputMsg_HandleInputEvent,
IPC::WebInputEventPointer /* event */,
- ui::LatencyInfo /* latency_info */)
+ ui::LatencyInfo /* latency_info */,
+ content::InputEventDispatchType)
// Sends the cursor visibility state to the render widget.
IPC_MESSAGE_ROUTED1(InputMsg_CursorVisibilityChange,
diff --git a/content/content_common.gypi b/content/content_common.gypi
index 482e46e..f781de3 100644
--- a/content/content_common.gypi
+++ b/content/content_common.gypi
@@ -391,6 +391,7 @@
'common/input/input_event.h',
'common/input/input_event_ack.cc',
'common/input/input_event_ack.h',
+ 'common/input/input_event_dispatch_type.h',
'common/input/input_event_stream_validator.cc',
'common/input/input_event_stream_validator.h',
'common/input/input_event_utils.cc',
@@ -415,6 +416,7 @@
'common/input/synthetic_web_input_event_builders.h',
'common/input/touch_event_stream_validator.cc',
'common/input/touch_event_stream_validator.h',
+ 'common/input/web_input_event_queue.h',
'common/input/web_input_event_traits.cc',
'common/input/web_input_event_traits.h',
'common/input/web_touch_event_traits.cc',
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi
index 0592cf2..338c04a 100644
--- a/content/content_renderer.gypi
+++ b/content/content_renderer.gypi
@@ -250,6 +250,8 @@
'renderer/input/input_handler_wrapper.h',
'renderer/input/main_thread_input_event_filter.cc',
'renderer/input/main_thread_input_event_filter.h',
+ 'renderer/input/non_blocking_event_queue.cc',
+ 'renderer/input/non_blocking_event_queue.h',
'renderer/input/render_widget_input_handler.cc',
'renderer/input/render_widget_input_handler.h',
'renderer/input/render_widget_input_handler_delegate.h',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 5e03b22..5e56ecd 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -243,6 +243,7 @@
'browser/mojo_shell_browsertest.cc',
'browser/net_info_browsertest.cc',
'browser/renderer_host/input/composited_scrolling_browsertest.cc',
+ 'browser/renderer_host/input/non_blocking_event_browsertest.cc',
'browser/renderer_host/input/touch_action_browsertest.cc',
'browser/renderer_host/input/touch_input_browsertest.cc',
'browser/renderer_host/input/touch_selection_controller_client_aura_browsertest.cc',
@@ -709,6 +710,7 @@
'renderer/gpu/render_widget_compositor_unittest.cc',
'renderer/ico_image_decoder_unittest.cc',
'renderer/input/input_event_filter_unittest.cc',
+ 'renderer/input/non_blocking_event_queue_unittest.cc',
'renderer/manifest/manifest_parser_unittest.cc',
'renderer/media/android/media_info_loader_unittest.cc',
'renderer/media/audio_message_filter_unittest.cc',
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index be9b2e0..51ac846 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -25,6 +25,7 @@
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/web_contents/web_contents_view.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
+#include "content/common/input_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/histogram_fetcher.h"
@@ -1073,16 +1074,24 @@ FrameWatcher::FrameWatcher()
FrameWatcher::~FrameWatcher() {
}
-void FrameWatcher::ReceivedFrameSwap() {
+void FrameWatcher::ReceivedFrameSwap(cc::CompositorFrameMetadata metadata) {
--frames_to_wait_;
+ last_metadata_ = metadata;
if (frames_to_wait_ == 0)
quit_.Run();
}
bool FrameWatcher::OnMessageReceived(const IPC::Message& message) {
if (message.type() == ViewHostMsg_SwapCompositorFrame::ID) {
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- base::Bind(&FrameWatcher::ReceivedFrameSwap, this));
+ ViewHostMsg_SwapCompositorFrame::Param param;
+ if (!ViewHostMsg_SwapCompositorFrame::Read(&message, &param))
+ return false;
+ scoped_ptr<cc::CompositorFrame> frame(new cc::CompositorFrame);
+ base::get<1>(param).AssignTo(frame.get());
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&FrameWatcher::ReceivedFrameSwap, this, frame->metadata));
}
return false;
}
@@ -1103,6 +1112,10 @@ void FrameWatcher::WaitFrames(int frames_to_wait) {
run_loop.Run();
}
+const cc::CompositorFrameMetadata& FrameWatcher::LastMetadata() {
+ return last_metadata_;
+}
+
MainThreadFrameObserver::MainThreadFrameObserver(
RenderWidgetHost* render_widget_host)
: render_widget_host_(render_widget_host),
@@ -1140,4 +1153,48 @@ bool MainThreadFrameObserver::OnMessageReceived(const IPC::Message& msg) {
return true;
}
+InputMsgWatcher::InputMsgWatcher(RenderWidgetHost* render_widget_host,
+ blink::WebInputEvent::Type type)
+ : BrowserMessageFilter(InputMsgStart),
+ wait_for_type_(type),
+ ack_result_(INPUT_EVENT_ACK_STATE_UNKNOWN) {
+ render_widget_host->GetProcess()->AddFilter(this);
+}
+
+InputMsgWatcher::~InputMsgWatcher() {}
+
+void InputMsgWatcher::ReceivedAck(blink::WebInputEvent::Type ack_type,
+ uint32_t ack_state) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (wait_for_type_ == ack_type) {
+ ack_result_ = ack_state;
+ if (!quit_.is_null())
+ quit_.Run();
+ }
+}
+
+bool InputMsgWatcher::OnMessageReceived(const IPC::Message& message) {
+ DCHECK_CURRENTLY_ON(BrowserThread::IO);
+ if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) {
+ InputHostMsg_HandleInputEvent_ACK::Param params;
+ InputHostMsg_HandleInputEvent_ACK::Read(&message, &params);
+ blink::WebInputEvent::Type ack_type = base::get<0>(params).type;
+ InputEventAckState ack_state = base::get<0>(params).state;
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&InputMsgWatcher::ReceivedAck, this, ack_type, ack_state));
+ }
+ return false;
+}
+
+uint32_t InputMsgWatcher::WaitForAck() {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (ack_result_ != INPUT_EVENT_ACK_STATE_UNKNOWN)
+ return ack_result_;
+ base::RunLoop run_loop;
+ base::AutoReset<base::Closure> reset_quit(&quit_, run_loop.QuitClosure());
+ run_loop.Run();
+ return ack_result_;
+}
+
} // namespace content
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index ed6aa65..1793726 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -17,6 +17,7 @@
#include "base/process/process.h"
#include "base/strings/string16.h"
#include "build/build_config.h"
+#include "cc/output/compositor_frame.h"
#include "content/public/browser/browser_message_filter.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
@@ -425,16 +426,21 @@ class FrameWatcher : public BrowserMessageFilter {
// Wait for |frames_to_wait| swap mesages from the compositor.
void WaitFrames(int frames_to_wait);
+ // Return the meta data received in the last compositor
+ // swap frame.
+ const cc::CompositorFrameMetadata& LastMetadata();
+
private:
~FrameWatcher() override;
// Overridden BrowserMessageFilter methods.
bool OnMessageReceived(const IPC::Message& message) override;
- void ReceivedFrameSwap();
+ void ReceivedFrameSwap(cc::CompositorFrameMetadata meta_data);
int frames_to_wait_;
base::Closure quit_;
+ cc::CompositorFrameMetadata last_metadata_;
DISALLOW_COPY_AND_ASSIGN(FrameWatcher);
};
@@ -463,6 +469,31 @@ class MainThreadFrameObserver : public IPC::Listener {
DISALLOW_COPY_AND_ASSIGN(MainThreadFrameObserver);
};
+// Watches for an input msg to be consumed.
+class InputMsgWatcher : public BrowserMessageFilter {
+ public:
+ InputMsgWatcher(RenderWidgetHost* render_widget_host,
+ blink::WebInputEvent::Type type);
+
+ // Wait until ack message occurs, returning the ack result from
+ // the message.
+ uint32_t WaitForAck();
+
+ private:
+ ~InputMsgWatcher() override;
+
+ // Overridden BrowserMessageFilter methods.
+ bool OnMessageReceived(const IPC::Message& message) override;
+
+ void ReceivedAck(blink::WebInputEvent::Type ack_type, uint32_t ack_state);
+
+ blink::WebInputEvent::Type wait_for_type_;
+ uint32_t ack_result_;
+ base::Closure quit_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputMsgWatcher);
+};
+
} // namespace content
#endif // CONTENT_PUBLIC_TEST_BROWSER_TEST_UTILS_H_
diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc
index 2706d71..c622ee8 100644
--- a/content/public/test/render_view_test.cc
+++ b/content/public/test/render_view_test.cc
@@ -441,14 +441,16 @@ void RenderViewTest::SendWebKeyboardEvent(
const blink::WebKeyboardEvent& key_event) {
RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &key_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &key_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
}
void RenderViewTest::SendWebMouseEvent(
const blink::WebMouseEvent& mouse_event) {
RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
}
const char* const kGetCoordinatesScript =
@@ -515,10 +517,12 @@ void RenderViewTest::SimulatePointClick(const gfx::Point& point) {
mouse_event.clickCount = 1;
RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
mouse_event.type = WebInputEvent::MouseUp;
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
}
@@ -539,10 +543,12 @@ void RenderViewTest::SimulatePointRightClick(const gfx::Point& point) {
mouse_event.clickCount = 1;
RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
mouse_event.type = WebInputEvent::MouseUp;
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &mouse_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
}
void RenderViewTest::SimulateRectTap(const gfx::Rect& rect) {
@@ -556,7 +562,8 @@ void RenderViewTest::SimulateRectTap(const gfx::Rect& rect) {
gesture_event.sourceDevice = blink::WebGestureDeviceTouchpad;
RenderViewImpl* impl = static_cast<RenderViewImpl*>(view_);
impl->OnMessageReceived(
- InputMsg_HandleInputEvent(0, &gesture_event, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(0, &gesture_event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
impl->FocusChangeComplete();
}
diff --git a/content/renderer/android/synchronous_compositor_filter.cc b/content/renderer/android/synchronous_compositor_filter.cc
index 5f4c9d2..54dfed3 100644
--- a/content/renderer/android/synchronous_compositor_filter.cc
+++ b/content/renderer/android/synchronous_compositor_filter.cc
@@ -255,6 +255,10 @@ void SynchronousCompositorFilter::DidStopFlinging(int routing_id) {
Send(new InputHostMsg_DidStopFlinging(routing_id));
}
+void SynchronousCompositorFilter::NonBlockingInputEventHandled(
+ int routing_id,
+ blink::WebInputEvent::Type type) {}
+
SynchronousCompositorFilter::Entry::Entry()
: begin_frame_source(nullptr),
output_surface(nullptr),
diff --git a/content/renderer/android/synchronous_compositor_filter.h b/content/renderer/android/synchronous_compositor_filter.h
index 89d8300..bfab1f6 100644
--- a/content/renderer/android/synchronous_compositor_filter.h
+++ b/content/renderer/android/synchronous_compositor_filter.h
@@ -66,6 +66,8 @@ class SynchronousCompositorFilter : public IPC::MessageFilter,
void DidOverscroll(int routing_id,
const DidOverscrollParams& params) override;
void DidStopFlinging(int routing_id) override;
+ void NonBlockingInputEventHandled(int routing_id,
+ blink::WebInputEvent::Type type) override;
private:
~SynchronousCompositorFilter() override;
diff --git a/content/renderer/idle_user_detector.cc b/content/renderer/idle_user_detector.cc
index 339f68a..9ca3570 100644
--- a/content/renderer/idle_user_detector.cc
+++ b/content/renderer/idle_user_detector.cc
@@ -25,8 +25,10 @@ bool IdleUserDetector::OnMessageReceived(const IPC::Message& message) {
return false;
}
-void IdleUserDetector::OnHandleInputEvent(const blink::WebInputEvent* event,
- const ui::LatencyInfo& latency_info) {
+void IdleUserDetector::OnHandleInputEvent(
+ const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency_info,
+ InputEventDispatchType dispatch_type) {
if (GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) {
RenderThreadImpl* render_thread = RenderThreadImpl::current();
if (render_thread != NULL) {
diff --git a/content/renderer/idle_user_detector.h b/content/renderer/idle_user_detector.h
index 3e3beba..cbe85a0 100644
--- a/content/renderer/idle_user_detector.h
+++ b/content/renderer/idle_user_detector.h
@@ -6,6 +6,7 @@
#define CONTENT_RENDERER_IDLE_USER_DETECTOR_H_
#include "base/macros.h"
+#include "content/common/input/input_event_dispatch_type.h"
#include "content/public/renderer/render_view_observer.h"
namespace blink {
@@ -30,7 +31,8 @@ class IdleUserDetector : public RenderViewObserver {
bool OnMessageReceived(const IPC::Message& message) override;
void OnHandleInputEvent(const blink::WebInputEvent* event,
- const ui::LatencyInfo& latency_info);
+ const ui::LatencyInfo& latency_info,
+ InputEventDispatchType dispatch_type);
DISALLOW_COPY_AND_ASSIGN(IdleUserDetector);
};
diff --git a/content/renderer/input/input_event_filter.cc b/content/renderer/input/input_event_filter.cc
index 927a402..9a90141 100644
--- a/content/renderer/input/input_event_filter.cc
+++ b/content/renderer/input/input_event_filter.cc
@@ -65,11 +65,13 @@ void InputEventFilter::DidAddInputHandler(
synchronous_input_handler_proxy) {
base::AutoLock locked(routes_lock_);
routes_.insert(routing_id);
+ route_queues_[routing_id].reset(new NonBlockingEventQueue(routing_id, this));
}
void InputEventFilter::DidRemoveInputHandler(int routing_id) {
base::AutoLock locked(routes_lock_);
routes_.erase(routing_id);
+ route_queues_.erase(routing_id);
}
void InputEventFilter::DidOverscroll(int routing_id,
@@ -87,6 +89,17 @@ void InputEventFilter::DidStopFlinging(int routing_id) {
SendMessage(make_scoped_ptr(new InputHostMsg_DidStopFlinging(routing_id)));
}
+void InputEventFilter::NonBlockingInputEventHandled(
+ int routing_id,
+ blink::WebInputEvent::Type type) {
+ DCHECK(target_task_runner_->BelongsToCurrentThread());
+ RouteQueueMap::iterator iter = route_queues_.find(routing_id);
+ if (iter == route_queues_.end() || !iter->second)
+ return;
+
+ iter->second->EventHandled(type);
+}
+
void InputEventFilter::OnFilterAdded(IPC::Sender* sender) {
io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
sender_ = sender;
@@ -152,9 +165,11 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) {
return;
const WebInputEvent* event = base::get<0>(params);
ui::LatencyInfo latency_info = base::get<1>(params);
+ InputEventDispatchType dispatch_type = base::get<2>(params);
DCHECK(event);
+ DCHECK_EQ(DISPATCH_TYPE_NORMAL, dispatch_type);
- const bool send_ack = WebInputEventTraits::WillReceiveAckFromRenderer(*event);
+ bool send_ack = WebInputEventTraits::WillReceiveAckFromRenderer(*event);
// Intercept |DidOverscroll| notifications, bundling any triggered overscroll
// response with the input event ack.
@@ -165,16 +180,21 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) {
InputEventAckState ack_state = handler_.Run(routing_id, event, &latency_info);
- if (ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) {
+ if (ack_state == INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING) {
+ DCHECK(!overscroll_params);
+ RouteQueueMap::iterator iter = route_queues_.find(routing_id);
+ if (iter != route_queues_.end())
+ iter->second->HandleEvent(event, latency_info);
+ } else if (ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) {
DCHECK(!overscroll_params);
TRACE_EVENT_INSTANT0(
- "input",
- "InputEventFilter::ForwardToHandler::ForwardToMainListener",
+ "input", "InputEventFilter::ForwardToHandler::ForwardToMainListener",
TRACE_EVENT_SCOPE_THREAD);
IPC::Message new_msg =
- InputMsg_HandleInputEvent(routing_id, event, latency_info);
+ InputMsg_HandleInputEvent(routing_id, event, latency_info,
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL);
main_task_runner_->PostTask(FROM_HERE, base::Bind(main_listener_, new_msg));
- return;
+ send_ack = false;
}
if (!send_ack)
@@ -204,4 +224,15 @@ void InputEventFilter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
sender_->Send(message.release());
}
+void InputEventFilter::SendNonBlockingEvent(int routing_id,
+ const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency) {
+ TRACE_EVENT_INSTANT0("input", "InputEventFilter::SendNonBlockingEvent",
+ TRACE_EVENT_SCOPE_THREAD);
+ IPC::Message new_msg = InputMsg_HandleInputEvent(
+ routing_id, event, latency,
+ InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING);
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(main_listener_, new_msg));
+}
+
} // namespace content
diff --git a/content/renderer/input/input_event_filter.h b/content/renderer/input/input_event_filter.h
index e2d18f3..71d22f7 100644
--- a/content/renderer/input/input_event_filter.h
+++ b/content/renderer/input/input_event_filter.h
@@ -7,12 +7,14 @@
#include <queue>
#include <set>
+#include <unordered_map>
#include "base/callback.h"
#include "base/synchronization/lock.h"
#include "content/common/content_export.h"
#include "content/common/input/input_event_ack_state.h"
#include "content/renderer/input/input_handler_manager_client.h"
+#include "content/renderer/input/non_blocking_event_queue.h"
#include "ipc/message_filter.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
@@ -40,7 +42,8 @@ class Sender;
namespace content {
class CONTENT_EXPORT InputEventFilter : public InputHandlerManagerClient,
- public IPC::MessageFilter {
+ public IPC::MessageFilter,
+ public NonBlockingEventQueueClient {
public:
InputEventFilter(
const base::Callback<void(const IPC::Message&)>& main_listener,
@@ -66,6 +69,8 @@ class CONTENT_EXPORT InputEventFilter : public InputHandlerManagerClient,
void DidOverscroll(int routing_id,
const DidOverscrollParams& params) override;
void DidStopFlinging(int routing_id) override;
+ void NonBlockingInputEventHandled(int routing_id,
+ blink::WebInputEvent::Type type) override;
// IPC::MessageFilter methods:
void OnFilterAdded(IPC::Sender* sender) override;
@@ -73,6 +78,11 @@ class CONTENT_EXPORT InputEventFilter : public InputHandlerManagerClient,
void OnChannelClosing() override;
bool OnMessageReceived(const IPC::Message& message) override;
+ // NonBlockingEventQueueClient methods:
+ void SendNonBlockingEvent(int routing_id,
+ const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency) override;
+
private:
~InputEventFilter() override;
@@ -97,6 +107,10 @@ class CONTENT_EXPORT InputEventFilter : public InputHandlerManagerClient,
// Indicates the routing_ids for which input events should be filtered.
std::set<int> routes_;
+ using RouteQueueMap =
+ std::unordered_map<int, scoped_ptr<NonBlockingEventQueue>>;
+ RouteQueueMap route_queues_;
+
// Used to intercept overscroll notifications while an event is being
// dispatched. If the event causes overscroll, the overscroll metadata can be
// bundled in the event ack, saving an IPC. Note that we must continue
diff --git a/content/renderer/input/input_event_filter_unittest.cc b/content/renderer/input/input_event_filter_unittest.cc
index 1aace83..8027752 100644
--- a/content/renderer/input/input_event_filter_unittest.cc
+++ b/content/renderer/input/input_event_filter_unittest.cc
@@ -23,6 +23,8 @@
using blink::WebInputEvent;
using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
namespace content {
namespace {
@@ -34,12 +36,13 @@ class InputEventRecorder {
InputEventRecorder()
: filter_(NULL),
handle_events_(false),
- send_to_widget_(false) {
- }
+ send_to_widget_(false),
+ passive_(false) {}
void set_filter(InputEventFilter* filter) { filter_ = filter; }
void set_handle_events(bool value) { handle_events_ = value; }
void set_send_to_widget(bool value) { send_to_widget_ = value; }
+ void set_passive(bool value) { passive_ = value; }
size_t record_count() const { return records_.size(); }
@@ -61,9 +64,13 @@ class InputEventRecorder {
if (handle_events_) {
return INPUT_EVENT_ACK_STATE_CONSUMED;
+ } else if (send_to_widget_) {
+ if (passive_)
+ return INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING;
+ else
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
} else {
- return send_to_widget_ ? INPUT_EVENT_ACK_STATE_NOT_CONSUMED
- : INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+ return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
}
}
@@ -79,6 +86,7 @@ class InputEventRecorder {
InputEventFilter* filter_;
bool handle_events_;
bool send_to_widget_;
+ bool passive_;
std::vector<Record> records_;
};
@@ -112,13 +120,15 @@ void AddMessagesToFilter(IPC::MessageFilter* message_filter,
base::MessageLoop::current()->RunUntilIdle();
}
+template <typename T>
void AddEventsToFilter(IPC::MessageFilter* message_filter,
- const WebMouseEvent events[],
+ const T events[],
size_t count) {
std::vector<IPC::Message> messages;
for (size_t i = 0; i < count; ++i) {
- messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID, &events[i],
- ui::LatencyInfo()));
+ messages.push_back(InputMsg_HandleInputEvent(
+ kTestRoutingID, &events[i], ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
}
AddMessagesToFilter(message_filter, messages);
@@ -254,8 +264,9 @@ TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseUp);
std::vector<IPC::Message> messages;
- messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID, &mouse_down,
- ui::LatencyInfo()));
+ messages.push_back(
+ InputMsg_HandleInputEvent(kTestRoutingID, &mouse_down, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
// Control where input events are delivered.
messages.push_back(InputMsg_MouseCaptureLost(kTestRoutingID));
messages.push_back(InputMsg_SetFocus(kTestRoutingID, true));
@@ -282,7 +293,8 @@ TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
messages.push_back(InputMsg_MoveCaret(kTestRoutingID, gfx::Point()));
messages.push_back(
- InputMsg_HandleInputEvent(kTestRoutingID, &mouse_up, ui::LatencyInfo()));
+ InputMsg_HandleInputEvent(kTestRoutingID, &mouse_up, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL));
AddMessagesToFilter(filter_.get(), messages);
// We should have sent all messages back to the main thread and preserved
@@ -293,4 +305,156 @@ TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
}
}
+TEST_F(InputEventFilterTest, NonBlockingWheel) {
+ WebMouseWheelEvent kEvents[4] = {
+ SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false),
+ SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false),
+ SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false),
+ SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false),
+ };
+
+ filter_->DidAddInputHandler(kTestRoutingID, nullptr);
+ event_recorder_.set_send_to_widget(true);
+ event_recorder_.set_passive(true);
+
+ AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
+ EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count());
+ ASSERT_EQ(4u, ipc_sink_.message_count());
+
+ // First event is sent right away.
+ EXPECT_EQ(1u, message_recorder_.message_count());
+
+ // Second event was queued; ack the first.
+ filter_->NonBlockingInputEventHandled(kTestRoutingID,
+ WebInputEvent::MouseWheel);
+ base::MessageLoop::current()->RunUntilIdle();
+ ASSERT_EQ(4u, ipc_sink_.message_count());
+ EXPECT_EQ(2u, message_recorder_.message_count());
+
+ // Third event won't be coalesced into the second because modifiers are
+ // different.
+ filter_->NonBlockingInputEventHandled(kTestRoutingID,
+ WebInputEvent::MouseWheel);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(3u, message_recorder_.message_count());
+
+ // The last events will be coalesced.
+ filter_->NonBlockingInputEventHandled(kTestRoutingID,
+ WebInputEvent::MouseWheel);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(3u, message_recorder_.message_count());
+
+ // First two messages should be identical.
+ for (size_t i = 0; i < 2; ++i) {
+ const IPC::Message& message = message_recorder_.message_at(i);
+
+ ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
+ InputMsg_HandleInputEvent::Param params;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, &params));
+ const WebInputEvent* event = base::get<0>(params);
+ InputEventDispatchType dispatch_type = base::get<2>(params);
+
+ EXPECT_EQ(kEvents[i].size, event->size);
+ EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
+ EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING,
+ dispatch_type);
+ }
+
+ // Third message is coalesced.
+ {
+ const IPC::Message& message = message_recorder_.message_at(2);
+
+ ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
+ InputMsg_HandleInputEvent::Param params;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, &params));
+ const WebMouseWheelEvent* event =
+ static_cast<const WebMouseWheelEvent*>(base::get<0>(params));
+ InputEventDispatchType dispatch_type = base::get<2>(params);
+
+ EXPECT_EQ(kEvents[2].size, event->size);
+ EXPECT_EQ(kEvents[2].deltaX + kEvents[3].deltaX, event->deltaX);
+ EXPECT_EQ(kEvents[2].deltaY + kEvents[3].deltaY, event->deltaY);
+ EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING,
+ dispatch_type);
+ }
+}
+
+TEST_F(InputEventFilterTest, NonBlockingTouch) {
+ SyntheticWebTouchEvent kEvents[4];
+ kEvents[0].PressPoint(10, 10);
+ kEvents[1].PressPoint(10, 10);
+ kEvents[1].modifiers = 1;
+ kEvents[1].MovePoint(0, 20, 20);
+ kEvents[2].PressPoint(10, 10);
+ kEvents[2].MovePoint(0, 30, 30);
+ kEvents[3].PressPoint(10, 10);
+ kEvents[3].MovePoint(0, 35, 35);
+
+ filter_->DidAddInputHandler(kTestRoutingID, nullptr);
+ event_recorder_.set_send_to_widget(true);
+ event_recorder_.set_passive(true);
+
+ AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
+ EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count());
+ ASSERT_EQ(4u, ipc_sink_.message_count());
+
+ // First event is sent right away.
+ EXPECT_EQ(1u, message_recorder_.message_count());
+
+ // Second event was queued; ack the first.
+ filter_->NonBlockingInputEventHandled(kTestRoutingID,
+ WebInputEvent::TouchStart);
+ base::MessageLoop::current()->RunUntilIdle();
+ ASSERT_EQ(4u, ipc_sink_.message_count());
+ EXPECT_EQ(2u, message_recorder_.message_count());
+
+ // Third event won't be coalesced into the second because modifiers are
+ // different.
+ filter_->NonBlockingInputEventHandled(kTestRoutingID,
+ WebInputEvent::TouchMove);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(3u, message_recorder_.message_count());
+
+ // The last events will be coalesced.
+ filter_->NonBlockingInputEventHandled(kTestRoutingID,
+ WebInputEvent::TouchMove);
+ base::MessageLoop::current()->RunUntilIdle();
+ EXPECT_EQ(3u, message_recorder_.message_count());
+
+ // First two messages should be identical.
+ for (size_t i = 0; i < 2; ++i) {
+ const IPC::Message& message = message_recorder_.message_at(i);
+
+ ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
+ InputMsg_HandleInputEvent::Param params;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, &params));
+ const WebInputEvent* event = base::get<0>(params);
+ InputEventDispatchType dispatch_type = base::get<2>(params);
+
+ EXPECT_EQ(kEvents[i].size, event->size);
+ EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
+ EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING,
+ dispatch_type);
+ }
+
+ // Third message is coalesced.
+ {
+ const IPC::Message& message = message_recorder_.message_at(2);
+
+ ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
+ InputMsg_HandleInputEvent::Param params;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, &params));
+ const WebTouchEvent* event =
+ static_cast<const WebTouchEvent*>(base::get<0>(params));
+ InputEventDispatchType dispatch_type = base::get<2>(params);
+
+ EXPECT_EQ(kEvents[3].size, event->size);
+ EXPECT_EQ(1u, kEvents[3].touchesLength);
+ EXPECT_EQ(kEvents[3].touches[0].position.x, event->touches[0].position.x);
+ EXPECT_EQ(kEvents[3].touches[0].position.y, event->touches[0].position.y);
+ EXPECT_EQ(InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING,
+ dispatch_type);
+ }
+}
+
} // namespace content
diff --git a/content/renderer/input/input_handler_manager.cc b/content/renderer/input/input_handler_manager.cc
index 35ef3b4..58fd95f 100644
--- a/content/renderer/input/input_handler_manager.cc
+++ b/content/renderer/input/input_handler_manager.cc
@@ -36,6 +36,8 @@ InputEventAckState InputEventDispositionToAck(
return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
case InputHandlerProxy::DROP_EVENT:
return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+ case InputHandlerProxy::DID_HANDLE_NON_BLOCKING:
+ return INPUT_EVENT_ACK_STATE_SET_NON_BLOCKING;
}
NOTREACHED();
return INPUT_EVENT_ACK_STATE_UNKNOWN;
@@ -63,20 +65,20 @@ void InputHandlerManager::AddInputHandler(
int routing_id,
const base::WeakPtr<cc::InputHandler>& input_handler,
const base::WeakPtr<RenderViewImpl>& render_view_impl,
- bool enable_smooth_scrolling) {
+ bool enable_smooth_scrolling,
+ bool enable_wheel_gestures) {
if (task_runner_->BelongsToCurrentThread()) {
- AddInputHandlerOnCompositorThread(routing_id,
- base::ThreadTaskRunnerHandle::Get(),
- input_handler, render_view_impl,
- enable_smooth_scrolling);
+ AddInputHandlerOnCompositorThread(
+ routing_id, base::ThreadTaskRunnerHandle::Get(), input_handler,
+ render_view_impl, enable_smooth_scrolling, enable_wheel_gestures);
} else {
task_runner_->PostTask(
FROM_HERE,
base::Bind(&InputHandlerManager::AddInputHandlerOnCompositorThread,
base::Unretained(this), routing_id,
base::ThreadTaskRunnerHandle::Get(), input_handler,
- render_view_impl,
- enable_smooth_scrolling));
+ render_view_impl, enable_smooth_scrolling,
+ enable_wheel_gestures));
}
}
@@ -85,7 +87,8 @@ void InputHandlerManager::AddInputHandlerOnCompositorThread(
const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
const base::WeakPtr<cc::InputHandler>& input_handler,
const base::WeakPtr<RenderViewImpl>& render_view_impl,
- bool enable_smooth_scrolling) {
+ bool enable_smooth_scrolling,
+ bool enable_wheel_gestures) {
DCHECK(task_runner_->BelongsToCurrentThread());
// The handler could be gone by this point if the compositor has shut down.
@@ -101,7 +104,7 @@ void InputHandlerManager::AddInputHandlerOnCompositorThread(
"result", "AddingRoute");
scoped_ptr<InputHandlerWrapper> wrapper(new InputHandlerWrapper(
this, routing_id, main_task_runner, input_handler, render_view_impl,
- enable_smooth_scrolling));
+ enable_smooth_scrolling, enable_wheel_gestures));
client_->DidAddInputHandler(routing_id, wrapper->input_handler_proxy());
input_handlers_.add(routing_id, std::move(wrapper));
}
@@ -141,6 +144,27 @@ void InputHandlerManager::ObserveWheelEventAndResultOnCompositorThread(
wheel_event, scroll_result);
}
+void InputHandlerManager::NonBlockingInputEventHandledOnMainThread(
+ int routing_id,
+ blink::WebInputEvent::Type type) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &InputHandlerManager::NonBlockingInputEventHandledOnCompositorThread,
+ base::Unretained(this), routing_id, type));
+}
+
+void InputHandlerManager::NonBlockingInputEventHandledOnCompositorThread(
+ int routing_id,
+ blink::WebInputEvent::Type handled_type) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ auto it = input_handlers_.find(routing_id);
+ if (it == input_handlers_.end())
+ return;
+
+ client_->NonBlockingInputEventHandled(routing_id, handled_type);
+}
+
InputEventAckState InputHandlerManager::HandleInputEvent(
int routing_id,
const WebInputEvent* input_event,
diff --git a/content/renderer/input/input_handler_manager.h b/content/renderer/input/input_handler_manager.h
index 6905a88..c8bfb75 100644
--- a/content/renderer/input/input_handler_manager.h
+++ b/content/renderer/input/input_handler_manager.h
@@ -50,17 +50,20 @@ class InputHandlerManager {
~InputHandlerManager();
// Callable from the main thread only.
- void AddInputHandler(
- int routing_id,
- const base::WeakPtr<cc::InputHandler>& input_handler,
- const base::WeakPtr<RenderViewImpl>& render_view_impl,
- bool enable_smooth_scrolling);
+ void AddInputHandler(int routing_id,
+ const base::WeakPtr<cc::InputHandler>& input_handler,
+ const base::WeakPtr<RenderViewImpl>& render_view_impl,
+ bool enable_smooth_scrolling,
+ bool enable_wheel_gestures);
void ObserveWheelEventAndResultOnMainThread(
int routing_id,
const blink::WebMouseWheelEvent& wheel_event,
const cc::InputHandlerScrollResult& scroll_result);
+ void NonBlockingInputEventHandledOnMainThread(int routing_id,
+ blink::WebInputEvent::Type);
+
// Callback only from the compositor's thread.
void RemoveInputHandler(int routing_id);
@@ -85,13 +88,18 @@ class InputHandlerManager {
const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
const base::WeakPtr<cc::InputHandler>& input_handler,
const base::WeakPtr<RenderViewImpl>& render_view_impl,
- bool enable_smooth_scrolling);
+ bool enable_smooth_scrolling,
+ bool enable_wheel_gestures);
void ObserveWheelEventAndResultOnCompositorThread(
int routing_id,
const blink::WebMouseWheelEvent& wheel_event,
const cc::InputHandlerScrollResult& scroll_result);
+ void NonBlockingInputEventHandledOnCompositorThread(
+ int routing_id,
+ blink::WebInputEvent::Type);
+
typedef base::ScopedPtrHashMap<int, // routing_id
scoped_ptr<InputHandlerWrapper>>
InputHandlerMap;
diff --git a/content/renderer/input/input_handler_manager_client.h b/content/renderer/input/input_handler_manager_client.h
index cf4fe00..c794bd2 100644
--- a/content/renderer/input/input_handler_manager_client.h
+++ b/content/renderer/input/input_handler_manager_client.h
@@ -10,6 +10,7 @@
#include "base/macros.h"
#include "content/common/content_export.h"
#include "content/common/input/input_event_ack_state.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/geometry/vector2d_f.h"
namespace ui {
@@ -20,10 +21,6 @@ namespace cc {
class InputHandler;
}
-namespace blink {
-class WebInputEvent;
-}
-
namespace ui {
class SynchronousInputHandlerProxy;
}
@@ -54,6 +51,9 @@ class CONTENT_EXPORT InputHandlerManagerClient {
virtual void DidOverscroll(int routing_id,
const DidOverscrollParams& params) = 0;
virtual void DidStopFlinging(int routing_id) = 0;
+ virtual void NonBlockingInputEventHandled(
+ int routing_id,
+ blink::WebInputEvent::Type type) = 0;
protected:
InputHandlerManagerClient() {}
diff --git a/content/renderer/input/input_handler_wrapper.cc b/content/renderer/input/input_handler_wrapper.cc
index 6265744..c8280e4 100644
--- a/content/renderer/input/input_handler_wrapper.cc
+++ b/content/renderer/input/input_handler_wrapper.cc
@@ -20,7 +20,8 @@ InputHandlerWrapper::InputHandlerWrapper(
const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
const base::WeakPtr<cc::InputHandler>& input_handler,
const base::WeakPtr<RenderViewImpl>& render_view_impl,
- bool enable_smooth_scrolling)
+ bool enable_smooth_scrolling,
+ bool enable_wheel_gestures)
: input_handler_manager_(input_handler_manager),
routing_id_(routing_id),
input_handler_proxy_(input_handler.get(), this),
@@ -28,6 +29,8 @@ InputHandlerWrapper::InputHandlerWrapper(
render_view_impl_(render_view_impl) {
DCHECK(input_handler);
input_handler_proxy_.set_smooth_scroll_enabled(enable_smooth_scrolling);
+ input_handler_proxy_.set_use_gesture_events_for_mouse_wheel(
+ enable_wheel_gestures);
}
InputHandlerWrapper::~InputHandlerWrapper() {
diff --git a/content/renderer/input/input_handler_wrapper.h b/content/renderer/input/input_handler_wrapper.h
index bc864d9..d3781315 100644
--- a/content/renderer/input/input_handler_wrapper.h
+++ b/content/renderer/input/input_handler_wrapper.h
@@ -28,7 +28,8 @@ class InputHandlerWrapper : public ui::InputHandlerProxyClient {
const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
const base::WeakPtr<cc::InputHandler>& input_handler,
const base::WeakPtr<RenderViewImpl>& render_view_impl,
- bool enable_smooth_scrolling);
+ bool enable_smooth_scrolling,
+ bool enable_wheel_gestures);
~InputHandlerWrapper() override;
int routing_id() const { return routing_id_; }
diff --git a/content/renderer/input/non_blocking_event_queue.cc b/content/renderer/input/non_blocking_event_queue.cc
new file mode 100644
index 0000000..4fc5d11
--- /dev/null
+++ b/content/renderer/input/non_blocking_event_queue.cc
@@ -0,0 +1,60 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/input/non_blocking_event_queue.h"
+
+namespace content {
+
+NonBlockingEventQueue::NonBlockingEventQueue(
+ int routing_id,
+ NonBlockingEventQueueClient* client)
+ : routing_id_(routing_id), client_(client) {}
+
+NonBlockingEventQueue::~NonBlockingEventQueue() {}
+
+void NonBlockingEventQueue::HandleEvent(const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency) {
+ if (event->type == blink::WebInputEvent::MouseWheel) {
+ if (wheel_events_.state() == WebInputEventQueueState::ITEM_PENDING) {
+ wheel_events_.Queue(MouseWheelEventWithLatencyInfo(
+ *static_cast<const blink::WebMouseWheelEvent*>(event), latency));
+ } else {
+ wheel_events_.set_state(WebInputEventQueueState::ITEM_PENDING);
+ client_->SendNonBlockingEvent(routing_id_, event, latency);
+ }
+ } else if (blink::WebInputEvent::isTouchEventType(event->type)) {
+ if (touch_events_.state() == WebInputEventQueueState::ITEM_PENDING) {
+ touch_events_.Queue(TouchEventWithLatencyInfo(
+ *static_cast<const blink::WebTouchEvent*>(event), latency));
+ } else {
+ touch_events_.set_state(WebInputEventQueueState::ITEM_PENDING);
+ client_->SendNonBlockingEvent(routing_id_, event, latency);
+ }
+ } else {
+ NOTREACHED() << "Invalid passive event type";
+ }
+}
+
+void NonBlockingEventQueue::EventHandled(blink::WebInputEvent::Type type) {
+ if (type == blink::WebInputEvent::MouseWheel) {
+ if (!wheel_events_.empty()) {
+ scoped_ptr<MouseWheelEventWithLatencyInfo> event = wheel_events_.Pop();
+
+ client_->SendNonBlockingEvent(routing_id_, &event->event, event->latency);
+ } else {
+ wheel_events_.set_state(WebInputEventQueueState::ITEM_NOT_PENDING);
+ }
+ } else if (blink::WebInputEvent::isTouchEventType(type)) {
+ if (!touch_events_.empty()) {
+ scoped_ptr<TouchEventWithLatencyInfo> event = touch_events_.Pop();
+ client_->SendNonBlockingEvent(routing_id_, &event->event, event->latency);
+ } else {
+ touch_events_.set_state(WebInputEventQueueState::ITEM_NOT_PENDING);
+ }
+ } else {
+ NOTREACHED() << "Invalid passive event type";
+ }
+}
+
+} // namespace content
diff --git a/content/renderer/input/non_blocking_event_queue.h b/content/renderer/input/non_blocking_event_queue.h
new file mode 100644
index 0000000..14b99fd
--- /dev/null
+++ b/content/renderer/input/non_blocking_event_queue.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_INPUT_NON_BLOCKING_EVENT_QUEUE_H_
+#define CONTENT_RENDERER_INPUT_NON_BLOCKING_EVENT_QUEUE_H_
+
+#include <deque>
+#include "content/common/content_export.h"
+#include "content/common/input/event_with_latency_info.h"
+#include "content/common/input/web_input_event_queue.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/latency_info.h"
+
+namespace content {
+
+class CONTENT_EXPORT NonBlockingEventQueueClient {
+ public:
+ // Send an |event| that was previously queued (possibly
+ // coalesced with another event) to the |routing_id|'s
+ // channel. Implementors must implement this callback.
+ virtual void SendNonBlockingEvent(int routing_id,
+ const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency) = 0;
+};
+
+// NonBlockingEventQueue implements a series of queues (one touch
+// and one mouse wheel) for events that need to be queued between
+// the compositor and main threads. When a non-blocking event is sent
+// from the compositor to main it can either be sent directly if no
+// outstanding events of that type are in flight; or it needs to
+// wait in a queue until the main thread has finished processing
+// the in-flight event. This class tracks the state and queues
+// for the event types. Methods on this class should only be called
+// from the compositor thread.
+//
+class CONTENT_EXPORT NonBlockingEventQueue {
+ public:
+ NonBlockingEventQueue(int routing_id, NonBlockingEventQueueClient* client);
+ ~NonBlockingEventQueue();
+
+ // Called once compositor has handled |event| and indicated that it is
+ // a non-blocking event to be queued to the main thread.
+ void HandleEvent(const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency);
+
+ // Call once main thread has handled outstanding |type| event in flight.
+ void EventHandled(blink::WebInputEvent::Type type);
+
+ private:
+ friend class NonBlockingEventQueueTest;
+ int routing_id_;
+ NonBlockingEventQueueClient* client_;
+ WebInputEventQueue<MouseWheelEventWithLatencyInfo> wheel_events_;
+ WebInputEventQueue<TouchEventWithLatencyInfo> touch_events_;
+
+ DISALLOW_COPY_AND_ASSIGN(NonBlockingEventQueue);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_INPUT_NON_BLOCKING_EVENT_QUEUE_H_
diff --git a/content/renderer/input/non_blocking_event_queue_unittest.cc b/content/renderer/input/non_blocking_event_queue_unittest.cc
new file mode 100644
index 0000000..8953d85
--- /dev/null
+++ b/content/renderer/input/non_blocking_event_queue_unittest.cc
@@ -0,0 +1,120 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "build/build_config.h"
+#include "content/common/input/synthetic_web_input_event_builders.h"
+#include "content/renderer/input/non_blocking_event_queue.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebTouchEvent;
+
+namespace content {
+namespace {
+
+const int kTestRoutingID = 13;
+}
+
+class NonBlockingEventQueueTest : public testing::Test,
+ public NonBlockingEventQueueClient {
+ public:
+ NonBlockingEventQueueTest() : queue_(kTestRoutingID, this) {}
+
+ void SendNonBlockingEvent(int routing_id,
+ const blink::WebInputEvent* event,
+ const ui::LatencyInfo& latency) override {
+ ASSERT_EQ(kTestRoutingID, routing_id);
+ const unsigned char* eventPtr =
+ reinterpret_cast<const unsigned char*>(event);
+ last_event_.assign(eventPtr, eventPtr + event->size);
+ }
+
+ WebInputEventQueue<MouseWheelEventWithLatencyInfo>& wheel_event_queue() {
+ return queue_.wheel_events_;
+ }
+
+ WebInputEventQueue<TouchEventWithLatencyInfo>& touch_event_queue() {
+ return queue_.touch_events_;
+ }
+
+ protected:
+ NonBlockingEventQueue queue_;
+ std::vector<unsigned char> last_event_;
+};
+
+TEST_F(NonBlockingEventQueueTest, NonBlockingWheel) {
+ WebMouseWheelEvent kEvents[4] = {
+ SyntheticWebMouseWheelEventBuilder::Build(10, 10, 0, 53, 0, false),
+ SyntheticWebMouseWheelEventBuilder::Build(20, 20, 0, 53, 0, false),
+ SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false),
+ SyntheticWebMouseWheelEventBuilder::Build(30, 30, 0, 53, 1, false),
+ };
+
+ ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING,
+ wheel_event_queue().state());
+ queue_.HandleEvent(&kEvents[0], ui::LatencyInfo());
+ ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, wheel_event_queue().state());
+ queue_.HandleEvent(&kEvents[1], ui::LatencyInfo());
+ ASSERT_EQ(kEvents[0].size, last_event_.size());
+ ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[0], kEvents[0].size) == 0);
+ queue_.EventHandled(blink::WebInputEvent::MouseWheel);
+ ASSERT_EQ(kEvents[1].size, last_event_.size());
+ ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[1], kEvents[1].size) == 0);
+ ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, wheel_event_queue().state());
+ queue_.EventHandled(blink::WebInputEvent::MouseWheel);
+ ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING,
+ wheel_event_queue().state());
+
+ // Ensure that coalescing takes place.
+ queue_.HandleEvent(&kEvents[0], ui::LatencyInfo());
+ queue_.HandleEvent(&kEvents[2], ui::LatencyInfo());
+ queue_.HandleEvent(&kEvents[3], ui::LatencyInfo());
+ ASSERT_EQ(1u, wheel_event_queue().size());
+ ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, wheel_event_queue().state());
+}
+
+TEST_F(NonBlockingEventQueueTest, NonBlockingTouch) {
+ SyntheticWebTouchEvent kEvents[4];
+ kEvents[0].PressPoint(10, 10);
+ kEvents[1].PressPoint(10, 10);
+ kEvents[1].modifiers = 1;
+ kEvents[1].MovePoint(0, 20, 20);
+ kEvents[2].PressPoint(10, 10);
+ kEvents[2].MovePoint(0, 30, 30);
+ kEvents[3].PressPoint(10, 10);
+ kEvents[3].MovePoint(0, 35, 35);
+
+ ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING,
+ touch_event_queue().state());
+ queue_.HandleEvent(&kEvents[0], ui::LatencyInfo());
+ ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, touch_event_queue().state());
+ queue_.HandleEvent(&kEvents[1], ui::LatencyInfo());
+ ASSERT_EQ(kEvents[0].size, last_event_.size());
+ ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[0], kEvents[0].size) == 0);
+ queue_.EventHandled(blink::WebInputEvent::TouchStart);
+ ASSERT_EQ(kEvents[1].size, last_event_.size());
+ ASSERT_TRUE(memcmp(&last_event_[0], &kEvents[1], kEvents[1].size) == 0);
+ ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, touch_event_queue().state());
+ queue_.EventHandled(blink::WebInputEvent::TouchMove);
+ ASSERT_EQ(WebInputEventQueueState::ITEM_NOT_PENDING,
+ touch_event_queue().state());
+
+ // Ensure that coalescing takes place.
+ queue_.HandleEvent(&kEvents[0], ui::LatencyInfo());
+ queue_.HandleEvent(&kEvents[2], ui::LatencyInfo());
+ queue_.HandleEvent(&kEvents[3], ui::LatencyInfo());
+ ASSERT_EQ(1u, touch_event_queue().size());
+ ASSERT_EQ(WebInputEventQueueState::ITEM_PENDING, touch_event_queue().state());
+}
+
+} // namespace content
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 54f2b9c..385d388 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -131,7 +131,7 @@ void LogPassiveLatency(int64_t latency) {
}
void LogPassiveEventListenersUma(WebInputEventResult result,
- bool passive,
+ bool non_blocking,
bool cancelable,
double event_timestamp,
const ui::LatencyInfo& latency_info) {
@@ -145,7 +145,7 @@ void LogPassiveEventListenersUma(WebInputEventResult result,
};
int enum_value;
- if (passive)
+ if (non_blocking)
enum_value = PASSIVE_LISTENER_UMA_ENUM_PASSIVE;
else if (!cancelable)
enum_value = PASSIVE_LISTENER_UMA_ENUM_UNCANCELABLE;
@@ -191,9 +191,8 @@ RenderWidgetInputHandler::~RenderWidgetInputHandler() {}
void RenderWidgetInputHandler::HandleInputEvent(
const WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info) {
- // TODO(dtapuska): Passive support not implemented yet crbug.com/489802
- bool passive = false;
+ const ui::LatencyInfo& latency_info,
+ InputEventDispatchType dispatch_type) {
base::AutoReset<bool> handling_input_event_resetter(&handling_input_event_,
true);
base::AutoReset<WebInputEvent::Type> handling_event_type_resetter(
@@ -314,6 +313,8 @@ void RenderWidgetInputHandler::HandleInputEvent(
processed = widget_->webwidget()->handleInputEvent(input_event);
}
+ bool non_blocking =
+ dispatch_type == InputEventDispatchType::DISPATCH_TYPE_NON_BLOCKING;
// TODO(dtapuska): Use the input_event.timeStampSeconds as the start
// ideally this should be when the event was sent by the compositor to the
// renderer. crbug.com/565348
@@ -321,11 +322,11 @@ void RenderWidgetInputHandler::HandleInputEvent(
input_event.type == WebInputEvent::TouchMove ||
input_event.type == WebInputEvent::TouchEnd) {
LogPassiveEventListenersUma(
- processed, passive,
+ processed, non_blocking,
static_cast<const WebTouchEvent&>(input_event).cancelable,
input_event.timeStampSeconds, latency_info);
} else if (input_event.type == WebInputEvent::MouseWheel) {
- LogPassiveEventListenersUma(processed, passive, !passive,
+ LogPassiveEventListenersUma(processed, non_blocking, !non_blocking,
input_event.timeStampSeconds, latency_info);
}
@@ -362,11 +363,15 @@ void RenderWidgetInputHandler::HandleInputEvent(
// Send mouse wheel events and their disposition to the compositor thread, so
// that they can be used to produce the elastic overscroll effect on Mac.
if (input_event.type == WebInputEvent::MouseWheel) {
- delegate_->ObserveWheelEventAndResult(
- static_cast<const WebMouseWheelEvent&>(input_event),
- event_overscroll ? event_overscroll->latest_overscroll_delta
- : gfx::Vector2dF(),
- processed != WebInputEventResult::NotHandled);
+ const WebMouseWheelEvent& wheel_event =
+ static_cast<const WebMouseWheelEvent&>(input_event);
+ if (wheel_event.canScroll) {
+ delegate_->ObserveWheelEventAndResult(
+ wheel_event,
+ event_overscroll ? event_overscroll->latest_overscroll_delta
+ : gfx::Vector2dF(),
+ processed != WebInputEventResult::NotHandled);
+ }
}
bool frame_pending =
@@ -390,7 +395,12 @@ void RenderWidgetInputHandler::HandleInputEvent(
// by reentrant calls for events after the paused one.
bool no_ack = ignore_ack_for_mouse_move_from_debugger_ &&
input_event.type == WebInputEvent::MouseMove;
- if (WebInputEventTraits::WillReceiveAckFromRenderer(input_event) && !no_ack) {
+ if (non_blocking) {
+ // |non_blocking| means it was ack'd already by the InputHandlerProxy
+ // so let the delegate know the event has been handled.
+ delegate_->NonBlockingInputEventHandled(input_event.type);
+ } else if (WebInputEventTraits::WillReceiveAckFromRenderer(input_event) &&
+ !no_ack) {
scoped_ptr<InputEventAck> response(new InputEventAck(
input_event.type, ack_result, swap_latency_info,
std::move(event_overscroll),
diff --git a/content/renderer/input/render_widget_input_handler.h b/content/renderer/input/render_widget_input_handler.h
index f8ff1d3..3be968c 100644
--- a/content/renderer/input/render_widget_input_handler.h
+++ b/content/renderer/input/render_widget_input_handler.h
@@ -10,6 +10,7 @@
#include "base/time/time.h"
#include "content/common/input/did_overscroll_params.h"
#include "content/common/input/input_event_ack.h"
+#include "content/common/input/input_event_dispatch_type.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/base/ui_base_types.h"
@@ -37,7 +38,8 @@ class CONTENT_EXPORT RenderWidgetInputHandler {
// Handle input events from the input event provider.
void HandleInputEvent(const blink::WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info);
+ const ui::LatencyInfo& latency_info,
+ InputEventDispatchType dispatch_type);
// Handle overscroll from Blink.
void DidOverscrollFromBlink(
diff --git a/content/renderer/input/render_widget_input_handler_delegate.h b/content/renderer/input/render_widget_input_handler_delegate.h
index d00a080..7a7f544 100644
--- a/content/renderer/input/render_widget_input_handler_delegate.h
+++ b/content/renderer/input/render_widget_input_handler_delegate.h
@@ -58,6 +58,11 @@ class CONTENT_EXPORT RenderWidgetInputHandlerDelegate {
// Called when an ACK is ready to be sent to the input event provider.
virtual void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) = 0;
+ // Called when a non-blocking event (DISPATCH_TYPE_NON_BLOCKING) of
+ // |handled_type| has been processed by the main thread.
+ virtual void NonBlockingInputEventHandled(
+ blink::WebInputEvent::Type handled_type) = 0;
+
// Notifies the delegate of the |input_handler| managing it.
virtual void SetInputHandler(RenderWidgetInputHandler* input_handler) = 0;
diff --git a/content/renderer/mus/render_widget_mus_connection.cc b/content/renderer/mus/render_widget_mus_connection.cc
index c7dbac8..3ac0808 100644
--- a/content/renderer/mus/render_widget_mus_connection.cc
+++ b/content/renderer/mus/render_widget_mus_connection.cc
@@ -123,6 +123,11 @@ void RenderWidgetMusConnection::OnInputEventAck(
pending_ack_.Reset();
}
+void RenderWidgetMusConnection::NonBlockingInputEventHandled(
+ blink::WebInputEvent::Type handled_type) {
+ NOTIMPLEMENTED();
+}
+
void RenderWidgetMusConnection::SetInputHandler(
RenderWidgetInputHandler* input_handler) {
DCHECK(!input_handler_);
@@ -171,7 +176,8 @@ void RenderWidgetMusConnection::OnWindowInputEvent(
pending_ack_ = ack;
// TODO(fsamuel, sadrul): Track real latency info.
ui::LatencyInfo latency_info;
- input_handler_->HandleInputEvent(*input_event, latency_info);
+ input_handler_->HandleInputEvent(*input_event, latency_info,
+ DISPATCH_TYPE_NORMAL);
}
} // namespace content
diff --git a/content/renderer/mus/render_widget_mus_connection.h b/content/renderer/mus/render_widget_mus_connection.h
index c6bb362..26f1b4b 100644
--- a/content/renderer/mus/render_widget_mus_connection.h
+++ b/content/renderer/mus/render_widget_mus_connection.h
@@ -46,6 +46,8 @@ class RenderWidgetMusConnection : public RenderWidgetInputHandlerDelegate {
void OnDidHandleKeyEvent() override;
void OnDidOverscroll(const DidOverscrollParams& params) override;
void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) override;
+ void NonBlockingInputEventHandled(
+ blink::WebInputEvent::Type handled_type) override;
void SetInputHandler(RenderWidgetInputHandler* input_handler) override;
void UpdateTextInputState(ShowIme show_ime,
ChangeSource change_source) override;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index e2e7603..fb533e0 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -48,6 +48,7 @@
#include "content/common/frame_messages.h"
#include "content/common/frame_replication_state.h"
#include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
+#include "content/common/input/input_event_utils.h"
#include "content/common/input_messages.h"
#include "content/common/pepper_messages.h"
#include "content/common/site_isolation_policy.h"
@@ -2161,7 +2162,8 @@ void RenderViewImpl::initializeLayerTreeView() {
if (input_handler_manager) {
input_handler_manager->AddInputHandler(
routing_id(), rwc->GetInputHandler(), AsWeakPtr(),
- webkit_preferences_.enable_scroll_animator);
+ webkit_preferences_.enable_scroll_animator,
+ UseGestureBasedWheelScrolling());
}
}
}
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 85cb9f3..9709d2e 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -656,10 +656,11 @@ GURL RenderWidget::GetURLForGraphicsContext3D() {
}
void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event,
- const ui::LatencyInfo& latency_info) {
+ const ui::LatencyInfo& latency_info,
+ InputEventDispatchType dispatch_type) {
if (!input_event)
return;
- input_handler_->HandleInputEvent(*input_event, latency_info);
+ input_handler_->HandleInputEvent(*input_event, latency_info, dispatch_type);
}
void RenderWidget::OnCursorVisibilityChange(bool is_visible) {
@@ -959,6 +960,17 @@ void RenderWidget::OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) {
Send(new InputHostMsg_HandleInputEvent_ACK(routing_id_, *input_event_ack));
}
+void RenderWidget::NonBlockingInputEventHandled(
+ blink::WebInputEvent::Type handled_type) {
+ RenderThreadImpl* render_thread = RenderThreadImpl::current();
+ InputHandlerManager* input_handler_manager =
+ render_thread ? render_thread->input_handler_manager() : NULL;
+ if (input_handler_manager) {
+ input_handler_manager->NonBlockingInputEventHandledOnMainThread(
+ routing_id_, handled_type);
+ }
+}
+
void RenderWidget::SetInputHandler(RenderWidgetInputHandler* input_handler) {
// Nothing to do here. RenderWidget created the |input_handler| and will take
// ownership of it. We just verify here that we don't already have an input
diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h
index 1185fa2b..a7bf289 100644
--- a/content/renderer/render_widget.h
+++ b/content/renderer/render_widget.h
@@ -207,6 +207,8 @@ class CONTENT_EXPORT RenderWidget
void OnDidHandleKeyEvent() override;
void OnDidOverscroll(const DidOverscrollParams& params) override;
void OnInputEventAck(scoped_ptr<InputEventAck> input_event_ack) override;
+ void NonBlockingInputEventHandled(
+ blink::WebInputEvent::Type handled_type) override;
void SetInputHandler(RenderWidgetInputHandler* input_handler) override;
void UpdateTextInputState(ShowIme show_ime,
ChangeSource change_source) override;
@@ -412,7 +414,8 @@ class CONTENT_EXPORT RenderWidget
// RenderWidget IPC message handlers
void OnHandleInputEvent(const blink::WebInputEvent* event,
- const ui::LatencyInfo& latency_info);
+ const ui::LatencyInfo& latency_info,
+ InputEventDispatchType dispatch_type);
void OnCursorVisibilityChange(bool is_visible);
void OnMouseCaptureLost();
virtual void OnSetFocus(bool enable);
diff --git a/content/renderer/render_widget_unittest.cc b/content/renderer/render_widget_unittest.cc
index cdd195a..afd9cd8 100644
--- a/content/renderer/render_widget_unittest.cc
+++ b/content/renderer/render_widget_unittest.cc
@@ -38,7 +38,8 @@ class InteractiveRenderWidget : public RenderWidget {
}
void SendInputEvent(const blink::WebInputEvent& event) {
- OnHandleInputEvent(&event, ui::LatencyInfo());
+ OnHandleInputEvent(&event, ui::LatencyInfo(),
+ InputEventDispatchType::DISPATCH_TYPE_NORMAL);
}
void set_always_overscroll(bool overscroll) {
diff --git a/ui/events/blink/input_handler_proxy.cc b/ui/events/blink/input_handler_proxy.cc
index 17de4c6..23bc94d 100644
--- a/ui/events/blink/input_handler_proxy.cc
+++ b/ui/events/blink/input_handler_proxy.cc
@@ -35,6 +35,8 @@ using blink::WebTouchPoint;
namespace {
+const int32_t kEventDispositionUndefined = -1;
+
// Maximum time between a fling event's timestamp and the first |Animate| call
// for the fling curve to use the fling timestamp as the initial animation time.
// Two frames allows a minor delay between event creation and the first animate.
@@ -227,7 +229,9 @@ InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler,
disallow_vertical_fling_scroll_(false),
has_fling_animation_started_(false),
smooth_scroll_enabled_(false),
- uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()) {
+ uma_latency_reporting_enabled_(base::TimeTicks::IsHighResolution()),
+ use_gesture_events_for_mouse_wheel_(true),
+ touch_start_result_(kEventDispositionUndefined) {
DCHECK(client);
input_handler_->BindToClient(this);
cc::ScrollElasticityHelper* scroll_elasticity_helper =
@@ -343,6 +347,12 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent(
case WebInputEvent::TouchStart:
return HandleTouchStart(static_cast<const WebTouchEvent&>(event));
+ case WebInputEvent::TouchMove:
+ return HandleTouchMove(static_cast<const WebTouchEvent&>(event));
+
+ case WebInputEvent::TouchEnd:
+ return HandleTouchEnd(static_cast<const WebTouchEvent&>(event));
+
case WebInputEvent::MouseMove: {
const WebMouseEvent& mouse_event =
static_cast<const WebMouseEvent&>(event);
@@ -426,6 +436,28 @@ bool InputHandlerProxy::ShouldAnimate(
InputHandlerProxy::EventDisposition InputHandlerProxy::HandleMouseWheel(
const WebMouseWheelEvent& wheel_event) {
+ if (use_gesture_events_for_mouse_wheel_) {
+ cc::EventListenerProperties properties =
+ input_handler_->GetEventListenerProperties(
+ cc::EventListenerClass::kMouseWheel);
+ switch (properties) {
+ case cc::EventListenerProperties::kPassive:
+ return DID_HANDLE_NON_BLOCKING;
+ case cc::EventListenerProperties::kBlockingAndPassive:
+ case cc::EventListenerProperties::kBlocking:
+ return DID_NOT_HANDLE;
+ case cc::EventListenerProperties::kNone:
+ return DROP_EVENT;
+ default:
+ NOTREACHED();
+ return DROP_EVENT;
+ }
+ }
+ return ScrollByMouseWheel(wheel_event);
+}
+
+InputHandlerProxy::EventDisposition InputHandlerProxy::ScrollByMouseWheel(
+ const WebMouseWheelEvent& wheel_event) {
InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE;
cc::InputHandlerScrollResult scroll_result;
@@ -734,18 +766,67 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureFlingStart(
InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchStart(
const blink::WebTouchEvent& touch_event) {
+ EventDisposition result = DROP_EVENT;
for (size_t i = 0; i < touch_event.touchesLength; ++i) {
if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
continue;
if (input_handler_->DoTouchEventsBlockScrollAt(
gfx::Point(touch_event.touches[i].position.x,
touch_event.touches[i].position.y))) {
- // TODO(rbyers): We should consider still sending the touch events to
- // main asynchronously (crbug.com/455539).
- return DID_NOT_HANDLE;
+ result = DID_NOT_HANDLE;
+ break;
+ }
+ }
+
+ // If |result| is DROP_EVENT it wasn't processed above.
+ if (result == DROP_EVENT) {
+ switch (input_handler_->GetEventListenerProperties(
+ cc::EventListenerClass::kTouch)) {
+ case cc::EventListenerProperties::kPassive:
+ result = DID_HANDLE_NON_BLOCKING;
+ break;
+ case cc::EventListenerProperties::kBlocking:
+ // The touch area rects above already have checked whether it hits
+ // a blocking region. Since it does not the event can be dropped.
+ result = DROP_EVENT;
+ break;
+ case cc::EventListenerProperties::kBlockingAndPassive:
+ // There is at least one passive listener that needs to possibly
+ // be notified so it can't be dropped.
+ result = DID_HANDLE_NON_BLOCKING;
+ break;
+ case cc::EventListenerProperties::kNone:
+ result = DROP_EVENT;
+ break;
+ default:
+ NOTREACHED();
+ result = DROP_EVENT;
+ break;
}
}
- return DROP_EVENT;
+
+ // Merge |touch_start_result_| and |result| so the result has the highest
+ // priority value according to the sequence; (DROP_EVENT,
+ // DID_HANDLE_NON_BLOCKING, DID_NOT_HANDLE).
+ if (touch_start_result_ == kEventDispositionUndefined ||
+ touch_start_result_ == DROP_EVENT || result == DID_NOT_HANDLE)
+ touch_start_result_ = result;
+
+ return result;
+}
+
+InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchMove(
+ const blink::WebTouchEvent& touch_event) {
+ if (touch_start_result_ != kEventDispositionUndefined)
+ return static_cast<EventDisposition>(touch_start_result_);
+ return DID_NOT_HANDLE;
+}
+
+InputHandlerProxy::EventDisposition InputHandlerProxy::HandleTouchEnd(
+ const blink::WebTouchEvent& touch_event) {
+ if (touch_event.touchesLength == 1)
+ touch_start_result_ = kEventDispositionUndefined;
+ return DID_NOT_HANDLE;
}
bool InputHandlerProxy::FilterInputEventForFlingBoosting(
@@ -1089,25 +1170,47 @@ void InputHandlerProxy::RequestAnimation() {
bool InputHandlerProxy::TouchpadFlingScroll(
const WebFloatSize& increment) {
- WebMouseWheelEvent synthetic_wheel;
- synthetic_wheel.type = WebInputEvent::MouseWheel;
- synthetic_wheel.timeStampSeconds = InSecondsF(base::TimeTicks::Now());
- synthetic_wheel.deltaX = increment.width;
- synthetic_wheel.deltaY = increment.height;
- synthetic_wheel.hasPreciseScrollingDeltas = true;
- synthetic_wheel.x = fling_parameters_.point.x;
- synthetic_wheel.y = fling_parameters_.point.y;
- synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
- synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
- synthetic_wheel.modifiers = fling_parameters_.modifiers;
-
- InputHandlerProxy::EventDisposition disposition =
- HandleInputEvent(synthetic_wheel);
+ InputHandlerProxy::EventDisposition disposition;
+ cc::EventListenerProperties properties =
+ input_handler_->GetEventListenerProperties(
+ cc::EventListenerClass::kMouseWheel);
+ switch (properties) {
+ case cc::EventListenerProperties::kPassive:
+ disposition = DID_HANDLE_NON_BLOCKING;
+ break;
+ case cc::EventListenerProperties::kBlocking:
+ disposition = DID_NOT_HANDLE;
+ break;
+ case cc::EventListenerProperties::kNone: {
+ WebMouseWheelEvent synthetic_wheel;
+ synthetic_wheel.type = WebInputEvent::MouseWheel;
+ synthetic_wheel.timeStampSeconds = InSecondsF(base::TimeTicks::Now());
+ synthetic_wheel.deltaX = increment.width;
+ synthetic_wheel.deltaY = increment.height;
+ synthetic_wheel.hasPreciseScrollingDeltas = true;
+ synthetic_wheel.x = fling_parameters_.point.x;
+ synthetic_wheel.y = fling_parameters_.point.y;
+ synthetic_wheel.globalX = fling_parameters_.globalPoint.x;
+ synthetic_wheel.globalY = fling_parameters_.globalPoint.y;
+ synthetic_wheel.modifiers = fling_parameters_.modifiers;
+
+ disposition = ScrollByMouseWheel(synthetic_wheel);
+ break;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+
switch (disposition) {
case DID_HANDLE:
return true;
case DROP_EVENT:
break;
+ case DID_HANDLE_NON_BLOCKING:
+ // TODO(dtapuska): Process the fling on the compositor thread
+ // but post the events to the main thread; for now just pass it to the
+ // main thread.
case DID_NOT_HANDLE:
TRACE_EVENT_INSTANT0("input",
"InputHandlerProxy::scrollBy::AbortFling",
diff --git a/ui/events/blink/input_handler_proxy.h b/ui/events/blink/input_handler_proxy.h
index e96b31b..32c0efc 100644
--- a/ui/events/blink/input_handler_proxy.h
+++ b/ui/events/blink/input_handler_proxy.h
@@ -46,10 +46,14 @@ class InputHandlerProxy
}
void set_smooth_scroll_enabled(bool value) { smooth_scroll_enabled_ = value; }
+ void set_use_gesture_events_for_mouse_wheel(bool value) {
+ use_gesture_events_for_mouse_wheel_ = value;
+ }
enum EventDisposition {
DID_HANDLE,
DID_NOT_HANDLE,
+ DID_HANDLE_NON_BLOCKING,
DROP_EVENT
};
EventDisposition HandleInputEventWithLatencyInfo(
@@ -91,6 +95,7 @@ class InputHandlerProxy
// Helper functions for handling more complicated input events.
EventDisposition HandleMouseWheel(
const blink::WebMouseWheelEvent& event);
+ EventDisposition ScrollByMouseWheel(const blink::WebMouseWheelEvent& event);
EventDisposition HandleGestureScrollBegin(
const blink::WebGestureEvent& event);
EventDisposition HandleGestureScrollUpdate(
@@ -99,8 +104,9 @@ class InputHandlerProxy
const blink::WebGestureEvent& event);
EventDisposition HandleGestureFlingStart(
const blink::WebGestureEvent& event);
- EventDisposition HandleTouchStart(
- const blink::WebTouchEvent& event);
+ EventDisposition HandleTouchStart(const blink::WebTouchEvent& event);
+ EventDisposition HandleTouchMove(const blink::WebTouchEvent& event);
+ EventDisposition HandleTouchEnd(const blink::WebTouchEvent& event);
// Returns true if the event should be suppressed due to to an active,
// boost-enabled fling, in which case further processing should cease.
@@ -184,8 +190,13 @@ class InputHandlerProxy
scoped_ptr<InputScrollElasticityController> scroll_elasticity_controller_;
bool smooth_scroll_enabled_;
-
bool uma_latency_reporting_enabled_;
+ bool use_gesture_events_for_mouse_wheel_;
+
+ // The merged result of the last touch start with previous touch starts.
+ // This value will get returned for subsequent TouchMove events to allow
+ // passive events not to block scrolling.
+ int32_t touch_start_result_;
base::TimeTicks last_fling_animate_time_;
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index 8106b12..a5cee97 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -369,6 +369,10 @@ class InputHandlerProxyTest
input_handler_->smooth_scroll_enabled_ = value;
}
+ void SetMouseWheelGesturesOn(bool value) {
+ input_handler_->set_use_gesture_events_for_mouse_wheel(value);
+ }
+
protected:
const bool synchronous_root_scroll_;
const bool install_synchronous_handler_;
@@ -385,6 +389,7 @@ class InputHandlerProxyTest
TEST_P(InputHandlerProxyTest, MouseWheelByPageMainThread) {
expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ SetMouseWheelGesturesOn(false);
WebMouseWheelEvent wheel;
wheel.type = WebInputEvent::MouseWheel;
wheel.scrollByPage = true;
@@ -395,6 +400,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelByPageMainThread) {
TEST_P(InputHandlerProxyTest, MouseWheelWithCtrlNotScroll) {
expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ SetMouseWheelGesturesOn(false);
WebMouseWheelEvent wheel;
wheel.type = WebInputEvent::MouseWheel;
wheel.modifiers = WebInputEvent::ControlKey;
@@ -403,6 +409,45 @@ TEST_P(InputHandlerProxyTest, MouseWheelWithCtrlNotScroll) {
VERIFY_AND_RESET_MOCKS();
}
+TEST_P(InputHandlerProxyTest, MouseWheelNoListener) {
+ expected_disposition_ = InputHandlerProxy::DROP_EVENT;
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
+
+ WebMouseWheelEvent wheel;
+ wheel.type = WebInputEvent::MouseWheel;
+ wheel.modifiers = WebInputEvent::ControlKey;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel));
+ VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_P(InputHandlerProxyTest, MouseWheelPassiveListener) {
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kPassive));
+
+ WebMouseWheelEvent wheel;
+ wheel.type = WebInputEvent::MouseWheel;
+ wheel.modifiers = WebInputEvent::ControlKey;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel));
+ VERIFY_AND_RESET_MOCKS();
+}
+
+TEST_P(InputHandlerProxyTest, MouseWheelBlockingListener) {
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kBlocking));
+
+ WebMouseWheelEvent wheel;
+ wheel.type = WebInputEvent::MouseWheel;
+ wheel.modifiers = WebInputEvent::ControlKey;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel));
+ VERIFY_AND_RESET_MOCKS();
+}
+
// Mac does not smooth scroll wheel events (crbug.com/574283).
#if !defined(OS_MACOSX)
TEST_P(InputHandlerProxyTest, MouseWheelWithPreciseScrollingDeltas) {
@@ -410,6 +455,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelWithPreciseScrollingDeltas) {
TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelWithPreciseScrollingDeltas) {
#endif
SetSmoothScrollEnabled(true);
+ SetMouseWheelGesturesOn(false);
expected_disposition_ = InputHandlerProxy::DID_HANDLE;
WebMouseWheelEvent wheel;
wheel.type = WebInputEvent::MouseWheel;
@@ -443,6 +489,7 @@ TEST_P(InputHandlerProxyTest, MouseWheelScrollIgnored) {
TEST_P(InputHandlerProxyTest, DISABLED_MouseWheelScrollIgnored) {
#endif
SetSmoothScrollEnabled(true);
+ SetMouseWheelGesturesOn(false);
expected_disposition_ = InputHandlerProxy::DROP_EVENT;
WebMouseWheelEvent wheel;
wheel.type = WebInputEvent::MouseWheel;
@@ -877,6 +924,7 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
VERIFY_AND_RESET_MOCKS();
+
// The first animate call should let us pick up an animation start time, but
// we shouldn't actually move anywhere just yet. The first frame after the
// fling start will typically include the last scroll from the gesture that
@@ -892,6 +940,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
// The second call should start scrolling in the -X direction.
EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(
@@ -909,6 +960,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
// We also should pass the current fling parameters out to the client so the
// rest of the fling can be
// transferred to the main thread.
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kMainThreadScrollState));
EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
@@ -959,6 +1013,97 @@ TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
VERIFY_AND_RESET_MOCKS();
}
+TEST_P(InputHandlerProxyTest, GestureFlingPassiveListener) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ // On the fling start, we should schedule an animation but not actually start
+ // scrolling.
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+ WebPoint fling_point = WebPoint(7, 13);
+ WebPoint fling_global_point = WebPoint(17, 23);
+ // Note that for trackpad, wheel events with the Control modifier are
+ // special (reserved for zoom), so don't set that here.
+ int modifiers = WebInputEvent::ShiftKey | WebInputEvent::AltKey;
+ gesture_ = CreateFling(blink::WebGestureDeviceTouchpad, fling_delta,
+ fling_point, fling_global_point, modifiers);
+ EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(kImplThreadScrollState));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // The first animate call should let us pick up an animation start time, but
+ // we shouldn't actually move anywhere just yet. The first frame after the
+ // fling start will typically include the last scroll from the gesture that
+ // lead to the scroll (either wheel or gesture scroll), so there should be no
+ // visible hitch.
+ EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ Animate(time);
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // The second call should punt the fling to the main thread
+ // because of a passive event listener.
+ EXPECT_SET_NEEDS_ANIMATE_INPUT(0);
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kPassive));
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+ // Expected wheel fling animation parameters:
+ // *) fling_delta and fling_point should match the original GestureFlingStart
+ // event
+ // *) startTime should be 10 to match the time parameter of the first
+ // Animate() call after the GestureFlingStart
+ EXPECT_CALL(
+ mock_client_,
+ TransferActiveWheelFlingAnimation(testing::AllOf(
+ testing::Field(&WebActiveWheelFlingParameters::delta,
+ testing::Eq(fling_delta)),
+ testing::Field(&WebActiveWheelFlingParameters::point,
+ testing::Eq(fling_point)),
+ testing::Field(&WebActiveWheelFlingParameters::globalPoint,
+ testing::Eq(fling_global_point)),
+ testing::Field(&WebActiveWheelFlingParameters::modifiers,
+ testing::Eq(modifiers)),
+ testing::Field(&WebActiveWheelFlingParameters::startTime,
+ testing::Eq(10)),
+ testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
+ testing::_))));
+ time += base::TimeDelta::FromMilliseconds(100);
+ Animate(time);
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // Since we've aborted the fling, the next animation should be a no-op and
+ // should not result in another
+ // frame being requested.
+ EXPECT_SET_NEEDS_ANIMATE_INPUT(0);
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ time += base::TimeDelta::FromMilliseconds(100);
+ Animate(time);
+
+ // Since we've transferred the fling to the main thread, we need to pass the
+ // next GestureFlingCancel to the main
+ // thread as well.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+}
+
TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
// We shouldn't send any events to the widget for this gesture.
expected_disposition_ = InputHandlerProxy::DID_HANDLE;
@@ -981,7 +1126,6 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
-
VERIFY_AND_RESET_MOCKS();
// Start the fling animation at time 10. This shouldn't actually scroll, just
@@ -996,6 +1140,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
// The second call should start scrolling in the -X direction.
EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(
@@ -1013,6 +1160,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
// We also should pass the current fling parameters out to the client so the
// rest of the fling can be
// transferred to the main thread.
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kMainThreadScrollState));
@@ -1097,6 +1247,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
// Tick the second fling once normally.
EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(
@@ -1110,6 +1263,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
VERIFY_AND_RESET_MOCKS();
// Then abort the second fling.
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kMainThreadScrollState));
EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
@@ -1569,6 +1725,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
VERIFY_AND_RESET_MOCKS();
// The second animate starts scrolling in the positive X and Y directions.
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(
@@ -1588,6 +1747,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
overscroll.did_overscroll_root = true;
overscroll.accumulated_root_overscroll = gfx::Vector2dF(0, 100);
overscroll.unused_scroll_delta = gfx::Vector2dF(0, 10);
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(
@@ -1609,6 +1771,9 @@ TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
// The next call to animate will no longer scroll vertically.
EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
.WillOnce(testing::Return(kImplThreadScrollState));
EXPECT_CALL(
@@ -1807,8 +1972,9 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestNegative) {
VERIFY_AND_RESET_MOCKS();
EXPECT_CALL(mock_input_handler_,
- DoTouchEventsBlockScrollAt(
- testing::Property(&gfx::Point::x, testing::Gt(0))))
+ GetEventListenerProperties(cc::EventListenerClass::kTouch))
+ .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
+ EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_))
.WillOnce(testing::Return(false));
EXPECT_CALL(mock_input_handler_,
DoTouchEventsBlockScrollAt(
@@ -1856,6 +2022,30 @@ TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPositive) {
VERIFY_AND_RESET_MOCKS();
}
+TEST_P(InputHandlerProxyTest, MultiTouchPointHitTestPassivePositive) {
+ // One of the touch points is on a touch-region. So the event should be sent
+ // to the main thread.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_,
+ GetEventListenerProperties(cc::EventListenerClass::kTouch))
+ .WillRepeatedly(testing::Return(cc::EventListenerProperties::kPassive));
+ EXPECT_CALL(mock_input_handler_, DoTouchEventsBlockScrollAt(testing::_))
+ .WillRepeatedly(testing::Return(false));
+
+ WebTouchEvent touch;
+ touch.type = WebInputEvent::TouchStart;
+
+ touch.touchesLength = 3;
+ touch.touches[0] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 0, 0);
+ touch.touches[1] = CreateWebTouchPoint(WebTouchPoint::StatePressed, 10, 10);
+ touch.touches[2] = CreateWebTouchPoint(WebTouchPoint::StatePressed, -10, 10);
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch));
+
+ VERIFY_AND_RESET_MOCKS();
+}
+
TEST_P(InputHandlerProxyTest, GestureFlingCancelledByKeyboardEvent) {
// We shouldn't send any events to the widget for this gesture.
expected_disposition_ = InputHandlerProxy::DID_HANDLE;