summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorjdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-12 00:32:49 +0000
committerjdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-09-12 00:32:49 +0000
commit6722816ee73f7002a9452ac93b1f6cbb8de036c9 (patch)
tree673d30a7ca142aa7d2cf06d03e92c25ecb476b78 /content
parent2ac0763be9d8497e34fee49f92f7f0fbebfc17d9 (diff)
downloadchromium_src-6722816ee73f7002a9452ac93b1f6cbb8de036c9.zip
chromium_src-6722816ee73f7002a9452ac93b1f6cbb8de036c9.tar.gz
chromium_src-6722816ee73f7002a9452ac93b1f6cbb8de036c9.tar.bz2
Provided batched input delivery with a BufferedInputRouter
The BufferedInputRouter batches input events into EventPackets. The delivery of these packets is dependent on periodic flush signals, as requested by the InputRouterClient. A given flush involves the forwarding of EventPackets to the renderer until all InputEvents from the original packet have been dispatched. BUG=245499 Review URL: https://chromiumcodereview.appspot.com/20356003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@222674 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/renderer_host/input/OWNERS3
-rw-r--r--content/browser/renderer_host/input/browser_input_event.cc47
-rw-r--r--content/browser/renderer_host/input/browser_input_event.h69
-rw-r--r--content/browser/renderer_host/input/buffered_input_router.cc346
-rw-r--r--content/browser/renderer_host/input/buffered_input_router.h148
-rw-r--r--content/browser/renderer_host/input/buffered_input_router_unittest.cc335
-rw-r--r--content/browser/renderer_host/input/immediate_input_router.cc69
-rw-r--r--content/browser/renderer_host/input/immediate_input_router.h10
-rw-r--r--content/browser/renderer_host/input/immediate_input_router_unittest.cc567
-rw-r--r--content/browser/renderer_host/input/input_ack_handler.h41
-rw-r--r--content/browser/renderer_host/input/input_queue.cc183
-rw-r--r--content/browser/renderer_host/input/input_queue.h82
-rw-r--r--content/browser/renderer_host/input/input_queue_client.h35
-rw-r--r--content/browser/renderer_host/input/input_queue_unittest.cc363
-rw-r--r--content/browser/renderer_host/input/input_router.h6
-rw-r--r--content/browser/renderer_host/input/input_router_client.h19
-rw-r--r--content/browser/renderer_host/input/input_router_unittest.cc225
-rw-r--r--content/browser/renderer_host/input/input_router_unittest.h83
-rw-r--r--content/browser/renderer_host/input/mock_input_ack_handler.cc77
-rw-r--r--content/browser/renderer_host/input/mock_input_ack_handler.h77
-rw-r--r--content/browser/renderer_host/input/mock_input_router_client.cc148
-rw-r--r--content/browser/renderer_host/input/mock_input_router_client.h126
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.cc23
-rw-r--r--content/browser/renderer_host/render_widget_host_impl.h9
-rw-r--r--content/browser/renderer_host/render_widget_host_unittest.cc10
-rw-r--r--content/content_browser.gypi8
-rw-r--r--content/content_tests.gypi9
27 files changed, 2567 insertions, 551 deletions
diff --git a/content/browser/renderer_host/input/OWNERS b/content/browser/renderer_host/input/OWNERS
index 4dabaf0..4893edb 100644
--- a/content/browser/renderer_host/input/OWNERS
+++ b/content/browser/renderer_host/input/OWNERS
@@ -1,2 +1,3 @@
-nduca@chromium.org
aelias@chromium.org
+jdduke@chromium.org
+nduca@chromium.org
diff --git a/content/browser/renderer_host/input/browser_input_event.cc b/content/browser/renderer_host/input/browser_input_event.cc
new file mode 100644
index 0000000..276245e
--- /dev/null
+++ b/content/browser/renderer_host/input/browser_input_event.cc
@@ -0,0 +1,47 @@
+// Copyright 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/browser_input_event.h"
+
+#include "content/common/input/web_input_event_payload.h"
+
+namespace content {
+
+BrowserInputEvent::BrowserInputEvent(BrowserInputEventClient* client)
+ : client_(client) {}
+
+BrowserInputEvent::~BrowserInputEvent() {}
+
+scoped_ptr<BrowserInputEvent> BrowserInputEvent::Create(
+ int64 id,
+ scoped_ptr<InputEvent::Payload> payload,
+ BrowserInputEventClient* client) {
+ DCHECK(client);
+ scoped_ptr<BrowserInputEvent> event(new BrowserInputEvent(client));
+ event->Initialize(id, payload.Pass());
+ return event.Pass();
+}
+
+void BrowserInputEvent::OnDispatched(
+ InputEventDisposition disposition,
+ ScopedVector<BrowserInputEvent>* followup_events) {
+ DCHECK(followup_events);
+
+ bool event_consumed =
+ disposition == INPUT_EVENT_MAIN_THREAD_CONSUMED ||
+ disposition == INPUT_EVENT_IMPL_THREAD_CONSUMED ||
+ disposition == INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED;
+
+ if (!event_consumed && CanCreateFollowupEvents())
+ client_->OnDispatched(*this, disposition, followup_events);
+ else
+ client_->OnDispatched(*this, disposition);
+}
+
+bool BrowserInputEvent::CanCreateFollowupEvents() const {
+ return payload()->GetType() == InputEvent::Payload::WEB_INPUT_EVENT &&
+ WebInputEventPayload::Cast(payload())->CanCreateFollowupEvents();
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/browser_input_event.h b/content/browser/renderer_host/input/browser_input_event.h
new file mode 100644
index 0000000..6022d80
--- /dev/null
+++ b/content/browser/renderer_host/input/browser_input_event.h
@@ -0,0 +1,69 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "content/common/input/input_event.h"
+#include "content/common/input/input_event_disposition.h"
+
+namespace content {
+
+class BrowserInputEvent;
+
+// Provides customized dispatch response for BrowserInputEvents.
+class BrowserInputEventClient {
+ public:
+ virtual ~BrowserInputEventClient() {}
+
+ virtual void OnDispatched(const BrowserInputEvent& event,
+ InputEventDisposition disposition) {}
+
+ // Called if the event went unconsumed and can create followup events. Any
+ // events added to |followup| by the client will be inserted into the
+ // current input event stream. |followup| will never be NULL.
+ virtual void OnDispatched(const BrowserInputEvent& event,
+ InputEventDisposition disposition,
+ ScopedVector<BrowserInputEvent>* followup) {}
+};
+
+// Augmented InputEvent allowing customized dispatch response in the browser.
+class CONTENT_EXPORT BrowserInputEvent : public InputEvent {
+ public:
+ // |client| is assumed to be non-NULL.
+ static scoped_ptr<BrowserInputEvent> Create(
+ int64 id,
+ scoped_ptr<InputEvent::Payload> payload,
+ BrowserInputEventClient* client);
+
+ template <typename PayloadType>
+ static scoped_ptr<BrowserInputEvent> Create(int64 id,
+ scoped_ptr<PayloadType> payload,
+ BrowserInputEventClient* client) {
+ return Create(id, payload.template PassAs<InputEvent::Payload>(), client);
+ }
+
+ virtual ~BrowserInputEvent();
+
+ // |followup_events| must not be NULL, and will only be modified if the
+ // event went unconsumed and can create followup events.
+ void OnDispatched(InputEventDisposition disposition,
+ ScopedVector<BrowserInputEvent>* followup_events);
+
+ protected:
+ explicit BrowserInputEvent(BrowserInputEventClient* client);
+
+ bool CanCreateFollowupEvents() const;
+
+ private:
+ BrowserInputEventClient* client_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_BROWSER_INPUT_EVENT_H_
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 0000000..3f0fb23
--- /dev/null
+++ b/content/browser/renderer_host/input/buffered_input_router.cc
@@ -0,0 +1,346 @@
+// Copyright 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/browser_input_event.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_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 WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+
+namespace content {
+
+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_->BeginFlush();
+}
+
+bool BufferedInputRouter::SendInput(scoped_ptr<IPC::Message> message) {
+ DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart);
+ DCHECK(message->type() != InputMsg_HandleEventPacket::ID);
+ DCHECK(message->type() != InputMsg_HandleInputEvent::ID);
+ input_queue_->QueueEvent(BrowserInputEvent::Create(
+ NextInputID(), IPCInputEventPayload::Create(message.Pass()), this));
+ return true;
+}
+
+void BufferedInputRouter::SendMouseEvent(
+ const MouseEventWithLatencyInfo& mouse_event) {
+ if (!client_->OnSendMouseEvent(mouse_event))
+ return;
+ // TODO(jdduke): Coalescing, http://crbug.com/289520
+ QueueWebEvent(mouse_event.event, mouse_event.latency, false);
+}
+
+void BufferedInputRouter::SendWheelEvent(
+ const MouseWheelEventWithLatencyInfo& wheel_event) {
+ if (!client_->OnSendWheelEvent(wheel_event))
+ return;
+ QueueWebEvent(wheel_event.event, wheel_event.latency, 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);
+ 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))
+ ++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))
+ ++queued_touch_count_;
+}
+
+void BufferedInputRouter::SendMouseEventImmediately(
+ const MouseEventWithLatencyInfo& mouse_event) {
+ if (!client_->OnSendMouseEventImmediately(mouse_event))
+ return;
+ QueueWebEvent(mouse_event.event, mouse_event.latency, false);
+}
+
+void BufferedInputRouter::SendTouchEventImmediately(
+ const TouchEventWithLatencyInfo& touch_event) {
+ if (!client_->OnSendTouchEventImmediately(touch_event))
+ return;
+ QueueWebEvent(touch_event.event, touch_event.latency, false);
+}
+
+void BufferedInputRouter::SendGestureEventImmediately(
+ const GestureEventWithLatencyInfo& gesture_event) {
+ if (!client_->OnSendGestureEventImmediately(gesture_event))
+ return;
+ QueueWebEvent(gesture_event.event, gesture_event.latency, false);
+}
+
+void BufferedInputRouter::Deliver(const EventPacket& packet) {
+ TRACE_EVENT2("input", "BufferedInputRouter::DeliverPacket",
+ "id", packet.id(),
+ "events", packet.size());
+ DCHECK(packet.id());
+ DCHECK(!in_flight_packet_id_);
+ if (!sender_->Send(new InputMsg_HandleEventPacket(
+ routing_id_, packet, InputEventDispositions()))) {
+ return;
+ }
+
+ in_flight_packet_id_ = packet.id();
+ client_->IncrementInFlightEventCount();
+}
+
+void BufferedInputRouter::DidFinishFlush() {
+ TRACE_EVENT0("input", "BufferedInputRouter::DidFinishFlush");
+ client_->DidFlush();
+}
+
+void BufferedInputRouter::SetNeedsFlush() {
+ TRACE_EVENT0("input", "BufferedInputRouter::SetNeedsFlush");
+ client_->SetNeedsFlush();
+}
+
+void BufferedInputRouter::OnDispatched(const BrowserInputEvent& event,
+ InputEventDisposition disposition) {
+ // Only WebInputEvents currently have ack response.
+ if (event.payload()->GetType() != InputEvent::Payload::WEB_INPUT_EVENT)
+ return;
+
+ const WebInputEventPayload* web_payload =
+ WebInputEventPayload::Cast(event.payload());
+
+ OnWebInputEventAck(event.id(),
+ *web_payload->web_event(),
+ web_payload->latency_info(),
+ ToAckState(disposition),
+ true);
+}
+
+void BufferedInputRouter::OnDispatched(
+ const BrowserInputEvent& event,
+ InputEventDisposition disposition,
+ ScopedVector<BrowserInputEvent>* followup) {
+ DCHECK(followup);
+ DCHECK_NE(INPUT_EVENT_ACK_STATE_CONSUMED, ToAckState(disposition));
+
+ // Events sent to the router within this scope will be added to |followup|.
+ base::AutoReset<ScopedVector<BrowserInputEvent>*> input_queue_override(
+ &input_queue_override_, followup);
+
+ OnDispatched(event, disposition);
+}
+
+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(InputAckHandler::BAD_ACK_MESSAGE);
+
+ 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::OnWebInputEventAck(
+ 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
+ NOTREACHED() << "Unexpected WebInputEvent in OnWebInputEventAck";
+}
+
+void BufferedInputRouter::OnEventPacketAck(
+ int64 packet_id,
+ const InputEventDispositions& dispositions) {
+ TRACE_EVENT2("input", "BufferedInputRouter::OnEventPacketAck",
+ "id", packet_id,
+ "dispositions", dispositions.size());
+ if (!in_flight_packet_id_ || packet_id != in_flight_packet_id_) {
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK);
+ return;
+ }
+
+ in_flight_packet_id_ = 0;
+ client_->DecrementInFlightEventCount();
+
+ InputQueue::AckResult ack_result =
+ input_queue_->OnEventPacketAck(packet_id, dispositions);
+ if (ack_result == InputQueue::ACK_UNEXPECTED)
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK);
+ else if (ack_result == InputQueue::ACK_INVALID)
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
+}
+
+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_key_shortcut) {
+ TRACE_EVENT0("input", "BufferedInputRouter::QueueWebEvent");
+
+ if (FilterWebEvent(web_event, latency_info)) {
+ TRACE_EVENT_INSTANT0("input",
+ "BufferedInputRouter::QueueWebEvent::Filtered",
+ TRACE_EVENT_SCOPE_THREAD);
+ return 0;
+ }
+
+ int64 event_id = NextInputID();
+ scoped_ptr<BrowserInputEvent> event = BrowserInputEvent::Create(
+ event_id,
+ WebInputEventPayload::Create(web_event,
+ latency_info,
+ is_key_shortcut),
+ this);
+
+ // The presence of |input_queue_override_| implies that we are in the
+ // scope of |OnInputEventDispatched()| with an event can create followup.
+ if (input_queue_override_)
+ input_queue_override_->push_back(event.release());
+ else
+ input_queue_->QueueEvent(event.Pass());
+
+ 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:
+ OnWebInputEventAck(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
diff --git a/content/browser/renderer_host/input/buffered_input_router.h b/content/browser/renderer_host/input/buffered_input_router.h
new file mode 100644
index 0000000..e1652f5
--- /dev/null
+++ b/content/browser/renderer_host/input/buffered_input_router.h
@@ -0,0 +1,148 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_
+#define CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_
+
+#include <map>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "content/browser/renderer_host/input/browser_input_event.h"
+#include "content/browser/renderer_host/input/input_queue.h"
+#include "content/browser/renderer_host/input/input_queue_client.h"
+#include "content/browser/renderer_host/input/input_router.h"
+
+namespace IPC {
+class Sender;
+}
+
+namespace content {
+
+class InputAckHandler;
+class RenderProcessHost;
+class RenderWidgetHostImpl;
+
+// Batches input events into EventPackets using a general input queue. Packets
+// are sent the renderer on |Flush()|, called in response to flush requests.
+class CONTENT_EXPORT BufferedInputRouter
+ : public NON_EXPORTED_BASE(BrowserInputEventClient),
+ public NON_EXPORTED_BASE(InputQueueClient),
+ public NON_EXPORTED_BASE(InputRouter) {
+ public:
+ // |sender|, |client| and |ack_handler| must outlive the BufferedInputRouter.
+ BufferedInputRouter(IPC::Sender* sender,
+ InputRouterClient* client,
+ InputAckHandler* ack_handler,
+ int routing_id);
+ virtual ~BufferedInputRouter();
+
+ // InputRouter
+ virtual void Flush() OVERRIDE;
+ virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE;
+
+ // Certain unhandled input event acks may create follow-up events, e.g.,
+ // TouchEvent -> GestureEvent. If these follow-up events are sent to the
+ // router synchronously from the original event's |OnDispatched()| ack, they
+ // will be inserted into the current input flush stream.
+ virtual void SendMouseEvent(
+ const MouseEventWithLatencyInfo& mouse_event) OVERRIDE;
+ virtual void SendWheelEvent(
+ const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE;
+ virtual void SendKeyboardEvent(const NativeWebKeyboardEvent& key_event,
+ const ui::LatencyInfo& latency_info) OVERRIDE;
+ virtual void SendGestureEvent(
+ const GestureEventWithLatencyInfo& gesture_event) OVERRIDE;
+ virtual void SendTouchEvent(
+ const TouchEventWithLatencyInfo& touch_event) OVERRIDE;
+ virtual void SendMouseEventImmediately(
+ const MouseEventWithLatencyInfo& mouse_event) OVERRIDE;
+ virtual void SendTouchEventImmediately(
+ const TouchEventWithLatencyInfo& touch_event) OVERRIDE;
+ virtual void SendGestureEventImmediately(
+ const GestureEventWithLatencyInfo& gesture_event) OVERRIDE;
+ virtual const NativeWebKeyboardEvent* GetLastKeyboardEvent() const OVERRIDE;
+ virtual bool ShouldForwardTouchEvent() const OVERRIDE;
+ virtual bool ShouldForwardGestureEvent(
+ const GestureEventWithLatencyInfo& gesture_event) const OVERRIDE;
+ virtual bool HasQueuedGestureEvents() const OVERRIDE;
+
+ // InputQueueClient
+ virtual void Deliver(const EventPacket& packet) OVERRIDE;
+ virtual void DidFinishFlush() OVERRIDE;
+ virtual void SetNeedsFlush() OVERRIDE;
+
+ // BrowserInputEventClient
+ virtual void OnDispatched(const BrowserInputEvent& event,
+ InputEventDisposition disposition) OVERRIDE;
+ // Events delivered to the router within the scope of
+ // |OnDispatched()| will be added to |followup|.
+ virtual void OnDispatched(const BrowserInputEvent& event,
+ InputEventDisposition disposition,
+ ScopedVector<BrowserInputEvent>* followup) OVERRIDE;
+
+ // IPC::Receiver
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ protected:
+ void OnWebInputEventAck(int64 event_id,
+ const WebKit::WebInputEvent& web_event,
+ const ui::LatencyInfo& latency_info,
+ InputEventAckState acked_result,
+ bool ack_from_input_queue);
+ void OnEventPacketAck(int64 packet_id,
+ const InputEventDispositions& dispositions);
+ void OnHasTouchEventHandlers(bool has_handlers);
+
+ // Returns the non-zero ID associated with the |InputEvent| added to the
+ // |input_queue_|. If the event was dropped or filtered, returns 0.
+ int64 QueueWebEvent(const WebKit::WebInputEvent& web_event,
+ const ui::LatencyInfo& latency_info,
+ bool is_key_shortcut);
+ // Used by |QueueWebEvent()|; returns true if an event was filtered and should
+ // not be added to the |input_queue_|.
+ bool FilterWebEvent(const WebKit::WebInputEvent& web_event,
+ const ui::LatencyInfo& latency_info);
+
+ // Generates a monotonically increasing sequences of id's, starting with 1.
+ int64 NextInputID();
+
+ const InputQueue* input_queue() const { return input_queue_.get(); }
+
+ private:
+ InputRouterClient* client_;
+ InputAckHandler* ack_handler_;
+ IPC::Sender* sender_;
+ int routing_id_;
+
+ scoped_ptr<InputQueue> input_queue_;
+
+ // TODO(jdduke): Remove when we can properly serialize NativeWebKeyboardEvent.
+ // Alternatively, attach WebInputEvents to InputEvents but don't serialize.
+ typedef std::map<int64, NativeWebKeyboardEvent> KeyMap;
+ KeyMap queued_key_map_;
+
+ // Necessary for |HasQueuedGestureEvents()|.
+ int queued_gesture_count_;
+
+ // Necessary for |ShouldForwardTouchEvent()|.
+ bool has_touch_handler_;
+ int queued_touch_count_;
+
+ // This is non-NULL ONLY in the scope of OnInputEventAck(event, injector).
+ ScopedVector<BrowserInputEvent>* input_queue_override_;
+
+ // Used to assign unique ID's to each InputEvent that is generated.
+ int64 next_input_id_;
+
+ // 0 if there no in-flight EventPacket.
+ int64 in_flight_packet_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedInputRouter);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_INPUT_RENDERER_HOST_BUFFERED_INPUT_ROUTER_H_
diff --git a/content/browser/renderer_host/input/buffered_input_router_unittest.cc b/content/browser/renderer_host/input/buffered_input_router_unittest.cc
new file mode 100644
index 0000000..e34cf60
--- /dev/null
+++ b/content/browser/renderer_host/input/buffered_input_router_unittest.cc
@@ -0,0 +1,335 @@
+// Copyright 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 "base/basictypes.h"
+#include "content/browser/renderer_host/input/buffered_input_router.h"
+#include "content/browser/renderer_host/input/input_router_unittest.h"
+#include "content/common/input/event_packet.h"
+#include "content/common/input_messages.h"
+#include "content/common/view_messages.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+using WebKit::WebTouchPoint;
+
+namespace content {
+
+class TestBufferedInputRouter : public BufferedInputRouter {
+ public:
+ TestBufferedInputRouter(IPC::Sender* sender,
+ InputRouterClient* client,
+ InputAckHandler* ack_handler,
+ int routing_id)
+ : BufferedInputRouter(sender, client, ack_handler, routing_id) {}
+
+
+ size_t QueuedEventCount() const { return input_queue()->QueuedEventCount(); }
+};
+
+class BufferedInputRouterTest : public InputRouterTest {
+ public:
+ BufferedInputRouterTest() {}
+ virtual ~BufferedInputRouterTest() {}
+
+ protected:
+ // InputRouterTest
+ virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process,
+ InputRouterClient* client,
+ InputAckHandler* handler,
+ int routing_id) OVERRIDE {
+ return scoped_ptr<InputRouter>(
+ new TestBufferedInputRouter(process, client, handler, routing_id));
+ }
+
+ bool FinishFlush(const InputEventDispositions& dispositions) {
+ if (!process_->sink().message_count())
+ return false;
+ IPC::Message message(*process_->sink().GetMessageAt(0));
+ process_->sink().ClearMessages();
+
+ InputMsg_HandleEventPacket::Param param;
+ InputMsg_HandleEventPacket::Read(&message, &param);
+ EventPacket& packet = param.a;
+
+ return SendEventPacketACK(packet.id(), dispositions);
+ }
+
+ bool FinishFlush(InputEventDisposition disposition) {
+ if (!process_->sink().message_count())
+ return false;
+ IPC::Message message(*process_->sink().GetMessageAt(0));
+ process_->sink().ClearMessages();
+
+ InputMsg_HandleEventPacket::Param param;
+ InputMsg_HandleEventPacket::Read(&message, &param);
+ EventPacket& packet = param.a;
+
+ return SendEventPacketACK(
+ packet.id(), InputEventDispositions(packet.size(), disposition));
+ }
+
+ bool SendEventPacketACK(int id, const InputEventDispositions& dispositions) {
+ return input_router_->OnMessageReceived(
+ InputHostMsg_HandleEventPacket_ACK(0, id, dispositions));
+ }
+
+ size_t QueuedEventCount() const {
+ return buffered_input_router()->QueuedEventCount();
+ }
+
+ TestBufferedInputRouter* buffered_input_router() const {
+ return static_cast<TestBufferedInputRouter*>(input_router_.get());
+ }
+};
+
+TEST_F(BufferedInputRouterTest, InputEventsProperlyQueued) {
+ EXPECT_TRUE(input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_Redo(MSG_ROUTING_NONE))));
+ EXPECT_EQ(1U, QueuedEventCount());
+
+ EXPECT_TRUE(input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_Cut(MSG_ROUTING_NONE))));
+ EXPECT_EQ(2U, QueuedEventCount());
+
+ EXPECT_TRUE(input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_Copy(MSG_ROUTING_NONE))));
+ EXPECT_EQ(3U, QueuedEventCount());
+
+ EXPECT_TRUE(input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_Paste(MSG_ROUTING_NONE))));
+ EXPECT_EQ(4U, QueuedEventCount());
+}
+
+#define SCOPED_EXPECT(CALL, MESSAGE) { SCOPED_TRACE(MESSAGE); CALL; }
+
+TEST_F(BufferedInputRouterTest, ClientOnSendEventCalled) {
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(1U, QueuedEventCount());
+
+ SimulateWheelEvent(5, 0, 0, false);
+ EXPECT_EQ(2U, QueuedEventCount());
+
+ SimulateMouseMove(5, 0, 0);
+ EXPECT_EQ(3U, QueuedEventCount());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ WebGestureEvent::Touchpad);
+ EXPECT_EQ(4U, QueuedEventCount());
+
+ SimulateTouchEvent(1, 1);
+ EXPECT_EQ(5U, QueuedEventCount());
+}
+
+TEST_F(BufferedInputRouterTest, ClientOnSendEventHonored) {
+ client_->set_allow_send_event(false);
+
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(0U, QueuedEventCount());
+
+ SimulateWheelEvent(5, 0, 0, false);
+ EXPECT_EQ(0U, QueuedEventCount());
+
+ SimulateMouseMove(5, 0, 0);
+ EXPECT_EQ(0U, QueuedEventCount());
+
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ WebGestureEvent::Touchpad);
+ EXPECT_EQ(0U, QueuedEventCount());
+
+ SimulateTouchEvent(1, 1);
+ EXPECT_EQ(0U, QueuedEventCount());
+}
+
+TEST_F(BufferedInputRouterTest, FlightCountIncrementedOnDeliver) {
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(0, client_->in_flight_event_count());
+
+ input_router_->Flush();
+ EXPECT_EQ(1, client_->in_flight_event_count());
+}
+
+TEST_F(BufferedInputRouterTest, FlightCountDecrementedOnAck) {
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(0, client_->in_flight_event_count());
+
+ input_router_->Flush();
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ // The in-flight count should continue until the flush has finished.
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER));
+ EXPECT_EQ(1, client_->in_flight_event_count());
+
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
+ EXPECT_EQ(0, client_->in_flight_event_count());
+}
+
+TEST_F(BufferedInputRouterTest, FilteredEventsNeverQueued) {
+ // Event should not be queued, but should be ack'ed.
+ client_->set_filter_state(INPUT_EVENT_ACK_STATE_CONSUMED);
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled");
+ EXPECT_EQ(0U, QueuedEventCount());
+ ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
+
+ // Event should not be queued, but should be ack'ed.
+ client_->set_filter_state(INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ SCOPED_EXPECT(ack_handler_->ExpectAckCalled(1), "AckCalled");
+ EXPECT_EQ(0U, QueuedEventCount());
+ ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
+
+ // |INPUT_EVENT_DISPOSITION_UNKNOWN| should drop the event without ack'ing.
+ client_->set_filter_state(INPUT_EVENT_ACK_STATE_UNKNOWN);
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled");
+ EXPECT_EQ(0U, QueuedEventCount());
+ ASSERT_EQ(NULL, input_router_->GetLastKeyboardEvent());
+
+ // Event should be queued.
+ client_->set_filter_state(INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ SCOPED_EXPECT(ack_handler_->ExpectAckCalled(0), "AckNotCalled");
+ EXPECT_EQ(1U, QueuedEventCount());
+}
+
+TEST_F(BufferedInputRouterTest, FollowupEventsInjected) {
+ // Enable a followup gesture event.
+ WebGestureEvent followup_event;
+ followup_event.type = WebInputEvent::GestureScrollBegin;
+ followup_event.data.scrollUpdate.deltaX = 10;
+ ack_handler_->set_followup_touch_event(make_scoped_ptr(
+ new GestureEventWithLatencyInfo(followup_event, ui::LatencyInfo())));
+
+ // Create an initial packet of { Touch, Key } and start flushing.
+ SimulateTouchEvent(1, 1);
+ EXPECT_EQ(1U, QueuedEventCount());
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(2U, QueuedEventCount());
+ input_router_->Flush();
+
+ // Followup only triggered when event handled.
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER));
+ SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled");
+ EXPECT_EQ(2U, QueuedEventCount());
+
+ // Ack the touch event.
+ InputEventDispositions dispositions;
+ dispositions.push_back(INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED);
+ dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER);
+ ASSERT_TRUE(FinishFlush(dispositions));
+
+ // Ack'ing the touch event should have inserted the followup gesture event;
+ // the flush is not complete until the inserted event is ack'ed.
+ SCOPED_EXPECT(client_->ExpectDidFlushCalled(false), "DidFlushNotCalled");
+ SCOPED_EXPECT(client_->ExpectSendCalled(true), "SendGestureCalled");
+ EXPECT_EQ(followup_event.type, client_->sent_gesture_event().event.type);
+ EXPECT_EQ(2U, QueuedEventCount());
+
+ // Our packet is now { Gesture, Key }.
+ InputMsg_HandleEventPacket::Param param;
+ ASSERT_EQ(1U, process_->sink().message_count());
+ ASSERT_TRUE(InputMsg_HandleEventPacket::Read(process_->sink().GetMessageAt(0),
+ &param));
+ EventPacket& followup_packet = param.a;
+ ASSERT_EQ(2U, followup_packet.size());
+ ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT,
+ followup_packet.events()[0]->payload()->GetType());
+ ASSERT_EQ(InputEvent::Payload::WEB_INPUT_EVENT,
+ followup_packet.events()[1]->payload()->GetType());
+ const WebInputEventPayload* payload0 =
+ WebInputEventPayload::Cast(followup_packet.events()[0]->payload());
+ const WebInputEventPayload* payload1 =
+ WebInputEventPayload::Cast(followup_packet.events()[1]->payload());
+ EXPECT_EQ(followup_event.type, payload0->web_event()->type);
+ EXPECT_EQ(WebInputEvent::RawKeyDown, payload1->web_event()->type);
+
+ // Complete the flush; the gesture should have been ack'ed.
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
+ SCOPED_EXPECT(client_->ExpectDidFlushCalled(true), "DidFlushCalled");
+ EXPECT_EQ(followup_event.type, ack_handler_->acked_gesture_event().type);
+ EXPECT_EQ(0U, QueuedEventCount());
+}
+
+TEST_F(BufferedInputRouterTest, FlushRequestedOnQueue) {
+ // The first queued event should trigger a flush request.
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(1U, QueuedEventCount());
+ SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(true), "SetNeedsFlushCalled");
+
+ // Subsequently queued events will not trigger another flush request.
+ SimulateWheelEvent(5, 0, 0, false);
+ EXPECT_EQ(2U, QueuedEventCount());
+ SCOPED_EXPECT(client_->ExpectNeedsFlushCalled(false), "SetNeedsFlushCalled");
+}
+
+TEST_F(BufferedInputRouterTest, HasQueuedGestureEvents) {
+ EXPECT_FALSE(input_router_->HasQueuedGestureEvents());
+ SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
+ WebGestureEvent::Touchpad);
+ EXPECT_TRUE(input_router_->HasQueuedGestureEvents());
+
+ // Only an ack'ed gesture should clear it from the queue.
+ input_router_->Flush();
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER));
+ EXPECT_TRUE(input_router_->HasQueuedGestureEvents());
+
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
+ EXPECT_FALSE(input_router_->HasQueuedGestureEvents());
+}
+
+TEST_F(BufferedInputRouterTest, GetLastKeyboardEvent) {
+ EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent());
+
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ EXPECT_EQ(WebInputEvent::RawKeyDown,
+ input_router_->GetLastKeyboardEvent()->type);
+
+ // Queueing another key event does not effect the "last" event.
+ SimulateKeyboardEvent(WebInputEvent::KeyUp);
+ EXPECT_EQ(WebInputEvent::RawKeyDown,
+ input_router_->GetLastKeyboardEvent()->type);
+
+ input_router_->Flush();
+
+ // Ack'ing the first event should make the second event the "last" event.
+ InputEventDispositions dispositions;
+ dispositions.push_back(INPUT_EVENT_IMPL_THREAD_CONSUMED);
+ dispositions.push_back(INPUT_EVENT_COULD_NOT_DELIVER);
+ ASSERT_TRUE(FinishFlush(dispositions));
+ EXPECT_EQ(WebInputEvent::KeyUp, input_router_->GetLastKeyboardEvent()->type);
+
+ // A key event queued during a flush becomes "last" upon flush completion.
+ SimulateKeyboardEvent(WebInputEvent::Char);
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
+ EXPECT_EQ(WebInputEvent::Char, input_router_->GetLastKeyboardEvent()->type);
+
+ // An empty queue should produce a null "last" event.
+ input_router_->Flush();
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
+ EXPECT_EQ(NULL, input_router_->GetLastKeyboardEvent());
+}
+
+TEST_F(BufferedInputRouterTest, UnexpectedAck) {
+ ASSERT_FALSE(ack_handler_->unexpected_event_ack_called());
+ input_router_->OnMessageReceived(
+ InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions()));
+ EXPECT_TRUE(ack_handler_->unexpected_event_ack_called());
+}
+
+TEST_F(BufferedInputRouterTest, BadAck) {
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+ input_router_->Flush();
+
+ ASSERT_FALSE(ack_handler_->unexpected_event_ack_called());
+ EventPacket packet;
+ input_router_->OnMessageReceived(
+ InputHostMsg_HandleEventPacket_ACK(0, 0, InputEventDispositions()));
+ EXPECT_TRUE(ack_handler_->unexpected_event_ack_called());
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/immediate_input_router.cc b/content/browser/renderer_host/input/immediate_input_router.cc
index 368619c..3e34bbe 100644
--- a/content/browser/renderer_host/input/immediate_input_router.cc
+++ b/content/browser/renderer_host/input/immediate_input_router.cc
@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "content/browser/renderer_host/input/gesture_event_filter.h"
+#include "content/browser/renderer_host/input/input_ack_handler.h"
#include "content/browser/renderer_host/input/input_router_client.h"
#include "content/browser/renderer_host/input/touch_event_queue.h"
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
@@ -71,12 +72,13 @@ const char* GetEventAckName(InputEventAckState ack_result) {
} // namespace
-ImmediateInputRouter::ImmediateInputRouter(
- RenderProcessHost* process,
- InputRouterClient* client,
- int routing_id)
+ImmediateInputRouter::ImmediateInputRouter(RenderProcessHost* process,
+ InputRouterClient* client,
+ InputAckHandler* ack_handler,
+ int routing_id)
: process_(process),
client_(client),
+ ack_handler_(ack_handler),
routing_id_(routing_id),
select_range_pending_(false),
move_caret_pending_(false),
@@ -86,8 +88,8 @@ ImmediateInputRouter::ImmediateInputRouter(
touch_event_queue_(new TouchEventQueue(this)),
gesture_event_filter_(new GestureEventFilter(this)) {
enable_no_touch_to_renderer_while_scrolling_ =
- CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kNoTouchToRendererWhileScrolling) == "1";
+ CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kNoTouchToRendererWhileScrolling);
DCHECK(process);
DCHECK(client);
}
@@ -95,20 +97,23 @@ ImmediateInputRouter::ImmediateInputRouter(
ImmediateInputRouter::~ImmediateInputRouter() {
}
-bool ImmediateInputRouter::SendInput(IPC::Message* message) {
+void ImmediateInputRouter::Flush() {
+ NOTREACHED() << "ImmediateInputRouter will never request a flush.";
+}
+
+bool ImmediateInputRouter::SendInput(scoped_ptr<IPC::Message> message) {
DCHECK(IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart);
- scoped_ptr<IPC::Message> scoped_message(message);
- switch (scoped_message->type()) {
+ switch (message->type()) {
// Check for types that require an ACK.
case InputMsg_SelectRange::ID:
- return SendSelectRange(scoped_message.release());
+ return SendSelectRange(message.Pass());
case InputMsg_MoveCaret::ID:
- return SendMoveCaret(scoped_message.release());
+ return SendMoveCaret(message.Pass());
case InputMsg_HandleInputEvent::ID:
NOTREACHED() << "WebInputEvents should never be sent via SendInput.";
return false;
default:
- return Send(scoped_message.release());
+ return Send(message.release());
}
}
@@ -299,7 +304,7 @@ bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) {
IPC_END_MESSAGE_MAP()
if (!message_is_ok)
- client_->OnUnexpectedEventAck(true);
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
return handled;
}
@@ -307,29 +312,29 @@ bool ImmediateInputRouter::OnMessageReceived(const IPC::Message& message) {
void ImmediateInputRouter::OnTouchEventAck(
const TouchEventWithLatencyInfo& event,
InputEventAckState ack_result) {
- client_->OnTouchEventAck(event, ack_result);
+ ack_handler_->OnTouchEventAck(event, ack_result);
}
-bool ImmediateInputRouter::SendSelectRange(IPC::Message* message) {
+bool ImmediateInputRouter::SendSelectRange(scoped_ptr<IPC::Message> message) {
DCHECK(message->type() == InputMsg_SelectRange::ID);
if (select_range_pending_) {
- next_selection_range_.reset(message);
+ next_selection_range_ = message.Pass();
return true;
}
select_range_pending_ = true;
- return Send(message);
+ return Send(message.release());
}
-bool ImmediateInputRouter::SendMoveCaret(IPC::Message* message) {
+bool ImmediateInputRouter::SendMoveCaret(scoped_ptr<IPC::Message> message) {
DCHECK(message->type() == InputMsg_MoveCaret::ID);
if (move_caret_pending_) {
- next_move_caret_.reset(message);
+ next_move_caret_ = message.Pass();
return true;
}
move_caret_pending_ = true;
- return Send(message);
+ return Send(message.release());
}
bool ImmediateInputRouter::Send(IPC::Message* message) {
@@ -403,7 +408,7 @@ void ImmediateInputRouter::FilterAndSendWebInputEvent(
// Proceed as normal.
case INPUT_EVENT_ACK_STATE_NOT_CONSUMED:
break;
- };
+ }
// Transmit any pending wheel events on a non-wheel event. This ensures that
// the renderer receives the final PhaseEnded wheel event, which is necessary
@@ -439,13 +444,13 @@ void ImmediateInputRouter::OnInputEventAck(
void ImmediateInputRouter::OnMsgMoveCaretAck() {
move_caret_pending_ = false;
if (next_move_caret_)
- SendMoveCaret(next_move_caret_.release());
+ SendMoveCaret(next_move_caret_.Pass());
}
void ImmediateInputRouter::OnSelectRangeAck() {
select_range_pending_ = false;
if (next_selection_range_)
- SendSelectRange(next_selection_range_.release());
+ SendSelectRange(next_selection_range_.Pass());
}
void ImmediateInputRouter::OnHasTouchEventHandlers(bool has_handlers) {
@@ -466,7 +471,7 @@ void ImmediateInputRouter::ProcessInputEventAck(
int type = static_cast<int>(event_type);
if (type < WebInputEvent::Undefined) {
- client_->OnUnexpectedEventAck(true);
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::BAD_ACK_MESSAGE);
} else if (type == WebInputEvent::MouseMove) {
mouse_move_pending_ = false;
@@ -506,23 +511,17 @@ void ImmediateInputRouter::ProcessKeyboardAck(
int type,
InputEventAckState ack_result) {
if (key_queue_.empty()) {
- LOG(ERROR) << "Got a KeyEvent back from the renderer but we "
- << "don't seem to have sent it to the renderer!";
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_ACK);
} else if (key_queue_.front().type != type) {
- LOG(ERROR) << "We seem to have a different key type sent from "
- << "the renderer. (" << key_queue_.front().type << " vs. "
- << type << "). Ignoring event.";
-
// Something must be wrong. Clear the |key_queue_| and char event
// suppression so that we can resume from the error.
key_queue_.clear();
- client_->OnUnexpectedEventAck(false);
+ ack_handler_->OnUnexpectedEventAck(InputAckHandler::UNEXPECTED_EVENT_TYPE);
} else {
NativeWebKeyboardEvent front_item = key_queue_.front();
key_queue_.pop_front();
- client_->OnKeyboardEventAck(front_item, ack_result);
-
+ ack_handler_->OnKeyboardEventAck(front_item, ack_result);
// WARNING: This ImmediateInputRouter can be deallocated at this point
// (i.e. in the case of Ctrl+W, where the call to
// HandleKeyboardEvent destroys this ImmediateInputRouter).
@@ -535,7 +534,7 @@ void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) {
// Process the unhandled wheel event here before calling
// ForwardWheelEventWithLatencyInfo() since it will mutate
// current_wheel_event_.
- client_->OnWheelEventAck(current_wheel_event_.event, ack_result);
+ ack_handler_->OnWheelEventAck(current_wheel_event_.event, ack_result);
// Now send the next (coalesced) mouse wheel event.
if (!coalesced_mouse_wheel_events_.empty()) {
@@ -549,7 +548,7 @@ void ImmediateInputRouter::ProcessWheelAck(InputEventAckState ack_result) {
void ImmediateInputRouter::ProcessGestureAck(int type,
InputEventAckState ack_result) {
const bool processed = (INPUT_EVENT_ACK_STATE_CONSUMED == ack_result);
- client_->OnGestureEventAck(
+ ack_handler_->OnGestureEventAck(
gesture_event_filter_->GetGestureEventAwaitingAck(), ack_result);
gesture_event_filter_->ProcessGestureAck(processed, type);
}
diff --git a/content/browser/renderer_host/input/immediate_input_router.h b/content/browser/renderer_host/input/immediate_input_router.h
index 0bfa19f..f5aa167 100644
--- a/content/browser/renderer_host/input/immediate_input_router.h
+++ b/content/browser/renderer_host/input/immediate_input_router.h
@@ -21,6 +21,7 @@ struct LatencyInfo;
namespace content {
class GestureEventFilter;
+class InputAckHandler;
class InputRouterClient;
class RenderProcessHost;
class RenderWidgetHostImpl;
@@ -33,11 +34,13 @@ class CONTENT_EXPORT ImmediateInputRouter
public:
ImmediateInputRouter(RenderProcessHost* process,
InputRouterClient* client,
+ InputAckHandler* ack_handler,
int routing_id);
virtual ~ImmediateInputRouter();
// InputRouter
- virtual bool SendInput(IPC::Message* message) OVERRIDE;
+ virtual void Flush() OVERRIDE;
+ virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE;
virtual void SendMouseEvent(
const MouseEventWithLatencyInfo& mouse_event) OVERRIDE;
virtual void SendWheelEvent(
@@ -79,8 +82,8 @@ private:
virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
InputEventAckState ack_result) OVERRIDE;
- bool SendMoveCaret(IPC::Message* message);
- bool SendSelectRange(IPC::Message* message);
+ bool SendMoveCaret(scoped_ptr<IPC::Message> message);
+ bool SendSelectRange(scoped_ptr<IPC::Message> message);
bool Send(IPC::Message* message);
// Transmits the given input event an as an IPC::Message. This is an internal
@@ -137,6 +140,7 @@ private:
RenderProcessHost* process_;
InputRouterClient* client_;
+ InputAckHandler* ack_handler_;
int routing_id_;
// (Similar to |mouse_move_pending_|.) True while waiting for SelectRange_ACK.
diff --git a/content/browser/renderer_host/input/immediate_input_router_unittest.cc b/content/browser/renderer_host/input/immediate_input_router_unittest.cc
index 371e02e..3d540e0 100644
--- a/content/browser/renderer_host/input/immediate_input_router_unittest.cc
+++ b/content/browser/renderer_host/input/immediate_input_router_unittest.cc
@@ -8,6 +8,8 @@
#include "content/browser/renderer_host/input/gesture_event_filter.h"
#include "content/browser/renderer_host/input/immediate_input_router.h"
#include "content/browser/renderer_host/input/input_router_client.h"
+#include "content/browser/renderer_host/input/input_router_unittest.h"
+#include "content/browser/renderer_host/input/mock_input_router_client.h"
#include "content/common/content_constants_internal.h"
#include "content/common/edit_command.h"
#include "content/common/input_messages.h"
@@ -100,266 +102,22 @@ bool EventListIsSubset(const ScopedVector<ui::TouchEvent>& subset,
} // namespace
-class MockInputRouterClient : public InputRouterClient {
+class ImmediateInputRouterTest : public InputRouterTest {
public:
- MockInputRouterClient()
- : input_router_(NULL),
- in_flight_event_count_(0),
- has_touch_handler_(false),
- ack_count_(0),
- unexpected_event_ack_called_(false),
- ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN),
- filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED),
- is_shortcut_(false),
- allow_send_key_event_(true),
- send_called_(false),
- send_immediately_called_(false) {
- }
- virtual ~MockInputRouterClient() {
- }
-
- // InputRouterClient
- virtual InputEventAckState FilterInputEvent(
- const WebInputEvent& input_event,
- const ui::LatencyInfo& latency_info) OVERRIDE {
- return filter_state_;
- }
-
- // Called each time a WebInputEvent IPC is sent.
- virtual void IncrementInFlightEventCount() OVERRIDE {
- ++in_flight_event_count_;
- }
-
- // Called each time a WebInputEvent ACK IPC is received.
- virtual void DecrementInFlightEventCount() OVERRIDE {
- --in_flight_event_count_;
- }
-
- // Called when the renderer notifies that it has touch event handlers.
- virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE {
- has_touch_handler_ = has_handlers;
- }
-
- virtual bool OnSendKeyboardEvent(
- const NativeWebKeyboardEvent& key_event,
- const ui::LatencyInfo& latency_info,
- bool* is_shortcut) OVERRIDE {
- send_called_ = true;
- sent_key_event_ = key_event;
- *is_shortcut = is_shortcut_;
-
- return allow_send_key_event_;
- }
-
- virtual bool OnSendWheelEvent(
- const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE {
- send_called_ = true;
- sent_wheel_event_ = wheel_event;
-
- return true;
- }
-
- virtual bool OnSendMouseEvent(
- const MouseEventWithLatencyInfo& mouse_event) OVERRIDE {
- send_called_ = true;
- sent_mouse_event_ = mouse_event;
-
- return true;
- }
-
- virtual bool OnSendTouchEvent(
- const TouchEventWithLatencyInfo& touch_event) OVERRIDE {
- send_called_ = true;
- sent_touch_event_ = touch_event;
-
- return true;
- }
-
- virtual bool OnSendGestureEvent(
- const GestureEventWithLatencyInfo& gesture_event) OVERRIDE {
- send_called_ = true;
- sent_gesture_event_ = gesture_event;
-
- return input_router_->ShouldForwardGestureEvent(gesture_event);
- }
-
- virtual bool OnSendMouseEventImmediately(
- const MouseEventWithLatencyInfo& mouse_event) OVERRIDE {
- send_immediately_called_ = true;
- immediately_sent_mouse_event_ = mouse_event;
-
- return true;
- }
-
- virtual bool OnSendTouchEventImmediately(
- const TouchEventWithLatencyInfo& touch_event) OVERRIDE {
- send_immediately_called_ = true;
- immediately_sent_touch_event_ = touch_event;
-
- return true;
- }
-
- virtual bool OnSendGestureEventImmediately(
- const GestureEventWithLatencyInfo& gesture_event) OVERRIDE {
- send_immediately_called_ = true;
- immediately_sent_gesture_event_ = gesture_event;
-
- return true;
- }
-
- // Called upon event ack receipt from the renderer.
- virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
- InputEventAckState ack_result) OVERRIDE {
- VLOG(1) << __FUNCTION__ << " called!";
- acked_key_event_ = event;
- RecordAckCalled(ack_result);
- }
- virtual void OnWheelEventAck(const WebMouseWheelEvent& event,
- InputEventAckState ack_result) OVERRIDE {
- VLOG(1) << __FUNCTION__ << " called!";
- acked_wheel_event_ = event;
- RecordAckCalled(ack_result);
- }
- virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
- InputEventAckState ack_result) OVERRIDE {
- VLOG(1) << __FUNCTION__ << " called!";
- acked_touch_event_ = event;
- RecordAckCalled(ack_result);
- }
- virtual void OnGestureEventAck(const WebGestureEvent& event,
- InputEventAckState ack_result) OVERRIDE {
- VLOG(1) << __FUNCTION__ << " called!";
- RecordAckCalled(ack_result);
- }
- virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE {
- VLOG(1) << __FUNCTION__ << " called!";
- unexpected_event_ack_called_ = true;
- }
-
- void ExpectSendCalled(bool called) {
- EXPECT_EQ(called, send_called_);
- send_called_ = false;
- }
- void ExpectSendImmediatelyCalled(bool called) {
- EXPECT_EQ(called, send_immediately_called_);
- send_immediately_called_ = false;
- }
- void ExpectAckCalled(int times) {
- EXPECT_EQ(times, ack_count_);
- ack_count_ = 0;
- }
-
- void set_input_router(InputRouter* input_router) {
- input_router_ = input_router;
- }
- bool has_touch_handler() const { return has_touch_handler_; }
- InputEventAckState ack_state() const { return ack_state_; }
- void set_filter_state(InputEventAckState filter_state) {
- filter_state_ = filter_state;
- }
- bool unexpected_event_ack_called() const {
- return unexpected_event_ack_called_;
- }
- const NativeWebKeyboardEvent& acked_keyboard_event() {
- return acked_key_event_;
- }
- const WebMouseWheelEvent& acked_wheel_event() {
- return acked_wheel_event_;
- }
- const TouchEventWithLatencyInfo& acked_touch_event() {
- return acked_touch_event_;
- }
- void set_is_shortcut(bool is_shortcut) {
- is_shortcut_ = is_shortcut;
- }
- void set_allow_send_key_event(bool allow) {
- allow_send_key_event_ = allow;
- }
- const NativeWebKeyboardEvent& sent_key_event() {
- return sent_key_event_;
- }
- const MouseWheelEventWithLatencyInfo& sent_wheel_event() {
- return sent_wheel_event_;
- }
- const MouseEventWithLatencyInfo& sent_mouse_event() {
- return sent_mouse_event_;
- }
- const GestureEventWithLatencyInfo& sent_gesture_event() {
- return sent_gesture_event_;
- }
- const MouseEventWithLatencyInfo& immediately_sent_mouse_event() {
- return immediately_sent_mouse_event_;
- }
- const TouchEventWithLatencyInfo& immediately_sent_touch_event() {
- return immediately_sent_touch_event_;
- }
- const GestureEventWithLatencyInfo& immediately_sent_gesture_event() {
- return immediately_sent_gesture_event_;
- }
-
- private:
- void RecordAckCalled(InputEventAckState ack_result) {
- ++ack_count_;
- ack_state_ = ack_result;
- }
-
- InputRouter* input_router_;
- int in_flight_event_count_;
- bool has_touch_handler_;
-
- int ack_count_;
- bool unexpected_event_ack_called_;
- InputEventAckState ack_state_;
- InputEventAckState filter_state_;
- NativeWebKeyboardEvent acked_key_event_;
- WebMouseWheelEvent acked_wheel_event_;
- TouchEventWithLatencyInfo acked_touch_event_;
-
- bool is_shortcut_;
- bool allow_send_key_event_;
- bool send_called_;
- NativeWebKeyboardEvent sent_key_event_;
- MouseWheelEventWithLatencyInfo sent_wheel_event_;
- MouseEventWithLatencyInfo sent_mouse_event_;
- TouchEventWithLatencyInfo sent_touch_event_;
- GestureEventWithLatencyInfo sent_gesture_event_;
-
- bool send_immediately_called_;
- MouseEventWithLatencyInfo immediately_sent_mouse_event_;
- TouchEventWithLatencyInfo immediately_sent_touch_event_;
- GestureEventWithLatencyInfo immediately_sent_gesture_event_;
-};
-
-class ImmediateInputRouterTest : public testing::Test {
- public:
- ImmediateInputRouterTest() {
- }
- virtual ~ImmediateInputRouterTest() {
- }
+ ImmediateInputRouterTest() {}
+ virtual ~ImmediateInputRouterTest() {}
protected:
- // testing::Test
- virtual void SetUp() {
- browser_context_.reset(new TestBrowserContext());
- process_.reset(new MockRenderProcessHost(browser_context_.get()));
- client_.reset(new MockInputRouterClient());
- input_router_.reset(new ImmediateInputRouter(
- process_.get(),
- client_.get(),
- MSG_ROUTING_NONE));
- client_->set_input_router(input_router_.get());
- }
- virtual void TearDown() {
- // Process all pending tasks to avoid leaks.
- base::MessageLoop::current()->RunUntilIdle();
-
- input_router_.reset();
- client_.reset();
- process_.reset();
- browser_context_.reset();
+ // InputRouterTest
+ virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process,
+ InputRouterClient* client,
+ InputAckHandler* handler,
+ int routing_id) OVERRIDE {
+ return scoped_ptr<InputRouter>(
+ new ImmediateInputRouter(process, client, handler, routing_id));
}
- void SendInputEventACK(WebInputEvent::Type type,
+ void SendInputEventACK(WebKit::WebInputEvent::Type type,
InputEventAckState ack_result) {
scoped_ptr<IPC::Message> response(
new InputHostMsg_HandleInputEvent_ACK(0, type, ack_result,
@@ -367,177 +125,16 @@ class ImmediateInputRouterTest : public testing::Test {
input_router_->OnMessageReceived(*response);
}
- void SimulateKeyboardEvent(WebInputEvent::Type type) {
- NativeWebKeyboardEvent key_event;
- key_event.type = type;
- key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value.
- input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo());
- client_->ExpectSendCalled(true);
- EXPECT_EQ(type, client_->sent_key_event().type);
- EXPECT_EQ(key_event.windowsKeyCode,
- client_->sent_key_event().windowsKeyCode);
- }
-
- void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise) {
- WebMouseWheelEvent wheel_event;
- wheel_event.type = WebInputEvent::MouseWheel;
- wheel_event.deltaX = dX;
- wheel_event.deltaY = dY;
- wheel_event.modifiers = modifiers;
- wheel_event.hasPreciseScrollingDeltas = precise;
- input_router_->SendWheelEvent(
- MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo()));
- client_->ExpectSendCalled(true);
- EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type);
- EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX);
- }
-
- void SimulateMouseMove(int x, int y, int modifiers) {
- WebMouseEvent mouse_event;
- mouse_event.type = WebInputEvent::MouseMove;
- mouse_event.x = mouse_event.windowX = x;
- mouse_event.y = mouse_event.windowY = y;
- mouse_event.modifiers = modifiers;
- input_router_->SendMouseEvent(
- MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo()));
- client_->ExpectSendCalled(true);
- client_->ExpectSendImmediatelyCalled(true);
- EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type);
- EXPECT_EQ(x, client_->sent_mouse_event().event.x);
- EXPECT_EQ(mouse_event.type,
- client_->immediately_sent_mouse_event().event.type);
- EXPECT_EQ(x, client_->immediately_sent_mouse_event().event.x);
- }
-
- void SimulateWheelEventWithPhase(WebMouseWheelEvent::Phase phase) {
- WebMouseWheelEvent wheel_event;
- wheel_event.type = WebInputEvent::MouseWheel;
- wheel_event.phase = phase;
- input_router_->SendWheelEvent(
- MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo()));
- client_->ExpectSendCalled(true);
- EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type);
- EXPECT_EQ(phase, client_->sent_wheel_event().event.phase);
- }
-
- // Inject provided synthetic WebGestureEvent instance.
- void SimulateGestureEventCore(WebInputEvent::Type type,
- WebGestureEvent::SourceDevice sourceDevice,
- WebGestureEvent* gesture_event) {
- gesture_event->type = type;
- gesture_event->sourceDevice = sourceDevice;
- GestureEventWithLatencyInfo gesture_with_latency(
- *gesture_event, ui::LatencyInfo());
- input_router_->SendGestureEvent(gesture_with_latency);
- client_->ExpectSendCalled(true);
- EXPECT_EQ(type, client_->sent_gesture_event().event.type);
- EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice);
- }
-
- // Inject simple synthetic WebGestureEvent instances.
- void SimulateGestureEvent(WebInputEvent::Type type,
- WebGestureEvent::SourceDevice sourceDevice) {
- WebGestureEvent gesture_event;
- SimulateGestureEventCore(type, sourceDevice, &gesture_event);
- }
-
- void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers) {
- WebGestureEvent gesture_event;
- gesture_event.data.scrollUpdate.deltaX = dX;
- gesture_event.data.scrollUpdate.deltaY = dY;
- gesture_event.modifiers = modifiers;
- SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate,
- WebGestureEvent::Touchscreen, &gesture_event);
- }
-
- void SimulateGesturePinchUpdateEvent(float scale,
- float anchorX,
- float anchorY,
- int modifiers) {
- WebGestureEvent gesture_event;
- gesture_event.data.pinchUpdate.scale = scale;
- gesture_event.x = anchorX;
- gesture_event.y = anchorY;
- gesture_event.modifiers = modifiers;
- SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate,
- WebGestureEvent::Touchscreen, &gesture_event);
- }
-
- // Inject synthetic GestureFlingStart events.
- void SimulateGestureFlingStartEvent(
- float velocityX,
- float velocityY,
- WebGestureEvent::SourceDevice sourceDevice) {
- WebGestureEvent gesture_event;
- gesture_event.data.flingStart.velocityX = velocityX;
- gesture_event.data.flingStart.velocityY = velocityY;
- SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice,
- &gesture_event);
- }
-
- // Set the timestamp for the touch-event.
- void SetTouchTimestamp(base::TimeDelta timestamp) {
- touch_event_.timeStampSeconds = timestamp.InSecondsF();
- }
-
- // Sends a touch event (irrespective of whether the page has a touch-event
- // handler or not).
- void SendTouchEvent() {
- input_router_->SendTouchEvent(
- TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo()));
-
- // Mark all the points as stationary. And remove the points that have been
- // released.
- int point = 0;
- for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) {
- if (touch_event_.touches[i].state == WebTouchPoint::StateReleased)
- continue;
-
- touch_event_.touches[point] = touch_event_.touches[i];
- touch_event_.touches[point].state =
- WebTouchPoint::StateStationary;
- ++point;
- }
- touch_event_.touchesLength = point;
- touch_event_.type = WebInputEvent::Undefined;
- }
-
- int PressTouchPoint(int x, int y) {
- if (touch_event_.touchesLength == touch_event_.touchesLengthCap)
- return -1;
- WebTouchPoint& point =
- touch_event_.touches[touch_event_.touchesLength];
- point.id = touch_event_.touchesLength;
- point.position.x = point.screenPosition.x = x;
- point.position.y = point.screenPosition.y = y;
- point.state = WebTouchPoint::StatePressed;
- point.radiusX = point.radiusY = 1.f;
- ++touch_event_.touchesLength;
- touch_event_.type = WebInputEvent::TouchStart;
- return point.id;
- }
-
- void MoveTouchPoint(int index, int x, int y) {
- CHECK(index >= 0 && index < touch_event_.touchesLengthCap);
- WebTouchPoint& point = touch_event_.touches[index];
- point.position.x = point.screenPosition.x = x;
- point.position.y = point.screenPosition.y = y;
- touch_event_.touches[index].state = WebTouchPoint::StateMoved;
- touch_event_.type = WebInputEvent::TouchMove;
- }
-
- void ReleaseTouchPoint(int index) {
- CHECK(index >= 0 && index < touch_event_.touchesLengthCap);
- touch_event_.touches[index].state = WebTouchPoint::StateReleased;
- touch_event_.type = WebInputEvent::TouchEnd;
+ ImmediateInputRouter* input_router() const {
+ return static_cast<ImmediateInputRouter*>(input_router_.get());
}
void set_debounce_interval_time_ms(int ms) {
- input_router_->gesture_event_filter()->debounce_interval_time_ms_ = ms;
+ input_router()->gesture_event_filter()->debounce_interval_time_ms_ = ms;
}
void set_maximum_tap_gap_time_ms(int delay_ms) {
- input_router_->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms;
+ input_router()->gesture_event_filter()->maximum_tap_gap_time_ms_ = delay_ms;
}
size_t TouchEventQueueSize() {
@@ -549,7 +146,7 @@ class ImmediateInputRouterTest : public testing::Test {
}
void EnableNoTouchToRendererWhileScrolling() {
- input_router_->enable_no_touch_to_renderer_while_scrolling_ = true;
+ input_router()->enable_no_touch_to_renderer_while_scrolling_ = true;
}
bool no_touch_move_to_renderer() {
@@ -557,7 +154,7 @@ class ImmediateInputRouterTest : public testing::Test {
}
TouchEventQueue* touch_event_queue() const {
- return input_router_->touch_event_queue();
+ return input_router()->touch_event_queue();
}
unsigned GestureEventLastQueueEventSize() {
@@ -598,18 +195,8 @@ class ImmediateInputRouterTest : public testing::Test {
}
GestureEventFilter* gesture_event_filter() const {
- return input_router_->gesture_event_filter();
+ return input_router()->gesture_event_filter();
}
-
- scoped_ptr<MockRenderProcessHost> process_;
- scoped_ptr<MockInputRouterClient> client_;
- scoped_ptr<ImmediateInputRouter> input_router_;
-
- private:
- base::MessageLoopForUI message_loop_;
- WebTouchEvent touch_event_;
-
- scoped_ptr<TestBrowserContext> browser_context_;
};
#if GTEST_HAS_PARAM_TEST
@@ -621,8 +208,8 @@ class ImmediateInputRouterWithSourceTest
#endif // GTEST_HAS_PARAM_TEST
TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) {
- input_router_->SendInput(
- new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4)));
+ input_router_->SendInput(scoped_ptr<IPC::Message>(
+ new InputMsg_SelectRange(0, gfx::Point(1, 2), gfx::Point(3, 4))));
EXPECT_EQ(1u, process_->sink().message_count());
ExpectIPCMessageWithArg2<InputMsg_SelectRange>(
process_->sink().GetMessageAt(0),
@@ -631,12 +218,12 @@ TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) {
process_->sink().ClearMessages();
// Send two more messages without acking.
- input_router_->SendInput(
- new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8)));
+ input_router_->SendInput(scoped_ptr<IPC::Message>(
+ new InputMsg_SelectRange(0, gfx::Point(5, 6), gfx::Point(7, 8))));
EXPECT_EQ(0u, process_->sink().message_count());
- input_router_->SendInput(
- new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12)));
+ input_router_->SendInput(scoped_ptr<IPC::Message>(
+ new InputMsg_SelectRange(0, gfx::Point(9, 10), gfx::Point(11, 12))));
EXPECT_EQ(0u, process_->sink().message_count());
// Now ack the first message.
@@ -662,17 +249,20 @@ TEST_F(ImmediateInputRouterTest, CoalescesRangeSelection) {
}
TEST_F(ImmediateInputRouterTest, CoalescesCaretMove) {
- input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(1, 2)));
+ input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(1, 2))));
EXPECT_EQ(1u, process_->sink().message_count());
ExpectIPCMessageWithArg1<InputMsg_MoveCaret>(
process_->sink().GetMessageAt(0), gfx::Point(1, 2));
process_->sink().ClearMessages();
// Send two more messages without acking.
- input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(5, 6)));
+ input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(5, 6))));
EXPECT_EQ(0u, process_->sink().message_count());
- input_router_->SendInput(new InputMsg_MoveCaret(0, gfx::Point(9, 10)));
+ input_router_->SendInput(
+ scoped_ptr<IPC::Message>(new InputMsg_MoveCaret(0, gfx::Point(9, 10))));
EXPECT_EQ(0u, process_->sink().message_count());
// Now ack the first message.
@@ -705,7 +295,7 @@ TEST_F(ImmediateInputRouterTest, HandledInputEvent) {
EXPECT_EQ(0u, process_->sink().message_count());
// OnKeyboardEventAck should be triggered without actual ack.
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
// As the event was acked already, keyboard event queue should be
// empty.
@@ -713,14 +303,14 @@ TEST_F(ImmediateInputRouterTest, HandledInputEvent) {
}
TEST_F(ImmediateInputRouterTest, ClientCanceledKeyboardEvent) {
- client_->set_allow_send_key_event(false);
+ client_->set_allow_send_event(false);
// Simulate a keyboard event.
SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
// Make sure no input event is sent to the renderer.
EXPECT_EQ(0u, process_->sink().message_count());
- client_->ExpectAckCalled(0);
+ ack_handler_->ExpectAckCalled(0);
}
TEST_F(ImmediateInputRouterTest, ShortcutKeyboardEvent) {
@@ -742,7 +332,7 @@ TEST_F(ImmediateInputRouterTest, NoncorrespondingKeyEvents) {
SendInputEventACK(WebInputEvent::KeyUp,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_TRUE(client_->unexpected_event_ack_called());
+ EXPECT_TRUE(ack_handler_->unexpected_event_ack_called());
}
// Tests ported from RenderWidgetHostTest --------------------------------------
@@ -762,8 +352,9 @@ TEST_F(ImmediateInputRouterTest, HandleKeyEventsWeSent) {
// Send the simulated response from the renderer back.
SendInputEventACK(WebInputEvent::RawKeyDown,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- client_->ExpectAckCalled(1);
- EXPECT_EQ(WebInputEvent::RawKeyDown, client_->acked_keyboard_event().type);
+ ack_handler_->ExpectAckCalled(1);
+ EXPECT_EQ(WebInputEvent::RawKeyDown,
+ ack_handler_->acked_keyboard_event().type);
}
TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) {
@@ -771,7 +362,7 @@ TEST_F(ImmediateInputRouterTest, IgnoreKeyEventsWeDidntSend) {
SendInputEventACK(WebInputEvent::RawKeyDown,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- client_->ExpectAckCalled(0);
+ ack_handler_->ExpectAckCalled(0);
}
TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) {
@@ -794,7 +385,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) {
// so that additional input events can be processed before
// we turn off coalescing.
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
@@ -804,7 +395,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) {
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
@@ -814,7 +405,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesWheelEvents) {
SendInputEventACK(WebInputEvent::MouseWheel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(0U, process_->sink().message_count());
// FIXME(kouhei): Below is testing gesture event filter. Maybe separate test?
@@ -917,7 +508,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) {
SendInputEventACK(WebInputEvent::GestureScrollBegin,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
@@ -927,7 +518,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) {
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
@@ -937,7 +528,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) {
SendInputEventACK(WebInputEvent::GestureScrollUpdate,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
InputMsg_HandleInputEvent::ID));
@@ -947,7 +538,7 @@ TEST_F(ImmediateInputRouterTest, CoalescesScrollGestureEvents) {
SendInputEventACK(WebInputEvent::GestureScrollEnd,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(0U, process_->sink().message_count());
}
@@ -1200,14 +791,14 @@ TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) {
SendInputEventACK(WebInputEvent::GestureFlingStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
EXPECT_FALSE(FlingInProgress());
EXPECT_EQ(2U, process_->sink().message_count());
SendInputEventACK(WebInputEvent::GestureFlingCancel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// GFC before previous GFS is acked.
@@ -1226,7 +817,7 @@ TEST_P(ImmediateInputRouterWithSourceTest, GestureFlingCancelsFiltered) {
SendInputEventACK(WebInputEvent::GestureFlingCancel,
INPUT_EVENT_ACK_STATE_CONSUMED);
base::MessageLoop::current()->RunUntilIdle();
- client_->ExpectAckCalled(2);
+ ack_handler_->ExpectAckCalled(2);
EXPECT_EQ(0U, GestureEventLastQueueEventSize());
// GFS is added to the queue if another event is pending
@@ -1571,18 +1162,18 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueue) {
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(1U, TouchEventQueueSize());
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(WebInputEvent::TouchStart,
- client_->acked_touch_event().event.type);
+ ack_handler_->acked_touch_event().event.type);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
SendInputEventACK(WebInputEvent::TouchMove,
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(0U, TouchEventQueueSize());
- client_->ExpectAckCalled(1);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(WebInputEvent::TouchMove,
- client_->acked_touch_event().event.type);
+ ack_handler_->acked_touch_event().event.type);
EXPECT_EQ(0U, process_->sink().message_count());
}
@@ -1623,8 +1214,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueFlush) {
INPUT_EVENT_ACK_STATE_CONSUMED);
EXPECT_EQ(31U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchStart,
- client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(1);
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(1U, process_->sink().message_count());
process_->sink().ClearMessages();
@@ -1671,8 +1262,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) {
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchStart,
- client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(1);
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(1);
process_->sink().ClearMessages();
// Coalesced touch-move events should be sent.
@@ -1686,8 +1277,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) {
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(1U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
- client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(10);
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(10);
process_->sink().ClearMessages();
// ACK the release.
@@ -1696,8 +1287,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueCoalesce) {
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchEnd,
- client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(1);
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(1);
}
// Tests that an event that has already been sent but hasn't been ack'ed yet
@@ -1852,8 +1443,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) {
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
- client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(2);
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(2);
EXPECT_EQ(0U, process_->sink().message_count());
process_->sink().ClearMessages();
@@ -1862,8 +1453,8 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) {
SendTouchEvent();
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(WebInputEvent::TouchEnd,
- client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(1);
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(1);
// Send a press-event, followed by move and release events, and another press
// event, before the ACK for the first press event comes back. All of the
@@ -1891,8 +1482,9 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) {
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(1U, process_->sink().message_count());
- EXPECT_EQ(WebInputEvent::TouchEnd, client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(4);
+ EXPECT_EQ(WebInputEvent::TouchEnd,
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(4);
EXPECT_EQ(1U, TouchEventQueueSize());
process_->sink().ClearMessages();
@@ -1900,8 +1492,9 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueNoConsumer) {
SendInputEventACK(WebInputEvent::TouchStart,
INPUT_EVENT_ACK_STATE_NO_CONSUMER_EXISTS);
EXPECT_EQ(0U, process_->sink().message_count());
- EXPECT_EQ(WebInputEvent::TouchStart, client_->acked_touch_event().event.type);
- client_->ExpectAckCalled(1);
+ EXPECT_EQ(WebInputEvent::TouchStart,
+ ack_handler_->acked_touch_event().event.type);
+ ack_handler_->ExpectAckCalled(1);
EXPECT_EQ(0U, TouchEventQueueSize());
// Send a second press event. Even though the first touch had NO_CONSUMER,
@@ -2038,7 +1631,7 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) {
EXPECT_EQ(1U, process_->sink().message_count());
EXPECT_EQ(2U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
- client_->acked_touch_event().event.type);
+ ack_handler_->acked_touch_event().event.type);
process_->sink().ClearMessages();
// ACK the press with NO_CONSUMED_EXISTS. This should release the queued
@@ -2048,7 +1641,7 @@ TEST_F(ImmediateInputRouterTest, TouchEventQueueConsumerIgnoreMultiFinger) {
EXPECT_EQ(0U, process_->sink().message_count());
EXPECT_EQ(0U, TouchEventQueueSize());
EXPECT_EQ(WebInputEvent::TouchMove,
- client_->acked_touch_event().event.type);
+ ack_handler_->acked_touch_event().event.type);
ReleaseTouchPoint(2);
ReleaseTouchPoint(1);
@@ -2126,11 +1719,11 @@ TEST_F(ImmediateInputRouterTest, AckedTouchEventState) {
for (size_t i = 0; i < arraysize(acks); ++i) {
SendInputEventACK(acks[i],
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
- EXPECT_EQ(acks[i], client_->acked_touch_event().event.type);
+ EXPECT_EQ(acks[i], ack_handler_->acked_touch_event().event.type);
ScopedVector<ui::TouchEvent> acked;
MakeUITouchEventsFromWebTouchEvents(
- client_->acked_touch_event(), &acked, coordinate_system);
+ ack_handler_->acked_touch_event(), &acked, coordinate_system);
bool success = EventListIsSubset(acked, expected_events);
EXPECT_TRUE(success) << "Failed on step: " << i;
if (!success)
@@ -2159,9 +1752,9 @@ TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) {
INPUT_EVENT_ACK_STATE_NOT_CONSUMED);
// Check that the correct unhandled wheel event was received.
- client_->ExpectAckCalled(1);
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, client_->ack_state());
- EXPECT_EQ(client_->acked_wheel_event().deltaY, -5);
+ ack_handler_->ExpectAckCalled(1);
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, ack_handler_->ack_state());
+ EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5);
// Check that the second event was sent.
EXPECT_EQ(1U, process_->sink().message_count());
@@ -2170,7 +1763,7 @@ TEST_F(ImmediateInputRouterTest, UnhandledWheelEvent) {
process_->sink().ClearMessages();
// Check that the correct unhandled wheel event was received.
- EXPECT_EQ(client_->acked_wheel_event().deltaY, -5);
+ EXPECT_EQ(ack_handler_->acked_wheel_event().deltaY, -5);
}
// Tests that no touch move events are sent to renderer during scrolling.
diff --git a/content/browser/renderer_host/input/input_ack_handler.h b/content/browser/renderer_host/input/input_ack_handler.h
new file mode 100644
index 0000000..3e09b07
--- /dev/null
+++ b/content/browser/renderer_host/input/input_ack_handler.h
@@ -0,0 +1,41 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_
+
+#include "base/basictypes.h"
+#include "content/port/browser/event_with_latency_info.h"
+#include "content/port/common/input_event_ack_state.h"
+#include "content/public/browser/native_web_keyboard_event.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace content {
+
+// Provided customized ack response for input events.
+class CONTENT_EXPORT InputAckHandler {
+ public:
+ virtual ~InputAckHandler() {}
+
+ // Called upon event ack receipt from the renderer.
+ virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
+ InputEventAckState ack_result) = 0;
+ virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) = 0;
+ virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
+ InputEventAckState ack_result) = 0;
+ virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event,
+ InputEventAckState ack_result) = 0;
+
+ enum UnexpectedEventAckType {
+ UNEXPECTED_ACK,
+ UNEXPECTED_EVENT_TYPE,
+ BAD_ACK_MESSAGE
+ };
+ virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ACK_HANDLER_H_
diff --git a/content/browser/renderer_host/input/input_queue.cc b/content/browser/renderer_host/input/input_queue.cc
new file mode 100644
index 0000000..c10500b
--- /dev/null
+++ b/content/browser/renderer_host/input/input_queue.cc
@@ -0,0 +1,183 @@
+// Copyright 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/input_queue.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "content/browser/renderer_host/input/browser_input_event.h"
+#include "content/browser/renderer_host/input/input_queue_client.h"
+#include "content/common/input/event_packet.h"
+#include "content/common/input/input_event.h"
+
+namespace content {
+
+// A specialized EventPacket with utility methods for dispatched event handling.
+class InputQueue::BrowserEventPacket : public EventPacket {
+ public:
+ typedef ScopedVector<BrowserInputEvent> BrowserInputEvents;
+
+ BrowserEventPacket() : weak_factory_(this) {}
+ virtual ~BrowserEventPacket() {}
+
+ // Validate the response and signal dispatch to the processed events.
+ // Undelivered events will be re-enqueued, and any generated followup events
+ // will be inserted at the same relative order as their generating event.
+ AckResult ValidateAndDispatchAck(int64 packet_id,
+ const InputEventDispositions& dispositions) {
+ if (!Validate(packet_id, dispositions))
+ return ACK_INVALID;
+
+ // Empty the packet; events will be re-enqueued as necessary.
+ InputEvents dispatched_events;
+ events_.swap(dispatched_events);
+
+ // The packet could be deleted as a result of event dispatch; use a local
+ // weak ref to ensure proper shutdown.
+ base::WeakPtr<EventPacket> weak_ref_this = weak_factory_.GetWeakPtr();
+
+ BrowserInputEvents followup_events;
+ for (size_t i = 0; i < dispatched_events.size(); ++i) {
+ // Take ownership of the event.
+ scoped_ptr<BrowserInputEvent> event(
+ static_cast<BrowserInputEvent*>(dispatched_events[i]));
+ dispatched_events[i] = NULL;
+
+ // Re-enqueue undelivered events.
+ InputEventDisposition disposition = dispositions[i];
+ if (disposition == INPUT_EVENT_COULD_NOT_DELIVER) {
+ Add(event.PassAs<InputEvent>());
+ continue;
+ }
+
+ event->OnDispatched(disposition, &followup_events);
+
+ // TODO(jdduke): http://crbug.com/274029
+ if (!weak_ref_this.get())
+ return ACK_SHUTDOWN;
+
+ AddAll(&followup_events);
+ }
+ return ACK_OK;
+ }
+
+ protected:
+ // Add and take ownership of events in |followup_events|.
+ void AddAll(BrowserInputEvents* followup_events) {
+ for (BrowserInputEvents::iterator iter = followup_events->begin();
+ iter != followup_events->end();
+ ++iter) {
+ Add(scoped_ptr<InputEvent>(*iter));
+ }
+ followup_events->weak_clear();
+ }
+
+ // Perform a sanity check of the ack against the current packet.
+ // |packet_id| should match that of this packet, and |dispositions| should
+ // be of size equal to the number of events in this packet.
+ bool Validate(int64 packet_id,
+ const InputEventDispositions& dispositions) const {
+ if (packet_id != id())
+ return false;
+
+ if (dispositions.size() != size())
+ return false;
+
+ return true;
+ }
+
+ private:
+ base::WeakPtrFactory<EventPacket> weak_factory_;
+};
+
+InputQueue::InputQueue(InputQueueClient* client)
+ : client_(client),
+ next_packet_id_(1),
+ flush_requested_(false),
+ in_flush_packet_(new BrowserEventPacket()),
+ pending_flush_packet_(new BrowserEventPacket()) {
+ DCHECK(client_);
+}
+
+InputQueue::~InputQueue() {}
+
+void InputQueue::QueueEvent(scoped_ptr<BrowserInputEvent> event) {
+ DCHECK(event);
+ DCHECK(event->valid());
+ pending_flush_packet_->Add(event.PassAs<InputEvent>());
+ RequestFlushIfNecessary();
+}
+
+void InputQueue::BeginFlush() {
+ // Ignore repeated flush attempts.
+ if (!flush_requested_)
+ return;
+
+ DCHECK(!FlushInProgress());
+ DCHECK(!pending_flush_packet_->empty());
+
+ flush_requested_ = false;
+ in_flush_packet_.swap(pending_flush_packet_);
+ DeliverInFlushPacket();
+}
+
+InputQueue::AckResult InputQueue::OnEventPacketAck(
+ int64 packet_id,
+ const InputEventDispositions& dispositions) {
+ if (!FlushInProgress())
+ return ACK_UNEXPECTED;
+
+ TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "AckPacket",
+ "id", packet_id);
+
+ AckResult ack_result =
+ in_flush_packet_->ValidateAndDispatchAck(packet_id, dispositions);
+
+ if (ack_result != ACK_OK)
+ return ack_result;
+
+ if (FlushInProgress()) {
+ DeliverInFlushPacket();
+ } else {
+ TRACE_EVENT_ASYNC_END0("input", "InputQueueFlush", this);
+ client_->DidFinishFlush();
+ RequestFlushIfNecessary();
+ }
+
+ return ACK_OK;
+}
+
+size_t InputQueue::QueuedEventCount() const {
+ return in_flush_packet_->size() + pending_flush_packet_->size();
+}
+
+void InputQueue::DeliverInFlushPacket() {
+ DCHECK(FlushInProgress());
+ TRACE_EVENT_ASYNC_STEP1("input", "InputQueueFlush", this, "DeliverPacket",
+ "id", next_packet_id_);
+ in_flush_packet_->set_id(next_packet_id_++);
+ client_->Deliver(*in_flush_packet_);
+}
+
+void InputQueue::RequestFlushIfNecessary() {
+ if (flush_requested_)
+ return;
+
+ // Defer flush requests until the current flush has finished.
+ if (FlushInProgress())
+ return;
+
+ // No additional events to flush.
+ if (pending_flush_packet_->empty())
+ return;
+
+ TRACE_EVENT_ASYNC_BEGIN0("input", "InputQueueFlush", this);
+ TRACE_EVENT_ASYNC_STEP0("input", "InputQueueFlush", this, "Request");
+ flush_requested_ = true;
+ client_->SetNeedsFlush();
+}
+
+bool InputQueue::FlushInProgress() const { return !in_flush_packet_->empty(); }
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/input_queue.h b/content/browser/renderer_host/input/input_queue.h
new file mode 100644
index 0000000..2c7acb7
--- /dev/null
+++ b/content/browser/renderer_host/input/input_queue.h
@@ -0,0 +1,82 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/common/content_export.h"
+#include "content/common/input/input_event_disposition.h"
+
+namespace content {
+
+class BrowserInputEvent;
+class EventPacketAck;
+class InputQueueClient;
+
+// |InputQueue| handles browser input event batching and response.
+// Event packets are assembled into sequential event packets. A flush entails
+// delivery and dispatch of a single event packet, and continues until the
+// packet is ack'ed and all its events have been dispatched to the renderer.
+class CONTENT_EXPORT InputQueue {
+ public:
+ // The |client| should outlive the InputQueue.
+ InputQueue(InputQueueClient* client);
+ virtual ~InputQueue();
+
+ // If a flush is in progress, |event| will be dispatched in the next flush.
+ // If no flush is in progress, a flush will be requested if necessary.
+ // |event| is assumed to be both non-NULL and valid.
+ void QueueEvent(scoped_ptr<BrowserInputEvent> event);
+
+ // Initiates the flush of the pending event packet to |client_|, if necessary.
+ // This should only be called in response to |client_->SetNeedsFlush()|.
+ void BeginFlush();
+
+ // Called by the owner upon EventPacket responses from the sender target. This
+ // will dispatch event acks for events with a corresponding |ack_handler|.
+ enum AckResult {
+ ACK_OK, // |acked_packet| was processed successfully.
+ ACK_UNEXPECTED, // |acked_packet| was unexpected; no flush was in-progress.
+ ACK_INVALID, // |acked_packet| contains invalid data.
+ ACK_SHUTDOWN // |acked_packet| processing triggered queue shutdown.
+ };
+ AckResult OnEventPacketAck(int64 packet_id,
+ const InputEventDispositions& dispositions);
+
+ // Total number of evenst in the queue, both being flushed and pending flush.
+ size_t QueuedEventCount() const;
+
+ protected:
+ friend class InputQueueTest;
+
+ // Delivers |in_flush_packet_| to the client.
+ void DeliverInFlushPacket();
+
+ // Requests a flush via |client_| if the necessary request has not been made.
+ void RequestFlushIfNecessary();
+
+ // True when |in_flush_packet_| is non-empty.
+ bool FlushInProgress() const;
+
+ private:
+ InputQueueClient* client_;
+
+ // Used to assign unique, non-zero ID's to each delivered EventPacket.
+ int64 next_packet_id_;
+
+ // Avoid spamming the client with redundant flush requests.
+ bool flush_requested_;
+
+ class BrowserEventPacket;
+ scoped_ptr<BrowserEventPacket> in_flush_packet_;
+ scoped_ptr<BrowserEventPacket> pending_flush_packet_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputQueue);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_H_
diff --git a/content/browser/renderer_host/input/input_queue_client.h b/content/browser/renderer_host/input/input_queue_client.h
new file mode 100644
index 0000000..f1151d9
--- /dev/null
+++ b/content/browser/renderer_host/input/input_queue_client.h
@@ -0,0 +1,35 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "content/common/input/input_event_disposition.h"
+
+namespace content {
+
+class EventPacket;
+
+class InputQueueClient {
+ public:
+ virtual ~InputQueueClient() {}
+
+ // Used by the flush process for sending batched events to the desired target.
+ virtual void Deliver(const EventPacket& packet) = 0;
+
+ // Signalled when the InputQueue has received and dispatched
+ // event ACK's from the previous flush.
+ virtual void DidFinishFlush() = 0;
+
+ // Signalled when the InputQueue receives an input event.
+ virtual void SetNeedsFlush() = 0;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_QUEUE_CLIENT_H_
diff --git a/content/browser/renderer_host/input/input_queue_unittest.cc b/content/browser/renderer_host/input/input_queue_unittest.cc
new file mode 100644
index 0000000..ec66f2a
--- /dev/null
+++ b/content/browser/renderer_host/input/input_queue_unittest.cc
@@ -0,0 +1,363 @@
+// Copyright 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 <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/input/browser_input_event.h"
+#include "content/browser/renderer_host/input/input_queue.h"
+#include "content/browser/renderer_host/input/input_queue_client.h"
+#include "content/common/input/event_packet.h"
+#include "content/common/input/input_event.h"
+#include "content/common/input/ipc_input_event_payload.h"
+#include "content/common/input/web_input_event_payload.h"
+#include "content/common/input_messages.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/latency_info.h"
+
+namespace content {
+namespace {
+
+using WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+
+class InputQueueTest : public testing::Test,
+ public InputQueueClient,
+ public BrowserInputEventClient {
+ public:
+ InputQueueTest()
+ : queue_(new InputQueue(this)),
+ routing_id_(0),
+ num_flush_completions_(0),
+ num_flush_requests_(0),
+ num_packet_deliveries_(0),
+ next_input_id_(1) {}
+
+ // InputQueueClient
+ virtual void Deliver(const EventPacket& packet) OVERRIDE {
+ EXPECT_LT(0u, packet.size());
+ ++num_packet_deliveries_;
+ current_packet_id_ = packet.id();
+ current_packet_dispositions_.resize(packet.size(), INPUT_EVENT_UNHANDLED);
+ }
+
+ virtual void DidFinishFlush() OVERRIDE { ++num_flush_completions_; }
+ virtual void SetNeedsFlush() OVERRIDE { ++num_flush_requests_; }
+
+ // BrowserInputEventClient
+ virtual void OnDispatched(const BrowserInputEvent& event,
+ InputEventDisposition disposition) OVERRIDE {
+ acked_dispositions_.push_back(disposition);
+ }
+
+ virtual void OnDispatched(
+ const BrowserInputEvent& event,
+ InputEventDisposition disposition,
+ ScopedVector<BrowserInputEvent>* followup) OVERRIDE {
+ acked_followup_dispositions_.push_back(disposition);
+ if (event_to_inject_)
+ followup->push_back(event_to_inject_.release());
+ }
+
+ int num_flush_requests() const { return num_flush_requests_; }
+ int num_flush_completions() const { return num_flush_completions_; }
+ int num_packet_deliveries() const { return num_packet_deliveries_; }
+
+ protected:
+ scoped_ptr<BrowserInputEvent> CreateIPCInputEvent(IPC::Message* message) {
+ return BrowserInputEvent::Create(
+ NextInputID(),
+ IPCInputEventPayload::Create(make_scoped_ptr(message)),
+ this);
+ }
+
+ scoped_ptr<BrowserInputEvent> CreateWebInputEvent(
+ WebInputEvent::Type web_type) {
+ WebKit::WebMouseEvent mouse;
+ WebKit::WebMouseWheelEvent wheel;
+ WebKit::WebTouchEvent touch;
+ WebKit::WebGestureEvent gesture;
+ WebKit::WebKeyboardEvent keyboard;
+
+ WebKit::WebInputEvent* web_event = NULL;
+ if (WebInputEvent::isMouseEventType(web_type))
+ web_event = &mouse;
+ else if (WebInputEvent::isKeyboardEventType(web_type))
+ web_event = &keyboard;
+ else if (WebInputEvent::isTouchEventType(web_type))
+ web_event = &touch;
+ else if (WebInputEvent::isGestureEventType(web_type))
+ web_event = &gesture;
+ else
+ web_event = &wheel;
+ web_event->type = web_type;
+
+ return BrowserInputEvent::Create(
+ NextInputID(),
+ WebInputEventPayload::Create(*web_event, ui::LatencyInfo(), false),
+ this);
+ }
+
+ void QueueEvent(IPC::Message* message) {
+ queue_->QueueEvent(CreateIPCInputEvent(message));
+ }
+
+ void QueueEvent(WebInputEvent::Type web_type) {
+ queue_->QueueEvent(CreateWebInputEvent(web_type));
+ }
+
+ bool Flush(InputEventDisposition disposition) {
+ StartFlush();
+ return FinishFlush(disposition);
+ }
+
+ void StartFlush() {
+ acked_dispositions_.clear();
+ acked_followup_dispositions_.clear();
+ current_packet_id_ = 0;
+ current_packet_dispositions_.clear();
+ queue_->BeginFlush();
+ }
+
+ bool FinishFlush(InputEventDisposition disposition) {
+ if (!current_packet_id_)
+ return false;
+ current_packet_dispositions_ = InputEventDispositions(
+ current_packet_dispositions_.size(), disposition);
+ return InputQueue::ACK_OK ==
+ queue_->OnEventPacketAck(current_packet_id_,
+ current_packet_dispositions_);
+ }
+
+ int64 NextInputID() { return next_input_id_++; }
+
+ scoped_ptr<InputQueue> queue_;
+
+ int routing_id_;
+ int64 current_packet_id_;
+ InputEventDispositions current_packet_dispositions_;
+
+ InputEventDispositions acked_dispositions_;
+ InputEventDispositions acked_followup_dispositions_;
+ scoped_ptr<BrowserInputEvent> event_to_inject_;
+
+ int num_flush_completions_;
+ int num_flush_requests_;
+ int num_packet_deliveries_;
+ int next_input_id_;
+};
+
+TEST_F(InputQueueTest, SetNeedsFlushOnQueueEvent) {
+ EXPECT_EQ(0, num_flush_requests());
+
+ QueueEvent(WebInputEvent::MouseDown);
+ EXPECT_EQ(1, num_flush_requests());
+
+ // Additional queued events should not trigger additional flush requests.
+ QueueEvent(WebInputEvent::MouseUp);
+ EXPECT_EQ(1, num_flush_requests());
+ QueueEvent(WebInputEvent::TouchStart);
+ EXPECT_EQ(1, num_flush_requests());
+}
+
+TEST_F(InputQueueTest, NoSetNeedsFlushOnQueueIfFlushing) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ EXPECT_EQ(1, num_flush_requests());
+
+ StartFlush();
+ EXPECT_EQ(1, num_flush_requests());
+ EXPECT_EQ(1, num_packet_deliveries());
+
+ // Events queued after a flush will not trigger an additional flush request.
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ EXPECT_EQ(1, num_flush_requests());
+ QueueEvent(WebInputEvent::GestureScrollEnd);
+ EXPECT_EQ(1, num_flush_requests());
+}
+
+TEST_F(InputQueueTest, SetNeedsFlushAfterDidFinishFlushIfEventsQueued) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ EXPECT_EQ(1, num_flush_requests());
+
+ StartFlush();
+ EXPECT_EQ(1, num_packet_deliveries());
+
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ EXPECT_EQ(1, num_flush_requests());
+
+ // An additional flush request is sent for the event queued after the flush.
+ ASSERT_TRUE(current_packet_id_);
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_IMPL_THREAD_CONSUMED));
+ EXPECT_EQ(1, num_flush_completions());
+ EXPECT_EQ(2, num_flush_requests());
+}
+
+TEST_F(InputQueueTest, EventPacketSentAfterFlush) {
+ EXPECT_EQ(0, num_packet_deliveries());
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ EXPECT_EQ(0, num_packet_deliveries());
+ StartFlush();
+ EXPECT_EQ(1, num_packet_deliveries());
+}
+
+TEST_F(InputQueueTest, AcksHandledInProperOrder) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ QueueEvent(WebInputEvent::GestureScrollEnd);
+ QueueEvent(WebInputEvent::GestureFlingStart);
+
+ queue_->BeginFlush();
+ ASSERT_EQ(3u, current_packet_dispositions_.size());
+ current_packet_dispositions_[0] = INPUT_EVENT_IMPL_THREAD_CONSUMED;
+ current_packet_dispositions_[1] = INPUT_EVENT_MAIN_THREAD_CONSUMED;
+ current_packet_dispositions_[2] = INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS;
+ queue_->OnEventPacketAck(current_packet_id_, current_packet_dispositions_);
+ EXPECT_EQ(1, num_flush_completions());
+
+ ASSERT_EQ(3u, acked_dispositions_.size());
+ EXPECT_EQ(acked_dispositions_[0], INPUT_EVENT_IMPL_THREAD_CONSUMED);
+ EXPECT_EQ(acked_dispositions_[1], INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(acked_dispositions_[2], INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS);
+}
+
+TEST_F(InputQueueTest, FollowupWhenFollowupEventNotConsumed) {
+ InputEventDisposition unconsumed_dispositions[] = {
+ INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS,
+ INPUT_EVENT_MAIN_THREAD_NOT_PREVENT_DEFAULTED,
+ INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS
+ };
+ for (size_t i = 0; i < arraysize(unconsumed_dispositions); ++i) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ QueueEvent(WebInputEvent::TouchStart);
+ QueueEvent(WebInputEvent::TouchMove);
+
+ Flush(unconsumed_dispositions[i]);
+ EXPECT_EQ(1u, acked_dispositions_.size()) << i;
+ EXPECT_EQ(2u, acked_followup_dispositions_.size()) << i;
+ }
+}
+
+TEST_F(InputQueueTest, NoFollowupWhenFollowupEventConsumed) {
+ InputEventDisposition consumed_dispositions[] = {
+ INPUT_EVENT_IMPL_THREAD_CONSUMED,
+ INPUT_EVENT_MAIN_THREAD_PREVENT_DEFAULTED,
+ INPUT_EVENT_MAIN_THREAD_CONSUMED
+ };
+ for (size_t i = 0; i < arraysize(consumed_dispositions); ++i) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ QueueEvent(WebInputEvent::TouchStart);
+ QueueEvent(WebInputEvent::TouchMove);
+
+ Flush(consumed_dispositions[i]);
+ EXPECT_EQ(3u, acked_dispositions_.size()) << i;
+ EXPECT_EQ(0u, acked_followup_dispositions_.size()) << i;
+ }
+}
+
+TEST_F(InputQueueTest, FlushOnEmptyQueueIgnored) {
+ Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(0, num_flush_requests());
+ EXPECT_EQ(0, num_flush_completions());
+
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(1, num_flush_requests());
+ EXPECT_EQ(1, num_flush_completions());
+
+ Flush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(1, num_flush_requests());
+ EXPECT_EQ(1, num_flush_completions());
+}
+
+TEST_F(InputQueueTest, FlushContinuesUntilAllEventsProcessed) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ QueueEvent(WebInputEvent::GestureScrollEnd);
+ QueueEvent(WebInputEvent::GestureFlingStart);
+
+ EXPECT_EQ(1, num_flush_requests());
+ Flush(INPUT_EVENT_COULD_NOT_DELIVER);
+ EXPECT_EQ(0, num_flush_completions());
+
+ FinishFlush(INPUT_EVENT_COULD_NOT_DELIVER);
+ EXPECT_EQ(0, num_flush_completions());
+
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED));
+ EXPECT_EQ(1, num_flush_completions());
+}
+
+TEST_F(InputQueueTest, InvalidPacketAckIgnored) {
+ // Packet never flushed, any ack should be ignored.
+ InputQueue::AckResult result =
+ queue_->OnEventPacketAck(0, InputEventDispositions());
+ EXPECT_EQ(InputQueue::ACK_UNEXPECTED, result);
+
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ StartFlush();
+ // Tamper with the sent packet by adding an extra event.
+ current_packet_dispositions_.push_back(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ bool valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(0, num_flush_completions());
+ EXPECT_FALSE(valid_packet_ack);
+
+ // Fix the packet.
+ current_packet_dispositions_.pop_back();
+ valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(1, num_flush_completions());
+ EXPECT_TRUE(valid_packet_ack);
+
+ // Tamper with the packet by changing the id.
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ StartFlush();
+ int64 packet_ack_id = -1;
+ std::swap(current_packet_id_, packet_ack_id);
+ valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(1, num_flush_completions());
+ EXPECT_FALSE(valid_packet_ack);
+
+ // Fix the packet.
+ std::swap(current_packet_id_, packet_ack_id);
+ valid_packet_ack = FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED);
+ EXPECT_EQ(2, num_flush_completions());
+ EXPECT_TRUE(valid_packet_ack);
+}
+
+TEST_F(InputQueueTest, InjectedEventsAckedBeforeDidFinishFlush) {
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ QueueEvent(WebInputEvent::TouchMove);
+
+ event_to_inject_ = CreateIPCInputEvent(new InputMsg_Copy(routing_id_));
+ Flush(INPUT_EVENT_IMPL_THREAD_NO_CONSUMER_EXISTS);
+ EXPECT_EQ(0, num_flush_completions());
+
+ // The injected event should now be in the event packet.
+ EXPECT_EQ(1u, current_packet_dispositions_.size());
+ EXPECT_EQ(1u, acked_followup_dispositions_.size());
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED));
+ EXPECT_EQ(1, num_flush_completions());
+
+ QueueEvent(WebInputEvent::GestureScrollBegin);
+ QueueEvent(WebInputEvent::TouchStart);
+ event_to_inject_ = CreateWebInputEvent(WebInputEvent::TouchMove);
+ Flush(INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS);
+ // |event_to_inject_| is now in the event packet.
+ EXPECT_EQ(1u, acked_followup_dispositions_.size());
+ EXPECT_EQ(1u, current_packet_dispositions_.size());
+
+ event_to_inject_ = CreateWebInputEvent(WebInputEvent::TouchMove);
+ // the next |event_to_inject_| is now in the event packet.
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_NO_CONSUMER_EXISTS));
+
+ EXPECT_EQ(2u, acked_followup_dispositions_.size());
+ EXPECT_EQ(1u, current_packet_dispositions_.size());
+ EXPECT_EQ(1, num_flush_completions());
+
+ ASSERT_TRUE(FinishFlush(INPUT_EVENT_MAIN_THREAD_CONSUMED));
+ EXPECT_EQ(2, num_flush_completions());
+}
+
+} // namespace
+} // namespace content
diff --git a/content/browser/renderer_host/input/input_router.h b/content/browser/renderer_host/input/input_router.h
index 2d8a4c6..93b7b3f 100644
--- a/content/browser/renderer_host/input/input_router.h
+++ b/content/browser/renderer_host/input/input_router.h
@@ -24,10 +24,14 @@ class InputRouter : public IPC::Listener {
public:
virtual ~InputRouter() {}
+ // Should be called only in response to |SetNeedsFlush| requests made via
+ // the |InputRouterClient|.
+ virtual void Flush() = 0;
+
// Send and take ownership of the the given InputMsg_*. This should be used
// only for event types not associated with a WebInputEvent. Returns true on
// success and false otherwise.
- virtual bool SendInput(IPC::Message* message) = 0;
+ virtual bool SendInput(scoped_ptr<IPC::Message> message) = 0;
// WebInputEvents
virtual void SendMouseEvent(
diff --git a/content/browser/renderer_host/input/input_router_client.h b/content/browser/renderer_host/input/input_router_client.h
index 9c85422..6479332 100644
--- a/content/browser/renderer_host/input/input_router_client.h
+++ b/content/browser/renderer_host/input/input_router_client.h
@@ -61,16 +61,15 @@ class CONTENT_EXPORT InputRouterClient {
virtual bool OnSendGestureEventImmediately(
const GestureEventWithLatencyInfo& gesture_event) = 0;
- // Called upon event ack receipt from the renderer.
- virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
- InputEventAckState ack_result) = 0;
- virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event,
- InputEventAckState ack_result) = 0;
- virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
- InputEventAckState ack_result) = 0;
- virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event,
- InputEventAckState ack_result) = 0;
- virtual void OnUnexpectedEventAck(bool bad_message) = 0;
+ // Certain router implementations require periodic flushing of queued events.
+ // When this method is called, the client should ensure a timely call, either
+ // synchronous or asynchronous, of |Flush| on the InputRouter.
+ virtual void SetNeedsFlush() = 0;
+
+ // Called when the router has finished flushing all events queued at the time
+ // of the call to Flush. The call will typically be asynchronous with
+ // respect to the call to |Flush| on the InputRouter.
+ virtual void DidFlush() = 0;
};
} // namespace content
diff --git a/content/browser/renderer_host/input/input_router_unittest.cc b/content/browser/renderer_host/input/input_router_unittest.cc
new file mode 100644
index 0000000..746b3c3
--- /dev/null
+++ b/content/browser/renderer_host/input/input_router_unittest.cc
@@ -0,0 +1,225 @@
+// Copyright 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/input_router_unittest.h"
+
+#include "content/browser/renderer_host/input/input_router.h"
+#include "content/common/input_messages.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+
+#if defined(OS_WIN) || defined(USE_AURA)
+#include "content/browser/renderer_host/ui_events_helper.h"
+#include "ui/base/events/event.h"
+#endif
+
+using WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+using WebKit::WebTouchPoint;
+
+namespace content {
+
+InputRouterTest::InputRouterTest() {}
+InputRouterTest::~InputRouterTest() {}
+
+void InputRouterTest::SetUp() {
+ browser_context_.reset(new TestBrowserContext());
+ process_.reset(new MockRenderProcessHost(browser_context_.get()));
+ client_.reset(new MockInputRouterClient());
+ ack_handler_.reset(new MockInputAckHandler());
+ input_router_ = CreateInputRouter(process_.get(),
+ client_.get(),
+ ack_handler_.get(),
+ MSG_ROUTING_NONE);
+ client_->set_input_router(input_router_.get());
+ ack_handler_->set_input_router(input_router_.get());
+}
+
+void InputRouterTest::TearDown() {
+ // Process all pending tasks to avoid InputRouterTest::leaks.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ input_router_.reset();
+ client_.reset();
+ process_.reset();
+ browser_context_.reset();
+}
+
+void InputRouterTest::SimulateKeyboardEvent(WebInputEvent::Type type) {
+ NativeWebKeyboardEvent key_event;
+ key_event.type = type;
+ key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value.
+ input_router_->SendKeyboardEvent(key_event, ui::LatencyInfo());
+ client_->ExpectSendCalled(true);
+ EXPECT_EQ(type, client_->sent_key_event().type);
+ EXPECT_EQ(key_event.windowsKeyCode,
+ client_->sent_key_event().windowsKeyCode);
+}
+
+void InputRouterTest::SimulateWheelEvent(float dX,
+ float dY,
+ int modifiers,
+ bool precise) {
+ WebMouseWheelEvent wheel_event;
+ wheel_event.type = WebInputEvent::MouseWheel;
+ wheel_event.deltaX = dX;
+ wheel_event.deltaY = dY;
+ wheel_event.modifiers = modifiers;
+ wheel_event.hasPreciseScrollingDeltas = precise;
+ input_router_->SendWheelEvent(
+ MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo()));
+ client_->ExpectSendCalled(true);
+ EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type);
+ EXPECT_EQ(dX, client_->sent_wheel_event().event.deltaX);
+}
+
+void InputRouterTest::SimulateMouseMove(int x, int y, int modifiers) {
+ WebMouseEvent mouse_event;
+ mouse_event.type = WebInputEvent::MouseMove;
+ mouse_event.x = mouse_event.windowX = x;
+ mouse_event.y = mouse_event.windowY = y;
+ mouse_event.modifiers = modifiers;
+ input_router_->SendMouseEvent(
+ MouseEventWithLatencyInfo(mouse_event, ui::LatencyInfo()));
+ client_->ExpectSendCalled(true);
+ EXPECT_EQ(mouse_event.type, client_->sent_mouse_event().event.type);
+ EXPECT_EQ(x, client_->sent_mouse_event().event.x);
+}
+
+void InputRouterTest::SimulateWheelEventWithPhase(
+ WebMouseWheelEvent::Phase phase) {
+ WebMouseWheelEvent wheel_event;
+ wheel_event.type = WebInputEvent::MouseWheel;
+ wheel_event.phase = phase;
+ input_router_->SendWheelEvent(
+ MouseWheelEventWithLatencyInfo(wheel_event, ui::LatencyInfo()));
+ client_->ExpectSendCalled(true);
+ EXPECT_EQ(wheel_event.type, client_->sent_wheel_event().event.type);
+ EXPECT_EQ(phase, client_->sent_wheel_event().event.phase);
+}
+
+// Inject provided synthetic WebGestureEvent instance.
+void InputRouterTest::SimulateGestureEventCore(WebInputEvent::Type type,
+ WebGestureEvent::SourceDevice sourceDevice,
+ WebGestureEvent* gesture_event) {
+ gesture_event->type = type;
+ gesture_event->sourceDevice = sourceDevice;
+ GestureEventWithLatencyInfo gesture_with_latency(
+ *gesture_event, ui::LatencyInfo());
+ input_router_->SendGestureEvent(gesture_with_latency);
+ client_->ExpectSendCalled(true);
+ EXPECT_EQ(type, client_->sent_gesture_event().event.type);
+ EXPECT_EQ(sourceDevice, client_->sent_gesture_event().event.sourceDevice);
+}
+
+// Inject simple synthetic WebGestureEvent instances.
+void InputRouterTest::SimulateGestureEvent(WebInputEvent::Type type,
+ WebGestureEvent::SourceDevice sourceDevice) {
+ WebGestureEvent gesture_event;
+ SimulateGestureEventCore(type, sourceDevice, &gesture_event);
+}
+
+void InputRouterTest::SimulateGestureScrollUpdateEvent(float dX,
+ float dY,
+ int modifiers) {
+ WebGestureEvent gesture_event;
+ gesture_event.data.scrollUpdate.deltaX = dX;
+ gesture_event.data.scrollUpdate.deltaY = dY;
+ gesture_event.modifiers = modifiers;
+ SimulateGestureEventCore(WebInputEvent::GestureScrollUpdate,
+ WebGestureEvent::Touchscreen, &gesture_event);
+}
+
+void InputRouterTest::SimulateGesturePinchUpdateEvent(float scale,
+ float anchorX,
+ float anchorY,
+ int modifiers) {
+ WebGestureEvent gesture_event;
+ gesture_event.data.pinchUpdate.scale = scale;
+ gesture_event.x = anchorX;
+ gesture_event.y = anchorY;
+ gesture_event.modifiers = modifiers;
+ SimulateGestureEventCore(WebInputEvent::GesturePinchUpdate,
+ WebGestureEvent::Touchscreen, &gesture_event);
+}
+
+// Inject synthetic GestureFlingStart events.
+void InputRouterTest::SimulateGestureFlingStartEvent(
+ float velocityX,
+ float velocityY,
+ WebGestureEvent::SourceDevice sourceDevice) {
+ WebGestureEvent gesture_event;
+ gesture_event.data.flingStart.velocityX = velocityX;
+ gesture_event.data.flingStart.velocityY = velocityY;
+ SimulateGestureEventCore(WebInputEvent::GestureFlingStart, sourceDevice,
+ &gesture_event);
+}
+
+void InputRouterTest::SimulateTouchEvent(
+ int x,
+ int y) {
+ PressTouchPoint(x, y);
+ SendTouchEvent();
+}
+
+// Set the timestamp for the touch-event.
+void InputRouterTest::SetTouchTimestamp(base::TimeDelta timestamp) {
+ touch_event_.timeStampSeconds = timestamp.InSecondsF();
+}
+
+// Sends a touch event (irrespective of whether the page has a touch-event
+// handler or not).
+void InputRouterTest::SendTouchEvent() {
+ input_router_->SendTouchEvent(
+ TouchEventWithLatencyInfo(touch_event_, ui::LatencyInfo()));
+
+ // Mark all the points as stationary. And remove the points that have been
+ // released.
+ int point = 0;
+ for (unsigned int i = 0; i < touch_event_.touchesLength; ++i) {
+ if (touch_event_.touches[i].state == WebTouchPoint::StateReleased)
+ continue;
+
+ touch_event_.touches[point] = touch_event_.touches[i];
+ touch_event_.touches[point].state =
+ WebTouchPoint::StateStationary;
+ ++point;
+ }
+ touch_event_.touchesLength = point;
+ touch_event_.type = WebInputEvent::Undefined;
+}
+
+int InputRouterTest::PressTouchPoint(int x, int y) {
+ if (touch_event_.touchesLength == touch_event_.touchesLengthCap)
+ return -1;
+ WebTouchPoint& point =
+ touch_event_.touches[touch_event_.touchesLength];
+ point.id = touch_event_.touchesLength;
+ point.position.x = point.screenPosition.x = x;
+ point.position.y = point.screenPosition.y = y;
+ point.state = WebTouchPoint::StatePressed;
+ point.radiusX = point.radiusY = 1.f;
+ ++touch_event_.touchesLength;
+ touch_event_.type = WebInputEvent::TouchStart;
+ return point.id;
+}
+
+void InputRouterTest::MoveTouchPoint(int index, int x, int y) {
+ CHECK(index >= 0 && index < touch_event_.touchesLengthCap);
+ WebTouchPoint& point = touch_event_.touches[index];
+ point.position.x = point.screenPosition.x = x;
+ point.position.y = point.screenPosition.y = y;
+ touch_event_.touches[index].state = WebTouchPoint::StateMoved;
+ touch_event_.type = WebInputEvent::TouchMove;
+}
+
+void InputRouterTest::ReleaseTouchPoint(int index) {
+ CHECK(index >= 0 && index < touch_event_.touchesLengthCap);
+ touch_event_.touches[index].state = WebTouchPoint::StateReleased;
+ touch_event_.type = WebInputEvent::TouchEnd;
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/input_router_unittest.h b/content/browser/renderer_host/input/input_router_unittest.h
new file mode 100644
index 0000000..20a68af
--- /dev/null
+++ b/content/browser/renderer_host/input/input_router_unittest.h
@@ -0,0 +1,83 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_UNITTEST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_INPUT_ROUTER_UNITTEST_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/input/input_router_client.h"
+#include "content/browser/renderer_host/input/mock_input_ack_handler.h"
+#include "content/browser/renderer_host/input/mock_input_router_client.h"
+#include "content/public/test/mock_render_process_host.h"
+#include "content/public/test/test_browser_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/web/WebInputEvent.h"
+
+namespace content {
+
+class InputRouter;
+class MockInputRouterClient;
+
+class InputRouterTest : public testing::Test {
+ public:
+ InputRouterTest();
+ virtual ~InputRouterTest();
+
+ protected:
+ // Called on SetUp.
+ virtual scoped_ptr<InputRouter> CreateInputRouter(RenderProcessHost* process,
+ InputRouterClient* client,
+ InputAckHandler* handler,
+ int routing_id) = 0;
+
+ // testing::Test
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ void SendInputEventACK(WebKit::WebInputEvent::Type type,
+ InputEventAckState ack_result);
+ void SimulateKeyboardEvent(WebKit::WebInputEvent::Type type);
+ void SimulateWheelEvent(float dX, float dY, int modifiers, bool precise);
+ void SimulateMouseMove(int x, int y, int modifiers);
+ void SimulateWheelEventWithPhase(WebKit::WebMouseWheelEvent::Phase phase);
+ void SimulateGestureEventCore(WebKit::WebInputEvent::Type type,
+ WebKit::WebGestureEvent::SourceDevice sourceDevice,
+ WebKit::WebGestureEvent* gesture_event);
+ void SimulateGestureEvent(WebKit::WebInputEvent::Type type,
+ WebKit::WebGestureEvent::SourceDevice sourceDevice);
+ void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers);
+ void SimulateGesturePinchUpdateEvent(float scale,
+ float anchorX,
+ float anchorY,
+ int modifiers);
+ void SimulateGestureFlingStartEvent(
+ float velocityX,
+ float velocityY,
+ WebKit::WebGestureEvent::SourceDevice sourceDevice);
+ void SimulateTouchEvent(int x, int y);
+ void SetTouchTimestamp(base::TimeDelta timestamp);
+
+ // Sends a touch event (irrespective of whether the page has a touch-event
+ // handler or not).
+ void SendTouchEvent();
+
+ int PressTouchPoint(int x, int y);
+ void MoveTouchPoint(int index, int x, int y);
+ void ReleaseTouchPoint(int index);
+
+ scoped_ptr<MockRenderProcessHost> process_;
+ scoped_ptr<MockInputRouterClient> client_;
+ scoped_ptr<MockInputAckHandler> ack_handler_;
+ scoped_ptr<InputRouter> input_router_;
+
+ private:
+ base::MessageLoopForUI message_loop_;
+ WebKit::WebTouchEvent touch_event_;
+
+ scoped_ptr<TestBrowserContext> browser_context_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_UNITTEST_H_
diff --git a/content/browser/renderer_host/input/mock_input_ack_handler.cc b/content/browser/renderer_host/input/mock_input_ack_handler.cc
new file mode 100644
index 0000000..fc97ab3
--- /dev/null
+++ b/content/browser/renderer_host/input/mock_input_ack_handler.cc
@@ -0,0 +1,77 @@
+// Copyright 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/mock_input_ack_handler.h"
+
+#include "content/browser/renderer_host/input/input_router.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+using WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+using WebKit::WebTouchPoint;
+
+namespace content {
+
+MockInputAckHandler::MockInputAckHandler()
+ : input_router_(NULL),
+ ack_count_(0),
+ unexpected_event_ack_called_(false),
+ ack_state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {}
+
+MockInputAckHandler::~MockInputAckHandler() {}
+
+void MockInputAckHandler::OnKeyboardEventAck(
+ const NativeWebKeyboardEvent& event,
+ InputEventAckState ack_result) {
+ VLOG(1) << __FUNCTION__ << " called!";
+ acked_key_event_ = event;
+ RecordAckCalled(ack_result);
+}
+
+void MockInputAckHandler::OnWheelEventAck(
+ const WebMouseWheelEvent& event,
+ InputEventAckState ack_result) {
+ VLOG(1) << __FUNCTION__ << " called!";
+ acked_wheel_event_ = event;
+ RecordAckCalled(ack_result);
+}
+
+void MockInputAckHandler::OnTouchEventAck(
+ const TouchEventWithLatencyInfo& event,
+ InputEventAckState ack_result) {
+ VLOG(1) << __FUNCTION__ << " called!";
+ acked_touch_event_ = event;
+ RecordAckCalled(ack_result);
+ if (touch_followup_event_)
+ input_router_->SendGestureEvent(*touch_followup_event_);
+}
+
+void MockInputAckHandler::OnGestureEventAck(
+ const WebGestureEvent& event,
+ InputEventAckState ack_result) {
+ VLOG(1) << __FUNCTION__ << " called!";
+ acked_gesture_event_ = event;
+ RecordAckCalled(ack_result);
+}
+
+void MockInputAckHandler::OnUnexpectedEventAck(UnexpectedEventAckType type) {
+ VLOG(1) << __FUNCTION__ << " called!";
+ unexpected_event_ack_called_ = true;
+}
+
+void MockInputAckHandler::ExpectAckCalled(int times) {
+ EXPECT_EQ(times, ack_count_);
+ ack_count_ = 0;
+}
+
+void MockInputAckHandler::RecordAckCalled(InputEventAckState ack_result) {
+ ++ack_count_;
+ ack_state_ = ack_result;
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/mock_input_ack_handler.h b/content/browser/renderer_host/input/mock_input_ack_handler.h
new file mode 100644
index 0000000..604b26b
--- /dev/null
+++ b/content/browser/renderer_host/input/mock_input_ack_handler.h
@@ -0,0 +1,77 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/input/input_ack_handler.h"
+
+namespace content {
+
+class InputRouter;
+
+class MockInputAckHandler : public InputAckHandler {
+ public:
+ MockInputAckHandler();
+ virtual ~MockInputAckHandler();
+
+ // InputAckHandler
+ virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
+ InputEventAckState ack_result) OVERRIDE;
+ virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event,
+ InputEventAckState ack_result) OVERRIDE;
+ virtual void OnTouchEventAck(const TouchEventWithLatencyInfo& event,
+ InputEventAckState ack_result) OVERRIDE;
+ virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event,
+ InputEventAckState ack_result) OVERRIDE;
+ virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE;
+
+ void ExpectAckCalled(int times);
+
+ void set_input_router(InputRouter* input_router) {
+ input_router_ = input_router;
+ }
+
+ void set_followup_touch_event(scoped_ptr<GestureEventWithLatencyInfo> event) {
+ touch_followup_event_ = event.Pass();
+ }
+
+ bool unexpected_event_ack_called() const {
+ return unexpected_event_ack_called_;
+ }
+ InputEventAckState ack_state() const { return ack_state_; }
+
+ const NativeWebKeyboardEvent& acked_keyboard_event() const {
+ return acked_key_event_;
+ }
+ const WebKit::WebMouseWheelEvent& acked_wheel_event() const {
+ return acked_wheel_event_;
+ }
+ const TouchEventWithLatencyInfo& acked_touch_event() const {
+ return acked_touch_event_;
+ }
+ const WebKit::WebGestureEvent& acked_gesture_event() const {
+ return acked_gesture_event_;
+ }
+
+ private:
+ void RecordAckCalled(InputEventAckState ack_result);
+
+ InputRouter* input_router_;
+
+ int ack_count_;
+ bool unexpected_event_ack_called_;
+ InputEventAckState ack_state_;
+ NativeWebKeyboardEvent acked_key_event_;
+ WebKit::WebMouseWheelEvent acked_wheel_event_;
+ TouchEventWithLatencyInfo acked_touch_event_;
+ WebKit::WebGestureEvent acked_gesture_event_;
+
+ scoped_ptr<GestureEventWithLatencyInfo> touch_followup_event_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ACK_HANDLER_H_
diff --git a/content/browser/renderer_host/input/mock_input_router_client.cc b/content/browser/renderer_host/input/mock_input_router_client.cc
new file mode 100644
index 0000000..86fb53b
--- /dev/null
+++ b/content/browser/renderer_host/input/mock_input_router_client.cc
@@ -0,0 +1,148 @@
+// Copyright 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/mock_input_router_client.h"
+
+#include "content/browser/renderer_host/input/input_router.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+using WebKit::WebGestureEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
+using WebKit::WebTouchPoint;
+
+namespace content {
+
+MockInputRouterClient::MockInputRouterClient()
+ : input_router_(NULL),
+ in_flight_event_count_(0),
+ has_touch_handler_(false),
+ filter_state_(INPUT_EVENT_ACK_STATE_NOT_CONSUMED),
+ is_shortcut_(false),
+ allow_send_event_(true),
+ send_called_(false),
+ send_immediately_called_(false),
+ did_flush_called_(false),
+ set_needs_flush_called_(false) {}
+
+MockInputRouterClient::~MockInputRouterClient() {}
+
+InputEventAckState MockInputRouterClient::FilterInputEvent(
+ const WebInputEvent& input_event,
+ const ui::LatencyInfo& latency_info) {
+ return filter_state_;
+}
+
+void MockInputRouterClient::IncrementInFlightEventCount() {
+ ++in_flight_event_count_;
+}
+
+void MockInputRouterClient::DecrementInFlightEventCount() {
+ --in_flight_event_count_;
+}
+
+void MockInputRouterClient::OnHasTouchEventHandlers(
+ bool has_handlers) {
+ has_touch_handler_ = has_handlers;
+}
+
+bool MockInputRouterClient::OnSendKeyboardEvent(
+ const NativeWebKeyboardEvent& key_event,
+ const ui::LatencyInfo& latency_info,
+ bool* is_shortcut) {
+ send_called_ = true;
+ sent_key_event_ = key_event;
+ *is_shortcut = is_shortcut_;
+
+ return allow_send_event_;
+}
+
+bool MockInputRouterClient::OnSendWheelEvent(
+ const MouseWheelEventWithLatencyInfo& wheel_event) {
+ send_called_ = true;
+ sent_wheel_event_ = wheel_event;
+
+ return allow_send_event_;
+}
+
+bool MockInputRouterClient::OnSendMouseEvent(
+ const MouseEventWithLatencyInfo& mouse_event) {
+ send_called_ = true;
+ sent_mouse_event_ = mouse_event;
+
+ return allow_send_event_;
+}
+
+bool MockInputRouterClient::OnSendTouchEvent(
+ const TouchEventWithLatencyInfo& touch_event) {
+ send_called_ = true;
+ sent_touch_event_ = touch_event;
+
+ return allow_send_event_;
+}
+
+bool MockInputRouterClient::OnSendGestureEvent(
+ const GestureEventWithLatencyInfo& gesture_event) {
+ send_called_ = true;
+ sent_gesture_event_ = gesture_event;
+
+ return allow_send_event_ &&
+ input_router_->ShouldForwardGestureEvent(gesture_event);
+}
+
+bool MockInputRouterClient::OnSendMouseEventImmediately(
+ const MouseEventWithLatencyInfo& mouse_event) {
+ send_immediately_called_ = true;
+ immediately_sent_mouse_event_ = mouse_event;
+
+ return allow_send_event_;
+}
+
+bool MockInputRouterClient::OnSendTouchEventImmediately(
+ const TouchEventWithLatencyInfo& touch_event) {
+ send_immediately_called_ = true;
+ immediately_sent_touch_event_ = touch_event;
+
+ return allow_send_event_;
+}
+
+bool MockInputRouterClient::OnSendGestureEventImmediately(
+ const GestureEventWithLatencyInfo& gesture_event) {
+ send_immediately_called_ = true;
+ immediately_sent_gesture_event_ = gesture_event;
+ return allow_send_event_;
+}
+
+void MockInputRouterClient::ExpectSendCalled(bool called) {
+ EXPECT_EQ(called, send_called_);
+ send_called_ = false;
+}
+
+void MockInputRouterClient::ExpectSendImmediatelyCalled(bool called) {
+ EXPECT_EQ(called, send_immediately_called_);
+ send_immediately_called_ = false;
+}
+
+void MockInputRouterClient::ExpectNeedsFlushCalled(bool called) {
+ EXPECT_EQ(called, set_needs_flush_called_);
+ set_needs_flush_called_ = false;
+}
+
+void MockInputRouterClient::ExpectDidFlushCalled(bool called) {
+ EXPECT_EQ(called, did_flush_called_);
+ did_flush_called_ = false;
+}
+
+void MockInputRouterClient::DidFlush() {
+ did_flush_called_ = true;
+}
+
+void MockInputRouterClient::SetNeedsFlush() {
+ set_needs_flush_called_ = true;
+}
+
+} // namespace content
diff --git a/content/browser/renderer_host/input/mock_input_router_client.h b/content/browser/renderer_host/input/mock_input_router_client.h
new file mode 100644
index 0000000..a02f738
--- /dev/null
+++ b/content/browser/renderer_host/input/mock_input_router_client.h
@@ -0,0 +1,126 @@
+// Copyright 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_
+#define CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/renderer_host/input/input_router_client.h"
+
+namespace content {
+
+class InputRouter;
+
+class MockInputRouterClient : public InputRouterClient {
+ public:
+ MockInputRouterClient();
+ virtual ~MockInputRouterClient();
+
+ // InputRouterClient
+ virtual InputEventAckState FilterInputEvent(
+ const WebKit::WebInputEvent& input_event,
+ const ui::LatencyInfo& latency_info) OVERRIDE;
+ virtual void IncrementInFlightEventCount() OVERRIDE;
+ virtual void DecrementInFlightEventCount() OVERRIDE;
+ virtual void OnHasTouchEventHandlers(bool has_handlers) OVERRIDE;
+ virtual bool OnSendKeyboardEvent(
+ const NativeWebKeyboardEvent& key_event,
+ const ui::LatencyInfo& latency_info,
+ bool* is_shortcut) OVERRIDE;
+ virtual bool OnSendWheelEvent(
+ const MouseWheelEventWithLatencyInfo& wheel_event) OVERRIDE;
+ virtual bool OnSendMouseEvent(
+ const MouseEventWithLatencyInfo& mouse_event) OVERRIDE;
+ virtual bool OnSendTouchEvent(
+ const TouchEventWithLatencyInfo& touch_event) OVERRIDE;
+ virtual bool OnSendGestureEvent(
+ const GestureEventWithLatencyInfo& gesture_event) OVERRIDE;
+ virtual bool OnSendMouseEventImmediately(
+ const MouseEventWithLatencyInfo& mouse_event) OVERRIDE;
+ virtual bool OnSendTouchEventImmediately(
+ const TouchEventWithLatencyInfo& touch_event) OVERRIDE;
+ virtual bool OnSendGestureEventImmediately(
+ const GestureEventWithLatencyInfo& gesture_event) OVERRIDE;
+ virtual void DidFlush() OVERRIDE;
+ virtual void SetNeedsFlush() OVERRIDE;
+
+ void ExpectSendCalled(bool called);
+ void ExpectSendImmediatelyCalled(bool called);
+ void ExpectNeedsFlushCalled(bool called);
+ void ExpectDidFlushCalled(bool called);
+
+ void set_input_router(InputRouter* input_router) {
+ input_router_ = input_router;
+ }
+
+ bool has_touch_handler() const { return has_touch_handler_; }
+ void set_filter_state(InputEventAckState filter_state) {
+ filter_state_ = filter_state;
+ }
+ int in_flight_event_count() const {
+ return in_flight_event_count_;
+ }
+ void set_is_shortcut(bool is_shortcut) {
+ is_shortcut_ = is_shortcut;
+ }
+ void set_allow_send_event(bool allow) {
+ allow_send_event_ = allow;
+ }
+ const NativeWebKeyboardEvent& sent_key_event() {
+ return sent_key_event_;
+ }
+ const MouseWheelEventWithLatencyInfo& sent_wheel_event() {
+ return sent_wheel_event_;
+ }
+ const MouseEventWithLatencyInfo& sent_mouse_event() {
+ return sent_mouse_event_;
+ }
+ const GestureEventWithLatencyInfo& sent_gesture_event() {
+ return sent_gesture_event_;
+ }
+ const MouseEventWithLatencyInfo& immediately_sent_mouse_event() {
+ return immediately_sent_mouse_event_;
+ }
+ const TouchEventWithLatencyInfo& immediately_sent_touch_event() {
+ return immediately_sent_touch_event_;
+ }
+ const GestureEventWithLatencyInfo& immediately_sent_gesture_event() {
+ return immediately_sent_gesture_event_;
+ }
+
+ bool did_flush_called() const { return did_flush_called_; }
+ bool needs_flush_called() const { return set_needs_flush_called_; }
+ void set_followup_touch_event(scoped_ptr<GestureEventWithLatencyInfo> event) {
+ touch_followup_event_ = event.Pass();
+ }
+
+ private:
+ InputRouter* input_router_;
+ int in_flight_event_count_;
+ bool has_touch_handler_;
+
+ InputEventAckState filter_state_;
+
+ bool is_shortcut_;
+ bool allow_send_event_;
+ bool send_called_;
+ NativeWebKeyboardEvent sent_key_event_;
+ MouseWheelEventWithLatencyInfo sent_wheel_event_;
+ MouseEventWithLatencyInfo sent_mouse_event_;
+ TouchEventWithLatencyInfo sent_touch_event_;
+ GestureEventWithLatencyInfo sent_gesture_event_;
+
+ bool send_immediately_called_;
+ MouseEventWithLatencyInfo immediately_sent_mouse_event_;
+ TouchEventWithLatencyInfo immediately_sent_touch_event_;
+ GestureEventWithLatencyInfo immediately_sent_gesture_event_;
+
+ bool did_flush_called_;
+ bool set_needs_flush_called_;
+ scoped_ptr<GestureEventWithLatencyInfo> touch_followup_event_;
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_INPUT_MOCK_INPUT_ROUTER_CLIENT_H_
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index ec3387c..15d8808 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -29,6 +29,7 @@
#include "content/browser/renderer_host/backing_store.h"
#include "content/browser/renderer_host/backing_store_manager.h"
#include "content/browser/renderer_host/dip_util.h"
+#include "content/browser/renderer_host/input/buffered_input_router.h"
#include "content/browser/renderer_host/input/immediate_input_router.h"
#include "content/browser/renderer_host/overscroll_controller.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
@@ -225,7 +226,8 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
for (size_t i = 0; i < g_created_callbacks.Get().size(); i++)
g_created_callbacks.Get().at(i).Run(this);
- input_router_.reset(new ImmediateInputRouter(process, this, routing_id_));
+ input_router_.reset(
+ new ImmediateInputRouter(process_, this, this, routing_id_));
#if defined(USE_AURA)
bool overscroll_enabled = CommandLine::ForCurrentProcess()->
@@ -517,7 +519,7 @@ bool RenderWidgetHostImpl::OnMessageReceived(const IPC::Message &msg) {
bool RenderWidgetHostImpl::Send(IPC::Message* msg) {
if (IPC_MESSAGE_ID_CLASS(msg->type()) == InputMsgStart)
- return input_router_->SendInput(msg);
+ return input_router_->SendInput(make_scoped_ptr(msg));
return process_->Send(msg);
}
@@ -1274,7 +1276,8 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status,
waiting_for_screen_rects_ack_ = false;
// Reset to ensure that input routing works with a new renderer.
- input_router_.reset(new ImmediateInputRouter(process_, this, routing_id_));
+ input_router_.reset(
+ new ImmediateInputRouter(process_, this, this, routing_id_));
if (overscroll_controller_)
overscroll_controller_->Reset();
@@ -2141,6 +2144,12 @@ bool RenderWidgetHostImpl::OnSendGestureEventImmediately(
return !IgnoreInputEvents();
}
+void RenderWidgetHostImpl::SetNeedsFlush() {
+}
+
+void RenderWidgetHostImpl::DidFlush() {
+}
+
void RenderWidgetHostImpl::OnKeyboardEventAck(
const NativeWebKeyboardEvent& event,
InputEventAckState ack_result) {
@@ -2192,13 +2201,13 @@ void RenderWidgetHostImpl::OnTouchEventAck(
view_->ProcessAckedTouchEvent(event, ack_result);
}
-void RenderWidgetHostImpl::OnUnexpectedEventAck(bool bad_message) {
- if (bad_message) {
+void RenderWidgetHostImpl::OnUnexpectedEventAck(UnexpectedEventAckType type) {
+ if (type == BAD_ACK_MESSAGE) {
RecordAction(UserMetricsAction("BadMessageTerminate_RWH2"));
process_->ReceivedBadMessage();
+ } else if (type == UNEXPECTED_EVENT_TYPE) {
+ suppress_next_char_events_ = false;
}
-
- suppress_next_char_events_ = false;
}
const gfx::Vector2d& RenderWidgetHostImpl::GetLastScrollOffset() const {
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 5c9c2d6..a2fc415 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -23,6 +23,7 @@
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
+#include "content/browser/renderer_host/input/input_ack_handler.h"
#include "content/browser/renderer_host/input/input_router_client.h"
#include "content/browser/renderer_host/synthetic_gesture_controller.h"
#include "content/common/browser_rendering_stats.h"
@@ -88,6 +89,7 @@ struct EditCommand;
// embedders of content, and adds things only visible to content.
class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
public InputRouterClient,
+ public InputAckHandler,
public IPC::Listener {
public:
// routing_id can be MSG_ROUTING_NONE, in which case the next available
@@ -732,6 +734,10 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
const TouchEventWithLatencyInfo& touch_event) OVERRIDE;
virtual bool OnSendGestureEventImmediately(
const GestureEventWithLatencyInfo& gesture_event) OVERRIDE;
+ virtual void SetNeedsFlush() OVERRIDE;
+ virtual void DidFlush() OVERRIDE;
+
+ // InputAckHandler
virtual void OnKeyboardEventAck(const NativeWebKeyboardEvent& event,
InputEventAckState ack_result) OVERRIDE;
virtual void OnWheelEventAck(const WebKit::WebMouseWheelEvent& event,
@@ -740,7 +746,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
InputEventAckState ack_result) OVERRIDE;
virtual void OnGestureEventAck(const WebKit::WebGestureEvent& event,
InputEventAckState ack_result) OVERRIDE;
- virtual void OnUnexpectedEventAck(bool bad_message) OVERRIDE;
+ virtual void OnUnexpectedEventAck(UnexpectedEventAckType type) OVERRIDE;
void SimulateTouchGestureWithMouse(const WebKit::WebMouseEvent& mouse_event);
@@ -748,7 +754,6 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost,
// which may get in recursive loops).
void DelayedAutoResized();
-
// Our delegate, which wants to know mainly about keyboard events.
// It will remain non-NULL until DetachDelegate() is called.
RenderWidgetHostDelegate* delegate_;
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
index 54138de..95620f9 100644
--- a/content/browser/renderer_host/render_widget_host_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -122,12 +122,11 @@ class MockInputRouter : public InputRouter {
virtual ~MockInputRouter() {}
// InputRouter
- virtual bool SendInput(IPC::Message* message) OVERRIDE {
+ virtual void Flush() OVERRIDE {
+ flush_called_ = true;
+ }
+ virtual bool SendInput(scoped_ptr<IPC::Message> message) OVERRIDE {
send_event_called_ = true;
-
- // SendInput takes ownership of message
- delete message;
-
return true;
}
virtual void SendMouseEvent(
@@ -180,6 +179,7 @@ class MockInputRouter : public InputRouter {
return false;
}
+ bool flush_called_;
bool send_event_called_;
bool sent_mouse_event_;
bool sent_wheel_event_;
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index aa45e74..77c8dff 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -813,10 +813,18 @@
'browser/renderer_host/image_transport_factory_android.h',
'browser/renderer_host/ime_adapter_android.cc',
'browser/renderer_host/ime_adapter_android.h',
+ 'browser/renderer_host/input/browser_input_event.cc',
+ 'browser/renderer_host/input/browser_input_event.h',
+ 'browser/renderer_host/input/buffered_input_router.cc',
+ 'browser/renderer_host/input/buffered_input_router.h',
'browser/renderer_host/input/gesture_event_filter.cc',
'browser/renderer_host/input/gesture_event_filter.h',
'browser/renderer_host/input/immediate_input_router.cc',
'browser/renderer_host/input/immediate_input_router.h',
+ 'browser/renderer_host/input/input_ack_handler.h',
+ 'browser/renderer_host/input/input_queue.cc',
+ 'browser/renderer_host/input/input_queue.h',
+ 'browser/renderer_host/input/input_queue_client.h',
'browser/renderer_host/input/input_router.h',
'browser/renderer_host/input/input_router_client.h',
'browser/renderer_host/input/tap_suppression_controller.cc',
diff --git a/content/content_tests.gypi b/content/content_tests.gypi
index 5a5b66a..e3f8e19 100644
--- a/content/content_tests.gypi
+++ b/content/content_tests.gypi
@@ -337,6 +337,15 @@
'browser/power_monitor_message_broadcaster_unittest.cc',
'browser/renderer_host/compositing_iosurface_transformer_mac_unittest.cc',
'browser/renderer_host/gtk_key_bindings_handler_unittest.cc',
+ 'browser/renderer_host/input/buffered_input_router_unittest.cc',
+ 'browser/renderer_host/input/immediate_input_router_unittest.cc',
+ 'browser/renderer_host/input/input_router_unittest.h',
+ 'browser/renderer_host/input/input_router_unittest.cc',
+ 'browser/renderer_host/input/input_queue_unittest.cc',
+ 'browser/renderer_host/input/mock_input_ack_handler.cc',
+ 'browser/renderer_host/input/mock_input_ack_handler.h',
+ 'browser/renderer_host/input/mock_input_router_client.cc',
+ 'browser/renderer_host/input/mock_input_router_client.h',
'browser/renderer_host/media/audio_input_device_manager_unittest.cc',
'browser/renderer_host/media/audio_mirroring_manager_unittest.cc',
'browser/renderer_host/media/audio_renderer_host_unittest.cc',