Chromium Code Reviews| OLD | NEW | 
|---|---|
| (Empty) | |
| 1 // Copyright (c) 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/browser/renderer_host/input/buffered_input_router.h" | |
| 6 | |
| 7 #include "base/auto_reset.h" | |
| 8 #include "content/browser/renderer_host/input/input_ack_handler.h" | |
| 9 #include "content/browser/renderer_host/input/input_queue.h" | |
| 10 #include "content/browser/renderer_host/render_widget_host_impl.h" | |
| 11 #include "content/common/input/input_event_utils.h" | |
| 12 #include "content/common/input_messages.h" | |
| 13 #include "content/common/view_messages.h" | |
| 14 #include "content/public/browser/native_web_keyboard_event.h" | |
| 15 #include "content/public/browser/user_metrics.h" | |
| 16 | |
| 17 using base::Time; | |
| 
 
aelias_OOO_until_Jul13
2013/08/15 00:52:15
Shouldn't need this.
 
jdduke (slow)
2013/08/15 23:22:26
Done.
 
 | |
| 18 using base::TimeDelta; | |
| 19 using base::TimeTicks; | |
| 20 using WebKit::WebGestureEvent; | |
| 21 using WebKit::WebInputEvent; | |
| 22 using WebKit::WebKeyboardEvent; | |
| 23 using WebKit::WebMouseEvent; | |
| 24 using WebKit::WebMouseWheelEvent; | |
| 25 using WebKit::WebTouchEvent; | |
| 26 | |
| 27 namespace content { | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 InputEventAckState FromState(InputEventState state) { | |
| 32 switch (state) { | |
| 33 case INPUT_EVENT_UNHANDLED: | |
| 34 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
| 35 case INPUT_EVENT_IMPL_THREAD_ABSORBED: | |
| 36 return INPUT_EVENT_ACK_STATE_CONSUMED; | |
| 37 case INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS: | |
| 38 return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; | |
| 39 case INPUT_EVENT_IMPL_THREAD_BOUNCE_TO_MAIN: | |
| 40 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
| 41 case INPUT_EVENT_IMPL_THREAD_COULD_NOT_DELIVER: | |
| 42 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
| 43 case INPUT_EVENT_MAIN_THREAD_ABSORBED: | |
| 44 return INPUT_EVENT_ACK_STATE_CONSUMED; | |
| 45 case INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED: | |
| 46 return INPUT_EVENT_ACK_STATE_CONSUMED; | |
| 47 case INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED: | |
| 48 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
| 49 case INPUT_EVENT_MAIN_THREAD_NO_HANDLER_EXISTS: | |
| 50 return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS; | |
| 51 case INPUT_EVENT_MAIN_THREAD_COULD_NOT_DELIVER: | |
| 52 return INPUT_EVENT_ACK_STATE_NOT_CONSUMED; | |
| 53 } | |
| 54 | |
| 55 NOTREACHED(); | |
| 56 return INPUT_EVENT_ACK_STATE_UNKNOWN; | |
| 57 } | |
| 58 | |
| 59 } // namespace | |
| 60 | |
| 61 BufferedInputRouter::BufferedInputRouter(IPC::Sender* sender, | |
| 62 InputRouterClient* client, | |
| 63 InputAckHandler* ack_handler, | |
| 64 int routing_id) | |
| 65 : client_(client), | |
| 66 ack_handler_(ack_handler), | |
| 67 sender_(sender), | |
| 68 routing_id_(routing_id), | |
| 69 queued_gesture_count_(0), | |
| 70 has_touch_handler_(false), | |
| 71 queued_touch_count_(0), | |
| 72 input_queue_override_(NULL), | |
| 73 next_input_id_(1), | |
| 74 in_flight_packet_id_(0) { | |
| 75 input_queue_.reset(new InputQueue(this)); | |
| 76 } | |
| 77 | |
| 78 BufferedInputRouter::~BufferedInputRouter() {} | |
| 79 | |
| 80 void BufferedInputRouter::Flush() { | |
| 81 TRACE_EVENT0("input", "BufferedInputRouter::Flush"); | |
| 82 DCHECK_EQ(0, in_flight_packet_id_); | |
| 83 | |
| 84 input_queue_->FlushEventsInCurrentFrame(); | |
| 85 } | |
| 86 | |
| 87 bool BufferedInputRouter::SendInput(scoped_ptr<IPC::Message> message) { | |
| 88 DCHECK_EQ(InputMsgStart, IPC_MESSAGE_ID_CLASS(message->type())); | |
| 89 DCHECK_NE(InputMsg_HandleEventPacket::ID, message->type()); | |
| 90 input_queue_->QueueEvent(InputEvent(NextInputID(), | |
| 91 INPUT_EVENT_ONE_WAY, | |
| 92 *message), NULL); | |
| 93 return true; | |
| 94 } | |
| 95 | |
| 96 void BufferedInputRouter::SendMouseEvent( | |
| 97 const MouseEventWithLatencyInfo& mouse_event) { | |
| 98 if (!client_->OnSendMouseEvent(mouse_event)) | |
| 99 return; | |
| 100 QueueWebEvent(mouse_event.event, mouse_event.latency, false, false); | |
| 101 } | |
| 102 | |
| 103 void BufferedInputRouter::SendWheelEvent( | |
| 104 const MouseWheelEventWithLatencyInfo& wheel_event) { | |
| 105 if (!client_->OnSendWheelEvent(wheel_event)) | |
| 106 return; | |
| 107 QueueWebEvent(wheel_event.event, wheel_event.latency, false, false); | |
| 108 } | |
| 109 | |
| 110 void BufferedInputRouter::SendKeyboardEvent( | |
| 111 const NativeWebKeyboardEvent& key_event, | |
| 112 const ui::LatencyInfo& latency_info) { | |
| 113 bool is_shortcut = false; | |
| 114 if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut)) | |
| 115 return; | |
| 116 int64 event_id = QueueWebEvent(key_event, latency_info, is_shortcut, false); | |
| 117 if (event_id) { | |
| 118 DCHECK(queued_key_map_.find(event_id) == queued_key_map_.end()); | |
| 119 queued_key_map_[event_id] = key_event; | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 void BufferedInputRouter::SendGestureEvent( | |
| 124 const GestureEventWithLatencyInfo& gesture_event) { | |
| 125 if (!client_->OnSendGestureEvent(gesture_event)) | |
| 126 return; | |
| 127 if (QueueWebEvent(gesture_event.event, gesture_event.latency, false, false)) | |
| 128 ++queued_gesture_count_; | |
| 129 } | |
| 130 | |
| 131 void BufferedInputRouter::SendTouchEvent( | |
| 132 const TouchEventWithLatencyInfo& touch_event) { | |
| 133 if (!client_->OnSendTouchEvent(touch_event)) | |
| 134 return; | |
| 135 if (QueueWebEvent(touch_event.event, touch_event.latency, false, true)) | |
| 136 ++queued_touch_count_; | |
| 137 } | |
| 138 | |
| 139 void BufferedInputRouter::SendMouseEventImmediately( | |
| 140 const MouseEventWithLatencyInfo& mouse_event) { | |
| 141 if (!client_->OnSendMouseEventImmediately(mouse_event)) | |
| 142 return; | |
| 143 QueueWebEvent(mouse_event.event, mouse_event.latency, false, false); | |
| 144 } | |
| 145 | |
| 146 void BufferedInputRouter::SendTouchEventImmediately( | |
| 147 const TouchEventWithLatencyInfo& touch_event) { | |
| 148 if (!client_->OnSendTouchEventImmediately(touch_event)) | |
| 149 return; | |
| 150 QueueWebEvent(touch_event.event, touch_event.latency, false, false); | |
| 151 } | |
| 152 | |
| 153 void BufferedInputRouter::SendGestureEventImmediately( | |
| 154 const GestureEventWithLatencyInfo& gesture_event) { | |
| 155 if (!client_->OnSendGestureEventImmediately(gesture_event)) | |
| 156 return; | |
| 157 QueueWebEvent(gesture_event.event, gesture_event.latency, false, false); | |
| 158 } | |
| 159 | |
| 160 void BufferedInputRouter::Deliver(const EventPacket& packet) { | |
| 161 TRACE_EVENT2("input", "BufferedInputRouter::DeliverPacket", | |
| 162 "id", packet.id, | |
| 163 "events", packet.events.size()); | |
| 164 DCHECK(packet.id); | |
| 165 DCHECK(!in_flight_packet_id_); | |
| 166 if (!sender_->Send(new InputMsg_HandleEventPacket(routing_id_, packet))) | |
| 167 return; | |
| 168 | |
| 169 in_flight_packet_id_ = packet.id; | |
| 170 client_->IncrementInFlightEventCount(); | |
| 171 } | |
| 172 | |
| 173 void BufferedInputRouter::DidFlush() { | |
| 174 TRACE_EVENT0("input", "BufferedInputRouter::DidFlush"); | |
| 175 client_->DidFlush(); | |
| 176 } | |
| 177 | |
| 178 void BufferedInputRouter::SetNeedsFlush() { | |
| 179 TRACE_EVENT0("input", "BufferedInputRouter::SetNeedsFlush"); | |
| 180 client_->SetNeedsFlush(); | |
| 181 } | |
| 182 | |
| 183 std::vector<InputEvent> BufferedInputRouter::OnInputEventAck( | |
| 184 const InputEvent& acked_event) { | |
| 185 DCHECK(IsWebInputEventMessage(acked_event.message)); | |
| 186 | |
| 187 const WebKit::WebInputEvent* web_event = NULL; | |
| 188 ui::LatencyInfo latency_info; | |
| 189 DCHECK(CrackWebInputEventMessage(acked_event.message, | |
| 190 &web_event, | |
| 191 &latency_info, | |
| 192 NULL)); | |
| 193 | |
| 194 InputEventAckState ack_state = FromState(acked_event.state); | |
| 195 | |
| 196 if (acked_event.type == INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP) { | |
| 197 std::vector<InputEvent> followup_events; | |
| 198 base::AutoReset<std::vector<InputEvent>*> input_queue_override( | |
| 199 &input_queue_override_, &followup_events); | |
| 200 OnInputEventAck(acked_event.id, *web_event, latency_info, ack_state, true); | |
| 201 return followup_events; | |
| 202 } | |
| 203 | |
| 204 OnInputEventAck(acked_event.id, *web_event, latency_info, ack_state, true); | |
| 205 return std::vector<InputEvent>(); | |
| 206 } | |
| 207 | |
| 208 bool BufferedInputRouter::OnMessageReceived(const IPC::Message& message) { | |
| 209 bool handled = true; | |
| 210 bool message_is_ok = true; | |
| 211 IPC_BEGIN_MESSAGE_MAP_EX(BufferedInputRouter, message, message_is_ok) | |
| 212 IPC_MESSAGE_HANDLER(InputHostMsg_HandleEventPacket_ACK, OnEventPacketAck) | |
| 213 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, | |
| 214 OnHasTouchEventHandlers) | |
| 215 IPC_MESSAGE_UNHANDLED(handled = false) | |
| 216 IPC_END_MESSAGE_MAP() | |
| 217 | |
| 218 if (!message_is_ok) | |
| 219 ack_handler_->OnUnexpectedEventAck(true); | |
| 220 | |
| 221 return handled; | |
| 222 } | |
| 223 | |
| 224 const NativeWebKeyboardEvent* | |
| 225 BufferedInputRouter::GetLastKeyboardEvent() const { | |
| 226 return queued_key_map_.empty() ? NULL : &queued_key_map_.begin()->second; | |
| 227 } | |
| 228 | |
| 229 bool BufferedInputRouter::ShouldForwardTouchEvent() const { | |
| 230 return has_touch_handler_ && queued_touch_count_ > 0; | |
| 231 } | |
| 232 | |
| 233 bool BufferedInputRouter::ShouldForwardGestureEvent( | |
| 234 const GestureEventWithLatencyInfo& touch_event) const { | |
| 235 return true; | |
| 236 } | |
| 237 | |
| 238 bool BufferedInputRouter::HasQueuedGestureEvents() const { | |
| 239 return queued_gesture_count_ > 0; | |
| 240 } | |
| 241 | |
| 242 void BufferedInputRouter::OnInputEventAck( | |
| 
 
aelias_OOO_until_Jul13
2013/08/15 00:52:15
Please rename to OnWebInputEventAck.
 
jdduke (slow)
2013/08/15 23:22:26
Done.
 
 | |
| 243 int64 event_id, | |
| 244 const WebKit::WebInputEvent& web_event, | |
| 245 const ui::LatencyInfo& latency_info, | |
| 246 InputEventAckState acked_result, | |
| 247 bool ack_from_input_queue) { | |
| 248 if (WebInputEvent::isKeyboardEventType(web_event.type)) { | |
| 249 if (ack_from_input_queue) { | |
| 250 KeyMap::iterator key_it = queued_key_map_.find(event_id); | |
| 251 DCHECK(key_it != queued_key_map_.end()); | |
| 252 NativeWebKeyboardEvent key_event = key_it->second; | |
| 253 queued_key_map_.erase(key_it); | |
| 254 ack_handler_->OnKeyboardEventAck(key_event, acked_result); | |
| 255 } else { | |
| 256 DCHECK_EQ(0, event_id); | |
| 257 ack_handler_->OnKeyboardEventAck( | |
| 258 static_cast<const NativeWebKeyboardEvent&>(web_event), acked_result); | |
| 259 } | |
| 260 // WARNING: This BufferedInputRouter can be deallocated at this point | |
| 261 // (i.e. in the case of Ctrl+W, where the call to | |
| 262 // HandleKeyboardEvent destroys this BufferedInputRouter). | |
| 263 } else if (web_event.type == WebInputEvent::MouseWheel) { | |
| 264 ack_handler_->OnWheelEventAck( | |
| 265 static_cast<const WebMouseWheelEvent&>(web_event), acked_result); | |
| 266 } else if (WebInputEvent::isTouchEventType(web_event.type)) { | |
| 267 if (ack_from_input_queue) { | |
| 268 DCHECK_GT(queued_touch_count_, 0); | |
| 269 --queued_touch_count_; | |
| 270 } | |
| 271 ack_handler_->OnTouchEventAck( | |
| 272 TouchEventWithLatencyInfo(static_cast<const WebTouchEvent&>(web_event), | |
| 273 latency_info), acked_result); | |
| 274 } else if (WebInputEvent::isGestureEventType(web_event.type)) { | |
| 275 if (ack_from_input_queue) { | |
| 276 DCHECK_GT(queued_gesture_count_, 0); | |
| 277 --queued_gesture_count_; | |
| 278 } | |
| 279 ack_handler_->OnGestureEventAck( | |
| 280 static_cast<const WebGestureEvent&>(web_event), acked_result); | |
| 281 } else { | |
| 
 
aelias_OOO_until_Jul13
2013/08/15 00:52:15
nit: remove this else {}
 
jdduke (slow)
2013/08/15 23:22:26
Done.
 
 | |
| 282 NOTREACHED() << "Unexpected WebInputEvent in OnInputEventAck"; | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 void BufferedInputRouter::OnEventPacketAck(const EventPacket& packet) { | |
| 287 TRACE_EVENT1("input", "BufferedInputRouter::OnEventPacketAck", | |
| 288 "id", packet.id); | |
| 289 if (!in_flight_packet_id_ || packet.id != in_flight_packet_id_) { | |
| 290 ack_handler_->OnUnexpectedEventAck(false); | |
| 291 return; | |
| 292 } | |
| 293 | |
| 294 in_flight_packet_id_ = 0; | |
| 295 client_->DecrementInFlightEventCount(); | |
| 296 | |
| 297 InputQueue::AckResult ack_result = input_queue_->OnEventPacketAck(packet); | |
| 298 if (ack_result == InputQueue::ACK_UNEXPECTED) | |
| 299 ack_handler_->OnUnexpectedEventAck(false); | |
| 300 else if (ack_result == InputQueue::ACK_INVALID) | |
| 301 ack_handler_->OnUnexpectedEventAck(true); | |
| 302 } | |
| 303 | |
| 304 void BufferedInputRouter::OnHasTouchEventHandlers(bool has_handlers) { | |
| 305 if (has_touch_handler_ == has_handlers) | |
| 306 return; | |
| 307 has_touch_handler_ = has_handlers; | |
| 308 client_->OnHasTouchEventHandlers(has_handlers); | |
| 309 } | |
| 310 | |
| 311 int64 BufferedInputRouter::QueueWebEvent(const WebKit::WebInputEvent& web_event, | |
| 312 const ui::LatencyInfo& latency_info, | |
| 313 bool is_keyboard_shortcut, | |
| 314 bool has_followup) { | |
| 315 TRACE_EVENT0("input", "BufferedInputRouter::QueueWebEvent"); | |
| 316 | |
| 317 if (FilterWebEvent(web_event, latency_info)) { | |
| 318 TRACE_EVENT_INSTANT0("input", | |
| 319 "BufferedInputRouter::QueueWebEvent::Filtered", | |
| 320 TRACE_EVENT_SCOPE_THREAD); | |
| 321 return 0; | |
| 322 } | |
| 323 | |
| 324 InputEventType type = has_followup ? INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP | |
| 325 : INPUT_EVENT_NEEDS_ACK; | |
| 326 InputEvent event(NextInputID(), | |
| 327 type, | |
| 328 InputMsg_HandleInputEvent(routing_id_, | |
| 329 &web_event, | |
| 330 latency_info, | |
| 331 is_keyboard_shortcut)); | |
| 332 | |
| 333 // The presence of |input_queue_override_| implies that we are in the | |
| 334 // scope of OnInputEventAck with an event that has followup. | |
| 335 // TODO(jdduke): Remove when InputAckHandler returns followup events directly. | |
| 
 
aelias_OOO_until_Jul13
2013/08/15 00:52:15
Delete this comment since it's not clear we're goi
 
jdduke (slow)
2013/08/15 23:22:26
Done.
 
 | |
| 336 if (input_queue_override_) | |
| 337 input_queue_override_->push_back(event); | |
| 338 else | |
| 339 input_queue_->QueueEvent(event, NULL); | |
| 340 | |
| 341 return event.id; | |
| 342 } | |
| 343 | |
| 344 bool BufferedInputRouter::FilterWebEvent(const WebKit::WebInputEvent& web_event, | |
| 345 const ui::LatencyInfo& latency_info) { | |
| 346 // Perform optional, synchronous event handling, sending ACK messages for | |
| 347 // processed events, or proceeding as usual. | |
| 348 InputEventAckState filter_ack = client_->FilterInputEvent(web_event, | |
| 349 latency_info); | |
| 350 switch (filter_ack) { | |
| 351 // Send the ACK and early exit. | |
| 352 case INPUT_EVENT_ACK_STATE_CONSUMED: | |
| 353 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: | |
| 354 OnInputEventAck(0, web_event, latency_info, filter_ack, false); | |
| 355 // WARNING: |this| may be deleted at this point. | |
| 356 return true; | |
| 357 | |
| 358 // Drop the event. | |
| 359 case INPUT_EVENT_ACK_STATE_UNKNOWN: | |
| 360 return true; | |
| 361 | |
| 362 // Proceed as normal. | |
| 363 case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: | |
| 364 default: | |
| 365 break; | |
| 366 }; | |
| 367 | |
| 368 return false; | |
| 369 } | |
| 370 | |
| 371 int64 BufferedInputRouter::NextInputID() { | |
| 372 return next_input_id_++; | |
| 373 } | |
| 374 | |
| 375 } // namespace content | |
| OLD | NEW |