Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(294)

Unified Diff: content/browser/renderer_host/input/buffered_input_router.cc

Issue 20356003: Provided batched input delivery with a BufferedInputRouter (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Code review Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/renderer_host/input/buffered_input_router.cc
diff --git a/content/browser/renderer_host/input/buffered_input_router.cc b/content/browser/renderer_host/input/buffered_input_router.cc
new file mode 100644
index 0000000000000000000000000000000000000000..738ae03f26b0daf750f7a8143e06b734b11a227c
--- /dev/null
+++ b/content/browser/renderer_host/input/buffered_input_router.cc
@@ -0,0 +1,375 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/input/buffered_input_router.h"
+
+#include "base/auto_reset.h"
+#include "content/browser/renderer_host/input/input_ack_handler.h"
+#include "content/browser/renderer_host/input/input_queue.h"
+#include "content/browser/renderer_host/render_widget_host_impl.h"
+#include "content/common/input/input_event_utils.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "content/public/browser/user_metrics.h"
+
+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.
+using base::TimeDelta;
+using base::TimeTicks;
+using WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+
+namespace content {
+
+namespace {
+
+InputEventAckState FromState(InputEventState state) {
+ switch (state) {
+ case INPUT_EVENT_UNHANDLED:
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ case INPUT_EVENT_IMPL_THREAD_ABSORBED:
+ return INPUT_EVENT_ACK_STATE_CONSUMED;
+ case INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS:
+ return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+ case INPUT_EVENT_IMPL_THREAD_BOUNCE_TO_MAIN:
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ case INPUT_EVENT_IMPL_THREAD_COULD_NOT_DELIVER:
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ case INPUT_EVENT_MAIN_THREAD_ABSORBED:
+ return INPUT_EVENT_ACK_STATE_CONSUMED;
+ case INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED:
+ return INPUT_EVENT_ACK_STATE_CONSUMED;
+ case INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED:
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ case INPUT_EVENT_MAIN_THREAD_NO_HANDLER_EXISTS:
+ return INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS;
+ case INPUT_EVENT_MAIN_THREAD_COULD_NOT_DELIVER:
+ return INPUT_EVENT_ACK_STATE_NOT_CONSUMED;
+ }
+
+ NOTREACHED();
+ return INPUT_EVENT_ACK_STATE_UNKNOWN;
+}
+
+} // namespace
+
+BufferedInputRouter::BufferedInputRouter(IPC::Sender* sender,
+ InputRouterClient* client,
+ InputAckHandler* ack_handler,
+ int routing_id)
+ : client_(client),
+ ack_handler_(ack_handler),
+ sender_(sender),
+ routing_id_(routing_id),
+ queued_gesture_count_(0),
+ has_touch_handler_(false),
+ queued_touch_count_(0),
+ input_queue_override_(NULL),
+ next_input_id_(1),
+ in_flight_packet_id_(0) {
+ input_queue_.reset(new InputQueue(this));
+}
+
+BufferedInputRouter::~BufferedInputRouter() {}
+
+void BufferedInputRouter::Flush() {
+ TRACE_EVENT0("input", "BufferedInputRouter::Flush");
+ DCHECK_EQ(0, in_flight_packet_id_);
+
+ input_queue_->FlushEventsInCurrentFrame();
+}
+
+bool BufferedInputRouter::SendInput(scoped_ptr<IPC::Message> message) {
+ DCHECK_EQ(InputMsgStart, IPC_MESSAGE_ID_CLASS(message->type()));
+ DCHECK_NE(InputMsg_HandleEventPacket::ID, message->type());
+ input_queue_->QueueEvent(InputEvent(NextInputID(),
+ INPUT_EVENT_ONE_WAY,
+ *message), NULL);
+ return true;
+}
+
+void BufferedInputRouter::SendMouseEvent(
+ const MouseEventWithLatencyInfo& mouse_event) {
+ if (!client_->OnSendMouseEvent(mouse_event))
+ return;
+ QueueWebEvent(mouse_event.event, mouse_event.latency, false, false);
+}
+
+void BufferedInputRouter::SendWheelEvent(
+ const MouseWheelEventWithLatencyInfo& wheel_event) {
+ if (!client_->OnSendWheelEvent(wheel_event))
+ return;
+ QueueWebEvent(wheel_event.event, wheel_event.latency, false, false);
+}
+
+void BufferedInputRouter::SendKeyboardEvent(
+ const NativeWebKeyboardEvent& key_event,
+ const ui::LatencyInfo& latency_info) {
+ bool is_shortcut = false;
+ if (!client_->OnSendKeyboardEvent(key_event, latency_info, &is_shortcut))
+ return;
+ int64 event_id = QueueWebEvent(key_event, latency_info, is_shortcut, false);
+ if (event_id) {
+ DCHECK(queued_key_map_.find(event_id) == queued_key_map_.end());
+ queued_key_map_[event_id] = key_event;
+ }
+}
+
+void BufferedInputRouter::SendGestureEvent(
+ const GestureEventWithLatencyInfo& gesture_event) {
+ if (!client_->OnSendGestureEvent(gesture_event))
+ return;
+ if (QueueWebEvent(gesture_event.event, gesture_event.latency, false, false))
+ ++queued_gesture_count_;
+}
+
+void BufferedInputRouter::SendTouchEvent(
+ const TouchEventWithLatencyInfo& touch_event) {
+ if (!client_->OnSendTouchEvent(touch_event))
+ return;
+ if (QueueWebEvent(touch_event.event, touch_event.latency, false, true))
+ ++queued_touch_count_;
+}
+
+void BufferedInputRouter::SendMouseEventImmediately(
+ const MouseEventWithLatencyInfo& mouse_event) {
+ if (!client_->OnSendMouseEventImmediately(mouse_event))
+ return;
+ QueueWebEvent(mouse_event.event, mouse_event.latency, false, false);
+}
+
+void BufferedInputRouter::SendTouchEventImmediately(
+ const TouchEventWithLatencyInfo& touch_event) {
+ if (!client_->OnSendTouchEventImmediately(touch_event))
+ return;
+ QueueWebEvent(touch_event.event, touch_event.latency, false, false);
+}
+
+void BufferedInputRouter::SendGestureEventImmediately(
+ const GestureEventWithLatencyInfo& gesture_event) {
+ if (!client_->OnSendGestureEventImmediately(gesture_event))
+ return;
+ QueueWebEvent(gesture_event.event, gesture_event.latency, false, false);
+}
+
+void BufferedInputRouter::Deliver(const EventPacket& packet) {
+ TRACE_EVENT2("input", "BufferedInputRouter::DeliverPacket",
+ "id", packet.id,
+ "events", packet.events.size());
+ DCHECK(packet.id);
+ DCHECK(!in_flight_packet_id_);
+ if (!sender_->Send(new InputMsg_HandleEventPacket(routing_id_, packet)))
+ return;
+
+ in_flight_packet_id_ = packet.id;
+ client_->IncrementInFlightEventCount();
+}
+
+void BufferedInputRouter::DidFlush() {
+ TRACE_EVENT0("input", "BufferedInputRouter::DidFlush");
+ client_->DidFlush();
+}
+
+void BufferedInputRouter::SetNeedsFlush() {
+ TRACE_EVENT0("input", "BufferedInputRouter::SetNeedsFlush");
+ client_->SetNeedsFlush();
+}
+
+std::vector<InputEvent> BufferedInputRouter::OnInputEventAck(
+ const InputEvent& acked_event) {
+ DCHECK(IsWebInputEventMessage(acked_event.message));
+
+ const WebKit::WebInputEvent* web_event = NULL;
+ ui::LatencyInfo latency_info;
+ DCHECK(CrackWebInputEventMessage(acked_event.message,
+ &web_event,
+ &latency_info,
+ NULL));
+
+ InputEventAckState ack_state = FromState(acked_event.state);
+
+ if (acked_event.type == INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP) {
+ std::vector<InputEvent> followup_events;
+ base::AutoReset<std::vector<InputEvent>*> input_queue_override(
+ &input_queue_override_, &followup_events);
+ OnInputEventAck(acked_event.id, *web_event, latency_info, ack_state, true);
+ return followup_events;
+ }
+
+ OnInputEventAck(acked_event.id, *web_event, latency_info, ack_state, true);
+ return std::vector<InputEvent>();
+}
+
+bool BufferedInputRouter::OnMessageReceived(const IPC::Message& message) {
+ bool handled = true;
+ bool message_is_ok = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(BufferedInputRouter, message, message_is_ok)
+ IPC_MESSAGE_HANDLER(InputHostMsg_HandleEventPacket_ACK, OnEventPacketAck)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_HasTouchEventHandlers,
+ OnHasTouchEventHandlers)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+
+ if (!message_is_ok)
+ ack_handler_->OnUnexpectedEventAck(true);
+
+ return handled;
+}
+
+const NativeWebKeyboardEvent*
+ BufferedInputRouter::GetLastKeyboardEvent() const {
+ return queued_key_map_.empty() ? NULL : &queued_key_map_.begin()->second;
+}
+
+bool BufferedInputRouter::ShouldForwardTouchEvent() const {
+ return has_touch_handler_ && queued_touch_count_ > 0;
+}
+
+bool BufferedInputRouter::ShouldForwardGestureEvent(
+ const GestureEventWithLatencyInfo& touch_event) const {
+ return true;
+}
+
+bool BufferedInputRouter::HasQueuedGestureEvents() const {
+ return queued_gesture_count_ > 0;
+}
+
+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.
+ int64 event_id,
+ const WebKit::WebInputEvent& web_event,
+ const ui::LatencyInfo& latency_info,
+ InputEventAckState acked_result,
+ bool ack_from_input_queue) {
+ if (WebInputEvent::isKeyboardEventType(web_event.type)) {
+ if (ack_from_input_queue) {
+ KeyMap::iterator key_it = queued_key_map_.find(event_id);
+ DCHECK(key_it != queued_key_map_.end());
+ NativeWebKeyboardEvent key_event = key_it->second;
+ queued_key_map_.erase(key_it);
+ ack_handler_->OnKeyboardEventAck(key_event, acked_result);
+ } else {
+ DCHECK_EQ(0, event_id);
+ ack_handler_->OnKeyboardEventAck(
+ static_cast<const NativeWebKeyboardEvent&>(web_event), acked_result);
+ }
+ // WARNING: This BufferedInputRouter can be deallocated at this point
+ // (i.e. in the case of Ctrl+W, where the call to
+ // HandleKeyboardEvent destroys this BufferedInputRouter).
+ } else if (web_event.type == WebInputEvent::MouseWheel) {
+ ack_handler_->OnWheelEventAck(
+ static_cast<const WebMouseWheelEvent&>(web_event), acked_result);
+ } else if (WebInputEvent::isTouchEventType(web_event.type)) {
+ if (ack_from_input_queue) {
+ DCHECK_GT(queued_touch_count_, 0);
+ --queued_touch_count_;
+ }
+ ack_handler_->OnTouchEventAck(
+ TouchEventWithLatencyInfo(static_cast<const WebTouchEvent&>(web_event),
+ latency_info), acked_result);
+ } else if (WebInputEvent::isGestureEventType(web_event.type)) {
+ if (ack_from_input_queue) {
+ DCHECK_GT(queued_gesture_count_, 0);
+ --queued_gesture_count_;
+ }
+ ack_handler_->OnGestureEventAck(
+ static_cast<const WebGestureEvent&>(web_event), acked_result);
+ } else {
aelias_OOO_until_Jul13 2013/08/15 00:52:15 nit: remove this else {}
jdduke (slow) 2013/08/15 23:22:26 Done.
+ NOTREACHED() << "Unexpected WebInputEvent in OnInputEventAck";
+ }
+}
+
+void BufferedInputRouter::OnEventPacketAck(const EventPacket& packet) {
+ TRACE_EVENT1("input", "BufferedInputRouter::OnEventPacketAck",
+ "id", packet.id);
+ if (!in_flight_packet_id_ || packet.id != in_flight_packet_id_) {
+ ack_handler_->OnUnexpectedEventAck(false);
+ return;
+ }
+
+ in_flight_packet_id_ = 0;
+ client_->DecrementInFlightEventCount();
+
+ InputQueue::AckResult ack_result = input_queue_->OnEventPacketAck(packet);
+ if (ack_result == InputQueue::ACK_UNEXPECTED)
+ ack_handler_->OnUnexpectedEventAck(false);
+ else if (ack_result == InputQueue::ACK_INVALID)
+ ack_handler_->OnUnexpectedEventAck(true);
+}
+
+void BufferedInputRouter::OnHasTouchEventHandlers(bool has_handlers) {
+ if (has_touch_handler_ == has_handlers)
+ return;
+ has_touch_handler_ = has_handlers;
+ client_->OnHasTouchEventHandlers(has_handlers);
+}
+
+int64 BufferedInputRouter::QueueWebEvent(const WebKit::WebInputEvent& web_event,
+ const ui::LatencyInfo& latency_info,
+ bool is_keyboard_shortcut,
+ bool has_followup) {
+ TRACE_EVENT0("input", "BufferedInputRouter::QueueWebEvent");
+
+ if (FilterWebEvent(web_event, latency_info)) {
+ TRACE_EVENT_INSTANT0("input",
+ "BufferedInputRouter::QueueWebEvent::Filtered",
+ TRACE_EVENT_SCOPE_THREAD);
+ return 0;
+ }
+
+ InputEventType type = has_followup ? INPUT_EVENT_NEEDS_ACK_AND_HAS_FOLLOWUP
+ : INPUT_EVENT_NEEDS_ACK;
+ InputEvent event(NextInputID(),
+ type,
+ InputMsg_HandleInputEvent(routing_id_,
+ &web_event,
+ latency_info,
+ is_keyboard_shortcut));
+
+ // The presence of |input_queue_override_| implies that we are in the
+ // scope of OnInputEventAck with an event that has followup.
+ // 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.
+ if (input_queue_override_)
+ input_queue_override_->push_back(event);
+ else
+ input_queue_->QueueEvent(event, NULL);
+
+ return event.id;
+}
+
+bool BufferedInputRouter::FilterWebEvent(const WebKit::WebInputEvent& web_event,
+ const ui::LatencyInfo& latency_info) {
+ // Perform optional, synchronous event handling, sending ACK messages for
+ // processed events, or proceeding as usual.
+ InputEventAckState filter_ack = client_->FilterInputEvent(web_event,
+ latency_info);
+ switch (filter_ack) {
+ // Send the ACK and early exit.
+ case INPUT_EVENT_ACK_STATE_CONSUMED:
+ case INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS:
+ OnInputEventAck(0, web_event, latency_info, filter_ack, false);
+ // WARNING: |this| may be deleted at this point.
+ return true;
+
+ // Drop the event.
+ case INPUT_EVENT_ACK_STATE_UNKNOWN:
+ return true;
+
+ // Proceed as normal.
+ case INPUT_EVENT_ACK_STATE_NOT_CONSUMED:
+ default:
+ break;
+ };
+
+ return false;
+}
+
+int64 BufferedInputRouter::NextInputID() {
+ return next_input_id_++;
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698