diff options
author | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-12 00:32:49 +0000 |
---|---|---|
committer | jdduke@chromium.org <jdduke@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-12 00:32:49 +0000 |
commit | 6722816ee73f7002a9452ac93b1f6cbb8de036c9 (patch) | |
tree | 673d30a7ca142aa7d2cf06d03e92c25ecb476b78 /content | |
parent | 2ac0763be9d8497e34fee49f92f7f0fbebfc17d9 (diff) | |
download | chromium_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')
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, ¶m); + 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, ¶m); + 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), + ¶m)); + 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', |