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