1// Copyright 2013 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.
4
5#include <new>
6#include <utility>
7#include <vector>
8
9#include "base/bind.h"
10#include "base/message_loop/message_loop.h"
11#include "content/common/input/synthetic_web_input_event_builders.h"
12#include "content/common/input_messages.h"
13#include "content/common/view_messages.h"
14#include "content/renderer/input/input_event_filter.h"
15#include "ipc/ipc_listener.h"
16#include "ipc/ipc_test_sink.h"
17#include "ipc/message_filter.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20using blink::WebInputEvent;
21using blink::WebMouseEvent;
22
23namespace content {
24namespace {
25
26const int kTestRoutingID = 13;
27
28class InputEventRecorder {
29 public:
30  InputEventRecorder()
31      : filter_(NULL),
32        handle_events_(false),
33        send_to_widget_(false) {
34  }
35
36  void set_filter(InputEventFilter* filter) { filter_ = filter; }
37  void set_handle_events(bool value) { handle_events_ = value; }
38  void set_send_to_widget(bool value) { send_to_widget_ = value; }
39
40  size_t record_count() const { return records_.size(); }
41
42  const WebInputEvent* record_at(size_t i) const {
43    const Record& record = records_[i];
44    return reinterpret_cast<const WebInputEvent*>(&record.event_data[0]);
45  }
46
47  void Clear() {
48    records_.clear();
49  }
50
51  InputEventAckState HandleInputEvent(int routing_id,
52                                      const WebInputEvent* event,
53                                      ui::LatencyInfo* latency_info) {
54    DCHECK_EQ(kTestRoutingID, routing_id);
55
56    records_.push_back(Record(event));
57
58    if (handle_events_) {
59      return INPUT_EVENT_ACK_STATE_CONSUMED;
60    } else {
61      return send_to_widget_ ? INPUT_EVENT_ACK_STATE_NOT_CONSUMED
62                             : INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
63    }
64  }
65
66 private:
67  struct Record {
68    Record(const WebInputEvent* event) {
69      const char* ptr = reinterpret_cast<const char*>(event);
70      event_data.assign(ptr, ptr + event->size);
71    }
72    std::vector<char> event_data;
73  };
74
75  InputEventFilter* filter_;
76  bool handle_events_;
77  bool send_to_widget_;
78  std::vector<Record> records_;
79};
80
81class IPCMessageRecorder : public IPC::Listener {
82 public:
83  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
84    messages_.push_back(message);
85    return true;
86  }
87
88  size_t message_count() const { return messages_.size(); }
89
90  const IPC::Message& message_at(size_t i) const {
91    return messages_[i];
92  }
93
94  void Clear() {
95    messages_.clear();
96  }
97
98 private:
99  std::vector<IPC::Message> messages_;
100};
101
102void AddMessagesToFilter(IPC::MessageFilter* message_filter,
103                         const std::vector<IPC::Message>& events) {
104  for (size_t i = 0; i < events.size(); ++i) {
105    message_filter->OnMessageReceived(events[i]);
106  }
107
108  base::MessageLoop::current()->RunUntilIdle();
109}
110
111void AddEventsToFilter(IPC::MessageFilter* message_filter,
112                       const WebMouseEvent events[],
113                       size_t count) {
114  std::vector<IPC::Message> messages;
115  for (size_t i = 0; i < count; ++i) {
116    messages.push_back(
117        InputMsg_HandleInputEvent(
118            kTestRoutingID, &events[i], ui::LatencyInfo(), false));
119  }
120
121  AddMessagesToFilter(message_filter, messages);
122}
123
124}  // namespace
125
126class InputEventFilterTest : public testing::Test {
127 public:
128  virtual void SetUp() OVERRIDE {
129    filter_ = new InputEventFilter(&message_recorder_,
130                                   base::MessageLoopProxy::current(),
131                                   message_loop_.message_loop_proxy());
132    filter_->SetBoundHandler(
133        base::Bind(&InputEventRecorder::HandleInputEvent,
134            base::Unretained(&event_recorder_)));
135
136    event_recorder_.set_filter(filter_.get());
137
138    filter_->OnFilterAdded(&ipc_sink_);
139  }
140
141
142 protected:
143  base::MessageLoop message_loop_;
144
145  // Used to record IPCs sent by the filter to the RenderWidgetHost.
146  IPC::TestSink ipc_sink_;
147
148  // Used to record IPCs forwarded by the filter to the main thread.
149  IPCMessageRecorder message_recorder_;
150
151  // Used to record WebInputEvents delivered to the handler.
152  InputEventRecorder event_recorder_;
153
154  scoped_refptr<InputEventFilter> filter_;
155};
156
157TEST_F(InputEventFilterTest, Basic) {
158  WebMouseEvent kEvents[3] = {
159    SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseMove, 10, 10, 0),
160    SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseMove, 20, 20, 0),
161    SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseMove, 30, 30, 0)
162  };
163
164  AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
165  EXPECT_EQ(0U, ipc_sink_.message_count());
166  EXPECT_EQ(0U, event_recorder_.record_count());
167  EXPECT_EQ(0U, message_recorder_.message_count());
168
169  filter_->DidAddInputHandler(kTestRoutingID, NULL);
170
171  AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
172  ASSERT_EQ(arraysize(kEvents), ipc_sink_.message_count());
173  ASSERT_EQ(arraysize(kEvents), event_recorder_.record_count());
174  EXPECT_EQ(0U, message_recorder_.message_count());
175
176  for (size_t i = 0; i < arraysize(kEvents); ++i) {
177    const IPC::Message* message = ipc_sink_.GetMessageAt(i);
178    EXPECT_EQ(kTestRoutingID, message->routing_id());
179    EXPECT_EQ(InputHostMsg_HandleInputEvent_ACK::ID, message->type());
180
181    InputHostMsg_HandleInputEvent_ACK::Param params;
182    EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message, &params));
183    WebInputEvent::Type event_type = params.a.type;
184    InputEventAckState ack_result = params.a.state;
185
186    EXPECT_EQ(kEvents[i].type, event_type);
187    EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
188
189    const WebInputEvent* event = event_recorder_.record_at(i);
190    ASSERT_TRUE(event);
191
192    EXPECT_EQ(kEvents[i].size, event->size);
193    EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
194  }
195
196  event_recorder_.set_send_to_widget(true);
197
198  AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
199  EXPECT_EQ(arraysize(kEvents), ipc_sink_.message_count());
200  EXPECT_EQ(2 * arraysize(kEvents), event_recorder_.record_count());
201  EXPECT_EQ(arraysize(kEvents), message_recorder_.message_count());
202
203  for (size_t i = 0; i < arraysize(kEvents); ++i) {
204    const IPC::Message& message = message_recorder_.message_at(i);
205
206    ASSERT_EQ(InputMsg_HandleInputEvent::ID, message.type());
207    InputMsg_HandleInputEvent::Param params;
208    EXPECT_TRUE(InputMsg_HandleInputEvent::Read(&message, &params));
209    const WebInputEvent* event = params.a;
210
211    EXPECT_EQ(kEvents[i].size, event->size);
212    EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0);
213  }
214
215  // Now reset everything, and test that DidHandleInputEvent is called.
216
217  ipc_sink_.ClearMessages();
218  event_recorder_.Clear();
219  message_recorder_.Clear();
220
221  event_recorder_.set_handle_events(true);
222
223  AddEventsToFilter(filter_.get(), kEvents, arraysize(kEvents));
224  EXPECT_EQ(arraysize(kEvents), ipc_sink_.message_count());
225  EXPECT_EQ(arraysize(kEvents), event_recorder_.record_count());
226  EXPECT_EQ(0U, message_recorder_.message_count());
227
228  for (size_t i = 0; i < arraysize(kEvents); ++i) {
229    const IPC::Message* message = ipc_sink_.GetMessageAt(i);
230    EXPECT_EQ(kTestRoutingID, message->routing_id());
231    EXPECT_EQ(InputHostMsg_HandleInputEvent_ACK::ID, message->type());
232
233    InputHostMsg_HandleInputEvent_ACK::Param params;
234    EXPECT_TRUE(InputHostMsg_HandleInputEvent_ACK::Read(message, &params));
235    WebInputEvent::Type event_type = params.a.type;
236    InputEventAckState ack_result = params.a.state;
237    EXPECT_EQ(kEvents[i].type, event_type);
238    EXPECT_EQ(ack_result, INPUT_EVENT_ACK_STATE_CONSUMED);
239  }
240
241  filter_->OnFilterRemoved();
242}
243
244TEST_F(InputEventFilterTest, PreserveRelativeOrder) {
245  filter_->DidAddInputHandler(kTestRoutingID, NULL);
246  event_recorder_.set_send_to_widget(true);
247
248
249  WebMouseEvent mouse_down =
250      SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseDown);
251  WebMouseEvent mouse_up =
252      SyntheticWebMouseEventBuilder::Build(WebMouseEvent::MouseUp);
253
254  std::vector<IPC::Message> messages;
255  messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID,
256                                              &mouse_down,
257                                              ui::LatencyInfo(),
258                                              false));
259  // Control where input events are delivered.
260  messages.push_back(InputMsg_MouseCaptureLost(kTestRoutingID));
261  messages.push_back(InputMsg_SetFocus(kTestRoutingID, true));
262
263  // Editing operations
264  messages.push_back(InputMsg_Undo(kTestRoutingID));
265  messages.push_back(InputMsg_Redo(kTestRoutingID));
266  messages.push_back(InputMsg_Cut(kTestRoutingID));
267  messages.push_back(InputMsg_Copy(kTestRoutingID));
268#if defined(OS_MACOSX)
269  messages.push_back(InputMsg_CopyToFindPboard(kTestRoutingID));
270#endif
271  messages.push_back(InputMsg_Paste(kTestRoutingID));
272  messages.push_back(InputMsg_PasteAndMatchStyle(kTestRoutingID));
273  messages.push_back(InputMsg_Delete(kTestRoutingID));
274  messages.push_back(InputMsg_Replace(kTestRoutingID, base::string16()));
275  messages.push_back(InputMsg_ReplaceMisspelling(kTestRoutingID,
276                                                     base::string16()));
277  messages.push_back(InputMsg_Delete(kTestRoutingID));
278  messages.push_back(InputMsg_SelectAll(kTestRoutingID));
279  messages.push_back(InputMsg_Unselect(kTestRoutingID));
280  messages.push_back(InputMsg_SelectRange(kTestRoutingID,
281                                         gfx::Point(), gfx::Point()));
282  messages.push_back(InputMsg_MoveCaret(kTestRoutingID, gfx::Point()));
283
284  messages.push_back(InputMsg_HandleInputEvent(kTestRoutingID,
285                                              &mouse_up,
286                                              ui::LatencyInfo(),
287                                              false));
288  AddMessagesToFilter(filter_.get(), messages);
289
290  // We should have sent all messages back to the main thread and preserved
291  // their relative order.
292  ASSERT_EQ(message_recorder_.message_count(), messages.size());
293  for (size_t i = 0; i < messages.size(); ++i) {
294    EXPECT_EQ(message_recorder_.message_at(i).type(), messages[i].type()) << i;
295  }
296}
297
298}  // namespace content
299