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, ¶ms)) 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 ¤t_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