summaryrefslogtreecommitdiff
path: root/chromium/content/renderer/input
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/content/renderer/input
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/content/renderer/input')
-rw-r--r--chromium/content/renderer/input/OWNERS2
-rw-r--r--chromium/content/renderer/input/input_event_filter.cc175
-rw-r--r--chromium/content/renderer/input/input_event_filter.h94
-rw-r--r--chromium/content/renderer/input/input_event_filter_unittest.cc312
-rw-r--r--chromium/content/renderer/input/input_handler_manager.cc132
-rw-r--r--chromium/content/renderer/input/input_handler_manager.h78
-rw-r--r--chromium/content/renderer/input/input_handler_manager_client.h60
-rw-r--r--chromium/content/renderer/input/input_handler_proxy.cc477
-rw-r--r--chromium/content/renderer/input/input_handler_proxy.h95
-rw-r--r--chromium/content/renderer/input/input_handler_proxy_client.h43
-rw-r--r--chromium/content/renderer/input/input_handler_proxy_unittest.cc1152
-rw-r--r--chromium/content/renderer/input/input_handler_wrapper.cc58
-rw-r--r--chromium/content/renderer/input/input_handler_wrapper.h52
13 files changed, 2730 insertions, 0 deletions
diff --git a/chromium/content/renderer/input/OWNERS b/chromium/content/renderer/input/OWNERS
new file mode 100644
index 00000000000..d012c830472
--- /dev/null
+++ b/chromium/content/renderer/input/OWNERS
@@ -0,0 +1,2 @@
+aelias@chromium.org
+jdduke@chromium.org
diff --git a/chromium/content/renderer/input/input_event_filter.cc b/chromium/content/renderer/input/input_event_filter.cc
new file mode 100644
index 00000000000..a0399783988
--- /dev/null
+++ b/chromium/content/renderer/input/input_event_filter.cc
@@ -0,0 +1,175 @@
+// Copyright 2013 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/input_event_filter.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/debug/trace_event.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "cc/input/input_handler.h"
+#include "content/common/input/web_input_event_traits.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/common/content_switches.h"
+#include "ui/gfx/vector2d_f.h"
+
+using blink::WebInputEvent;
+
+namespace content {
+
+InputEventFilter::InputEventFilter(
+ IPC::Listener* main_listener,
+ const scoped_refptr<base::MessageLoopProxy>& target_loop)
+ : main_loop_(base::MessageLoopProxy::current()),
+ main_listener_(main_listener),
+ sender_(NULL),
+ target_loop_(target_loop),
+ overscroll_notifications_enabled_(false) {
+ DCHECK(target_loop_.get());
+ overscroll_notifications_enabled_ =
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableOverscrollNotifications);
+}
+
+void InputEventFilter::SetBoundHandler(const Handler& handler) {
+ DCHECK(main_loop_->BelongsToCurrentThread());
+ handler_ = handler;
+}
+
+void InputEventFilter::DidAddInputHandler(int routing_id,
+ cc::InputHandler* input_handler) {
+ base::AutoLock locked(routes_lock_);
+ routes_.insert(routing_id);
+}
+
+void InputEventFilter::DidRemoveInputHandler(int routing_id) {
+ base::AutoLock locked(routes_lock_);
+ routes_.erase(routing_id);
+}
+
+void InputEventFilter::DidOverscroll(int routing_id,
+ const cc::DidOverscrollParams& params) {
+ DCHECK(target_loop_->BelongsToCurrentThread());
+
+ if (!overscroll_notifications_enabled_)
+ return;
+
+ io_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&InputEventFilter::SendMessageOnIOThread, this,
+ ViewHostMsg_DidOverscroll(routing_id,
+ params.accumulated_overscroll,
+ params.current_fling_velocity)));
+}
+
+void InputEventFilter::OnFilterAdded(IPC::Channel* channel) {
+ io_loop_ = base::MessageLoopProxy::current();
+ sender_ = channel;
+}
+
+void InputEventFilter::OnFilterRemoved() {
+ sender_ = NULL;
+}
+
+void InputEventFilter::OnChannelClosing() {
+ sender_ = NULL;
+}
+
+// This function returns true if the IPC message is one that the compositor
+// thread can handle *or* one that needs to preserve relative order with
+// messages that the compositor thread can handle. Returning true for a message
+// type means that the message will go through an extra copy and thread hop, so
+// use with care.
+static bool RequiresThreadBounce(const IPC::Message& message) {
+ return IPC_MESSAGE_ID_CLASS(message.type()) == InputMsgStart;
+}
+
+bool InputEventFilter::OnMessageReceived(const IPC::Message& message) {
+ TRACE_EVENT0("input", "InputEventFilter::OnMessageReceived");
+
+ if (!RequiresThreadBounce(message))
+ return false;
+
+ {
+ base::AutoLock locked(routes_lock_);
+ if (routes_.find(message.routing_id()) == routes_.end())
+ return false;
+ }
+
+ target_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&InputEventFilter::ForwardToHandler, this, message));
+ return true;
+}
+
+InputEventFilter::~InputEventFilter() {
+}
+
+void InputEventFilter::ForwardToMainListener(const IPC::Message& message) {
+ main_listener_->OnMessageReceived(message);
+}
+
+void InputEventFilter::ForwardToHandler(const IPC::Message& message) {
+ DCHECK(!handler_.is_null());
+ DCHECK(target_loop_->BelongsToCurrentThread());
+
+ if (message.type() != InputMsg_HandleInputEvent::ID) {
+ main_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&InputEventFilter::ForwardToMainListener,
+ this, message));
+ return;
+ }
+
+ int routing_id = message.routing_id();
+ ui::LatencyInfo latency_info;
+ const WebInputEvent* event = NULL;
+ bool is_keyboard_shortcut;
+ if (!InputMsg_HandleInputEvent::Read(
+ &message, &event, &latency_info, &is_keyboard_shortcut))
+ return;
+ DCHECK(event);
+
+ InputEventAckState ack = handler_.Run(routing_id, event, &latency_info);
+
+ if (ack == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) {
+ TRACE_EVENT0("input", "InputEventFilter::ForwardToHandler");
+ IPC::Message new_msg = InputMsg_HandleInputEvent(
+ routing_id, event, latency_info, is_keyboard_shortcut);
+ main_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&InputEventFilter::ForwardToMainListener,
+ this, new_msg));
+ return;
+ }
+
+ if (!WebInputEventTraits::IgnoresAckDisposition(event->type))
+ SendACK(event->type, ack, latency_info, routing_id);
+}
+
+void InputEventFilter::SendACK(blink::WebInputEvent::Type type,
+ InputEventAckState ack_result,
+ const ui::LatencyInfo& latency_info,
+ int routing_id) {
+ DCHECK(target_loop_->BelongsToCurrentThread());
+
+ io_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&InputEventFilter::SendMessageOnIOThread, this,
+ InputHostMsg_HandleInputEvent_ACK(
+ routing_id, type, ack_result, latency_info)));
+}
+
+void InputEventFilter::SendMessageOnIOThread(const IPC::Message& message) {
+ DCHECK(io_loop_->BelongsToCurrentThread());
+
+ if (!sender_)
+ return; // Filter was removed.
+
+ sender_->Send(new IPC::Message(message));
+}
+
+} // namespace content
diff --git a/chromium/content/renderer/input/input_event_filter.h b/chromium/content/renderer/input/input_event_filter.h
new file mode 100644
index 00000000000..240f939f721
--- /dev/null
+++ b/chromium/content/renderer/input/input_event_filter.h
@@ -0,0 +1,94 @@
+// Copyright 2013 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_INPUT_EVENT_FILTER_H_
+#define CONTENT_RENDERER_INPUT_INPUT_EVENT_FILTER_H_
+
+#include <queue>
+#include <set>
+
+#include "base/callback_forward.h"
+#include "base/synchronization/lock.h"
+#include "content/common/content_export.h"
+#include "content/port/common/input_event_ack_state.h"
+#include "content/renderer/input/input_handler_manager_client.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+// This class can be used to intercept InputMsg_HandleInputEvent messages
+// and have them be delivered to a target thread. Input events are filtered
+// based on routing_id (see AddRoute and RemoveRoute).
+//
+// The user of this class provides an instance of InputEventFilter::Handler,
+// which will be passed WebInputEvents on the target thread.
+//
+
+namespace content {
+
+class CONTENT_EXPORT InputEventFilter
+ : public InputHandlerManagerClient,
+ public IPC::ChannelProxy::MessageFilter {
+ public:
+ InputEventFilter(IPC::Listener* main_listener,
+ const scoped_refptr<base::MessageLoopProxy>& target_loop);
+
+ // The |handler| is invoked on the thread associated with |target_loop| to
+ // handle input events matching the filtered routes.
+ //
+ // If INPUT_EVENT_ACK_STATE_NOT_CONSUMED is returned by the handler,
+ // the original InputMsg_HandleInputEvent message will be delivered to
+ // |main_listener| on the main thread. (The "main thread" in this context is
+ // the thread where the InputEventFilter was constructed.) The responsibility
+ // is left to the eventual handler to deliver the corresponding
+ // InputHostMsg_HandleInputEvent_ACK.
+ //
+ virtual void SetBoundHandler(const Handler& handler) OVERRIDE;
+ virtual void DidAddInputHandler(int routing_id,
+ cc::InputHandler* input_handler) OVERRIDE;
+ virtual void DidRemoveInputHandler(int routing_id) OVERRIDE;
+ virtual void DidOverscroll(int routing_id,
+ const cc::DidOverscrollParams& params) OVERRIDE;
+
+ // IPC::ChannelProxy::MessageFilter methods:
+ virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE;
+ virtual void OnFilterRemoved() OVERRIDE;
+ virtual void OnChannelClosing() OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ friend class IPC::ChannelProxy::MessageFilter;
+ virtual ~InputEventFilter();
+
+ void ForwardToMainListener(const IPC::Message& message);
+ void ForwardToHandler(const IPC::Message& message);
+ void SendACK(blink::WebInputEvent::Type type,
+ InputEventAckState ack_result,
+ const ui::LatencyInfo& latency_info,
+ int routing_id);
+ void SendMessageOnIOThread(const IPC::Message& message);
+
+ scoped_refptr<base::MessageLoopProxy> main_loop_;
+ IPC::Listener* main_listener_;
+
+ // The sender_ only gets invoked on the thread corresponding to io_loop_.
+ scoped_refptr<base::MessageLoopProxy> io_loop_;
+ IPC::Sender* sender_;
+
+ // The handler_ only gets Run on the thread corresponding to target_loop_.
+ scoped_refptr<base::MessageLoopProxy> target_loop_;
+ Handler handler_;
+
+ // Protects access to routes_.
+ base::Lock routes_lock_;
+
+ // Indicates the routing_ids for which input events should be filtered.
+ std::set<int> routes_;
+
+ // Specifies whether overscroll notifications are forwarded to the host.
+ bool overscroll_notifications_enabled_;
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_INPUT_INPUT_EVENT_FILTER_H_
diff --git a/chromium/content/renderer/input/input_event_filter_unittest.cc b/chromium/content/renderer/input/input_event_filter_unittest.cc
new file mode 100644
index 00000000000..a131ceb47dc
--- /dev/null
+++ b/chromium/content/renderer/input/input_event_filter_unittest.cc
@@ -0,0 +1,312 @@
+// Copyright 2013 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 <new>
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "content/renderer/input/input_event_filter.h"
+#include "ipc/ipc_test_sink.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+
+namespace content {
+namespace {
+
+const int kTestRoutingID = 13;
+
+class InputEventRecorder {
+ public:
+ InputEventRecorder()
+ : filter_(NULL),
+ handle_events_(false),
+ send_to_widget_(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; }
+
+ size_t record_count() const { return records_.size(); }
+
+ const WebInputEvent* record_at(size_t i) const {
+ const Record& record = records_[i];
+ return reinterpret_cast<const WebInputEvent*>(&record.event_data[0]);
+ }
+
+ void Clear() {
+ records_.clear();
+ }
+
+ InputEventAckState HandleInputEvent(int routing_id,
+ const WebInputEvent* event,
+ ui::LatencyInfo* latency_info) {
+ DCHECK_EQ(kTestRoutingID, routing_id);
+
+ records_.push_back(Record(event));
+
+ if (handle_events_) {
+ return INPUT_EVENT_ACK_STATE_CONSUMED;
+ } else {
+ return send_to_widget_ ? INPUT_EVENT_ACK_STATE_NOT_CONSUMED
+ : INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+ }
+ }
+
+ private:
+ struct Record {
+ Record(const WebInputEvent* event) {
+ const char* ptr = reinterpret_cast<const char*>(event);
+ event_data.assign(ptr, ptr + event->size);
+ }
+ std::vector<char> event_data;
+ };
+
+ InputEventFilter* filter_;
+ bool handle_events_;
+ bool send_to_widget_;
+ std::vector<Record> records_;
+};
+
+class IPCMessageRecorder : public IPC::Listener {
+ public:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
+ messages_.push_back(message);
+ return true;
+ }
+
+ size_t message_count() const { return messages_.size(); }
+
+ const IPC::Message& message_at(size_t i) const {
+ return messages_[i];
+ }
+
+ void Clear() {
+ messages_.clear();
+ }
+
+ private:
+ std::vector<IPC::Message> messages_;
+};
+
+void InitMouseEvent(WebMouseEvent* event, WebInputEvent::Type type,
+ int x, int y) {
+ // Avoid valgrind false positives by initializing memory completely.
+ memset(event, 0, sizeof(*event));
+
+ new (event) WebMouseEvent();
+ event->type = type;
+ event->x = x;
+ event->y = y;
+}
+
+void AddMessagesToFilter(IPC::ChannelProxy::MessageFilter* message_filter,
+ const std::vector<IPC::Message>& events) {
+ for (size_t i = 0; i < events.size(); ++i) {
+ message_filter->OnMessageReceived(events[i]);
+ }
+
+ base::MessageLoop::current()->RunUntilIdle();
+}
+
+void AddEventsToFilter(IPC::ChannelProxy::MessageFilter* message_filter,
+ const WebMouseEvent 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(), false));
+ }
+
+ AddMessagesToFilter(message_filter, messages);
+}
+
+} // namespace
+
+class InputEventFilterTest : public testing::Test {
+ public:
+ virtual void SetUp() OVERRIDE {
+ filter_ = new InputEventFilter(
+ &message_recorder_,
+ message_loop_.message_loop_proxy());
+ filter_->SetBoundHandler(
+ base::Bind(&InputEventRecorder::HandleInputEvent,
+ base::Unretained(&event_recorder_)));
+
+ event_recorder_.set_filter(filter_.get());
+
+ filter_->OnFilterAdded(&ipc_sink_);
+ }
+
+
+ protected:
+ base::MessageLoop message_loop_;
+
+ // Used to record IPCs sent by the filter to the RenderWidgetHost.
+ IPC::TestSink ipc_sink_;
+
+ // Used to record IPCs forwarded by the filter to the main thread.
+ IPCMessageRecorder message_recorder_;
+
+ // Used to record WebInputEvents delivered to the handler.
+ InputEventRecorder event_recorder_;
+
+ scoped_refptr<InputEventFilter> filter_;
+};
+
+TEST_F(InputEventFilterTest, Basic) {
+ WebMouseEvent kEvents[3];
+ InitMouseEvent(&kEvents[0], WebInputEvent::MouseDown, 10, 10);
+ InitMouseEvent(&kEvents[1], WebInputEvent::MouseMove, 20, 20);
+ InitMouseEvent(&kEvents[2], WebInputEvent::MouseUp, 30, 30);
+
+ AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
+ EXPECT_EQ(0U, ipc_sink_.message_count());
+ EXPECT_EQ(0U, event_recorder_.record_count());
+ EXPECT_EQ(0U, message_recorder_.message_count());
+
+ filter_->DidAddInputHandler(kTestRoutingID, NULL);
+
+ AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
+ ASSERT_EQ(arraysize(kEvents), ipc_sink_.message_count());
+ ASSERT_EQ(arraysize(kEvents), event_recorder_.record_count());
+ EXPECT_EQ(0U, message_recorder_.message_count());
+
+ for (size_t i = 0; i < arraysize(kEvents); ++i) {
+ const IPC::Message* message = ipc_sink_.GetMessageAt(i);
+ EXPECT_EQ(kTestRoutingID, message->routing_id());
+ EXPECT_EQ(InputHostMsg_HandleInputEvent_ACK::ID, message->type());
+
+ WebInputEvent::Type event_type = WebInputEvent::Undefined;
+ InputEventAckState ack_result = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ ui::LatencyInfo latency_info;
+ EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message,
+ &event_type,
+ &ack_result,
+ &latency_info));
+ EXPECT_EQ(kEvents[i].type, event_type);
+ EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+
+ const WebInputEvent* event = event_recorder_.record_at(i);
+ ASSERT_TRUE(event);
+
+ EXPECT_EQ(kEvents[i].size, event->size);
+ EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
+ }
+
+ event_recorder_.set_send_to_widget(true);
+
+ AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
+ EXPECT_EQ(arraysize(kEvents), ipc_sink_.message_count());
+ EXPECT_EQ(2 * arraysize(kEvents), event_recorder_.record_count());
+ EXPECT_EQ(arraysize(kEvents), message_recorder_.message_count());
+
+ for (size_t i = 0; i < arraysize(kEvents); ++i) {
+ const IPC::Message& message = message_recorder_.message_at(i);
+
+ ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
+ const WebInputEvent* event = NULL;
+ ui::LatencyInfo latency_info;
+ bool is_kbd_shortcut;
+ EXPECT_TRUE(InputMsg_HandleInputEvent::Read(
+ &message, &event, &latency_info, &is_kbd_shortcut));
+
+ EXPECT_EQ(kEvents[i].size, event->size);
+ EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
+ }
+
+ // Now reset everything, and test that DidHandleInputEvent is called.
+
+ ipc_sink_.ClearMessages();
+ event_recorder_.Clear();
+ message_recorder_.Clear();
+
+ event_recorder_.set_handle_events(true);
+
+ AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
+ EXPECT_EQ(arraysize(kEvents), ipc_sink_.message_count());
+ EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count());
+ EXPECT_EQ(0U, message_recorder_.message_count());
+
+ for (size_t i = 0; i < arraysize(kEvents); ++i) {
+ const IPC::Message* message = ipc_sink_.GetMessageAt(i);
+ EXPECT_EQ(kTestRoutingID, message->routing_id());
+ EXPECT_EQ(InputHostMsg_HandleInputEvent_ACK::ID, message->type());
+
+ WebInputEvent::Type event_type = WebInputEvent::Undefined;
+ InputEventAckState ack_result = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ ui::LatencyInfo latency_info;
+ EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message,
+ &event_type,
+ &ack_result,
+ &latency_info));
+ EXPECT_EQ(kEvents[i].type, event_type);
+ EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_CONSUMED);
+ }
+
+ filter_->OnFilterRemoved();
+}
+
+TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
+ filter_->DidAddInputHandler(kTestRoutingID, NULL);
+ event_recorder_.set_send_to_widget(true);
+
+
+ WebMouseEvent mouse_down;
+ mouse_down.type = WebMouseEvent::MouseDown;
+ WebMouseEvent mouse_up;
+ mouse_up.type = WebMouseEvent::MouseUp;
+
+ std::vector<IPC::Message> messages;
+ messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID,
+ &mouse_down,
+ ui::LatencyInfo(),
+ false));
+ // Control where input events are delivered.
+ messages.push_back(InputMsg_MouseCaptureLost(kTestRoutingID));
+ messages.push_back(InputMsg_SetFocus(kTestRoutingID, true));
+
+ // Editing operations
+ messages.push_back(InputMsg_Undo(kTestRoutingID));
+ messages.push_back(InputMsg_Redo(kTestRoutingID));
+ messages.push_back(InputMsg_Cut(kTestRoutingID));
+ messages.push_back(InputMsg_Copy(kTestRoutingID));
+#if defined(OS_MACOSX)
+ messages.push_back(InputMsg_CopyToFindPboard(kTestRoutingID));
+#endif
+ messages.push_back(InputMsg_Paste(kTestRoutingID));
+ messages.push_back(InputMsg_PasteAndMatchStyle(kTestRoutingID));
+ messages.push_back(InputMsg_Delete(kTestRoutingID));
+ messages.push_back(InputMsg_Replace(kTestRoutingID, base::string16()));
+ messages.push_back(InputMsg_ReplaceMisspelling(kTestRoutingID,
+ base::string16()));
+ messages.push_back(InputMsg_Delete(kTestRoutingID));
+ messages.push_back(InputMsg_SelectAll(kTestRoutingID));
+ messages.push_back(InputMsg_Unselect(kTestRoutingID));
+ messages.push_back(InputMsg_SelectRange(kTestRoutingID,
+ gfx::Point(), gfx::Point()));
+ messages.push_back(InputMsg_MoveCaret(kTestRoutingID, gfx::Point()));
+
+ messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID,
+ &mouse_up,
+ ui::LatencyInfo(),
+ false));
+ AddMessagesToFilter(filter_.get(), messages);
+
+ // We should have sent all messages back to the main thread and preserved
+ // their relative order.
+ ASSERT_EQ(message_recorder_.message_count(), messages.size());
+ for (size_t i = 0; i < messages.size(); ++i) {
+ EXPECT_EQ(message_recorder_.message_at(i).type(), messages[i].type()) << i;
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/renderer/input/input_handler_manager.cc b/chromium/content/renderer/input/input_handler_manager.cc
new file mode 100644
index 00000000000..6195f5195df
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_manager.cc
@@ -0,0 +1,132 @@
+// Copyright 2013 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/input_handler_manager.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "cc/input/input_handler.h"
+#include "content/renderer/input/input_event_filter.h"
+#include "content/renderer/input/input_handler_manager_client.h"
+#include "content/renderer/input/input_handler_wrapper.h"
+
+using blink::WebInputEvent;
+
+namespace content {
+
+namespace {
+
+InputEventAckState InputEventDispositionToAck(
+ InputHandlerProxy::EventDisposition disposition) {
+ switch (disposition) {
+ case InputHandlerProxy::DID_HANDLE:
+ return INPUT_EVENT_ACK_STATE_CONSUMED;
+ case InputHandlerProxy::DID_NOT_HANDLE:
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ case InputHandlerProxy::DROP_EVENT:
+ return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+ }
+ NOTREACHED();
+ return INPUT_EVENT_ACK_STATE_UNKNOWN;
+}
+
+} // namespace
+
+InputHandlerManager::InputHandlerManager(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
+ InputHandlerManagerClient* client)
+ : message_loop_proxy_(message_loop_proxy),
+ client_(client) {
+ DCHECK(client_);
+ client_->SetBoundHandler(base::Bind(&InputHandlerManager::HandleInputEvent,
+ base::Unretained(this)));
+}
+
+InputHandlerManager::~InputHandlerManager() {
+ client_->SetBoundHandler(InputHandlerManagerClient::Handler());
+}
+
+void InputHandlerManager::AddInputHandler(
+ int routing_id,
+ const base::WeakPtr<cc::InputHandler>& input_handler,
+ const base::WeakPtr<RenderViewImpl>& render_view_impl) {
+ if (message_loop_proxy_->BelongsToCurrentThread()) {
+ AddInputHandlerOnCompositorThread(routing_id,
+ base::MessageLoopProxy::current(),
+ input_handler,
+ render_view_impl);
+ } else {
+ message_loop_proxy_->PostTask(
+ FROM_HERE,
+ base::Bind(&InputHandlerManager::AddInputHandlerOnCompositorThread,
+ base::Unretained(this),
+ routing_id,
+ base::MessageLoopProxy::current(),
+ input_handler,
+ render_view_impl));
+ }
+}
+
+void InputHandlerManager::AddInputHandlerOnCompositorThread(
+ int routing_id,
+ const scoped_refptr<base::MessageLoopProxy>& main_loop,
+ const base::WeakPtr<cc::InputHandler>& input_handler,
+ const base::WeakPtr<RenderViewImpl>& render_view_impl) {
+ DCHECK(message_loop_proxy_->BelongsToCurrentThread());
+
+ // The handler could be gone by this point if the compositor has shut down.
+ if (!input_handler)
+ return;
+
+ // The same handler may be registered for a route multiple times.
+ if (input_handlers_.count(routing_id) != 0)
+ return;
+
+ TRACE_EVENT1("input",
+ "InputHandlerManager::AddInputHandlerOnCompositorThread",
+ "result", "AddingRoute");
+ client_->DidAddInputHandler(routing_id, input_handler.get());
+ input_handlers_.add(routing_id,
+ make_scoped_ptr(new InputHandlerWrapper(this,
+ routing_id, main_loop, input_handler, render_view_impl)));
+}
+
+void InputHandlerManager::RemoveInputHandler(int routing_id) {
+ DCHECK(message_loop_proxy_->BelongsToCurrentThread());
+ DCHECK(input_handlers_.contains(routing_id));
+
+ TRACE_EVENT0("input", "InputHandlerManager::RemoveInputHandler");
+
+ client_->DidRemoveInputHandler(routing_id);
+ input_handlers_.erase(routing_id);
+}
+
+InputEventAckState InputHandlerManager::HandleInputEvent(
+ int routing_id,
+ const WebInputEvent* input_event,
+ ui::LatencyInfo* latency_info) {
+ DCHECK(message_loop_proxy_->BelongsToCurrentThread());
+
+ InputHandlerMap::iterator it = input_handlers_.find(routing_id);
+ if (it == input_handlers_.end()) {
+ TRACE_EVENT1("input", "InputHandlerManager::HandleInputEvent",
+ "result", "NoInputHandlerFound");
+ // Oops, we no longer have an interested input handler..
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ }
+
+ InputHandlerProxy* proxy = it->second->input_handler_proxy();
+ return InputEventDispositionToAck(
+ proxy->HandleInputEventWithLatencyInfo(*input_event, latency_info));
+}
+
+void InputHandlerManager::DidOverscroll(int routing_id,
+ const cc::DidOverscrollParams& params) {
+ client_->DidOverscroll(routing_id, params);
+}
+
+
+
+} // namespace content
diff --git a/chromium/content/renderer/input/input_handler_manager.h b/chromium/content/renderer/input/input_handler_manager.h
new file mode 100644
index 00000000000..f2df0415f29
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_manager.h
@@ -0,0 +1,78 @@
+// Copyright 2013 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_INPUT_HANDLER_MANAGER_H_
+#define CONTENT_RENDERER_INPUT_INPUT_HANDLER_MANAGER_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "content/port/common/input_event_ack_state.h"
+#include "content/renderer/render_view_impl.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace cc {
+class InputHandler;
+struct DidOverscrollParams;
+}
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace content {
+
+class InputHandlerWrapper;
+class InputHandlerManagerClient;
+
+// InputHandlerManager class manages InputHandlerProxy instances for
+// the WebViews in this renderer.
+class InputHandlerManager {
+ public:
+ // |message_loop_proxy| is the MessageLoopProxy of the compositor thread. Both
+ // the underlying MessageLoop and supplied |client| must outlive this object.
+ InputHandlerManager(
+ const scoped_refptr<base::MessageLoopProxy>& message_loop_proxy,
+ InputHandlerManagerClient* client);
+ ~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);
+
+ // Callback only from the compositor's thread.
+ void RemoveInputHandler(int routing_id);
+
+ // Called from the compositor's thread.
+ InputEventAckState HandleInputEvent(int routing_id,
+ const blink::WebInputEvent* input_event,
+ ui::LatencyInfo* latency_info);
+
+ // Called from the compositor's thread.
+ void DidOverscroll(int routing_id, const cc::DidOverscrollParams& params);
+
+ private:
+ // Called from the compositor's thread.
+ void AddInputHandlerOnCompositorThread(
+ int routing_id,
+ const scoped_refptr<base::MessageLoopProxy>& main_loop,
+ const base::WeakPtr<cc::InputHandler>& input_handler,
+ const base::WeakPtr<RenderViewImpl>& render_view_impl);
+
+ typedef base::ScopedPtrHashMap<int, // routing_id
+ InputHandlerWrapper> InputHandlerMap;
+ InputHandlerMap input_handlers_;
+
+ scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
+ InputHandlerManagerClient* client_;
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_INPUT_INPUT_HANDLER_MANAGER_H_
diff --git a/chromium/content/renderer/input/input_handler_manager_client.h b/chromium/content/renderer/input/input_handler_manager_client.h
new file mode 100644
index 00000000000..0a36e50ba5d
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_manager_client.h
@@ -0,0 +1,60 @@
+// Copyright 2013 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_INPUT_HANDLER_MANAGER_CLIENT_H_
+#define CONTENT_RENDERER_INPUT_INPUT_HANDLER_MANAGER_CLIENT_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/callback_forward.h"
+#include "content/common/content_export.h"
+#include "ui/gfx/vector2d_f.h"
+
+namespace ui {
+struct LatencyInfo;
+}
+
+namespace cc {
+class InputHandler;
+struct DidOverscrollParams;
+}
+
+namespace blink {
+class WebInputEvent;
+}
+
+namespace content {
+
+class CONTENT_EXPORT InputHandlerManagerClient {
+ public:
+ virtual ~InputHandlerManagerClient() {}
+
+ // The Manager will supply a |handler| when bound to the client. This is valid
+ // until the manager shuts down, at which point it supplies a null |handler|.
+ // The client should only makes calls to |handler| on the compositor thread.
+ typedef base::Callback<
+ InputEventAckState(int /*routing_id*/,
+ const blink::WebInputEvent*,
+ ui::LatencyInfo* latency_info)> Handler;
+
+ // Called from the main thread.
+ virtual void SetBoundHandler(const Handler& handler) = 0;
+
+ // Called from the compositor thread.
+ virtual void DidAddInputHandler(int routing_id,
+ cc::InputHandler* input_handler) = 0;
+ virtual void DidRemoveInputHandler(int routing_id) = 0;
+ virtual void DidOverscroll(int routing_id,
+ const cc::DidOverscrollParams& params) = 0;
+
+ protected:
+ InputHandlerManagerClient() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(InputHandlerManagerClient);
+};
+
+} // namespace content
+
+#endif // CONTENT_COMMON_GPU_INPUT_HANDLER_MANAGER_CLIENT_H_
diff --git a/chromium/content/renderer/input/input_handler_proxy.cc b/chromium/content/renderer/input/input_handler_proxy.cc
new file mode 100644
index 00000000000..ce9ce22a02d
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_proxy.cc
@@ -0,0 +1,477 @@
+// Copyright 2013 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/input_handler_proxy.h"
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "content/renderer/input/input_handler_proxy_client.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/latency_info.h"
+#include "ui/gfx/frame_time.h"
+
+using blink::WebFloatPoint;
+using blink::WebFloatSize;
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebMouseEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebPoint;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace {
+
+// Validate provided event timestamps that interact with animation timestamps.
+const double kBadTimestampDeltaFromNowInS = 60. * 60. * 24. * 7.;
+
+double InSecondsF(const base::TimeTicks& time) {
+ return (time - base::TimeTicks()).InSecondsF();
+}
+
+void SendScrollLatencyUma(const WebInputEvent& event,
+ const ui::LatencyInfo& latency_info) {
+ if (!(event.type == WebInputEvent::GestureScrollBegin ||
+ event.type == WebInputEvent::GestureScrollUpdate ||
+ event.type == WebInputEvent::GestureScrollUpdateWithoutPropagation))
+ return;
+
+ ui::LatencyInfo::LatencyMap::const_iterator it =
+ latency_info.latency_components.find(std::make_pair(
+ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0));
+
+ if (it == latency_info.latency_components.end())
+ return;
+
+ base::TimeDelta delta = base::TimeTicks::HighResNow() - it->second.event_time;
+ for (size_t i = 0; i < it->second.event_count; ++i) {
+ UMA_HISTOGRAM_CUSTOM_COUNTS(
+ "Event.Latency.RendererImpl.GestureScroll2",
+ delta.InMicroseconds(),
+ 0,
+ 1000000,
+ 100);
+ }
+} // namespace
+
+}
+
+namespace content {
+
+InputHandlerProxy::InputHandlerProxy(cc::InputHandler* input_handler)
+ : client_(NULL),
+ input_handler_(input_handler),
+#ifndef NDEBUG
+ expect_scroll_update_end_(false),
+ expect_pinch_update_end_(false),
+#endif
+ gesture_scroll_on_impl_thread_(false),
+ gesture_pinch_on_impl_thread_(false),
+ fling_may_be_active_on_main_thread_(false),
+ fling_overscrolled_horizontally_(false),
+ fling_overscrolled_vertically_(false) {
+ input_handler_->BindToClient(this);
+}
+
+InputHandlerProxy::~InputHandlerProxy() {}
+
+void InputHandlerProxy::WillShutdown() {
+ input_handler_ = NULL;
+ DCHECK(client_);
+ client_->WillShutdown();
+}
+
+void InputHandlerProxy::SetClient(InputHandlerProxyClient* client) {
+ DCHECK(!client_ || !client);
+ client_ = client;
+}
+
+InputHandlerProxy::EventDisposition
+InputHandlerProxy::HandleInputEventWithLatencyInfo(
+ const WebInputEvent& event,
+ ui::LatencyInfo* latency_info) {
+ DCHECK(input_handler_);
+
+ SendScrollLatencyUma(event, *latency_info);
+
+ scoped_ptr<cc::SwapPromiseMonitor> latency_info_swap_promise_monitor =
+ input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info);
+ InputHandlerProxy::EventDisposition disposition = HandleInputEvent(event);
+ return disposition;
+}
+
+InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent(
+ const WebInputEvent& event) {
+ DCHECK(client_);
+ DCHECK(input_handler_);
+
+ if (event.type == WebInputEvent::MouseWheel) {
+ const WebMouseWheelEvent& wheel_event =
+ *static_cast<const WebMouseWheelEvent*>(&event);
+ if (wheel_event.scrollByPage) {
+ // TODO(jamesr): We don't properly handle scroll by page in the compositor
+ // thread, so punt it to the main thread. http://crbug.com/236639
+ return DID_NOT_HANDLE;
+ }
+ cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
+ gfx::Point(wheel_event.x, wheel_event.y), cc::InputHandler::Wheel);
+ switch (scroll_status) {
+ case cc::InputHandler::ScrollStarted: {
+ TRACE_EVENT_INSTANT2(
+ "renderer",
+ "InputHandlerProxy::handle_input wheel scroll",
+ TRACE_EVENT_SCOPE_THREAD,
+ "deltaX",
+ -wheel_event.deltaX,
+ "deltaY",
+ -wheel_event.deltaY);
+ bool did_scroll = input_handler_->ScrollBy(
+ gfx::Point(wheel_event.x, wheel_event.y),
+ gfx::Vector2dF(-wheel_event.deltaX, -wheel_event.deltaY));
+ input_handler_->ScrollEnd();
+ return did_scroll ? DID_HANDLE : DROP_EVENT;
+ }
+ case cc::InputHandler::ScrollIgnored:
+ // TODO(jamesr): This should be DROP_EVENT, but in cases where we fail
+ // to properly sync scrollability it's safer to send the event to the
+ // main thread. Change back to DROP_EVENT once we have synchronization
+ // bugs sorted out.
+ return DID_NOT_HANDLE;
+ case cc::InputHandler::ScrollOnMainThread:
+ return DID_NOT_HANDLE;
+ }
+ } else if (event.type == WebInputEvent::GestureScrollBegin) {
+ DCHECK(!gesture_scroll_on_impl_thread_);
+#ifndef NDEBUG
+ DCHECK(!expect_scroll_update_end_);
+ expect_scroll_update_end_ = true;
+#endif
+ const WebGestureEvent& gesture_event =
+ *static_cast<const WebGestureEvent*>(&event);
+ cc::InputHandler::ScrollStatus scroll_status = input_handler_->ScrollBegin(
+ gfx::Point(gesture_event.x, gesture_event.y),
+ cc::InputHandler::Gesture);
+ switch (scroll_status) {
+ case cc::InputHandler::ScrollStarted:
+ gesture_scroll_on_impl_thread_ = true;
+ return DID_HANDLE;
+ case cc::InputHandler::ScrollOnMainThread:
+ return DID_NOT_HANDLE;
+ case cc::InputHandler::ScrollIgnored:
+ return DROP_EVENT;
+ }
+ } else if (event.type == WebInputEvent::GestureScrollUpdate) {
+#ifndef NDEBUG
+ DCHECK(expect_scroll_update_end_);
+#endif
+
+ if (!gesture_scroll_on_impl_thread_ && !gesture_pinch_on_impl_thread_)
+ return DID_NOT_HANDLE;
+
+ const WebGestureEvent& gesture_event =
+ *static_cast<const WebGestureEvent*>(&event);
+ bool did_scroll = input_handler_->ScrollBy(
+ gfx::Point(gesture_event.x, gesture_event.y),
+ gfx::Vector2dF(-gesture_event.data.scrollUpdate.deltaX,
+ -gesture_event.data.scrollUpdate.deltaY));
+ return did_scroll ? DID_HANDLE : DROP_EVENT;
+ } else if (event.type == WebInputEvent::GestureScrollEnd) {
+#ifndef NDEBUG
+ DCHECK(expect_scroll_update_end_);
+ expect_scroll_update_end_ = false;
+#endif
+ input_handler_->ScrollEnd();
+
+ if (!gesture_scroll_on_impl_thread_)
+ return DID_NOT_HANDLE;
+
+ gesture_scroll_on_impl_thread_ = false;
+ return DID_HANDLE;
+ } else if (event.type == WebInputEvent::GesturePinchBegin) {
+#ifndef NDEBUG
+ DCHECK(!expect_pinch_update_end_);
+ expect_pinch_update_end_ = true;
+#endif
+ input_handler_->PinchGestureBegin();
+ gesture_pinch_on_impl_thread_ = true;
+ return DID_HANDLE;
+ } else if (event.type == WebInputEvent::GesturePinchEnd) {
+#ifndef NDEBUG
+ DCHECK(expect_pinch_update_end_);
+ expect_pinch_update_end_ = false;
+#endif
+ gesture_pinch_on_impl_thread_ = false;
+ input_handler_->PinchGestureEnd();
+ return DID_HANDLE;
+ } else if (event.type == WebInputEvent::GesturePinchUpdate) {
+#ifndef NDEBUG
+ DCHECK(expect_pinch_update_end_);
+#endif
+ const WebGestureEvent& gesture_event =
+ *static_cast<const WebGestureEvent*>(&event);
+ input_handler_->PinchGestureUpdate(
+ gesture_event.data.pinchUpdate.scale,
+ gfx::Point(gesture_event.x, gesture_event.y));
+ return DID_HANDLE;
+ } else if (event.type == WebInputEvent::GestureFlingStart) {
+ const WebGestureEvent& gesture_event =
+ *static_cast<const WebGestureEvent*>(&event);
+ return HandleGestureFling(gesture_event);
+ } else if (event.type == WebInputEvent::GestureFlingCancel) {
+ if (CancelCurrentFling())
+ return DID_HANDLE;
+ else if (!fling_may_be_active_on_main_thread_)
+ return DROP_EVENT;
+ } else if (event.type == WebInputEvent::TouchStart) {
+ const WebTouchEvent& touch_event =
+ *static_cast<const WebTouchEvent*>(&event);
+ for (size_t i = 0; i < touch_event.touchesLength; ++i) {
+ if (touch_event.touches[i].state != WebTouchPoint::StatePressed)
+ continue;
+ if (input_handler_->HaveTouchEventHandlersAt(touch_event.touches[i]
+ .position))
+ return DID_NOT_HANDLE;
+ }
+ return DROP_EVENT;
+ } else if (WebInputEvent::isKeyboardEventType(event.type)) {
+ CancelCurrentFling();
+ }
+
+ return DID_NOT_HANDLE;
+}
+
+InputHandlerProxy::EventDisposition
+InputHandlerProxy::HandleGestureFling(
+ const WebGestureEvent& gesture_event) {
+ cc::InputHandler::ScrollStatus scroll_status;
+
+ if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) {
+ scroll_status = input_handler_->ScrollBegin(
+ gfx::Point(gesture_event.x, gesture_event.y),
+ cc::InputHandler::NonBubblingGesture);
+ } else {
+ if (!gesture_scroll_on_impl_thread_)
+ scroll_status = cc::InputHandler::ScrollOnMainThread;
+ else
+ scroll_status = input_handler_->FlingScrollBegin();
+ }
+
+#ifndef NDEBUG
+ expect_scroll_update_end_ = false;
+#endif
+
+ switch (scroll_status) {
+ case cc::InputHandler::ScrollStarted: {
+ if (gesture_event.sourceDevice == WebGestureEvent::Touchpad)
+ input_handler_->ScrollEnd();
+
+ fling_curve_.reset(client_->CreateFlingAnimationCurve(
+ gesture_event.sourceDevice,
+ WebFloatPoint(gesture_event.data.flingStart.velocityX,
+ gesture_event.data.flingStart.velocityY),
+ blink::WebSize()));
+ fling_overscrolled_horizontally_ = false;
+ fling_overscrolled_vertically_ = false;
+ TRACE_EVENT_ASYNC_BEGIN0(
+ "renderer",
+ "InputHandlerProxy::HandleGestureFling::started",
+ this);
+ if (gesture_event.timeStampSeconds) {
+ fling_parameters_.startTime = gesture_event.timeStampSeconds;
+ DCHECK_LT(fling_parameters_.startTime -
+ InSecondsF(gfx::FrameTime::Now()),
+ kBadTimestampDeltaFromNowInS);
+ }
+ fling_parameters_.delta =
+ WebFloatPoint(gesture_event.data.flingStart.velocityX,
+ gesture_event.data.flingStart.velocityY);
+ fling_parameters_.point = WebPoint(gesture_event.x, gesture_event.y);
+ fling_parameters_.globalPoint =
+ WebPoint(gesture_event.globalX, gesture_event.globalY);
+ fling_parameters_.modifiers = gesture_event.modifiers;
+ fling_parameters_.sourceDevice = gesture_event.sourceDevice;
+ input_handler_->ScheduleAnimation();
+ return DID_HANDLE;
+ }
+ case cc::InputHandler::ScrollOnMainThread: {
+ TRACE_EVENT_INSTANT0("renderer",
+ "InputHandlerProxy::HandleGestureFling::"
+ "scroll_on_main_thread",
+ TRACE_EVENT_SCOPE_THREAD);
+ fling_may_be_active_on_main_thread_ = true;
+ return DID_NOT_HANDLE;
+ }
+ case cc::InputHandler::ScrollIgnored: {
+ TRACE_EVENT_INSTANT0(
+ "renderer",
+ "InputHandlerProxy::HandleGestureFling::ignored",
+ TRACE_EVENT_SCOPE_THREAD);
+ if (gesture_event.sourceDevice == WebGestureEvent::Touchpad) {
+ // We still pass the curve to the main thread if there's nothing
+ // scrollable, in case something
+ // registers a handler before the curve is over.
+ return DID_NOT_HANDLE;
+ }
+ return DROP_EVENT;
+ }
+ }
+ return DID_NOT_HANDLE;
+}
+
+void InputHandlerProxy::Animate(base::TimeTicks time) {
+ if (!fling_curve_)
+ return;
+
+ double monotonic_time_sec = InSecondsF(time);
+ if (!fling_parameters_.startTime) {
+ fling_parameters_.startTime = monotonic_time_sec;
+ input_handler_->ScheduleAnimation();
+ return;
+ }
+
+ if (fling_curve_->apply(monotonic_time_sec - fling_parameters_.startTime,
+ this)) {
+ input_handler_->ScheduleAnimation();
+ } else {
+ TRACE_EVENT_INSTANT0("renderer",
+ "InputHandlerProxy::animate::flingOver",
+ TRACE_EVENT_SCOPE_THREAD);
+ CancelCurrentFling();
+ }
+}
+
+void InputHandlerProxy::MainThreadHasStoppedFlinging() {
+ fling_may_be_active_on_main_thread_ = false;
+}
+
+void InputHandlerProxy::DidOverscroll(const cc::DidOverscrollParams& params) {
+ DCHECK(client_);
+ if (fling_curve_) {
+ static const int kFlingOverscrollThreshold = 1;
+ fling_overscrolled_horizontally_ |=
+ std::abs(params.accumulated_overscroll.x()) >=
+ kFlingOverscrollThreshold;
+ fling_overscrolled_vertically_ |=
+ std::abs(params.accumulated_overscroll.y()) >=
+ kFlingOverscrollThreshold;
+ }
+
+ client_->DidOverscroll(params);
+}
+
+bool InputHandlerProxy::CancelCurrentFling() {
+ bool had_fling_animation = fling_curve_;
+ if (had_fling_animation &&
+ fling_parameters_.sourceDevice == WebGestureEvent::Touchscreen) {
+ input_handler_->ScrollEnd();
+ TRACE_EVENT_ASYNC_END0(
+ "renderer",
+ "InputHandlerProxy::HandleGestureFling::started",
+ this);
+ }
+
+ TRACE_EVENT_INSTANT1("renderer",
+ "InputHandlerProxy::CancelCurrentFling",
+ TRACE_EVENT_SCOPE_THREAD,
+ "had_fling_animation",
+ had_fling_animation);
+ fling_curve_.reset();
+ gesture_scroll_on_impl_thread_ = false;
+ fling_parameters_ = blink::WebActiveWheelFlingParameters();
+ return had_fling_animation;
+}
+
+bool InputHandlerProxy::TouchpadFlingScroll(
+ const WebFloatSize& increment) {
+ WebMouseWheelEvent synthetic_wheel;
+ synthetic_wheel.type = WebInputEvent::MouseWheel;
+ 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);
+ switch (disposition) {
+ case DID_HANDLE:
+ return true;
+ case DROP_EVENT:
+ break;
+ case DID_NOT_HANDLE:
+ TRACE_EVENT_INSTANT0("renderer",
+ "InputHandlerProxy::scrollBy::AbortFling",
+ TRACE_EVENT_SCOPE_THREAD);
+ // If we got a DID_NOT_HANDLE, that means we need to deliver wheels on the
+ // main thread. In this case we need to schedule a commit and transfer the
+ // fling curve over to the main thread and run the rest of the wheels from
+ // there. This can happen when flinging a page that contains a scrollable
+ // subarea that we can't scroll on the thread if the fling starts outside
+ // the subarea but then is flung "under" the pointer.
+ client_->TransferActiveWheelFlingAnimation(fling_parameters_);
+ fling_may_be_active_on_main_thread_ = true;
+ CancelCurrentFling();
+ break;
+ }
+
+ return false;
+}
+
+static gfx::Vector2dF ToClientScrollIncrement(const WebFloatSize& increment) {
+ return gfx::Vector2dF(-increment.width, -increment.height);
+}
+
+void InputHandlerProxy::scrollBy(const WebFloatSize& increment) {
+ WebFloatSize clipped_increment;
+ if (!fling_overscrolled_horizontally_)
+ clipped_increment.width = increment.width;
+ if (!fling_overscrolled_vertically_)
+ clipped_increment.height = increment.height;
+
+ if (clipped_increment == WebFloatSize())
+ return;
+
+ TRACE_EVENT2("renderer",
+ "InputHandlerProxy::scrollBy",
+ "x",
+ clipped_increment.width,
+ "y",
+ clipped_increment.height);
+
+ bool did_scroll = false;
+
+ switch (fling_parameters_.sourceDevice) {
+ case WebGestureEvent::Touchpad:
+ did_scroll = TouchpadFlingScroll(clipped_increment);
+ break;
+ case WebGestureEvent::Touchscreen:
+ clipped_increment = ToClientScrollIncrement(clipped_increment);
+ did_scroll = input_handler_->ScrollBy(fling_parameters_.point,
+ clipped_increment);
+ break;
+ }
+
+ if (did_scroll) {
+ fling_parameters_.cumulativeScroll.width += clipped_increment.width;
+ fling_parameters_.cumulativeScroll.height += clipped_increment.height;
+ }
+}
+
+void InputHandlerProxy::notifyCurrentFlingVelocity(
+ const WebFloatSize& velocity) {
+ TRACE_EVENT2("renderer",
+ "InputHandlerProxy::notifyCurrentFlingVelocity",
+ "vx",
+ velocity.width,
+ "vy",
+ velocity.height);
+ input_handler_->NotifyCurrentFlingVelocity(ToClientScrollIncrement(velocity));
+}
+
+} // namespace content
diff --git a/chromium/content/renderer/input/input_handler_proxy.h b/chromium/content/renderer/input/input_handler_proxy.h
new file mode 100644
index 00000000000..46f88ab5ea4
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_proxy.h
@@ -0,0 +1,95 @@
+// Copyright 2013 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_INPUT_HANDLER_PROXY_H_
+#define CONTENT_RENDERER_INPUT_INPUT_HANDLER_PROXY_H_
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/input/input_handler.h"
+#include "content/common/content_export.h"
+#include "third_party/WebKit/public/platform/WebGestureCurve.h"
+#include "third_party/WebKit/public/platform/WebGestureCurveTarget.h"
+#include "third_party/WebKit/public/web/WebActiveWheelFlingParameters.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace content {
+
+class InputHandlerProxyClient;
+
+// This class is a proxy between the content input event filtering and the
+// compositor's input handling logic. InputHandlerProxy instances live entirely
+// on the compositor thread. Each InputHandler instance handles input events
+// intended for a specific WebWidget.
+class CONTENT_EXPORT InputHandlerProxy
+ : public cc::InputHandlerClient,
+ public NON_EXPORTED_BASE(blink::WebGestureCurveTarget) {
+ public:
+ explicit InputHandlerProxy(cc::InputHandler* input_handler);
+ virtual ~InputHandlerProxy();
+
+ void SetClient(InputHandlerProxyClient* client);
+
+ enum EventDisposition {
+ DID_HANDLE,
+ DID_NOT_HANDLE,
+ DROP_EVENT
+ };
+ EventDisposition HandleInputEventWithLatencyInfo(
+ const blink::WebInputEvent& event,
+ ui::LatencyInfo* latency_info);
+ EventDisposition HandleInputEvent(const blink::WebInputEvent& event);
+
+ // cc::InputHandlerClient implementation.
+ virtual void WillShutdown() OVERRIDE;
+ virtual void Animate(base::TimeTicks time) OVERRIDE;
+ virtual void MainThreadHasStoppedFlinging() OVERRIDE;
+ virtual void DidOverscroll(const cc::DidOverscrollParams& params) OVERRIDE;
+
+ // blink::WebGestureCurveTarget implementation.
+ virtual void scrollBy(const blink::WebFloatSize& offset);
+ virtual void notifyCurrentFlingVelocity(const blink::WebFloatSize& velocity);
+
+ bool gesture_scroll_on_impl_thread_for_testing() const {
+ return gesture_scroll_on_impl_thread_;
+ }
+
+ private:
+ EventDisposition HandleGestureFling(const blink::WebGestureEvent& event);
+
+ // Returns true if we scrolled by the increment.
+ bool TouchpadFlingScroll(const blink::WebFloatSize& increment);
+
+ // Returns true if we actually had an active fling to cancel.
+ bool CancelCurrentFling();
+
+ scoped_ptr<blink::WebGestureCurve> fling_curve_;
+ // Parameters for the active fling animation, stored in case we need to
+ // transfer it out later.
+ blink::WebActiveWheelFlingParameters fling_parameters_;
+
+ InputHandlerProxyClient* client_;
+ cc::InputHandler* input_handler_;
+
+#ifndef NDEBUG
+ bool expect_scroll_update_end_;
+ bool expect_pinch_update_end_;
+#endif
+ bool gesture_scroll_on_impl_thread_;
+ bool gesture_pinch_on_impl_thread_;
+ // This is always false when there are no flings on the main thread, but
+ // conservative in the sense that we might not be actually flinging when it is
+ // true.
+ bool fling_may_be_active_on_main_thread_;
+ // The axes on which the current fling has overshot the bounds of the content.
+ bool fling_overscrolled_horizontally_;
+ bool fling_overscrolled_vertically_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputHandlerProxy);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_INPUT_INPUT_HANDLER_PROXY_H_
diff --git a/chromium/content/renderer/input/input_handler_proxy_client.h b/chromium/content/renderer/input/input_handler_proxy_client.h
new file mode 100644
index 00000000000..7ef1121bf7c
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_proxy_client.h
@@ -0,0 +1,43 @@
+// Copyright 2013 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_INPUT_HANDLER_PROXY_CLIENT_H_
+#define CONTENT_RENDERER_INPUT_INPUT_HANDLER_PROXY_CLIENT_H_
+
+namespace blink {
+class WebGestureCurve;
+struct WebActiveWheelFlingParameters;
+struct WebFloatPoint;
+struct WebSize;
+}
+
+namespace content {
+
+// All callbacks invoked from the compositor thread.
+class InputHandlerProxyClient {
+ public:
+ // Called just before the InputHandlerProxy shuts down.
+ virtual void WillShutdown() = 0;
+
+ // Transfers an active wheel fling animation initiated by a previously
+ // handled input event out to the client.
+ virtual void TransferActiveWheelFlingAnimation(
+ const blink::WebActiveWheelFlingParameters& params) = 0;
+
+ // Creates a new fling animation curve instance for device |device_source|
+ // with |velocity| and already scrolled |cumulative_scroll| pixels.
+ virtual blink::WebGestureCurve* CreateFlingAnimationCurve(
+ int device_source,
+ const blink::WebFloatPoint& velocity,
+ const blink::WebSize& cumulative_scroll) = 0;
+
+ virtual void DidOverscroll(const cc::DidOverscrollParams& params) = 0;
+
+ protected:
+ virtual ~InputHandlerProxyClient() {}
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_INPUT_INPUT_HANDLER_PROXY_CLIENT_H_
diff --git a/chromium/content/renderer/input/input_handler_proxy_unittest.cc b/chromium/content/renderer/input/input_handler_proxy_unittest.cc
new file mode 100644
index 00000000000..c7cc9168c18
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_proxy_unittest.cc
@@ -0,0 +1,1152 @@
+// Copyright 2013 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/input_handler_proxy.h"
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/swap_promise_monitor.h"
+#include "content/renderer/input/input_handler_proxy_client.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebFloatPoint.h"
+#include "third_party/WebKit/public/platform/WebFloatSize.h"
+#include "third_party/WebKit/public/platform/WebGestureCurve.h"
+#include "third_party/WebKit/public/platform/WebPoint.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+#include "ui/events/latency_info.h"
+
+using blink::WebActiveWheelFlingParameters;
+using blink::WebFloatPoint;
+using blink::WebFloatSize;
+using blink::WebGestureEvent;
+using blink::WebInputEvent;
+using blink::WebMouseWheelEvent;
+using blink::WebPoint;
+using blink::WebSize;
+using blink::WebTouchEvent;
+using blink::WebTouchPoint;
+
+namespace content {
+namespace {
+
+class MockInputHandler : public cc::InputHandler {
+ public:
+ MockInputHandler() {}
+ virtual ~MockInputHandler() {}
+
+ MOCK_METHOD0(PinchGestureBegin, void());
+ MOCK_METHOD2(PinchGestureUpdate,
+ void(float magnify_delta, gfx::Point anchor));
+ MOCK_METHOD0(PinchGestureEnd, void());
+
+ MOCK_METHOD0(ScheduleAnimation, void());
+
+ MOCK_METHOD2(ScrollBegin,
+ ScrollStatus(gfx::Point viewport_point,
+ cc::InputHandler::ScrollInputType type));
+ MOCK_METHOD2(ScrollBy,
+ bool(gfx::Point viewport_point, gfx::Vector2dF scroll_delta));
+ MOCK_METHOD2(ScrollVerticallyByPage,
+ bool(gfx::Point viewport_point,
+ cc::ScrollDirection direction));
+ MOCK_METHOD0(ScrollEnd, void());
+ MOCK_METHOD0(FlingScrollBegin, cc::InputHandler::ScrollStatus());
+
+ virtual scoped_ptr<cc::SwapPromiseMonitor>
+ CreateLatencyInfoSwapPromiseMonitor(ui::LatencyInfo* latency) OVERRIDE {
+ return scoped_ptr<cc::SwapPromiseMonitor>();
+ }
+
+ virtual void BindToClient(cc::InputHandlerClient* client) OVERRIDE {}
+
+ virtual void StartPageScaleAnimation(gfx::Vector2d target_offset,
+ bool anchor_point,
+ float page_scale,
+ base::TimeDelta duration) OVERRIDE {}
+
+ virtual void NotifyCurrentFlingVelocity(gfx::Vector2dF velocity) OVERRIDE {}
+ virtual void MouseMoveAt(gfx::Point mouse_position) OVERRIDE {}
+
+ MOCK_METHOD1(HaveTouchEventHandlersAt,
+ bool(gfx::Point point));
+
+ virtual void SetRootLayerScrollOffsetDelegate(
+ cc::LayerScrollOffsetDelegate* root_layer_scroll_offset_delegate)
+ OVERRIDE {}
+
+ virtual void OnRootLayerDelegatedScrollOffsetChanged() OVERRIDE {}
+
+ DISALLOW_COPY_AND_ASSIGN(MockInputHandler);
+};
+
+// A simple WebGestureCurve implementation that flings at a constant velocity
+// indefinitely.
+class FakeWebGestureCurve : public blink::WebGestureCurve {
+ public:
+ FakeWebGestureCurve(const blink::WebFloatPoint& velocity,
+ const blink::WebSize& cumulative_scroll)
+ : velocity_(velocity), cumulative_scroll_(cumulative_scroll) {}
+
+ virtual ~FakeWebGestureCurve() {}
+
+ // Returns false if curve has finished and can no longer be applied.
+ virtual bool apply(double time, blink::WebGestureCurveTarget* target) {
+ blink::WebSize displacement(velocity_.x * time, velocity_.y * time);
+ blink::WebFloatSize increment(
+ displacement.width - cumulative_scroll_.width,
+ displacement.height - cumulative_scroll_.height);
+ cumulative_scroll_ = displacement;
+ // scrollBy() could delete this curve if the animation is over, so don't
+ // touch any member variables after making that call.
+ target->scrollBy(increment);
+ return true;
+ }
+
+ private:
+ blink::WebFloatPoint velocity_;
+ blink::WebSize cumulative_scroll_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeWebGestureCurve);
+};
+
+class MockInputHandlerProxyClient
+ : public content::InputHandlerProxyClient {
+ public:
+ MockInputHandlerProxyClient() {}
+ virtual ~MockInputHandlerProxyClient() {}
+
+ virtual void WillShutdown() OVERRIDE {}
+
+ MOCK_METHOD1(TransferActiveWheelFlingAnimation,
+ void(const WebActiveWheelFlingParameters&));
+
+ virtual blink::WebGestureCurve* CreateFlingAnimationCurve(
+ int deviceSource,
+ const WebFloatPoint& velocity,
+ const WebSize& cumulative_scroll) OVERRIDE {
+ return new FakeWebGestureCurve(velocity, cumulative_scroll);
+ }
+
+ virtual void DidOverscroll(const cc::DidOverscrollParams& params) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockInputHandlerProxyClient);
+};
+
+class InputHandlerProxyTest : public testing::Test {
+ public:
+ InputHandlerProxyTest()
+ : expected_disposition_(InputHandlerProxy::DID_HANDLE) {
+ input_handler_.reset(
+ new content::InputHandlerProxy(&mock_input_handler_));
+ input_handler_->SetClient(&mock_client_);
+ }
+
+ ~InputHandlerProxyTest() {
+ input_handler_.reset();
+ }
+
+// This is defined as a macro because when an expectation is not satisfied the
+// only output you get
+// out of gmock is the line number that set the expectation.
+#define VERIFY_AND_RESET_MOCKS() \
+ do { \
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); \
+ testing::Mock::VerifyAndClearExpectations(&mock_client_); \
+ } while (false)
+
+ protected:
+ testing::StrictMock<MockInputHandler> mock_input_handler_;
+ scoped_ptr<content::InputHandlerProxy> input_handler_;
+ testing::StrictMock<MockInputHandlerProxyClient> mock_client_;
+ WebGestureEvent gesture_;
+
+ InputHandlerProxy::EventDisposition expected_disposition_;
+};
+
+TEST_F(InputHandlerProxyTest, MouseWheelByPageMainThread) {
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ WebMouseWheelEvent wheel;
+ wheel.type = WebInputEvent::MouseWheel;
+ wheel.scrollByPage = true;
+
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel));
+ testing::Mock::VerifyAndClearExpectations(&mock_client_);
+}
+
+TEST_F(InputHandlerProxyTest, GestureScrollStarted) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ EXPECT_EQ(expected_disposition_,input_handler_->HandleInputEvent(gesture_));
+
+ // The event should not be marked as handled if scrolling is not possible.
+ expected_disposition_ = InputHandlerProxy::DROP_EVENT;
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollUpdate;
+ gesture_.data.scrollUpdate.deltaY =
+ -40; // -Y means scroll down - i.e. in the +Y direction.
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::y, testing::Gt(0))))
+ .WillOnce(testing::Return(false));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // Mark the event as handled if scroll happens.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollUpdate;
+ gesture_.data.scrollUpdate.deltaY =
+ -40; // -Y means scroll down - i.e. in the +Y direction.
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::y, testing::Gt(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollEnd;
+ gesture_.data.scrollUpdate.deltaY = 0;
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureScrollOnMainThread) {
+ // We should send all events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollUpdate;
+ gesture_.data.scrollUpdate.deltaY = 40;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollEnd;
+ gesture_.data.scrollUpdate.deltaY = 0;
+ EXPECT_CALL(mock_input_handler_, ScrollEnd()).WillOnce(testing::Return());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureScrollIgnored) {
+ // We shouldn't handle the GestureScrollBegin.
+ // Instead, we should get a DROP_EVENT result, indicating
+ // that we could determine that there's nothing that could scroll or otherwise
+ // react to this gesture sequence and thus we should drop the whole gesture
+ // sequence on the floor, except for the ScrollEnd.
+ expected_disposition_ = InputHandlerProxy::DROP_EVENT;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ gesture_.type = WebInputEvent::GestureScrollEnd;
+ EXPECT_CALL(mock_input_handler_, ScrollEnd()).WillOnce(testing::Return());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GesturePinch) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchBegin;
+ EXPECT_CALL(mock_input_handler_, PinchGestureBegin());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchUpdate;
+ gesture_.data.pinchUpdate.scale = 1.5;
+ gesture_.x = 7;
+ gesture_.y = 13;
+ EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.5, gfx::Point(7, 13)));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchUpdate;
+ gesture_.data.pinchUpdate.scale = 0.5;
+ gesture_.x = 9;
+ gesture_.y = 6;
+ EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(.5, gfx::Point(9, 6)));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchEnd;
+ EXPECT_CALL(mock_input_handler_, PinchGestureEnd());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GesturePinchAfterScrollOnMainThread) {
+ // Scrolls will start by being sent to the main thread.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollUpdate;
+ gesture_.data.scrollUpdate.deltaY = 40;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // However, after the pinch gesture starts, they should go to the impl
+ // thread.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchBegin;
+ EXPECT_CALL(mock_input_handler_, PinchGestureBegin());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchUpdate;
+ gesture_.data.pinchUpdate.scale = 1.5;
+ gesture_.x = 7;
+ gesture_.y = 13;
+ EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(1.5, gfx::Point(7, 13)));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollUpdate;
+ gesture_.data.scrollUpdate.deltaY =
+ -40; // -Y means scroll down - i.e. in the +Y direction.
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::y, testing::Gt(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchUpdate;
+ gesture_.data.pinchUpdate.scale = 0.5;
+ gesture_.x = 9;
+ gesture_.y = 6;
+ EXPECT_CALL(mock_input_handler_, PinchGestureUpdate(.5, gfx::Point(9, 6)));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GesturePinchEnd;
+ EXPECT_CALL(mock_input_handler_, PinchGestureEnd());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // After the pinch gesture ends, they should go to back to the main
+ // thread.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ gesture_.type = WebInputEvent::GestureScrollEnd;
+ gesture_.data.scrollUpdate.deltaY = 0;
+ EXPECT_CALL(mock_input_handler_, ScrollEnd())
+ .WillOnce(testing::Return());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingStartedTouchpad) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ gesture_.data.flingStart.velocityX = 10;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // Verify that a GestureFlingCancel during an animation cancels it.
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingOnMainThreadTouchpad) {
+ // We should send all events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // Since we returned ScrollStatusOnMainThread from scrollBegin, ensure the
+ // input handler knows it's scrolling off the impl thread
+ ASSERT_FALSE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // Even if we didn't start a fling ourselves, we still need to send the cancel
+ // event to the widget.
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchpad) {
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
+
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ expected_disposition_ = InputHandlerProxy::DROP_EVENT;
+ VERIFY_AND_RESET_MOCKS();
+
+ // Since the previous fling was ignored, we should also be dropping the next
+ // fling_cancel.
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
+ // 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);
+ int modifiers = 7;
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ gesture_.x = fling_point.x;
+ gesture_.y = fling_point.y;
+ gesture_.globalX = fling_global_point.x;
+ gesture_.globalY = fling_global_point.y;
+ gesture_.modifiers = modifiers;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ // 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_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // The second call should start scrolling in the -X direction.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Let's say on the third call we hit a non-scrollable region. We should abort
+ // the fling and not scroll.
+ // 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_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+ EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd()).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
+ // *) cumulativeScroll depends on the curve, but since we've animated in the
+ // -X direction the X value should be < 0
+ 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::Field(&WebSize::width, testing::Gt(0))))));
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ testing::Mock::VerifyAndClearExpectations(&mock_client_);
+
+ // Since we've aborted the fling, the next animation should be a no-op and
+ // should not result in another
+ // frame being requested.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation()).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->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_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ // Start a gesture fling in the -X direction with zero Y movement.
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+ WebPoint fling_point = WebPoint(7, 13);
+ WebPoint fling_global_point = WebPoint(17, 23);
+ int modifiers = 1;
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ gesture_.x = fling_point.x;
+ gesture_.y = fling_point.y;
+ gesture_.globalX = fling_global_point.x;
+ gesture_.globalY = fling_global_point.y;
+ gesture_.modifiers = modifiers;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Start the fling animation at time 10. This shouldn't actually scroll, just
+ // establish a start time.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // The second call should start scrolling in the -X direction.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Let's say on the third call we hit a non-scrollable region. We should abort
+ // the fling and not scroll.
+ // 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_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+ EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd()).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
+ // *) cumulativeScroll depends on the curve, but since we've animated in the
+ // -X direction the X value should be < 0
+ 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::Field(&WebSize::width, testing::Gt(0))))));
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ testing::Mock::VerifyAndClearExpectations(&mock_client_);
+
+ // Since we've aborted the fling, the next animation should be a no-op and
+ // should not result in another
+ // frame being requested.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation()).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // 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();
+ input_handler_->MainThreadHasStoppedFlinging();
+
+ // Start a second gesture fling, this time in the +Y direction with no X.
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ fling_delta = WebFloatPoint(0, -1000);
+ fling_point = WebPoint(95, 87);
+ fling_global_point = WebPoint(32, 71);
+ modifiers = 2;
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ gesture_.sourceDevice = WebGestureEvent::Touchpad;
+ gesture_.x = fling_point.x;
+ gesture_.y = fling_point.y;
+ gesture_.globalX = fling_global_point.x;
+ gesture_.globalY = fling_global_point.y;
+ gesture_.modifiers = modifiers;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Start the second fling animation at time 30.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .Times(0);
+ time = base::TimeTicks() + base::TimeDelta::FromSeconds(30);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Tick the second fling once normally.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::y, testing::Gt(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Then abort the second fling.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+ EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_, testing::_)).Times(0);
+ EXPECT_CALL(mock_input_handler_, ScrollEnd()).Times(0);
+
+ // We should get parameters from the second fling, nothing from the first
+ // fling should "leak".
+ 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(30)),
+ testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
+ testing::Field(&WebSize::height, testing::Lt(0))))));
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingStartedTouchscreen) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ gesture_.data.flingStart.velocityX = 10;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+
+ // Verify that a GestureFlingCancel during an animation cancels it.
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingOnMainThreadTouchscreen) {
+ // We should send all events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollOnMainThread));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, FlingScrollBegin()).Times(0);
+
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // Even if we didn't start a fling ourselves, we still need to send the cancel
+ // event to the widget.
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingIgnoredTouchscreen) {
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ expected_disposition_ = InputHandlerProxy::DROP_EVENT;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+ .WillOnce(testing::Return(cc::InputHandler::ScrollIgnored));
+
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // Even if we didn't start a fling ourselves, we still need to send the cancel
+ // event to the widget.
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingAnimatesTouchscreen) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ 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);
+ int modifiers = 7;
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ gesture_.x = fling_point.x;
+ gesture_.y = fling_point.y;
+ gesture_.globalX = fling_global_point.x;
+ gesture_.globalY = fling_global_point.y;
+ gesture_.modifiers = modifiers;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ // 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_CALL(mock_input_handler_, ScheduleAnimation());
+ base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // The second call should start scrolling in the -X direction.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+ .WillOnce(testing::Return(true));
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingWithValidTimestamp) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ VERIFY_AND_RESET_MOCKS();
+
+ // On the fling start, we should schedule an animation but not actually start
+ // scrolling.
+ base::TimeDelta startTimeOffset = base::TimeDelta::FromMilliseconds(10);
+ gesture_.type = WebInputEvent::GestureFlingStart;
+ WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
+ WebPoint fling_point = WebPoint(7, 13);
+ WebPoint fling_global_point = WebPoint(17, 23);
+ int modifiers = 7;
+ gesture_.timeStampSeconds = startTimeOffset.InSecondsF();
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ gesture_.x = fling_point.x;
+ gesture_.y = fling_point.y;
+ gesture_.globalX = fling_global_point.x;
+ gesture_.globalY = fling_global_point.y;
+ gesture_.modifiers = modifiers;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ // With a valid time stamp, the first animate call should skip start time
+ // initialization and immediately begin scroll update production. This reduces
+ // the likelihood of a hitch between the scroll preceding the fling and
+ // the first scroll generated by the fling.
+ // Scrolling should start in the -X direction.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+ .WillOnce(testing::Return(true));
+ base::TimeTicks time = base::TimeTicks() + 2 * startTimeOffset;
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+}
+
+TEST_F(InputHandlerProxyTest,
+ GestureScrollOnImplThreadFlagClearedAfterFling) {
+ // We shouldn't send any events to the widget for this gesture.
+ expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+
+ gesture_.type = WebInputEvent::GestureScrollBegin;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // After sending a GestureScrollBegin, the member variable
+ // |gesture_scroll_on_impl_thread_| should be true.
+ EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+ 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);
+ int modifiers = 7;
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ gesture_.sourceDevice = WebGestureEvent::Touchscreen;
+ gesture_.x = fling_point.x;
+ gesture_.y = fling_point.y;
+ gesture_.globalX = fling_global_point.x;
+ gesture_.globalY = fling_global_point.y;
+ gesture_.modifiers = modifiers;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, FlingScrollBegin())
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // |gesture_scroll_on_impl_thread_| should still be true after
+ // a GestureFlingStart is sent.
+ EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+ // 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_CALL(mock_input_handler_, ScheduleAnimation());
+ base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // The second call should start scrolling in the -X direction.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::x, testing::Lt(0))))
+ .WillOnce(testing::Return(true));
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ gesture_.type = WebInputEvent::GestureFlingCancel;
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+ // |gesture_scroll_on_impl_thread_| should be false once
+ // the fling has finished (note no GestureScrollEnd has been sent).
+ EXPECT_TRUE(!input_handler_->gesture_scroll_on_impl_thread_for_testing());
+}
+
+TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
+ // 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, 1000);
+ gesture_.data.flingStart.velocityX = fling_delta.x;
+ gesture_.data.flingStart.velocityY = fling_delta.y;
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // The first animate doesn't cause any scrolling.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ base::TimeTicks time = base::TimeTicks() + base::TimeDelta::FromSeconds(10);
+ input_handler_->Animate(time);
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // The second animate starts scrolling in the positive X and Y directions.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::y, testing::Lt(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+
+ // Simulate hitting the bottom content edge.
+ cc::DidOverscrollParams overscroll_params;
+ overscroll_params.accumulated_overscroll = gfx::Vector2dF(0, 100);
+ overscroll_params.current_fling_velocity = gfx::Vector2dF(0, 10);
+ input_handler_->DidOverscroll(overscroll_params);
+
+ // The next call to animate will no longer scroll vertically.
+ EXPECT_CALL(mock_input_handler_, ScheduleAnimation());
+ EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+ .WillOnce(testing::Return(cc::InputHandler::ScrollStarted));
+ EXPECT_CALL(mock_input_handler_,
+ ScrollBy(testing::_,
+ testing::Property(&gfx::Vector2dF::y, testing::Eq(0))))
+ .WillOnce(testing::Return(true));
+ EXPECT_CALL(mock_input_handler_, ScrollEnd());
+ time += base::TimeDelta::FromMilliseconds(100);
+ input_handler_->Animate(time);
+ testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
+}
+
+TEST_F(InputHandlerProxyTest, MultiTouchPointHitTestNegative) {
+ // None of the three touch points fall in the touch region. So the event
+ // should be dropped.
+ expected_disposition_ = InputHandlerProxy::DROP_EVENT;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_,
+ HaveTouchEventHandlersAt(
+ testing::Property(&gfx::Point::x, testing::Gt(0))))
+ .WillOnce(testing::Return(false));
+ EXPECT_CALL(mock_input_handler_,
+ HaveTouchEventHandlersAt(
+ testing::Property(&gfx::Point::x, testing::Lt(0))))
+ .WillOnce(testing::Return(false));
+
+ WebTouchEvent touch;
+ touch.type = WebInputEvent::TouchStart;
+
+ touch.touchesLength = 3;
+ touch.touches[0].state = WebTouchPoint::StateStationary;
+ touch.touches[0].screenPosition = WebPoint();
+ touch.touches[0].position = WebPoint();
+
+ touch.touches[1].state = WebTouchPoint::StatePressed;
+ touch.touches[1].screenPosition = WebPoint(10, 10);
+ touch.touches[1].position = WebPoint(10, 10);
+
+ touch.touches[2].state = WebTouchPoint::StatePressed;
+ touch.touches[2].screenPosition = WebPoint(-10, 10);
+ touch.touches[2].position = WebPoint(-10, 10);
+
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch));
+}
+
+TEST_F(InputHandlerProxyTest, MultiTouchPointHitTestPositive) {
+ // One of the touch points is on a touch-region. So the event should be sent
+ // to the main thread.
+ expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE;
+ VERIFY_AND_RESET_MOCKS();
+
+ EXPECT_CALL(mock_input_handler_,
+ HaveTouchEventHandlersAt(
+ testing::Property(&gfx::Point::x, testing::Eq(0))))
+ .WillOnce(testing::Return(false));
+ EXPECT_CALL(mock_input_handler_,
+ HaveTouchEventHandlersAt(
+ testing::Property(&gfx::Point::x, testing::Gt(0))))
+ .WillOnce(testing::Return(true));
+ // Since the second touch point hits a touch-region, there should be no
+ // hit-testing for the third touch point.
+
+ WebTouchEvent touch;
+ touch.type = WebInputEvent::TouchStart;
+
+ touch.touchesLength = 3;
+ touch.touches[0].state = WebTouchPoint::StatePressed;
+ touch.touches[0].screenPosition = WebPoint();
+ touch.touches[0].position = WebPoint();
+
+ touch.touches[1].state = WebTouchPoint::StatePressed;
+ touch.touches[1].screenPosition = WebPoint(10, 10);
+ touch.touches[1].position = WebPoint(10, 10);
+
+ touch.touches[2].state = WebTouchPoint::StatePressed;
+ touch.touches[2].screenPosition = WebPoint(-10, 10);
+ touch.touches[2].position = WebPoint(-10, 10);
+
+ EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(touch));
+}
+
+} // namespace
+} // namespace content
diff --git a/chromium/content/renderer/input/input_handler_wrapper.cc b/chromium/content/renderer/input/input_handler_wrapper.cc
new file mode 100644
index 00000000000..14e23ebf3ad
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_wrapper.cc
@@ -0,0 +1,58 @@
+// Copyright 2013 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/input_handler_wrapper.h"
+
+#include "base/message_loop/message_loop_proxy.h"
+#include "content/renderer/input/input_event_filter.h"
+#include "content/renderer/input/input_handler_manager.h"
+#include "third_party/WebKit/public/platform/Platform.h"
+
+namespace content {
+
+InputHandlerWrapper::InputHandlerWrapper(
+ InputHandlerManager* input_handler_manager,
+ int routing_id,
+ const scoped_refptr<base::MessageLoopProxy>& main_loop,
+ const base::WeakPtr<cc::InputHandler>& input_handler,
+ const base::WeakPtr<RenderViewImpl>& render_view_impl)
+ : input_handler_manager_(input_handler_manager),
+ routing_id_(routing_id),
+ input_handler_proxy_(input_handler.get()),
+ main_loop_(main_loop),
+ render_view_impl_(render_view_impl) {
+ DCHECK(input_handler);
+ input_handler_proxy_.SetClient(this);
+}
+
+InputHandlerWrapper::~InputHandlerWrapper() {
+ input_handler_proxy_.SetClient(NULL);
+}
+
+void InputHandlerWrapper::TransferActiveWheelFlingAnimation(
+ const blink::WebActiveWheelFlingParameters& params) {
+ main_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&RenderViewImpl::TransferActiveWheelFlingAnimation,
+ render_view_impl_,
+ params));
+}
+
+void InputHandlerWrapper::WillShutdown() {
+ input_handler_manager_->RemoveInputHandler(routing_id_);
+}
+
+blink::WebGestureCurve* InputHandlerWrapper::CreateFlingAnimationCurve(
+ int deviceSource,
+ const blink::WebFloatPoint& velocity,
+ const blink::WebSize& cumulative_scroll) {
+ return blink::Platform::current()->createFlingAnimationCurve(
+ deviceSource, velocity, cumulative_scroll);
+}
+
+void InputHandlerWrapper::DidOverscroll(const cc::DidOverscrollParams& params) {
+ input_handler_manager_->DidOverscroll(routing_id_, params);
+}
+
+} // namespace content
diff --git a/chromium/content/renderer/input/input_handler_wrapper.h b/chromium/content/renderer/input/input_handler_wrapper.h
new file mode 100644
index 00000000000..562012ace1d
--- /dev/null
+++ b/chromium/content/renderer/input/input_handler_wrapper.h
@@ -0,0 +1,52 @@
+// Copyright 2013 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_INPUT_HANDLER_WRAPPER_H_
+#define CONTENT_RENDERER_INPUT_INPUT_HANDLER_WRAPPER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "content/renderer/input/input_handler_manager.h"
+#include "content/renderer/input/input_handler_proxy.h"
+#include "content/renderer/input/input_handler_proxy_client.h"
+
+namespace content {
+
+// This class lives on the compositor thread.
+class InputHandlerWrapper : public InputHandlerProxyClient {
+ public:
+ InputHandlerWrapper(InputHandlerManager* input_handler_manager,
+ int routing_id,
+ const scoped_refptr<base::MessageLoopProxy>& main_loop,
+ const base::WeakPtr<cc::InputHandler>& input_handler,
+ const base::WeakPtr<RenderViewImpl>& render_view_impl);
+ virtual ~InputHandlerWrapper();
+
+ int routing_id() const { return routing_id_; }
+ InputHandlerProxy* input_handler_proxy() { return &input_handler_proxy_; }
+
+ // InputHandlerProxyClient implementation.
+ virtual void WillShutdown() OVERRIDE;
+ virtual void TransferActiveWheelFlingAnimation(
+ const blink::WebActiveWheelFlingParameters& params) OVERRIDE;
+ virtual blink::WebGestureCurve* CreateFlingAnimationCurve(
+ int deviceSource,
+ const blink::WebFloatPoint& velocity,
+ const blink::WebSize& cumulativeScroll) OVERRIDE;
+ virtual void DidOverscroll(const cc::DidOverscrollParams& params) OVERRIDE;
+
+ private:
+ InputHandlerManager* input_handler_manager_;
+ int routing_id_;
+ InputHandlerProxy input_handler_proxy_;
+ scoped_refptr<base::MessageLoopProxy> main_loop_;
+
+ // Can only be accessed on the main thread.
+ base::WeakPtr<RenderViewImpl> render_view_impl_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputHandlerWrapper);
+};
+
+} // namespace content
+
+#endif // CONTENT_RENDERER_INPUT_INPUT_HANDLER_WRAPPER_H_