diff options
author | jonross <jonross@chromium.org> | 2016-03-08 06:03:26 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-08 14:04:36 +0000 |
commit | c3385cc179280dd4b09733b2954be7e9ae3b5112 (patch) | |
tree | fcb8a119477033e1e18f3b8ca5b4bd0b0445b7ea | |
parent | 1d3dd7dbbeea929087e089ae1e8acdaa4fae80aa (diff) | |
download | chromium_src-c3385cc179280dd4b09733b2954be7e9ae3b5112.zip chromium_src-c3385cc179280dd4b09733b2954be7e9ae3b5112.tar.gz chromium_src-c3385cc179280dd4b09733b2954be7e9ae3b5112.tar.bz2 |
Update WindowTree::OnWindowInputEventAck to include handled
To support post-target accelerators we plan to have the client application report whether an input event was handled or not, when ack-ing the event.
This change updates WindowTree::OnWindowInputEventAck to include a boolean parameter |handled|.
PlatformWindowMus will always mark events as handled. CompositorMusConnection will base the handled state on the ack state of the renderer.
TEST=WindowTreeClientImplTest, WindowTreeApptest, WindowTreeTest, CompositorMusConnectionTest
BUG=560478
Review URL: https://codereview.chromium.org/1749323002
Cr-Commit-Position: refs/heads/master@{#379821}
20 files changed, 555 insertions, 56 deletions
diff --git a/components/mus/public/cpp/input_event_handler.h b/components/mus/public/cpp/input_event_handler.h index ebd1597..597653b 100644 --- a/components/mus/public/cpp/input_event_handler.h +++ b/components/mus/public/cpp/input_event_handler.h @@ -16,11 +16,14 @@ class Window; class InputEventHandler { public: // The event handler can asynchronously ack the event by taking ownership of - // the |ack_callback|. If the handler does not take ownership of the callback, - // then WindowTreeClientImpl will ack the event. - virtual void OnWindowInputEvent(Window* target, - mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) = 0; + // the |ack_callback|. The callback takes a bool representing whether the + // handler has consumed the event. If the handler does not take ownership of + // the callback, then WindowTreeClientImpl will ack the event as not consumed. + virtual void OnWindowInputEvent( + Window* target, + mojom::EventPtr event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) = 0; + protected: virtual ~InputEventHandler() {} }; diff --git a/components/mus/public/cpp/lib/window_tree_client_impl.cc b/components/mus/public/cpp/lib/window_tree_client_impl.cc index cdcd7b6..7c0a477 100644 --- a/components/mus/public/cpp/lib/window_tree_client_impl.cc +++ b/components/mus/public/cpp/lib/window_tree_client_impl.cc @@ -820,17 +820,21 @@ void WindowTreeClientImpl::OnWindowInputEvent(uint32_t event_id, mojom::EventPtr event) { Window* window = GetWindowById(window_id); if (!window || !window->input_event_handler_) { - tree_->OnWindowInputEventAck(event_id); + tree_->OnWindowInputEventAck(event_id, false); return; } - scoped_ptr<base::Closure> ack_callback( - new base::Closure(base::Bind(&mojom::WindowTree::OnWindowInputEventAck, - base::Unretained(tree_), event_id))); + scoped_ptr<base::Callback<void(bool)>> ack_callback( + new base::Callback<void(bool)>( + base::Bind(&mojom::WindowTree::OnWindowInputEventAck, + base::Unretained(tree_), event_id))); window->input_event_handler_->OnWindowInputEvent(window, std::move(event), &ack_callback); + + // The handler did not take ownership of the callback, so we send the ack, + // marking the event as not consumed. if (ack_callback) - ack_callback->Run(); + ack_callback->Run(false); } void WindowTreeClientImpl::OnWindowFocused(Id focused_window_id) { diff --git a/components/mus/public/cpp/tests/test_window_tree.cc b/components/mus/public/cpp/tests/test_window_tree.cc index 398a661..7301bd9 100644 --- a/components/mus/public/cpp/tests/test_window_tree.cc +++ b/components/mus/public/cpp/tests/test_window_tree.cc @@ -132,7 +132,7 @@ void TestWindowTree::SetImeVisibility(uint32_t window_id, bool visible, mojo::TextInputStatePtr state) {} -void TestWindowTree::OnWindowInputEventAck(uint32_t event_id) { +void TestWindowTree::OnWindowInputEventAck(uint32_t event_id, bool handled) { EXPECT_FALSE(acked_events_.count(event_id)); acked_events_.insert(event_id); } diff --git a/components/mus/public/cpp/tests/test_window_tree.h b/components/mus/public/cpp/tests/test_window_tree.h index d392341..daaebb30 100644 --- a/components/mus/public/cpp/tests/test_window_tree.h +++ b/components/mus/public/cpp/tests/test_window_tree.h @@ -87,7 +87,7 @@ class TestWindowTree : public mojom::WindowTree { void SetImeVisibility(uint32_t window_id, bool visible, mojo::TextInputStatePtr state) override; - void OnWindowInputEventAck(uint32_t event_id) override; + void OnWindowInputEventAck(uint32_t event_id, bool handled) override; void GetWindowManagerClient( mojo::AssociatedInterfaceRequest<mojom::WindowManagerClient> internal) override; diff --git a/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc b/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc index 69c498b..b822257 100644 --- a/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc +++ b/components/mus/public/cpp/tests/window_tree_client_impl_unittest.cc @@ -27,6 +27,12 @@ #include "ui/events/event_utils.h" #include "ui/gfx/geometry/rect.h" +namespace { + +void DoNothingBool(bool result) {} + +} // namespace + namespace mus { mojo::Array<uint8_t> Int32ToPropertyTransportValue(int32_t value) { @@ -122,21 +128,22 @@ class TestInputEventHandler : public InputEventHandler { void AckEvent() { DCHECK(should_manually_ack_); DCHECK(!ack_callback_.is_null()); - ack_callback_.Run(); - ack_callback_ = base::Closure(); + ack_callback_.Run(true); + ack_callback_ = base::Bind(&::DoNothingBool); } void Reset() { received_event_ = false; - ack_callback_ = base::Closure(); + ack_callback_ = base::Bind(&::DoNothingBool); } bool received_event() const { return received_event_; } private: // InputEventHandler: - void OnWindowInputEvent(Window* target, - mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) override { + void OnWindowInputEvent( + Window* target, + mojom::EventPtr event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) override { EXPECT_FALSE(received_event_) << "Observer was not reset after receiving event."; received_event_ = true; @@ -148,7 +155,7 @@ class TestInputEventHandler : public InputEventHandler { bool received_event_; bool should_manually_ack_; - base::Closure ack_callback_; + base::Callback<void(bool)> ack_callback_; DISALLOW_COPY_AND_ASSIGN(TestInputEventHandler); }; diff --git a/components/mus/public/interfaces/window_tree.mojom b/components/mus/public/interfaces/window_tree.mojom index 5114297..09d2972 100644 --- a/components/mus/public/interfaces/window_tree.mojom +++ b/components/mus/public/interfaces/window_tree.mojom @@ -246,7 +246,7 @@ interface WindowTree { SetImeVisibility(uint32 window_id, bool visible, mojo.TextInputState? state); // See documentation for WindowTreeClient::OnWindowInputEvent(). - OnWindowInputEventAck(uint32 event_id); + OnWindowInputEventAck(uint32 event_id, bool handled); // See description of WindowManager for details. GetWindowManagerClient(associated WindowManagerClient& internal); @@ -350,8 +350,9 @@ interface WindowTreeClient { // Invoked when an event is targeted at the specified window. The client must // call WindowTree::OnWindowInputEventAck() with the same |event_id| to notify - // that the event has been processed. The client will not receive farther - // events until the event is ack'ed. + // that the event has been processed, and with a boolean |handled| to notify + // if the event was consumed. The client will not receive farther events until + // the event is ack'ed. OnWindowInputEvent(uint32 event_id, uint32 window, Event event); OnWindowFocused(uint32 focused_window_id); diff --git a/components/mus/ws/window_tree.cc b/components/mus/ws/window_tree.cc index 7782e8b..0356a8e 100644 --- a/components/mus/ws/window_tree.cc +++ b/components/mus/ws/window_tree.cc @@ -1145,7 +1145,7 @@ void WindowTree::SetImeVisibility(Id transport_window_id, } } -void WindowTree::OnWindowInputEventAck(uint32_t event_id) { +void WindowTree::OnWindowInputEventAck(uint32_t event_id, bool handled) { if (event_ack_id_ == 0 || event_id != event_ack_id_) { // TODO(sad): Something bad happened. Kill the client? NOTIMPLEMENTED() << "Wrong event acked."; diff --git a/components/mus/ws/window_tree.h b/components/mus/ws/window_tree.h index 378186d..0c103e7 100644 --- a/components/mus/ws/window_tree.h +++ b/components/mus/ws/window_tree.h @@ -344,7 +344,7 @@ class WindowTree : public mojom::WindowTree, void SetImeVisibility(Id transport_window_id, bool visible, mojo::TextInputStatePtr state) override; - void OnWindowInputEventAck(uint32_t event_id) override; + void OnWindowInputEventAck(uint32_t event_id, bool handled) override; void SetClientArea( Id transport_window_id, mojo::InsetsPtr insets, diff --git a/components/mus/ws/window_tree_apptest.cc b/components/mus/ws/window_tree_apptest.cc index 8970435..b143ab0 100644 --- a/components/mus/ws/window_tree_apptest.cc +++ b/components/mus/ws/window_tree_apptest.cc @@ -355,7 +355,7 @@ class TestWindowTreeClientImpl : public mojom::WindowTreeClient, // Ack input events to clear the state on the server. These can be received // during test startup. X11Window::DispatchEvent sends a synthetic move // event to notify of entry. - tree()->OnWindowInputEventAck(event_id); + tree()->OnWindowInputEventAck(event_id, true); // Don't log input events as none of the tests care about them and they // may come in at random points. } diff --git a/components/mus/ws/window_tree_unittest.cc b/components/mus/ws/window_tree_unittest.cc index 17f59eb..99d88fe 100644 --- a/components/mus/ws/window_tree_unittest.cc +++ b/components/mus/ws/window_tree_unittest.cc @@ -198,7 +198,7 @@ class WindowTreeTest : public testing::Test { void AckPreviousEvent() { DisplayTestApi test_api(display_); while (test_api.tree_awaiting_input_ack()) - test_api.tree_awaiting_input_ack()->OnWindowInputEventAck(0); + test_api.tree_awaiting_input_ack()->OnWindowInputEventAck(0, true); } void DispatchEventAndAckImmediately(const ui::Event& event) { diff --git a/content/renderer/input/input_handler_manager.h b/content/renderer/input/input_handler_manager.h index c8bfb75..a77a67e 100644 --- a/content/renderer/input/input_handler_manager.h +++ b/content/renderer/input/input_handler_manager.h @@ -47,7 +47,7 @@ class InputHandlerManager { const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, InputHandlerManagerClient* client, scheduler::RendererScheduler* renderer_scheduler); - ~InputHandlerManager(); + virtual ~InputHandlerManager(); // Callable from the main thread only. void AddInputHandler(int routing_id, @@ -68,9 +68,10 @@ class InputHandlerManager { void RemoveInputHandler(int routing_id); // Called from the compositor's thread. - InputEventAckState HandleInputEvent(int routing_id, - const blink::WebInputEvent* input_event, - ui::LatencyInfo* latency_info); + virtual InputEventAckState HandleInputEvent( + int routing_id, + const blink::WebInputEvent* input_event, + ui::LatencyInfo* latency_info); // Called from the compositor's thread. void DidOverscroll(int routing_id, const DidOverscrollParams& params); diff --git a/content/renderer/input/render_widget_input_handler.h b/content/renderer/input/render_widget_input_handler.h index 3be968c..a82968b 100644 --- a/content/renderer/input/render_widget_input_handler.h +++ b/content/renderer/input/render_widget_input_handler.h @@ -34,12 +34,12 @@ class CONTENT_EXPORT RenderWidgetInputHandler { public: RenderWidgetInputHandler(RenderWidgetInputHandlerDelegate* delegate, RenderWidget* widget); - ~RenderWidgetInputHandler(); + virtual ~RenderWidgetInputHandler(); // Handle input events from the input event provider. - void HandleInputEvent(const blink::WebInputEvent& input_event, - const ui::LatencyInfo& latency_info, - InputEventDispatchType dispatch_type); + virtual void HandleInputEvent(const blink::WebInputEvent& input_event, + const ui::LatencyInfo& latency_info, + InputEventDispatchType dispatch_type); // Handle overscroll from Blink. void DidOverscrollFromBlink( diff --git a/content/renderer/mus/compositor_mus_connection.cc b/content/renderer/mus/compositor_mus_connection.cc index 6973de6..f469f56 100644 --- a/content/renderer/mus/compositor_mus_connection.cc +++ b/content/renderer/mus/compositor_mus_connection.cc @@ -11,6 +11,12 @@ #include "mojo/converters/blink/blink_input_events_type_converters.h" #include "ui/events/latency_info.h" +namespace { + +void DoNothingBool(bool result) {} + +} // namespace + namespace content { CompositorMusConnection::CompositorMusConnection( @@ -71,21 +77,22 @@ void CompositorMusConnection::OnConnectionLostOnMainThread() { void CompositorMusConnection::OnWindowInputEventOnMainThread( scoped_ptr<blink::WebInputEvent> web_event, - const base::Closure& ack) { + const base::Callback<void(bool)>& ack) { DCHECK(main_task_runner_->BelongsToCurrentThread()); RenderWidgetMusConnection* connection = RenderWidgetMusConnection::Get(routing_id_); if (!connection) { - ack.Run(); + ack.Run(false); return; } connection->OnWindowInputEvent(std::move(web_event), ack); } void CompositorMusConnection::OnWindowInputEventAckOnMainThread( - const base::Closure& ack) { + const base::Callback<void(bool)>& ack, + bool handled) { DCHECK(main_task_runner_->BelongsToCurrentThread()); - compositor_task_runner_->PostTask(FROM_HERE, ack); + compositor_task_runner_->PostTask(FROM_HERE, base::Bind(ack, handled)); } void CompositorMusConnection::OnConnectionLost( @@ -109,7 +116,7 @@ void CompositorMusConnection::OnEmbed(mus::Window* root) { void CompositorMusConnection::OnWindowInputEvent( mus::Window* window, mus::mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) { + scoped_ptr<base::Callback<void(bool)>>* ack_callback) { DCHECK(compositor_task_runner_->BelongsToCurrentThread()); scoped_ptr<blink::WebInputEvent> web_event = event.To<scoped_ptr<blink::WebInputEvent>>(); @@ -117,9 +124,11 @@ void CompositorMusConnection::OnWindowInputEvent( ui::LatencyInfo info; InputEventAckState ack_state = input_handler_manager_->HandleInputEvent( routing_id_, web_event.get(), &info); + // TODO(jonross): We probably need to ack the event based on the consumed + // state. if (ack_state != INPUT_EVENT_ACK_STATE_NOT_CONSUMED) return; - base::Closure ack = base::Bind(&base::DoNothing); + base::Callback<void(bool)> ack = base::Bind(&::DoNothingBool); const bool send_ack = WebInputEventTraits::WillReceiveAckFromRenderer(*web_event); if (send_ack) { diff --git a/content/renderer/mus/compositor_mus_connection.h b/content/renderer/mus/compositor_mus_connection.h index 7b0f696..a2c377e 100644 --- a/content/renderer/mus/compositor_mus_connection.h +++ b/content/renderer/mus/compositor_mus_connection.h @@ -42,6 +42,7 @@ class CompositorMusConnection scoped_ptr<mus::WindowSurfaceBinding> surface_binding); private: + friend class CompositorMusConnectionTest; friend class base::RefCountedThreadSafe<CompositorMusConnection>; ~CompositorMusConnection() override; @@ -56,18 +57,20 @@ class CompositorMusConnection void OnWindowInputEventOnMainThread( scoped_ptr<blink::WebInputEvent> web_event, - const base::Closure& ack); + const base::Callback<void(bool)>& ack); - void OnWindowInputEventAckOnMainThread(const base::Closure& ack); + void OnWindowInputEventAckOnMainThread(const base::Callback<void(bool)>& ack, + bool handled); // WindowTreeDelegate implementation: void OnConnectionLost(mus::WindowTreeConnection* connection) override; void OnEmbed(mus::Window* root) override; // InputEventHandler implementation: - void OnWindowInputEvent(mus::Window* window, - mus::mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) override; + void OnWindowInputEvent( + mus::Window* window, + mus::mojom::EventPtr event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) override; const int routing_id_; mus::Window* root_; diff --git a/content/renderer/mus/compositor_mus_connection_unittest.cc b/content/renderer/mus/compositor_mus_connection_unittest.cc new file mode 100644 index 0000000..0b75f2f --- /dev/null +++ b/content/renderer/mus/compositor_mus_connection_unittest.cc @@ -0,0 +1,466 @@ +// Copyright 2016 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/mus/compositor_mus_connection.h" + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/test_simple_task_runner.h" +#include "base/time/time.h" +#include "components/mus/public/cpp/tests/test_window.h" +#include "components/mus/public/interfaces/input_event_constants.mojom.h" +#include "components/mus/public/interfaces/input_events.mojom.h" +#include "components/mus/public/interfaces/input_key_codes.mojom.h" +#include "content/common/input/did_overscroll_params.h" +#include "content/common/input/input_event_ack.h" +#include "content/common/input/input_event_ack_state.h" +#include "content/public/test/mock_render_thread.h" +#include "content/renderer/input/input_handler_manager.h" +#include "content/renderer/input/input_handler_manager_client.h" +#include "content/renderer/input/render_widget_input_handler.h" +#include "content/renderer/mus/render_widget_mus_connection.h" +#include "content/renderer/render_widget.h" +#include "content/test/fake_compositor_dependencies.h" +#include "content/test/fake_renderer_scheduler.h" +#include "mojo/public/cpp/bindings/interface_request.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Wrapper for the callback provided to +// CompositorMusConnection:OnWindowInputEvent. This tracks whether the it was +// called, along with the result. +class TestCallback : public base::RefCounted<TestCallback> { + public: + TestCallback() : called_(false), result_(false) {} + + bool called() { return called_; } + bool result() { return result_; } + + void BoolCallback(bool result) { + called_ = true; + result_ = result; + } + + private: + friend class base::RefCounted<TestCallback>; + + ~TestCallback() {} + + bool called_; + bool result_; + + DISALLOW_COPY_AND_ASSIGN(TestCallback); +}; + +// Allows for overriding the behaviour of HandleInputEvent, to simulate input +// handlers which consume events before they are sent to the renderer. +class TestInputHandlerManager : public content::InputHandlerManager { + public: + TestInputHandlerManager( + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, + content::InputHandlerManagerClient* client, + scheduler::RendererScheduler* renderer_scheduler) + : InputHandlerManager(task_runner, client, renderer_scheduler), + override_result_(false), + result_(content::InputEventAckState::INPUT_EVENT_ACK_STATE_UNKNOWN) {} + ~TestInputHandlerManager() override {} + + // Stops overriding the behaviour of HandleInputEvent + void ClearHandleInputEventOverride(); + + // Overrides the behaviour of HandleInputEvent, returing |result|. + void SetHandleInputEventResult(content::InputEventAckState result); + + // content::InputHandlerManager: + content::InputEventAckState HandleInputEvent( + int routing_id, + const blink::WebInputEvent* input_event, + ui::LatencyInfo* latency_info) override; + + private: + // If true content::InputHandlerManager::HandleInputEvent is not called. + bool override_result_; + + // The result to return in HandleInputEvent if |override_result_|. + content::InputEventAckState result_; + + DISALLOW_COPY_AND_ASSIGN(TestInputHandlerManager); +}; + +void TestInputHandlerManager::ClearHandleInputEventOverride() { + override_result_ = false; +} + +void TestInputHandlerManager::SetHandleInputEventResult( + content::InputEventAckState result) { + override_result_ = true; + result_ = result; +} + +content::InputEventAckState TestInputHandlerManager::HandleInputEvent( + int routing_id, + const blink::WebInputEvent* input_event, + ui::LatencyInfo* latency_info) { + if (override_result_) + return result_; + return content::InputHandlerManager::HandleInputEvent(routing_id, input_event, + latency_info); +} + +// Empty implementation of InputHandlerManagerClient. +class TestInputHandlerManagerClient + : public content::InputHandlerManagerClient { + public: + TestInputHandlerManagerClient() {} + ~TestInputHandlerManagerClient() override{}; + + // content::InputHandlerManagerClient: + void SetBoundHandler(const Handler& handler) override {} + void DidAddInputHandler( + int routing_id, + ui::SynchronousInputHandlerProxy* synchronous_handler) override {} + void DidRemoveInputHandler(int routing_id) override {} + void DidOverscroll(int routing_id, + const content::DidOverscrollParams& params) override {} + void DidStopFlinging(int routing_id) override {} + void NonBlockingInputEventHandled(int routing_id, + blink::WebInputEvent::Type type) override {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestInputHandlerManagerClient); +}; + +// Implementation of RenderWidget for testing, performs no initialization. +class TestRenderWidget : public content::RenderWidget { + public: + explicit TestRenderWidget(content::CompositorDependencies* compositor_deps) + : content::RenderWidget(compositor_deps, + blink::WebPopupTypeNone, + blink::WebScreenInfo(), + true, + false, + false) {} + + protected: + ~TestRenderWidget() override {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestRenderWidget); +}; + +// Test override of RenderWidgetInputHandler to allow the control of +// HandleInputEvent. This will perform no actions on input until a +// RenderWidgetInputHandlerDelegate is set. Once set this will always ack +// received events. +class TestRenderWidgetInputHandler : public content::RenderWidgetInputHandler { + public: + TestRenderWidgetInputHandler(content::RenderWidget* render_widget); + ~TestRenderWidgetInputHandler() override {} + + void set_delegate(content::RenderWidgetInputHandlerDelegate* delegate) { + delegate_ = delegate; + } + void set_state(content::InputEventAckState state) { state_ = state; } + + // content::RenderWidgetInputHandler: + void HandleInputEvent(const blink::WebInputEvent& input_event, + const ui::LatencyInfo& latency_info, + content::InputEventDispatchType dispatch_type) override; + + private: + // The input delegate which receives event acks. + content::RenderWidgetInputHandlerDelegate* delegate_; + + // The result of input handling to send to |delegate_| during the ack. + content::InputEventAckState state_; + + DISALLOW_COPY_AND_ASSIGN(TestRenderWidgetInputHandler); +}; + +TestRenderWidgetInputHandler::TestRenderWidgetInputHandler( + content::RenderWidget* render_widget) + : content::RenderWidgetInputHandler(render_widget, render_widget), + delegate_(nullptr), + state_(content::InputEventAckState::INPUT_EVENT_ACK_STATE_UNKNOWN) {} + +void TestRenderWidgetInputHandler::HandleInputEvent( + const blink::WebInputEvent& input_event, + const ui::LatencyInfo& latency_info, + content::InputEventDispatchType dispatch_type) { + if (delegate_) { + scoped_ptr<content::InputEventAck> ack( + new content::InputEventAck(input_event.type, state_)); + delegate_->OnInputEventAck(std::move(ack)); + } +} + +} // namespace + +namespace content { + +// Test suite for CompositorMusConnection, this does not setup a full renderer +// environment. This does not establish a connection to a mus server, nor does +// it initialize one. +class CompositorMusConnectionTest : public testing::Test { + public: + CompositorMusConnectionTest() {} + ~CompositorMusConnectionTest() override {} + + // Initializes |event| with valid parameters for a key event, so that it can + // be converted to a web event by CompositorMusConnection. + void GenerateKeyEvent(mus::mojom::EventPtr& event); + + // Calls CompositorMusConnection::OnWindowInputEvent. + void OnWindowInputEvent(mus::Window* window, + mus::mojom::EventPtr event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback); + + // Confirms the state of pending tasks enqueued on each task runner, and runs + // until idle. + void VerifyAndRunQueues(bool main_task_runner_enqueued, + bool compositor_task_runner_enqueued); + + CompositorMusConnection* compositor_connection() { + return compositor_connection_.get(); + } + RenderWidgetMusConnection* connection() { return connection_; } + TestInputHandlerManager* input_handler_manager() { + return input_handler_manager_.get(); + } + TestRenderWidgetInputHandler* render_widget_input_handler() { + return render_widget_input_handler_.get(); + } + + // testing::Test: + void SetUp() override; + void TearDown() override; + + private: + // Mocks/Fakes of the testing environment. + TestInputHandlerManagerClient input_handler_manager_client_; + FakeCompositorDependencies compositor_dependencies_; + FakeRendererScheduler renderer_scheduler_; + MockRenderThread render_thread_; + scoped_refptr<TestRenderWidget> render_widget_; + mojo::InterfaceRequest<mus::mojom::WindowTreeClient> request_; + + // Not owned, RenderWidgetMusConnection tracks in static state. Cleared during + // TearDown. + RenderWidgetMusConnection* connection_; + + // Test versions of task runners, see VerifyAndRunQueues to use in testing. + scoped_refptr<base::TestSimpleTaskRunner> main_task_runner_; + scoped_refptr<base::TestSimpleTaskRunner> compositor_task_runner_; + + // Actual CompositorMusConnection for testing. + scoped_refptr<CompositorMusConnection> compositor_connection_; + + // Test implementations, to control input given to |compositor_connection_|. + scoped_ptr<TestInputHandlerManager> input_handler_manager_; + scoped_ptr<TestRenderWidgetInputHandler> render_widget_input_handler_; + + DISALLOW_COPY_AND_ASSIGN(CompositorMusConnectionTest); +}; + +void CompositorMusConnectionTest::GenerateKeyEvent( + mus::mojom::EventPtr& event) { + event->action = mus::mojom::EventType::KEY_PRESSED; + event->time_stamp = base::TimeTicks::Now().ToInternalValue(); + event->key_data = mus::mojom::KeyData::New(); + event->key_data->is_char = true; + event->key_data->windows_key_code = mus::mojom::KeyboardCode::A; +} + +void CompositorMusConnectionTest::OnWindowInputEvent( + mus::Window* window, + mus::mojom::EventPtr event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) { + compositor_connection_->OnWindowInputEvent(window, std::move(event), + ack_callback); +} + +void CompositorMusConnectionTest::VerifyAndRunQueues( + bool main_task_runner_enqueued, + bool compositor_task_runner_enqueued) { + // Run through the enqueued actions. + EXPECT_EQ(main_task_runner_enqueued, main_task_runner_->HasPendingTask()); + main_task_runner_->RunUntilIdle(); + + EXPECT_EQ(compositor_task_runner_enqueued, + compositor_task_runner_->HasPendingTask()); + compositor_task_runner_->RunUntilIdle(); +} + +void CompositorMusConnectionTest::SetUp() { + testing::Test::SetUp(); + + main_task_runner_ = new base::TestSimpleTaskRunner(); + compositor_task_runner_ = new base::TestSimpleTaskRunner(); + + input_handler_manager_.reset(new TestInputHandlerManager( + compositor_task_runner_, &input_handler_manager_client_, + &renderer_scheduler_)); + + const int routing_id = 42; + compositor_connection_ = new CompositorMusConnection( + routing_id, main_task_runner_, compositor_task_runner_, + std::move(request_), input_handler_manager_.get()); + + // CompositorMusConnection attempts to create connection to the non-existant + // server. Clear that. + compositor_task_runner_->ClearPendingTasks(); + + render_widget_ = new TestRenderWidget(&compositor_dependencies_); + render_widget_input_handler_.reset( + new TestRenderWidgetInputHandler(render_widget_.get())); + connection_ = RenderWidgetMusConnection::GetOrCreate(routing_id); + connection_->SetInputHandler(render_widget_input_handler_.get()); +} + +void CompositorMusConnectionTest::TearDown() { + // Clear static state. + connection_->OnConnectionLost(); + testing::Test::TearDown(); +} + +// Tests that for events which the renderer will ack, yet not consume, that +// CompositorMusConnection consumes the ack during OnWindowInputEvent, and calls +// it with the correct state once processed. +TEST_F(CompositorMusConnectionTest, NotConsumed) { + TestRenderWidgetInputHandler* input_handler = render_widget_input_handler(); + input_handler->set_delegate(connection()); + input_handler->set_state( + InputEventAckState::INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + + mus::TestWindow test_window; + mus::mojom::EventPtr event = mus::mojom::Event::New(); + GenerateKeyEvent(event); + scoped_refptr<TestCallback> test_callback(new TestCallback); + scoped_ptr<base::Callback<void(bool)>> ack_callback( + new base::Callback<void(bool)>( + base::Bind(&::TestCallback::BoolCallback, test_callback))); + + OnWindowInputEvent(&test_window, std::move(event), &ack_callback); + // OnWindowInputEvent is expected to clear the callback if it plans on + // handling the ack. + EXPECT_FALSE(ack_callback.get()); + + VerifyAndRunQueues(true, true); + + // The ack callback should have been called + EXPECT_TRUE(test_callback->called()); + EXPECT_FALSE(test_callback->result()); +} + +// Tests that for events which the renderer will ack, and consume, that +// CompositorMusConnection consumes the ack during OnWindowInputEvent, and calls +// it with the correct state once processed. +TEST_F(CompositorMusConnectionTest, Consumed) { + TestRenderWidgetInputHandler* input_handler = render_widget_input_handler(); + input_handler->set_delegate(connection()); + input_handler->set_state(InputEventAckState::INPUT_EVENT_ACK_STATE_CONSUMED); + + mus::TestWindow test_window; + mus::mojom::EventPtr event = mus::mojom::Event::New(); + GenerateKeyEvent(event); + scoped_refptr<TestCallback> test_callback(new TestCallback); + scoped_ptr<base::Callback<void(bool)>> ack_callback( + new base::Callback<void(bool)>( + base::Bind(&::TestCallback::BoolCallback, test_callback))); + + OnWindowInputEvent(&test_window, std::move(event), &ack_callback); + // OnWindowInputEvent is expected to clear the callback if it plans on + // handling the ack. + EXPECT_FALSE(ack_callback.get()); + + VerifyAndRunQueues(true, true); + + // The ack callback should have been called + EXPECT_TRUE(test_callback->called()); + EXPECT_TRUE(test_callback->result()); +} + +// Tests that when the RenderWidgetInputHandler does not ack before a new event +// arrives, that only the most recent ack is fired. +TEST_F(CompositorMusConnectionTest, LostAck) { + mus::TestWindow test_window; + mus::mojom::EventPtr event1 = mus::mojom::Event::New(); + GenerateKeyEvent(event1); + scoped_refptr<TestCallback> test_callback1(new TestCallback); + scoped_ptr<base::Callback<void(bool)>> ack_callback1( + new base::Callback<void(bool)>( + base::Bind(&::TestCallback::BoolCallback, test_callback1))); + + OnWindowInputEvent(&test_window, std::move(event1), &ack_callback1); + EXPECT_FALSE(ack_callback1.get()); + // When simulating the timeout the ack is never enqueued + VerifyAndRunQueues(true, false); + + // Setting a delegate will lead to the next event being acked. Having a + // cleared queue simulates the input handler timing out on an event. + TestRenderWidgetInputHandler* input_handler = render_widget_input_handler(); + input_handler->set_delegate(connection()); + input_handler->set_state(InputEventAckState::INPUT_EVENT_ACK_STATE_CONSUMED); + + mus::mojom::EventPtr event2 = mus::mojom::Event::New(); + GenerateKeyEvent(event2); + scoped_refptr<TestCallback> test_callback2(new TestCallback); + scoped_ptr<base::Callback<void(bool)>> ack_callback2( + new base::Callback<void(bool)>( + base::Bind(&::TestCallback::BoolCallback, test_callback2))); + OnWindowInputEvent(&test_window, std::move(event2), &ack_callback2); + EXPECT_FALSE(ack_callback2.get()); + + VerifyAndRunQueues(true, true); + + // Only the most recent ack was called. + EXPECT_FALSE(test_callback1->called()); + EXPECT_TRUE(test_callback2->called()); + EXPECT_TRUE(test_callback2->result()); +} + +// Tests that when an input handler consumes the event, that +// CompositorMusConnection does not consume the ack, nor calls it. +TEST_F(CompositorMusConnectionTest, InputHandlerConsumes) { + input_handler_manager()->SetHandleInputEventResult( + InputEventAckState::INPUT_EVENT_ACK_STATE_CONSUMED); + mus::TestWindow test_window; + mus::mojom::EventPtr event = mus::mojom::Event::New(); + GenerateKeyEvent(event); + scoped_refptr<TestCallback> test_callback(new TestCallback); + scoped_ptr<base::Callback<void(bool)>> ack_callback( + new base::Callback<void(bool)>( + base::Bind(&::TestCallback::BoolCallback, test_callback))); + + OnWindowInputEvent(&test_window, std::move(event), &ack_callback); + + EXPECT_TRUE(ack_callback.get()); + VerifyAndRunQueues(false, false); + EXPECT_FALSE(test_callback->called()); +} + +// Tests that when the renderer will not ack an event, that +// CompositorMusConnection does not consume the ack, nor calls it. +TEST_F(CompositorMusConnectionTest, RendererWillNotSendAck) { + mus::TestWindow test_window; + mus::mojom::EventPtr event = mus::mojom::Event::New(); + event->action = mus::mojom::EventType::POINTER_DOWN; + event->time_stamp = base::TimeTicks::Now().ToInternalValue(); + event->pointer_data = mus::mojom::PointerData::New(); + + scoped_refptr<TestCallback> test_callback(new TestCallback); + scoped_ptr<base::Callback<void(bool)>> ack_callback( + new base::Callback<void(bool)>( + base::Bind(&::TestCallback::BoolCallback, test_callback))); + + OnWindowInputEvent(&test_window, std::move(event), &ack_callback); + EXPECT_TRUE(ack_callback.get()); + + VerifyAndRunQueues(true, false); + EXPECT_FALSE(test_callback->called()); +} + +} // namespace content diff --git a/content/renderer/mus/render_widget_mus_connection.cc b/content/renderer/mus/render_widget_mus_connection.cc index c2dc1d1..bced0df 100644 --- a/content/renderer/mus/render_widget_mus_connection.cc +++ b/content/renderer/mus/render_widget_mus_connection.cc @@ -118,8 +118,8 @@ void RenderWidgetMusConnection::OnDidOverscroll( void RenderWidgetMusConnection::OnInputEventAck( scoped_ptr<InputEventAck> input_event_ack) { DCHECK(!pending_ack_.is_null()); - // TODO(fsamuel): Use the state in |input_event_ack|. - pending_ack_.Run(); + pending_ack_.Run(input_event_ack->state == + InputEventAckState::INPUT_EVENT_ACK_STATE_CONSUMED); pending_ack_.Reset(); } @@ -161,12 +161,12 @@ void RenderWidgetMusConnection::OnConnectionLost() { void RenderWidgetMusConnection::OnWindowInputEvent( scoped_ptr<blink::WebInputEvent> input_event, - const base::Closure& ack) { + const base::Callback<void(bool)>& ack) { DCHECK(thread_checker_.CalledOnValidThread()); // If we don't yet have a RenderWidgetInputHandler then we don't yet have // an initialized RenderWidget. if (!input_handler_) { - ack.Run(); + ack.Run(false); return; } // TODO(fsamuel): It would be nice to add this DCHECK but the reality is an diff --git a/content/renderer/mus/render_widget_mus_connection.h b/content/renderer/mus/render_widget_mus_connection.h index 26f1b4b..1c9bc03 100644 --- a/content/renderer/mus/render_widget_mus_connection.h +++ b/content/renderer/mus/render_widget_mus_connection.h @@ -33,6 +33,7 @@ class RenderWidgetMusConnection : public RenderWidgetInputHandlerDelegate { private: friend class CompositorMusConnection; + friend class CompositorMusConnectionTest; explicit RenderWidgetMusConnection(int routing_id); ~RenderWidgetMusConnection() override; @@ -56,14 +57,14 @@ class RenderWidgetMusConnection : public RenderWidgetInputHandlerDelegate { void OnConnectionLost(); void OnWindowInputEvent(scoped_ptr<blink::WebInputEvent> input_event, - const base::Closure& ack); + const base::Callback<void(bool)>& ack); const int routing_id_; RenderWidgetInputHandler* input_handler_; scoped_ptr<mus::WindowSurfaceBinding> window_surface_binding_; scoped_refptr<CompositorMusConnection> compositor_mus_connection_; - base::Closure pending_ack_; + base::Callback<void(bool)> pending_ack_; // Used to verify single threaded access. base::ThreadChecker thread_checker_; diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn index 55e264e..1070e26 100644 --- a/content/test/BUILD.gn +++ b/content/test/BUILD.gn @@ -765,10 +765,13 @@ test("content_unittests") { if (use_aura) { deps += [ + "//content/public/renderer:renderer_sources", + "//content/renderer/mus:mus", "//ui/aura", "//ui/aura_extra", "//ui/wm", ] + sources += [ "../renderer/mus/compositor_mus_connection_unittest.cc" ] } else { sources -= [ "../browser/renderer_host/render_widget_host_view_aura_unittest.cc", diff --git a/ui/views/mus/platform_window_mus.cc b/ui/views/mus/platform_window_mus.cc index 3961390..2a1fc4e 100644 --- a/ui/views/mus/platform_window_mus.cc +++ b/ui/views/mus/platform_window_mus.cc @@ -223,11 +223,11 @@ void PlatformWindowMus::OnRequestClose(mus::Window* window) { void PlatformWindowMus::OnWindowInputEvent( mus::Window* view, mus::mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) { + scoped_ptr<base::Callback<void(bool)>>* ack_callback) { // It's possible dispatching the event will spin a nested message loop. Ack // the callback now, otherwise we appear unresponsive for the life of the // nested message loop. - (*ack_callback)->Run(); + (*ack_callback)->Run(true); ack_callback->reset(); scoped_ptr<ui::Event> ui_event(event.To<scoped_ptr<ui::Event>>()); delegate_->DispatchEvent(ui_event.get()); diff --git a/ui/views/mus/platform_window_mus.h b/ui/views/mus/platform_window_mus.h index 2d95a21..725430c 100644 --- a/ui/views/mus/platform_window_mus.h +++ b/ui/views/mus/platform_window_mus.h @@ -82,9 +82,10 @@ class VIEWS_MUS_EXPORT PlatformWindowMus void OnRequestClose(mus::Window* window) override; // mus::InputEventHandler: - void OnWindowInputEvent(mus::Window* view, - mus::mojom::EventPtr event, - scoped_ptr<base::Closure>* ack_callback) override; + void OnWindowInputEvent( + mus::Window* view, + mus::mojom::EventPtr event, + scoped_ptr<base::Callback<void(bool)>>* ack_callback) override; ui::PlatformWindowDelegate* delegate_; mus::Window* mus_window_; |