1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
10 #include "base/message_loop/message_loop.h"
11 #include "content/common/input_messages.h"
12 #include "content/common/view_messages.h"
13 #include "content/renderer/gpu/input_event_filter.h"
14 #include "ipc/ipc_test_sink.h"
15 #include "testing/gtest/include/gtest/gtest.h"
17 using WebKit::WebInputEvent;
18 using WebKit::WebMouseEvent;
23 const int kTestRoutingID = 13;
25 class InputEventRecorder {
29 handle_events_(false),
30 send_to_widget_(false) {
33 void set_filter(InputEventFilter* filter) { filter_ = filter; }
34 void set_handle_events(bool value) { handle_events_ = value; }
35 void set_send_to_widget(bool value) { send_to_widget_ = value; }
37 size_t record_count() const { return records_.size(); }
39 const WebInputEvent* record_at(size_t i) const {
40 const Record& record = records_[i];
41 return reinterpret_cast<const WebInputEvent*>(&record.event_data[0]);
48 InputEventAckState HandleInputEvent(int routing_id,
49 const WebInputEvent* event,
50 const ui::LatencyInfo& latency_info) {
51 DCHECK_EQ(kTestRoutingID, routing_id);
53 records_.push_back(Record(event));
56 return INPUT_EVENT_ACK_STATE_CONSUMED;
58 return send_to_widget_ ? INPUT_EVENT_ACK_STATE_NOT_CONSUMED
59 : INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
65 Record(const WebInputEvent* event) {
66 const char* ptr = reinterpret_cast<const char*>(event);
67 event_data.assign(ptr, ptr + event->size);
69 std::vector<char> event_data;
72 InputEventFilter* filter_;
75 std::vector<Record> records_;
78 class IPCMessageRecorder : public IPC::Listener {
80 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
81 messages_.push_back(message);
85 size_t message_count() const { return messages_.size(); }
87 const IPC::Message& message_at(size_t i) const {
96 std::vector<IPC::Message> messages_;
99 void InitMouseEvent(WebMouseEvent* event, WebInputEvent::Type type,
101 // Avoid valgrind false positives by initializing memory completely.
102 memset(event, 0, sizeof(*event));
104 new (event) WebMouseEvent();
110 void AddMessagesToFilter(IPC::ChannelProxy::MessageFilter* message_filter,
111 const std::vector<IPC::Message>& events) {
112 for (size_t i = 0; i < events.size(); ++i) {
113 message_filter->OnMessageReceived(events[i]);
116 base::MessageLoop::current()->RunUntilIdle();
119 void AddEventsToFilter(IPC::ChannelProxy::MessageFilter* message_filter,
120 const WebMouseEvent events[],
122 std::vector<IPC::Message> messages;
123 for (size_t i = 0; i < count; ++i) {
125 InputMsg_HandleInputEvent(
126 kTestRoutingID, &events[i], ui::LatencyInfo(), false));
129 AddMessagesToFilter(message_filter, messages);
134 class InputEventFilterTest : public testing::Test {
136 virtual void SetUp() OVERRIDE {
137 filter_ = new InputEventFilter(
139 message_loop_.message_loop_proxy());
140 filter_->SetBoundHandler(
141 base::Bind(&InputEventRecorder::HandleInputEvent,
142 base::Unretained(&event_recorder_)));
144 event_recorder_.set_filter(filter_.get());
146 filter_->OnFilterAdded(&ipc_sink_);
151 base::MessageLoop message_loop_;
153 // Used to record IPCs sent by the filter to the RenderWidgetHost.
154 IPC::TestSink ipc_sink_;
156 // Used to record IPCs forwarded by the filter to the main thread.
157 IPCMessageRecorder message_recorder_;
159 // Used to record WebInputEvents delivered to the handler.
160 InputEventRecorder event_recorder_;
162 scoped_refptr<InputEventFilter> filter_;
165 TEST_F(InputEventFilterTest, Basic) {
166 WebMouseEvent kEvents[3];
167 InitMouseEvent(&kEvents[0], WebInputEvent::MouseDown, 10, 10);
168 InitMouseEvent(&kEvents[1], WebInputEvent::MouseMove, 20, 20);
169 InitMouseEvent(&kEvents[2], WebInputEvent::MouseUp, 30, 30);
171 AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
172 EXPECT_EQ(0U, ipc_sink_.message_count());
173 EXPECT_EQ(0U, event_recorder_.record_count());
174 EXPECT_EQ(0U, message_recorder_.message_count());
176 filter_->DidAddInputHandler(kTestRoutingID, NULL);
178 AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
179 ASSERT_EQ(arraysize(kEvents), ipc_sink_.message_count());
180 ASSERT_EQ(arraysize(kEvents), event_recorder_.record_count());
181 EXPECT_EQ(0U, message_recorder_.message_count());
183 for (size_t i = 0; i < arraysize(kEvents); ++i) {
184 const IPC::Message* message = ipc_sink_.GetMessageAt(i);
185 EXPECT_EQ(kTestRoutingID, message->routing_id());
186 EXPECT_EQ(InputHostMsg_HandleInputEvent_ACK::ID, message->type());
188 WebInputEvent::Type event_type = WebInputEvent::Undefined;
189 InputEventAckState ack_result = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
190 ui::LatencyInfo latency_info;
191 EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message,
195 EXPECT_EQ(kEvents[i].type, event_type);
196 EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
198 const WebInputEvent* event = event_recorder_.record_at(i);
201 EXPECT_EQ(kEvents[i].size, event->size);
202 EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
205 event_recorder_.set_send_to_widget(true);
207 AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
208 EXPECT_EQ(arraysize(kEvents), ipc_sink_.message_count());
209 EXPECT_EQ(2 * arraysize(kEvents), event_recorder_.record_count());
210 EXPECT_EQ(arraysize(kEvents), message_recorder_.message_count());
212 for (size_t i = 0; i < arraysize(kEvents); ++i) {
213 const IPC::Message& message = message_recorder_.message_at(i);
215 ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
216 const WebInputEvent* event = NULL;
217 ui::LatencyInfo latency_info;
218 bool is_kbd_shortcut;
219 EXPECT_TRUE(InputMsg_HandleInputEvent::Read(
220 &message, &event, &latency_info, &is_kbd_shortcut));
222 EXPECT_EQ(kEvents[i].size, event->size);
223 EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
226 // Now reset everything, and test that DidHandleInputEvent is called.
228 ipc_sink_.ClearMessages();
229 event_recorder_.Clear();
230 message_recorder_.Clear();
232 event_recorder_.set_handle_events(true);
234 AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
235 EXPECT_EQ(arraysize(kEvents), ipc_sink_.message_count());
236 EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count());
237 EXPECT_EQ(0U, message_recorder_.message_count());
239 for (size_t i = 0; i < arraysize(kEvents); ++i) {
240 const IPC::Message* message = ipc_sink_.GetMessageAt(i);
241 EXPECT_EQ(kTestRoutingID, message->routing_id());
242 EXPECT_EQ(InputHostMsg_HandleInputEvent_ACK::ID, message->type());
244 WebInputEvent::Type event_type = WebInputEvent::Undefined;
245 InputEventAckState ack_result = INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
246 ui::LatencyInfo latency_info;
247 EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message,
251 EXPECT_EQ(kEvents[i].type, event_type);
252 EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_CONSUMED);
255 filter_->OnFilterRemoved();
258 TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
259 filter_->DidAddInputHandler(kTestRoutingID, NULL);
260 event_recorder_.set_send_to_widget(true);
263 WebMouseEvent mouse_down;
264 mouse_down.type = WebMouseEvent::MouseDown;
265 WebMouseEvent mouse_up;
266 mouse_up.type = WebMouseEvent::MouseUp;
268 std::vector<IPC::Message> messages;
269 messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID,
273 // Control where input events are delivered.
274 messages.push_back(InputMsg_MouseCaptureLost(kTestRoutingID));
275 messages.push_back(InputMsg_SetFocus(kTestRoutingID, true));
277 // Editing operations
278 messages.push_back(InputMsg_Undo(kTestRoutingID));
279 messages.push_back(InputMsg_Redo(kTestRoutingID));
280 messages.push_back(InputMsg_Cut(kTestRoutingID));
281 messages.push_back(InputMsg_Copy(kTestRoutingID));
282 #if defined(OS_MACOSX)
283 messages.push_back(InputMsg_CopyToFindPboard(kTestRoutingID));
285 messages.push_back(InputMsg_Paste(kTestRoutingID));
286 messages.push_back(InputMsg_PasteAndMatchStyle(kTestRoutingID));
287 messages.push_back(InputMsg_Delete(kTestRoutingID));
288 messages.push_back(InputMsg_Replace(kTestRoutingID, string16()));
289 messages.push_back(InputMsg_ReplaceMisspelling(kTestRoutingID,
291 messages.push_back(InputMsg_Delete(kTestRoutingID));
292 messages.push_back(InputMsg_SelectAll(kTestRoutingID));
293 messages.push_back(InputMsg_Unselect(kTestRoutingID));
294 messages.push_back(InputMsg_SelectRange(kTestRoutingID,
295 gfx::Point(), gfx::Point()));
296 messages.push_back(InputMsg_MoveCaret(kTestRoutingID, gfx::Point()));
298 messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID,
302 AddMessagesToFilter(filter_.get(), messages);
304 // We should have sent all messages back to the main thread and preserved
305 // their relative order.
306 ASSERT_EQ(message_recorder_.message_count(), messages.size());
307 for (size_t i = 0; i < messages.size(); ++i) {
308 EXPECT_EQ(message_recorder_.message_at(i).type(), messages[i].type()) << i;
312 } // namespace content