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 "content/renderer/input/input_event_filter.h"
6
7#include "base/auto_reset.h"
8#include "base/bind.h"
9#include "base/command_line.h"
10#include "base/debug/trace_event.h"
11#include "base/location.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/single_thread_task_runner.h"
14#include "cc/input/input_handler.h"
15#include "content/common/input/did_overscroll_params.h"
16#include "content/common/input/web_input_event_traits.h"
17#include "content/common/input_messages.h"
18#include "content/common/view_messages.h"
19#include "content/public/common/content_switches.h"
20#include "ipc/ipc_listener.h"
21#include "ipc/ipc_sender.h"
22#include "ui/gfx/vector2d_f.h"
23
24using blink::WebInputEvent;
25
26#include "ipc/ipc_message_null_macros.h"
27#undef IPC_MESSAGE_DECL
28#define IPC_MESSAGE_DECL(kind, type, name, in, out, ilist, olist) \
29  case name::ID: return #name;
30
31const char* GetInputMessageTypeName(const IPC::Message& message) {
32  switch (message.type()) {
33#include "content/common/input_messages.h"
34    default:
35      NOTREACHED() << "Invalid message type: " << message.type();
36      break;
37  };
38  return "NonInputMsgType";
39}
40
41namespace content {
42
43InputEventFilter::InputEventFilter(
44    IPC::Listener* main_listener,
45    const scoped_refptr<base::SingleThreadTaskRunner>& main_task_runner,
46    const scoped_refptr<base::MessageLoopProxy>& target_loop)
47    : main_task_runner_(main_task_runner),
48      main_listener_(main_listener),
49      sender_(NULL),
50      target_loop_(target_loop),
51      overscroll_notifications_enabled_(false),
52      current_overscroll_params_(NULL) {
53  DCHECK(target_loop_.get());
54  overscroll_notifications_enabled_ =
55      CommandLine::ForCurrentProcess()->HasSwitch(
56          switches::kEnableOverscrollNotifications);
57}
58
59void InputEventFilter::SetBoundHandler(const Handler& handler) {
60  DCHECK(main_task_runner_->BelongsToCurrentThread());
61  handler_ = handler;
62}
63
64void InputEventFilter::DidAddInputHandler(int routing_id,
65                                          cc::InputHandler* input_handler) {
66  base::AutoLock locked(routes_lock_);
67  routes_.insert(routing_id);
68}
69
70void InputEventFilter::DidRemoveInputHandler(int routing_id) {
71  base::AutoLock locked(routes_lock_);
72  routes_.erase(routing_id);
73}
74
75void InputEventFilter::DidOverscroll(int routing_id,
76                                     const DidOverscrollParams& params) {
77  if (!overscroll_notifications_enabled_)
78    return;
79
80  if (current_overscroll_params_) {
81    current_overscroll_params_->reset(new DidOverscrollParams(params));
82    return;
83  }
84
85  SendMessage(scoped_ptr<IPC::Message>(
86      new InputHostMsg_DidOverscroll(routing_id, params)));
87}
88
89void InputEventFilter::DidStopFlinging(int routing_id) {
90  SendMessage(
91      scoped_ptr<IPC::Message>(new ViewHostMsg_DidStopFlinging(routing_id)));
92}
93
94void InputEventFilter::OnFilterAdded(IPC::Sender* sender) {
95  io_loop_ = base::MessageLoopProxy::current();
96  sender_ = sender;
97}
98
99void InputEventFilter::OnFilterRemoved() {
100  sender_ = NULL;
101}
102
103void InputEventFilter::OnChannelClosing() {
104  sender_ = NULL;
105}
106
107// This function returns true if the IPC message is one that the compositor
108// thread can handle *or* one that needs to preserve relative order with
109// messages that the compositor thread can handle. Returning true for a message
110// type means that the message will go through an extra copy and thread hop, so
111// use with care.
112static bool RequiresThreadBounce(const IPC::Message& message) {
113  return IPC_MESSAGE_ID_CLASS(message.type()) == InputMsgStart;
114}
115
116bool InputEventFilter::OnMessageReceived(const IPC::Message& message) {
117  if (!RequiresThreadBounce(message))
118    return false;
119
120  TRACE_EVENT0("input", "InputEventFilter::OnMessageReceived::InputMessage");
121
122  {
123    base::AutoLock locked(routes_lock_);
124    if (routes_.find(message.routing_id()) == routes_.end())
125      return false;
126  }
127
128  target_loop_->PostTask(
129      FROM_HERE,
130      base::Bind(&InputEventFilter::ForwardToHandler, this, message));
131  return true;
132}
133
134InputEventFilter::~InputEventFilter() {
135  DCHECK(!current_overscroll_params_);
136}
137
138void InputEventFilter::ForwardToMainListener(const IPC::Message& message) {
139  main_listener_->OnMessageReceived(message);
140}
141
142void InputEventFilter::ForwardToHandler(const IPC::Message& message) {
143  DCHECK(!handler_.is_null());
144  DCHECK(target_loop_->BelongsToCurrentThread());
145  TRACE_EVENT1("input", "InputEventFilter::ForwardToHandler",
146               "message_type", GetInputMessageTypeName(message));
147
148  if (message.type() != InputMsg_HandleInputEvent::ID) {
149    TRACE_EVENT_INSTANT0(
150        "input",
151        "InputEventFilter::ForwardToHandler::ForwardToMainListener",
152        TRACE_EVENT_SCOPE_THREAD);
153    main_task_runner_->PostTask(
154        FROM_HERE,
155        base::Bind(&InputEventFilter::ForwardToMainListener, this, message));
156    return;
157  }
158
159  int routing_id = message.routing_id();
160  InputMsg_HandleInputEvent::Param params;
161  if (!InputMsg_HandleInputEvent::Read(&message, &params))
162    return;
163  const WebInputEvent* event = params.a;
164  ui::LatencyInfo latency_info = params.b;
165  bool is_keyboard_shortcut = params.c;
166  DCHECK(event);
167
168  const bool send_ack = !WebInputEventTraits::IgnoresAckDisposition(*event);
169
170  // Intercept |DidOverscroll| notifications, bundling any triggered overscroll
171  // response with the input event ack.
172  scoped_ptr<DidOverscrollParams> overscroll_params;
173  base::AutoReset<scoped_ptr<DidOverscrollParams>*>
174      auto_reset_current_overscroll_params(
175          &current_overscroll_params_, send_ack ? &overscroll_params : NULL);
176
177  InputEventAckState ack_state = handler_.Run(routing_id, event, &latency_info);
178
179  if (ack_state == INPUT_EVENT_ACK_STATE_NOT_CONSUMED) {
180    DCHECK(!overscroll_params);
181    TRACE_EVENT_INSTANT0(
182        "input",
183        "InputEventFilter::ForwardToHandler::ForwardToMainListener",
184        TRACE_EVENT_SCOPE_THREAD);
185    IPC::Message new_msg = InputMsg_HandleInputEvent(
186        routing_id, event, latency_info, is_keyboard_shortcut);
187    main_task_runner_->PostTask(
188        FROM_HERE,
189        base::Bind(&InputEventFilter::ForwardToMainListener, this, new_msg));
190    return;
191  }
192
193  if (!send_ack)
194    return;
195
196  InputHostMsg_HandleInputEvent_ACK_Params ack;
197  ack.type = event->type;
198  ack.state = ack_state;
199  ack.latency = latency_info;
200  ack.overscroll = overscroll_params.Pass();
201  SendMessage(scoped_ptr<IPC::Message>(
202      new InputHostMsg_HandleInputEvent_ACK(routing_id, ack)));
203}
204
205void InputEventFilter::SendMessage(scoped_ptr<IPC::Message> message) {
206  DCHECK(target_loop_->BelongsToCurrentThread());
207
208  io_loop_->PostTask(FROM_HERE,
209                     base::Bind(&InputEventFilter::SendMessageOnIOThread,
210                                this,
211                                base::Passed(&message)));
212}
213
214void InputEventFilter::SendMessageOnIOThread(scoped_ptr<IPC::Message> message) {
215  DCHECK(io_loop_->BelongsToCurrentThread());
216
217  if (!sender_)
218    return;  // Filter was removed.
219
220  sender_->Send(message.release());
221}
222
223}  // namespace content
224