OLD | NEW |
| (Empty) |
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/browser/renderer_host/input/buffered_input_router.h" | |
6 | |
7 #include "base/auto_reset.h" | |
8 #include "content/browser/renderer_host/input/browser_input_event.h" | |
9 #include "content/browser/renderer_host/input/input_ack_handler.h" | |
10 #include "content/browser/renderer_host/input/input_queue.h" | |
11 #include "content/browser/renderer_host/render_widget_host_impl.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 WebKit::WebGestureEvent; | |
18 using WebKit::WebInputEvent; | |
19 using WebKit::WebKeyboardEvent; | |
20 using WebKit::WebMouseEvent; | |
21 using WebKit::WebMouseWheelEvent; | |
22 using WebKit::WebTouchEvent; | |
23 | |
24 namespace content { | |
25 | |
26 BufferedInputRouter::BufferedInputRouter(IPC::Sender* sender, | |
27 InputRouterClient* client, | |
28 InputAckHandler* ack_handler, | |
29 int routing_id) | |
30 : client_(client), | |
31 ack_handler_(ack_handler), | |
32 sender_(sender), | |
33 routing_id_(routing_id), | |
34 has_touch_handler_(false), | |
35 queued_touch_count_(0), | |
36 input_queue_override_(NULL), | |
37 next_input_id_(1), | |
38 in_flight_packet_id_(0) { | |
39 input_queue_.reset(new InputQueue(this)); | |
40 } | |
41 | |
42 BufferedInputRouter::~BufferedInputRouter() {} | |
43 | |
44 void BufferedInputRouter::Flush() { | |
45 TRACE_EVENT0("input", "BufferedInputRouter::Flush"); | |
46 DCHECK_EQ(0, in_flight_packet_id_); | |
47 input_queue_->BeginFlush(); | |
48 } | |
49 | |
50 bool BufferedInputRouter::SendInput(scoped_ptr<IPC::Message> message) { | |
51 DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart); | |
52 DCHECK(message->type() != InputMsg_HandleEventPacket::ID); | |
53 DCHECK(message->type() != InputMsg_HandleInputEvent::ID); | |
54 input_queue_->QueueEvent(BrowserInputEvent::Create( | |
55 NextInputID(), IPCInputEventPayload::Create(message.Pass()), this)); | |
56 return true; | |
57 } | |
58 | |
59 void BufferedInputRouter::SendMouseEvent( | |
60 const MouseEventWithLatencyInfo& mouse_event) { | |
61 if (!client_->OnSendMouseEvent(mouse_event)) | |
62 return; | |
63 // TODO(jdduke): Coalescing, http://crbug.com/289520 | |
64 QueueWebEvent(mouse_event.event, mouse_event.latency, false); | |
65 } | |
66 | |
67 void BufferedInputRouter::SendWheelEvent( | |
68 const MouseWheelEventWithLatencyInfo& wheel_event) { | |
69 if (!client_->OnSendWheelEvent(wheel_event)) | |
70 return; | |
71 QueueWebEvent(wheel_event.event, wheel_event.latency, false); | |
72 } | |
73 | |
74 void BufferedInputRouter::SendKeyboardEvent( | |
75 const NativeWebKeyboardEvent& key_event, | |
76 const ui::LatencyInfo& latency_info) { | |
77 bool is_shortcut = false; | |
78 if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut)) | |
79 return; | |
80 int64 event_id = QueueWebEvent(key_event, latency_info, is_shortcut); | |
81 if (event_id) { | |
82 DCHECK(queued_key_map_.find(event_id) == queued_key_map_.end()); | |
83 queued_key_map_[event_id] = key_event; | |
84 } | |
85 } | |
86 | |
87 void BufferedInputRouter::SendGestureEvent( | |
88 const GestureEventWithLatencyInfo& gesture_event) { | |
89 if (!client_->OnSendGestureEvent(gesture_event)) | |
90 return; | |
91 QueueWebEvent(gesture_event.event, gesture_event.latency, false); | |
92 } | |
93 | |
94 void BufferedInputRouter::SendTouchEvent( | |
95 const TouchEventWithLatencyInfo& touch_event) { | |
96 if (!client_->OnSendTouchEvent(touch_event)) | |
97 return; | |
98 if (QueueWebEvent(touch_event.event, touch_event.latency, false)) | |
99 ++queued_touch_count_; | |
100 } | |
101 | |
102 void BufferedInputRouter::SendMouseEventImmediately( | |
103 const MouseEventWithLatencyInfo& mouse_event) { | |
104 if (!client_->OnSendMouseEventImmediately(mouse_event)) | |
105 return; | |
106 QueueWebEvent(mouse_event.event, mouse_event.latency, false); | |
107 } | |
108 | |
109 void BufferedInputRouter::SendTouchEventImmediately( | |
110 const TouchEventWithLatencyInfo& touch_event) { | |
111 if (!client_->OnSendTouchEventImmediately(touch_event)) | |
112 return; | |
113 QueueWebEvent(touch_event.event, touch_event.latency, false); | |
114 } | |
115 | |
116 void BufferedInputRouter::SendGestureEventImmediately( | |
117 const GestureEventWithLatencyInfo& gesture_event) { | |
118 if (!client_->OnSendGestureEventImmediately(gesture_event)) | |
119 return; | |
120 QueueWebEvent(gesture_event.event, gesture_event.latency, false); | |
121 } | |
122 | |
123 void BufferedInputRouter::Deliver(const EventPacket& packet) { | |
124 TRACE_EVENT2("input", "BufferedInputRouter::DeliverPacket", | |
125 "id", packet.id(), | |
126 "events", packet.size()); | |
127 DCHECK(packet.id()); | |
128 DCHECK(!in_flight_packet_id_); | |
129 if (!sender_->Send(new InputMsg_HandleEventPacket( | |
130 routing_id_, packet, InputEventDispositions()))) { | |
131 return; | |
132 } | |
133 | |
134 in_flight_packet_id_ = packet.id(); | |
135 client_->IncrementInFlightEventCount(); | |
136 } | |
137 | |
138 void BufferedInputRouter::DidFinishFlush() { | |
139 TRACE_EVENT0("input", "BufferedInputRouter::DidFinishFlush"); | |
140 client_->DidFlush(); | |
141 } | |
142 | |
143 void BufferedInputRouter::SetNeedsFlush() { | |
144 TRACE_EVENT0("input", "BufferedInputRouter::SetNeedsFlush"); | |
145 client_->SetNeedsFlush(); | |
146 } | |
147 | |
148 void BufferedInputRouter::OnDispatched(const BrowserInputEvent& event, | |
149 InputEventDisposition disposition) { | |
150 // Only WebInputEvents currently have ack response. | |
151 if (event.payload()->GetType() != InputEvent::Payload::WEB_INPUT_EVENT) | |
152 return; | |
153 | |
154 const WebInputEventPayload* web_payload = | |
155 WebInputEventPayload::Cast(event.payload()); | |
156 | |
157 OnWebInputEventAck(event.id(), | |
158 *web_payload->web_event(), | |
159 web_payload->latency_info(), | |
160 ToAckState(disposition), | |
161 true); | |
162 } | |
163 | |
164 void BufferedInputRouter::OnDispatched( | |
165 const BrowserInputEvent& event, | |
166 InputEventDisposition disposition, | |
167 ScopedVector<BrowserInputEvent>* followup) { | |
168 DCHECK(followup); | |
169 DCHECK_NE(INPUT_EVENT_ACK_STATE_CONSUMED, ToAckState(disposition)); | |
170 | |
171 // Events sent to the router within this scope will be added to |followup|. | |
172 base::AutoReset<ScopedVector<BrowserInputEvent>*> input_queue_override( | |
173 &input_queue_override_, followup); | |
174 | |
175 OnDispatched(event, disposition); | |
176 } | |
177 | |
178 bool BufferedInputRouter::OnMessageReceived(const IPC::Message& message) { | |
179 bool handled = true; | |
180 bool message_is_ok = true; | |
181 IPC_BEGIN_MESSAGE_MAP_EX(BufferedInputRouter, message, message_is_ok) | |
182 IPC_MESSAGE_HANDLER(InputHostMsg_HandleEventPacket_ACK, OnEventPacketAck) | |
183 IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers, | |
184 OnHasTouchEventHandlers) | |
185 IPC_MESSAGE_UNHANDLED(handled = false) | |
186 IPC_END_MESSAGE_MAP() | |
187 | |
188 if (!message_is_ok) | |
189 ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); | |
190 | |
191 return handled; | |
192 } | |
193 | |
194 const NativeWebKeyboardEvent* | |
195 BufferedInputRouter::GetLastKeyboardEvent() const { | |
196 return queued_key_map_.empty() ? NULL : &queued_key_map_.begin()->second; | |
197 } | |
198 | |
199 bool BufferedInputRouter::ShouldForwardTouchEvent() const { | |
200 return has_touch_handler_ && queued_touch_count_ > 0; | |
201 } | |
202 | |
203 bool BufferedInputRouter::ShouldForwardGestureEvent( | |
204 const GestureEventWithLatencyInfo& touch_event) const { | |
205 return true; | |
206 } | |
207 | |
208 void BufferedInputRouter::OnWebInputEventAck( | |
209 int64 event_id, | |
210 const WebKit::WebInputEvent& web_event, | |
211 const ui::LatencyInfo& latency_info, | |
212 InputEventAckState acked_result, | |
213 bool ack_from_input_queue) { | |
214 if (WebInputEvent::isKeyboardEventType(web_event.type)) { | |
215 if (ack_from_input_queue) { | |
216 KeyMap::iterator key_it = queued_key_map_.find(event_id); | |
217 DCHECK(key_it != queued_key_map_.end()); | |
218 NativeWebKeyboardEvent key_event = key_it->second; | |
219 queued_key_map_.erase(key_it); | |
220 ack_handler_->OnKeyboardEventAck(key_event, acked_result); | |
221 } else { | |
222 DCHECK_EQ(0, event_id); | |
223 ack_handler_->OnKeyboardEventAck( | |
224 static_cast<const NativeWebKeyboardEvent&>(web_event), acked_result); | |
225 } | |
226 // WARNING: This BufferedInputRouter can be deallocated at this point | |
227 // (i.e. in the case of Ctrl+W, where the call to | |
228 // HandleKeyboardEvent destroys this BufferedInputRouter). | |
229 } else if (web_event.type == WebInputEvent::MouseWheel) { | |
230 ack_handler_->OnWheelEventAck( | |
231 static_cast<const WebMouseWheelEvent&>(web_event), acked_result); | |
232 } else if (WebInputEvent::isTouchEventType(web_event.type)) { | |
233 if (ack_from_input_queue) { | |
234 DCHECK_GT(queued_touch_count_, 0); | |
235 --queued_touch_count_; | |
236 } | |
237 ack_handler_->OnTouchEventAck( | |
238 TouchEventWithLatencyInfo(static_cast<const WebTouchEvent&>(web_event), | |
239 latency_info), | |
240 acked_result); | |
241 } else if (WebInputEvent::isGestureEventType(web_event.type)) { | |
242 ack_handler_->OnGestureEventAck( | |
243 static_cast<const WebGestureEvent&>(web_event), acked_result); | |
244 } else | |
245 NOTREACHED() << "Unexpected WebInputEvent in OnWebInputEventAck"; | |
246 } | |
247 | |
248 void BufferedInputRouter::OnEventPacketAck( | |
249 int64 packet_id, | |
250 const InputEventDispositions& dispositions) { | |
251 TRACE_EVENT2("input", "BufferedInputRouter::OnEventPacketAck", | |
252 "id", packet_id, | |
253 "dispositions", dispositions.size()); | |
254 if (!in_flight_packet_id_ || packet_id != in_flight_packet_id_) { | |
255 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); | |
256 return; | |
257 } | |
258 | |
259 in_flight_packet_id_ = 0; | |
260 client_->DecrementInFlightEventCount(); | |
261 | |
262 InputQueue::AckResult ack_result = | |
263 input_queue_->OnEventPacketAck(packet_id, dispositions); | |
264 if (ack_result == InputQueue::ACK_UNEXPECTED) | |
265 ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK); | |
266 else if (ack_result == InputQueue::ACK_INVALID) | |
267 ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE); | |
268 } | |
269 | |
270 void BufferedInputRouter::OnHasTouchEventHandlers(bool has_handlers) { | |
271 if (has_touch_handler_ == has_handlers) | |
272 return; | |
273 has_touch_handler_ = has_handlers; | |
274 client_->OnHasTouchEventHandlers(has_handlers); | |
275 } | |
276 | |
277 int64 BufferedInputRouter::QueueWebEvent(const WebKit::WebInputEvent& web_event, | |
278 const ui::LatencyInfo& latency_info, | |
279 bool is_key_shortcut) { | |
280 TRACE_EVENT0("input", "BufferedInputRouter::QueueWebEvent"); | |
281 | |
282 if (FilterWebEvent(web_event, latency_info)) { | |
283 TRACE_EVENT_INSTANT0("input", | |
284 "BufferedInputRouter::QueueWebEvent::Filtered", | |
285 TRACE_EVENT_SCOPE_THREAD); | |
286 return 0; | |
287 } | |
288 | |
289 int64 event_id = NextInputID(); | |
290 scoped_ptr<BrowserInputEvent> event = BrowserInputEvent::Create( | |
291 event_id, | |
292 WebInputEventPayload::Create(web_event, | |
293 latency_info, | |
294 is_key_shortcut), | |
295 this); | |
296 | |
297 // The presence of |input_queue_override_| implies that we are in the | |
298 // scope of |OnInputEventDispatched()| with an event can create followup. | |
299 if (input_queue_override_) | |
300 input_queue_override_->push_back(event.release()); | |
301 else | |
302 input_queue_->QueueEvent(event.Pass()); | |
303 | |
304 return event_id; | |
305 } | |
306 | |
307 bool BufferedInputRouter::FilterWebEvent(const WebKit::WebInputEvent& web_event, | |
308 const ui::LatencyInfo& latency_info) { | |
309 // Perform optional, synchronous event handling, sending ACK messages for | |
310 // processed events, or proceeding as usual. | |
311 InputEventAckState filter_ack = | |
312 client_->FilterInputEvent(web_event, latency_info); | |
313 switch (filter_ack) { | |
314 // Send the ACK and early exit. | |
315 case INPUT_EVENT_ACK_STATE_CONSUMED: | |
316 case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS: | |
317 OnWebInputEventAck(0, web_event, latency_info, filter_ack, false); | |
318 // WARNING: |this| may be deleted at this point. | |
319 return true; | |
320 | |
321 // Drop the event. | |
322 case INPUT_EVENT_ACK_STATE_UNKNOWN: | |
323 return true; | |
324 | |
325 // Proceed as normal. | |
326 case INPUT_EVENT_ACK_STATE_NOT_CONSUMED: | |
327 default: | |
328 break; | |
329 }; | |
330 | |
331 return false; | |
332 } | |
333 | |
334 int64 BufferedInputRouter::NextInputID() { return next_input_id_++; } | |
335 | |
336 } // namespace content | |
OLD | NEW |