diff options
author | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-06 06:58:46 +0000 |
---|---|---|
committer | darin@chromium.org <darin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-06 06:58:46 +0000 |
commit | a9fb30aa7879a6b8999bfd9918ca96934ec1b4aa (patch) | |
tree | e73489b12d86e446b9a2ae895a09ab8ab2e1374a | |
parent | 1f55484519e6a6d3c801e91e6c40cca7cef0a748 (diff) | |
download | chromium_src-a9fb30aa7879a6b8999bfd9918ca96934ec1b4aa.zip chromium_src-a9fb30aa7879a6b8999bfd9918ca96934ec1b4aa.tar.gz chromium_src-a9fb30aa7879a6b8999bfd9918ca96934ec1b4aa.tar.bz2 |
Input event filtering and compositor thread setup.
There are two new classes: InputEventFilter and CompositorThread.
The first sets up a MessageFilter designed to route input events
to the compositor thread. CompositorThread sets up the compositor
thread and it has a InputEventFilter.
When we pass an event to the CompositorThread, we are actually
passing it to a WebCompositor, which can respond asynchronously
via WebCompositorClient to tell us whether it handled the event or
wants us to punt it up to the WebWidget. It can also tell us that
it did not handle the event and that we should not bother sending
it to the WebWidget.
InputEventFilter contains all of the interesting thread marshalling
code. CompositorThread has the WebCompositor hookup.
Review URL: http://codereview.chromium.org/8089002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@104258 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/browser/renderer_host/render_widget_host.cc | 20 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host.h | 3 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_unittest.cc | 4 | ||||
-rw-r--r-- | content/common/view_messages.h | 8 | ||||
-rw-r--r-- | content/content_renderer.gypi | 4 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-rw-r--r-- | content/renderer/gpu/compositor_thread.cc | 114 | ||||
-rw-r--r-- | content/renderer/gpu/compositor_thread.h | 56 | ||||
-rw-r--r-- | content/renderer/gpu/input_event_filter.cc | 145 | ||||
-rw-r--r-- | content/renderer/gpu/input_event_filter.h | 91 | ||||
-rw-r--r-- | content/renderer/gpu/input_event_filter_unittest.cc | 218 | ||||
-rw-r--r-- | content/renderer/render_thread.cc | 9 | ||||
-rw-r--r-- | content/renderer/render_thread.h | 7 | ||||
-rw-r--r-- | content/renderer/render_widget.cc | 6 | ||||
-rw-r--r-- | webkit/glue/webthread_impl.h | 2 |
15 files changed, 661 insertions, 27 deletions
diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc index 37b72d8..de2fe56 100644 --- a/content/browser/renderer_host/render_widget_host.cc +++ b/content/browser/renderer_host/render_widget_host.cc @@ -1030,7 +1030,8 @@ void RenderWidgetHost::OnMsgUpdateRect( UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgUpdateRect", delta); } -void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) { +void RenderWidgetHost::OnMsgInputEventAck(WebInputEvent::Type event_type, + bool processed) { TRACE_EVENT0("renderer_host", "RenderWidgetHost::OnMsgInputEventAck"); // Log the time delta for processing an input event. @@ -1040,9 +1041,8 @@ void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) { // Cancel pending hung renderer checks since the renderer is responsive. StopHangMonitorTimeout(); - void* iter = NULL; - int type = 0; - if (!message.ReadInt(&iter, &type) || (type < WebInputEvent::Undefined)) { + int type = static_cast<int>(event_type); + if (type < WebInputEvent::Undefined) { UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH2")); process()->ReceivedBadMessage(); } else if (type == WebInputEvent::MouseMove) { @@ -1054,20 +1054,8 @@ void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) { ForwardMouseEvent(*next_mouse_move_); } } else if (WebInputEvent::isKeyboardEventType(type)) { - bool processed = false; - if (!message.ReadBool(&iter, &processed)) { - UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH3")); - process()->ReceivedBadMessage(); - } - ProcessKeyboardEventAck(type, processed); } else if (type == WebInputEvent::MouseWheel) { - bool processed = false; - if (!message.ReadBool(&iter, &processed)) { - UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH4")); - process()->ReceivedBadMessage(); - } - ProcessWheelAck(processed); } else if (type == WebInputEvent::TouchMove) { touch_move_pending_ = false; diff --git a/content/browser/renderer_host/render_widget_host.h b/content/browser/renderer_host/render_widget_host.h index 894e551..1c5202d 100644 --- a/content/browser/renderer_host/render_widget_host.h +++ b/content/browser/renderer_host/render_widget_host.h @@ -509,7 +509,8 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener, WebKit::WebTextDirection text_direction_hint); void OnMsgPaintAtSizeAck(int tag, const gfx::Size& size); void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params); - void OnMsgInputEventAck(const IPC::Message& message); + void OnMsgInputEventAck(WebKit::WebInputEvent::Type event_type, + bool processed); virtual void OnMsgFocus(); virtual void OnMsgBlur(); diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index 4f74039..bb7c7d464 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc @@ -268,9 +268,7 @@ class RenderWidgetHostTest : public testing::Test { void SendInputEventACK(WebInputEvent::Type type, bool processed) { scoped_ptr<IPC::Message> response( - new ViewHostMsg_HandleInputEvent_ACK(0)); - response->WriteInt(type); - response->WriteBool(processed); + new ViewHostMsg_HandleInputEvent_ACK(0, type, processed)); host_->OnMessageReceived(*response); } diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 0063cb0..e418265 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -25,6 +25,7 @@ #include "net/base/host_port_pair.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFindOptions.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerAction.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h" @@ -1394,10 +1395,9 @@ IPC_MESSAGE_ROUTED1(ViewHostMsg_DidActivateAcceleratedCompositing, bool /* true if the accelerated compositor is actve */) // Acknowledges receipt of a ViewMsg_HandleInputEvent message. -// Payload is a WebInputEvent::Type which is the type of the event, followed -// by an optional WebInputEvent which is provided only if the event was not -// processed. -IPC_MESSAGE_ROUTED0(ViewHostMsg_HandleInputEvent_ACK) +IPC_MESSAGE_ROUTED2(ViewHostMsg_HandleInputEvent_ACK, + WebKit::WebInputEvent::Type, + bool /* processed */) IPC_MESSAGE_ROUTED0(ViewHostMsg_Focus) IPC_MESSAGE_ROUTED0(ViewHostMsg_Blur) diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index c0571ac..bc68a41 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -47,6 +47,10 @@ 'renderer/external_popup_menu.h', 'renderer/geolocation_dispatcher.cc', 'renderer/geolocation_dispatcher.h', + 'renderer/gpu/compositor_thread.cc', + 'renderer/gpu/compositor_thread.h', + 'renderer/gpu/input_event_filter.cc', + 'renderer/gpu/input_event_filter.h', 'renderer/gpu/gpu_channel_host.cc', 'renderer/gpu/gpu_channel_host.h', 'renderer/gpu/gpu_video_decode_accelerator_host.cc', diff --git a/content/content_tests.gypi b/content/content_tests.gypi index bf45a0e..0cd7ebe 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -170,6 +170,7 @@ 'gpu/gpu_info_collector_unittest.cc', 'gpu/gpu_info_collector_unittest_win.cc', 'renderer/active_notification_tracker_unittest.cc', + 'renderer/gpu/input_event_filter_unittest.cc', 'renderer/media/audio_message_filter_unittest.cc', 'renderer/media/audio_renderer_impl_unittest.cc', 'renderer/media/capture_video_decoder_unittest.cc', diff --git a/content/renderer/gpu/compositor_thread.cc b/content/renderer/gpu/compositor_thread.cc new file mode 100644 index 0000000..e2ca18e --- /dev/null +++ b/content/renderer/gpu/compositor_thread.cc @@ -0,0 +1,114 @@ +// Copyright (c) 2011 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/renderer/gpu/compositor_thread.h" + +#include "base/bind.h" +#include "content/renderer/gpu/input_event_filter.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositor.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositorClient.h" + +using WebKit::WebCompositor; +using WebKit::WebInputEvent; + +//------------------------------------------------------------------------------ + +class CompositorThread::CompositorWrapper : public WebKit::WebCompositorClient { + public: + CompositorWrapper(CompositorThread* compositor_thread, + int routing_id, + WebKit::WebCompositor* compositor) + : compositor_thread_(compositor_thread), + routing_id_(routing_id), + compositor_(compositor) { + compositor_->setClient(this); + } + + virtual ~CompositorWrapper() { + compositor_->setClient(NULL); + } + + int routing_id() const { return routing_id_; } + WebKit::WebCompositor* compositor() const { return compositor_; } + + // WebCompositorClient methods: + + virtual void willShutdown() { + compositor_thread_->RemoveCompositor(routing_id_); + } + + virtual void didHandleInputEvent() { + compositor_thread_->filter_->DidHandleInputEvent(); + } + + virtual void didNotHandleInputEvent(bool send_to_widget) { + compositor_thread_->filter_->DidNotHandleInputEvent(send_to_widget); + } + + private: + CompositorThread* compositor_thread_; + int routing_id_; + WebKit::WebCompositor* compositor_; + + DISALLOW_COPY_AND_ASSIGN(CompositorWrapper); +}; + +//------------------------------------------------------------------------------ + +CompositorThread::CompositorThread(IPC::Channel::Listener* main_listener) + : thread_("Compositor") { + filter_ = + new InputEventFilter(main_listener, + thread_.message_loop()->message_loop_proxy(), + base::Bind(&CompositorThread::HandleInputEvent, + base::Unretained(this))); + WebCompositor::setThread(&thread_); +} + +CompositorThread::~CompositorThread() { +} + +IPC::ChannelProxy::MessageFilter* CompositorThread::GetMessageFilter() const { + return filter_; +} + +void CompositorThread::AddCompositor(int routing_id, int compositor_id) { + if (thread_.message_loop() != MessageLoop::current()) { + thread_.message_loop()->PostTask( + FROM_HERE, + base::Bind(&CompositorThread::AddCompositor, base::Unretained(this), + routing_id, compositor_id)); + return; + } + + WebCompositor* compositor = WebCompositor::fromIdentifier(compositor_id); + if (!compositor) + return; + + filter_->AddRoute(routing_id); + compositors_[routing_id] = + make_linked_ptr(new CompositorWrapper(this, routing_id, compositor)); +} + +void CompositorThread::RemoveCompositor(int routing_id) { + DCHECK(thread_.message_loop() == MessageLoop::current()); + + filter_->RemoveRoute(routing_id); + compositors_.erase(routing_id); +} + +void CompositorThread::HandleInputEvent( + int routing_id, + const WebInputEvent* input_event) { + DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + + CompositorMap::iterator it = compositors_.find(routing_id); + if (it == compositors_.end()) { + // Oops, we no longer have an interested compositor. + filter_->DidNotHandleInputEvent(true); + return; + } + + it->second->compositor()->handleInputEvent(*input_event); +} diff --git a/content/renderer/gpu/compositor_thread.h b/content/renderer/gpu/compositor_thread.h new file mode 100644 index 0000000..dd31c9a --- /dev/null +++ b/content/renderer/gpu/compositor_thread.h @@ -0,0 +1,56 @@ +// Copyright (c) 2011 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_RENDERER_GPU_COMPOSITOR_THREAD_H_ +#define CONTENT_RENDERER_GPU_COMPOSITOR_THREAD_H_ + +#include <map> + +#include "base/memory/linked_ptr.h" +#include "ipc/ipc_channel_proxy.h" +#include "webkit/glue/webthread_impl.h" + +namespace WebKit { +class WebInputEvent; +} + +class InputEventFilter; + +// The CompositorThread class manages the background thread for the compositor. +// The CompositorThread instance can be assumed to outlive the background +// thread it manages. +class CompositorThread { + public: + // |main_listener| refers to the central IPC message listener that lives on + // the main thread, where all incoming IPC messages are first handled. + explicit CompositorThread(IPC::Channel::Listener* main_listener); + ~CompositorThread(); + + // This MessageFilter should be added to allow input events to be redirected + // to the compositor's thread. + IPC::ChannelProxy::MessageFilter* GetMessageFilter() const; + + // Callable from the main thread or the compositor's thread. + void AddCompositor(int routing_id, int compositor_id); + + private: + // Callback only from the compositor's thread. + void RemoveCompositor(int routing_id); + + // Called from the compositor's thread. + void HandleInputEvent(int routing_id, + const WebKit::WebInputEvent* input_event); + + class CompositorWrapper; + friend class CompositorWrapper; + + typedef std::map<int, // routing_id + linked_ptr<CompositorWrapper> > CompositorMap; + CompositorMap compositors_; + + webkit_glue::WebThreadImpl thread_; + scoped_refptr<InputEventFilter> filter_; +}; + +#endif // CONTENT_RENDERER_GPU_COMPOSITOR_THREAD_H_ diff --git a/content/renderer/gpu/input_event_filter.cc b/content/renderer/gpu/input_event_filter.cc new file mode 100644 index 0000000..a0129ea --- /dev/null +++ b/content/renderer/gpu/input_event_filter.cc @@ -0,0 +1,145 @@ +// Copyright (c) 2011 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/bind.h" +#include "base/location.h" +#include "content/common/view_messages.h" +#include "content/renderer/gpu/input_event_filter.h" + +using WebKit::WebInputEvent; + +InputEventFilter::InputEventFilter(IPC::Channel::Listener* main_listener, + base::MessageLoopProxy* target_loop, + const Handler& handler) + : main_loop_(base::MessageLoopProxy::current()), + main_listener_(main_listener), + sender_(NULL), + target_loop_(target_loop), + handler_(handler) { + DCHECK(target_loop_); + DCHECK(!handler_.is_null()); +} + +void InputEventFilter::AddRoute(int routing_id) { + base::AutoLock locked(routes_lock_); + routes_.insert(routing_id); +} + +void InputEventFilter::RemoveRoute(int routing_id) { + base::AutoLock locked(routes_lock_); + routes_.erase(routing_id); +} + +void InputEventFilter::DidHandleInputEvent() { + DCHECK(target_loop_->BelongsToCurrentThread()); + + bool processed = true; + SendACK(messages_.front(), processed); + messages_.pop(); +} + +void InputEventFilter::DidNotHandleInputEvent(bool send_to_widget) { + DCHECK(target_loop_->BelongsToCurrentThread()); + + if (send_to_widget) { + // Forward to the renderer thread, and dispatch the message there. + main_loop_->PostTask( + FROM_HERE, + base::Bind(&InputEventFilter::ForwardToMainListener, + this, messages_.front())); + } else { + bool processed = false; + SendACK(messages_.front(), processed); + } + messages_.pop(); +} + +void InputEventFilter::OnFilterAdded(IPC::Channel* channel) { + io_loop_ = base::MessageLoopProxy::current(); + sender_ = channel; +} + +void InputEventFilter::OnFilterRemoved() { + sender_ = NULL; +} + +void InputEventFilter::OnChannelClosing() { + sender_ = NULL; +} + +bool InputEventFilter::OnMessageReceived(const IPC::Message& message) { + if (message.type() != ViewMsg_HandleInputEvent::ID) + return false; + + { + base::AutoLock locked(routes_lock_); + if (routes_.find(message.routing_id()) == routes_.end()) + return false; + } + + const WebInputEvent* event = CrackMessage(message); + if (event->type == WebInputEvent::Undefined) + return false; + + target_loop_->PostTask( + FROM_HERE, + base::Bind(&InputEventFilter::ForwardToHandler, this, message)); + return true; +} + +// static +const WebInputEvent* InputEventFilter::CrackMessage( + const IPC::Message& message) { + DCHECK(message.type() == ViewMsg_HandleInputEvent::ID); + + void* iter = NULL; + const char* data; + int data_length; + if (!message.ReadData(&iter, &data, &data_length)) + return NULL; + + return reinterpret_cast<const WebInputEvent*>(data); +} + +InputEventFilter::~InputEventFilter() { +} + +void InputEventFilter::ForwardToMainListener(const IPC::Message& message) { + main_listener_->OnMessageReceived(message); +} + +void InputEventFilter::ForwardToHandler(const IPC::Message& message) { + DCHECK(target_loop_->BelongsToCurrentThread()); + + // Save this message for later, in case we need to bounce it back up to the + // main listener. + // + // TODO(darin): Change RenderWidgetHost to always require an ACK before + // sending the next input event. This way we can nuke this queue. + // + messages_.push(message); + + handler_.Run(message.routing_id(), CrackMessage(message)); +} + +void InputEventFilter::SendACK(const IPC::Message& message, bool processed) { + DCHECK(target_loop_->BelongsToCurrentThread()); + + io_loop_->PostTask( + FROM_HERE, + base::Bind(&InputEventFilter::SendACKOnIOThread, this, + message.routing_id(), CrackMessage(message)->type, processed)); +} + +void InputEventFilter::SendACKOnIOThread(int routing_id, + WebInputEvent::Type event_type, + bool processed) { + DCHECK(io_loop_->BelongsToCurrentThread()); + + if (!sender_) + return; // Filter was removed. + + sender_->Send( + new ViewHostMsg_HandleInputEvent_ACK(routing_id, event_type, processed)); +} diff --git a/content/renderer/gpu/input_event_filter.h b/content/renderer/gpu/input_event_filter.h new file mode 100644 index 0000000..6eaa56d --- /dev/null +++ b/content/renderer/gpu/input_event_filter.h @@ -0,0 +1,91 @@ +// Copyright (c) 2011 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_RENDERER_GPU_INPUT_EVENT_FILTER_H_ +#define CONTENT_RENDERER_GPU_INPUT_EVENT_FILTER_H_ + +#include <queue> +#include <set> + +#include "base/callback.h" +#include "base/synchronization/lock.h" +#include "ipc/ipc_channel_proxy.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" + +// This class can be used to intercept ViewMsg_HandleInputEvent messages +// and have them be delivered to a target thread. Input events are filtered +// based on routing_id (see AddRoute and RemoveRoute). +// +// The user of this class provides an instance of InputEventFilter::Handler, +// which will be passed WebInputEvents on the target thread. +// +class InputEventFilter : public IPC::ChannelProxy::MessageFilter { + public: + typedef base::Callback<void(int /*routing_id*/, + const WebKit::WebInputEvent*)> Handler; + + // The |handler| is invoked on the thread associated with |target_loop| to + // handle input events matching the filtered routes. In response, the + // handler should call either DidHandleInputEvent or DidNotHandleInputEvent. + // These may be called asynchronously to the handler invocation, but they + // must be called on the target thread. + // + // If DidNotHandleInputEvent is called with send_to_widget set to true, then + // the original ViewMsg_HandleInputEvent message will be delivered to + // |main_listener| on the main thread. (The "main thread" in this context is + // the thread where the InputEventFilter was constructed.) If send_to_widget + // is true, then a ViewHostMsg_HandleInputEvent_ACK will not be generated, + // leaving that responsibility up to the eventual handler on the main thread. + // + InputEventFilter(IPC::Channel::Listener* main_listener, + base::MessageLoopProxy* target_loop, + const Handler& handler); + + // Define the message routes to be filtered. + void AddRoute(int routing_id); + void RemoveRoute(int routing_id); + + // Called on the target thread by the Handler. + void DidHandleInputEvent(); + void DidNotHandleInputEvent(bool send_to_widget); + + // IPC::ChannelProxy::MessageFilter methods: + virtual void OnFilterAdded(IPC::Channel* channel); + virtual void OnFilterRemoved(); + virtual void OnChannelClosing(); + virtual bool OnMessageReceived(const IPC::Message& message); + + // Expects a ViewMsg_HandleInputEvent message. + static const WebKit::WebInputEvent* CrackMessage(const IPC::Message& message); + + private: + friend class IPC::ChannelProxy::MessageFilter; + virtual ~InputEventFilter(); + + void ForwardToMainListener(const IPC::Message& message); + void ForwardToHandler(const IPC::Message& message); + void SendACK(const IPC::Message& message, bool processed); + void SendACKOnIOThread(int routing_id, WebKit::WebInputEvent::Type event_type, + bool processed); + + scoped_refptr<base::MessageLoopProxy> main_loop_; + IPC::Channel::Listener* main_listener_; + + // The sender_ only gets invoked on the thread corresponding to io_loop_. + scoped_refptr<base::MessageLoopProxy> io_loop_; + IPC::Message::Sender* sender_; + + // The handler_ only gets Run on the thread corresponding to target_loop_. + scoped_refptr<base::MessageLoopProxy> target_loop_; + Handler handler_; + std::queue<IPC::Message> messages_; + + // Protects access to routes_. + base::Lock routes_lock_; + + // Indicates the routing_ids for which input events should be filtered. + std::set<int> routes_; +}; + +#endif // CONTENT_RENDERER_GPU_INPUT_EVENT_FILTER_H_ diff --git a/content/renderer/gpu/input_event_filter_unittest.cc b/content/renderer/gpu/input_event_filter_unittest.cc new file mode 100644 index 0000000..0923caf --- /dev/null +++ b/content/renderer/gpu/input_event_filter_unittest.cc @@ -0,0 +1,218 @@ +// Copyright (c) 2011 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 <new> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/message_loop.h" +#include "content/common/view_messages.h" +#include "content/renderer/gpu/input_event_filter.h" +#include "ipc/ipc_test_sink.h" +#include "testing/gtest/include/gtest/gtest.h" + +using WebKit::WebInputEvent; +using WebKit::WebMouseEvent; + +namespace { + +const int kTestRoutingID = 13; + +class InputEventRecorder { + public: + InputEventRecorder() + : filter_(NULL), + handle_events_(false), + send_to_widget_(false) { + } + + void set_filter(InputEventFilter* filter) { filter_ = filter; } + void set_handle_events(bool value) { handle_events_ = value; } + void set_send_to_widget(bool value) { send_to_widget_ = value; } + + size_t record_count() const { return records_.size(); } + + const WebInputEvent* record_at(size_t i) const { + const Record& record = records_[i]; + return reinterpret_cast<const WebInputEvent*>(&record.event_data[0]); + } + + void Clear() { + records_.clear(); + } + + void HandleInputEvent(int routing_id, const WebInputEvent* event) { + DCHECK_EQ(kTestRoutingID, routing_id); + + records_.push_back(Record(event)); + + if (handle_events_) { + filter_->DidHandleInputEvent(); + } else { + filter_->DidNotHandleInputEvent(send_to_widget_); + } + } + + private: + struct Record { + Record(const WebInputEvent* event) { + const char* ptr = reinterpret_cast<const char*>(event); + event_data.assign(ptr, ptr + event->size); + } + std::vector<char> event_data; + }; + + InputEventFilter* filter_; + bool handle_events_; + bool send_to_widget_; + std::vector<Record> records_; +}; + +class IPCMessageRecorder : public IPC::Channel::Listener { + public: + virtual bool OnMessageReceived(const IPC::Message& message) { + messages_.push_back(message); + return true; + } + + size_t message_count() const { return messages_.size(); } + + const IPC::Message& message_at(size_t i) const { + return messages_[i]; + } + + void Clear() { + messages_.clear(); + } + + private: + std::vector<IPC::Message> messages_; +}; + +void InitMouseEvent(WebMouseEvent* event, WebInputEvent::Type type, + int x, int y) { + new (event) WebMouseEvent(); + event->type = type; + event->x = x; + event->y = y; +} + +void AddEventsToFilter(IPC::ChannelProxy::MessageFilter* message_filter, + const WebMouseEvent events[], + size_t count) { + for (size_t i = 0; i < count; ++i) { + ViewMsg_HandleInputEvent message(kTestRoutingID); + message.WriteData(reinterpret_cast<const char*>(&events[i]), + events[i].size); + message_filter->OnMessageReceived(message); + } + + MessageLoop::current()->RunAllPending(); +} + +} // namespace + +TEST(InputEventFilterTest, Basic) { + MessageLoop message_loop; + + // Used to record IPCs sent by the filter to the RenderWidgetHost. + IPC::TestSink ipc_sink; + + // Used to record IPCs forwarded by the filter to the main thread. + IPCMessageRecorder message_recorder; + + // Used to record WebInputEvents delivered to the handler. + InputEventRecorder event_recorder; + + scoped_refptr<InputEventFilter> filter = + new InputEventFilter(&message_recorder, + message_loop.message_loop_proxy(), + base::Bind(&InputEventRecorder::HandleInputEvent, + base::Unretained(&event_recorder))); + event_recorder.set_filter(filter); + + filter->OnFilterAdded(&ipc_sink); + + WebMouseEvent kEvents[3]; + InitMouseEvent(&kEvents[0], WebInputEvent::MouseDown, 10, 10); + InitMouseEvent(&kEvents[1], WebInputEvent::MouseMove, 20, 20); + InitMouseEvent(&kEvents[2], WebInputEvent::MouseUp, 30, 30); + + AddEventsToFilter(filter, kEvents, arraysize(kEvents)); + EXPECT_EQ(0U, ipc_sink.message_count()); + EXPECT_EQ(0U, event_recorder.record_count()); + EXPECT_EQ(0U, message_recorder.message_count()); + + filter->AddRoute(kTestRoutingID); + + AddEventsToFilter(filter, kEvents, arraysize(kEvents)); + ASSERT_EQ(arraysize(kEvents), ipc_sink.message_count()); + ASSERT_EQ(arraysize(kEvents), event_recorder.record_count()); + EXPECT_EQ(0U, message_recorder.message_count()); + + for (size_t i = 0; i < arraysize(kEvents); ++i) { + const IPC::Message* message = ipc_sink.GetMessageAt(i); + EXPECT_EQ(kTestRoutingID, message->routing_id()); + EXPECT_EQ(ViewHostMsg_HandleInputEvent_ACK::ID, message->type()); + + WebInputEvent::Type event_type = WebInputEvent::Undefined; + bool processed = false; + EXPECT_TRUE(ViewHostMsg_HandleInputEvent_ACK::Read(message, &event_type, + &processed)); + EXPECT_EQ(kEvents[i].type, event_type); + EXPECT_FALSE(processed); + + const WebInputEvent* event = event_recorder.record_at(i); + ASSERT_TRUE(event); + + EXPECT_EQ(kEvents[i].size, event->size); + EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0); + } + + event_recorder.set_send_to_widget(true); + + AddEventsToFilter(filter, kEvents, arraysize(kEvents)); + EXPECT_EQ(arraysize(kEvents), ipc_sink.message_count()); + EXPECT_EQ(2 * arraysize(kEvents), event_recorder.record_count()); + EXPECT_EQ(arraysize(kEvents), message_recorder.message_count()); + + for (size_t i = 0; i < arraysize(kEvents); ++i) { + const IPC::Message& message = message_recorder.message_at(i); + + ASSERT_EQ(ViewMsg_HandleInputEvent::ID, message.type()); + const WebInputEvent* event = InputEventFilter::CrackMessage(message); + + EXPECT_EQ(kEvents[i].size, event->size); + EXPECT_TRUE(memcmp(&kEvents[i], event, event->size) == 0); + } + + // Now reset everything, and test that DidHandleInputEvent is called. + + ipc_sink.ClearMessages(); + event_recorder.Clear(); + message_recorder.Clear(); + + event_recorder.set_handle_events(true); + + AddEventsToFilter(filter, kEvents, arraysize(kEvents)); + EXPECT_EQ(arraysize(kEvents), ipc_sink.message_count()); + EXPECT_EQ(arraysize(kEvents), event_recorder.record_count()); + EXPECT_EQ(0U, message_recorder.message_count()); + + for (size_t i = 0; i < arraysize(kEvents); ++i) { + const IPC::Message* message = ipc_sink.GetMessageAt(i); + EXPECT_EQ(kTestRoutingID, message->routing_id()); + EXPECT_EQ(ViewHostMsg_HandleInputEvent_ACK::ID, message->type()); + + WebInputEvent::Type event_type = WebInputEvent::Undefined; + bool processed = false; + EXPECT_TRUE(ViewHostMsg_HandleInputEvent_ACK::Read(message, &event_type, + &processed)); + EXPECT_EQ(kEvents[i].type, event_type); + EXPECT_TRUE(processed); + } + + filter->OnFilterRemoved(); +} diff --git a/content/renderer/render_thread.cc b/content/renderer/render_thread.cc index cc39b76..efd9d98 100644 --- a/content/renderer/render_thread.cc +++ b/content/renderer/render_thread.cc @@ -37,6 +37,7 @@ #include "content/public/renderer/render_process_observer.h" #include "content/public/renderer/render_view_visitor.h" #include "content/renderer/devtools_agent_filter.h" +#include "content/renderer/gpu/compositor_thread.h" #include "content/renderer/gpu/gpu_channel_host.h" #include "content/renderer/indexed_db_dispatcher.h" #include "content/renderer/media/audio_input_message_filter.h" @@ -239,6 +240,11 @@ RenderThread::~RenderThread() { if (file_thread_.get()) file_thread_->Stop(); + if (compositor_thread_.get()) { + RemoveFilter(compositor_thread_->GetMessageFilter()); + compositor_thread_.reset(); + } + if (webkit_platform_support_.get()) WebKit::shutdown(); @@ -439,6 +445,9 @@ void RenderThread::EnsureWebKitInitialized() { webkit_platform_support_.reset(new RendererWebKitPlatformSupportImpl); WebKit::initialize(webkit_platform_support_.get()); + compositor_thread_.reset(new CompositorThread(this)); + AddFilter(compositor_thread_->GetMessageFilter()); + WebScriptController::enableV8SingleThreadMode(); const CommandLine& command_line = *CommandLine::ForCurrentProcess(); diff --git a/content/renderer/render_thread.h b/content/renderer/render_thread.h index 88bd1e8..4520490 100644 --- a/content/renderer/render_thread.h +++ b/content/renderer/render_thread.h @@ -25,6 +25,7 @@ class AppCacheDispatcher; class AudioInputMessageFilter; class AudioMessageFilter; +class CompositorThread; class DBMessageFilter; class DevToolsAgentFilter; class FilePath; @@ -147,6 +148,10 @@ class CONTENT_EXPORT RenderThread : public RenderThreadBase, void DoNotSuspendWebKitSharedTimer(); void DoNotNotifyWebKitOfModalLoop(); + CompositorThread* compositor_thread() const { + return compositor_thread_.get(); + } + AppCacheDispatcher* appcache_dispatcher() const { return appcache_dispatcher_.get(); } @@ -279,6 +284,8 @@ class CONTENT_EXPORT RenderThread : public RenderThreadBase, // Map of registered v8 extensions. The key is the extension name. std::set<std::string> v8_extensions_; + scoped_ptr<CompositorThread> compositor_thread_; + ObserverList<content::RenderProcessObserver> observers_; DISALLOW_COPY_AND_ASSIGN(RenderThread); diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 5d7cf89..1378a8a 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -466,9 +466,9 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) { if (!processed && is_keyboard_shortcut) suppress_next_char_events_ = true; - IPC::Message* response = new ViewHostMsg_HandleInputEvent_ACK(routing_id_); - response->WriteInt(input_event->type); - response->WriteBool(processed); + IPC::Message* response = + new ViewHostMsg_HandleInputEvent_ACK(routing_id_, input_event->type, + processed); if ((input_event->type == WebInputEvent::MouseMove || input_event->type == WebInputEvent::MouseWheel || diff --git a/webkit/glue/webthread_impl.h b/webkit/glue/webthread_impl.h index d337cfd..50869d6 100644 --- a/webkit/glue/webthread_impl.h +++ b/webkit/glue/webthread_impl.h @@ -22,6 +22,8 @@ class WebThreadImpl : public WebKit::WebThread { virtual void postDelayedTask(Task* task, int64 delay_ms); #endif + MessageLoop* message_loop() const { return thread_->message_loop(); } + protected: scoped_ptr<base::Thread> thread_; }; |