blob: d4d93b313af8c3530c897c88a9d7624a006311a8 [file] [log] [blame]
// 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 <memory>
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/test/test_simple_task_runner.h"
#include "base/time/time.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 "mojo/public/cpp/bindings/interface_request.h"
#include "services/ui/public/cpp/tests/test_window.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/scheduler/test/fake_renderer_scheduler.h"
#include "ui/events/blink/did_overscroll_params.h"
#include "ui/events/event_utils.h"
#include "ui/events/mojo/event.mojom.h"
#include "ui/events/mojo/event_constants.mojom.h"
#include "ui/events/mojo/keyboard_codes.mojom.h"
using ui::mojom::EventResult;
namespace content {
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_(EventResult::UNHANDLED) {}
bool called() { return called_; }
EventResult result() { return result_; }
void ResultCallback(EventResult result) {
called_ = true;
result_ = result;
}
private:
friend class base::RefCounted<TestCallback>;
~TestCallback() {}
bool called_;
EventResult 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 InputHandlerManager {
public:
TestInputHandlerManager(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
InputHandlerManagerClient* client,
blink::scheduler::RendererScheduler* renderer_scheduler)
: InputHandlerManager(task_runner, client, nullptr, renderer_scheduler),
override_result_(false),
result_(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(InputEventAckState result);
// InputHandlerManager:
void HandleInputEvent(int routing_id,
blink::WebScopedInputEvent input_event,
const ui::LatencyInfo& latency_info,
const InputEventAckStateCallback& callback) override;
private:
// If true InputHandlerManager::HandleInputEvent is not called.
bool override_result_;
// The result to return in HandleInputEvent if |override_result_|.
InputEventAckState result_;
DISALLOW_COPY_AND_ASSIGN(TestInputHandlerManager);
};
void TestInputHandlerManager::ClearHandleInputEventOverride() {
override_result_ = false;
}
void TestInputHandlerManager::SetHandleInputEventResult(
InputEventAckState result) {
override_result_ = true;
result_ = result;
}
void TestInputHandlerManager::HandleInputEvent(
int routing_id,
blink::WebScopedInputEvent input_event,
const ui::LatencyInfo& latency_info,
const InputEventAckStateCallback& callback) {
if (override_result_) {
callback.Run(result_, std::move(input_event), latency_info, nullptr);
return;
}
InputHandlerManager::HandleInputEvent(routing_id, std::move(input_event),
latency_info, callback);
}
// Empty implementation of InputHandlerManagerClient.
class TestInputHandlerManagerClient : public InputHandlerManagerClient {
public:
TestInputHandlerManagerClient() {}
~TestInputHandlerManagerClient() override{};
// InputHandlerManagerClient:
void SetInputHandlerManager(
InputHandlerManager* input_handler_manager) override {}
void RegisterRoutingID(int routing_id) override {}
void UnregisterRoutingID(int routing_id) override {}
void DidOverscroll(int routing_id,
const ui::DidOverscrollParams& params) override {}
void DidStopFlinging(int routing_id) override {}
void DispatchNonBlockingEventToMainThread(
int routing_id,
blink::WebScopedInputEvent event,
const ui::LatencyInfo& latency_info) override {}
void NotifyInputEventHandled(int routing_id,
blink::WebInputEvent::Type type,
InputEventAckState ack_result) override {}
void ProcessRafAlignedInput(int routing_id) override {}
private:
DISALLOW_COPY_AND_ASSIGN(TestInputHandlerManagerClient);
};
// Implementation of RenderWidget for testing, performs no initialization.
class TestRenderWidget : public RenderWidget {
public:
explicit TestRenderWidget(CompositorDependencies* compositor_deps)
: RenderWidget(1,
compositor_deps,
blink::WebPopupTypeNone,
ScreenInfo(),
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 RenderWidgetInputHandler {
public:
TestRenderWidgetInputHandler(RenderWidget* render_widget);
~TestRenderWidgetInputHandler() override {}
void set_delegate(RenderWidgetInputHandlerDelegate* delegate) {
delegate_ = delegate;
}
void set_state(InputEventAckState state) { state_ = state; }
// RenderWidgetInputHandler:
void HandleInputEvent(const blink::WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
InputEventDispatchType dispatch_type) override;
private:
// The input delegate which receives event acks.
RenderWidgetInputHandlerDelegate* delegate_;
// The result of input handling to send to |delegate_| during the ack.
InputEventAckState state_;
DISALLOW_COPY_AND_ASSIGN(TestRenderWidgetInputHandler);
};
TestRenderWidgetInputHandler::TestRenderWidgetInputHandler(
RenderWidget* render_widget)
: RenderWidgetInputHandler(render_widget, render_widget),
delegate_(nullptr),
state_(InputEventAckState::INPUT_EVENT_ACK_STATE_UNKNOWN) {}
void TestRenderWidgetInputHandler::HandleInputEvent(
const blink::WebInputEvent& input_event,
const ui::LatencyInfo& latency_info,
InputEventDispatchType dispatch_type) {
if (delegate_) {
std::unique_ptr<InputEventAck> ack(new InputEventAck(
InputEventAckSource::COMPOSITOR_THREAD, input_event.type(), state_));
delegate_->OnInputEventAck(std::move(ack));
}
}
} // namespace
// 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 {}
// Returns a valid key event, so that it can be converted to a web event by
// CompositorMusConnection.
std::unique_ptr<ui::Event> GenerateKeyEvent();
// Calls CompositorMusConnection::OnWindowInputEvent.
void OnWindowInputEvent(
ui::Window* window,
const ui::Event& event,
std::unique_ptr<base::Callback<void(EventResult)>>* 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_;
blink::scheduler::FakeRendererScheduler renderer_scheduler_;
MockRenderThread render_thread_;
scoped_refptr<TestRenderWidget> render_widget_;
mojo::InterfaceRequest<ui::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_|.
std::unique_ptr<TestInputHandlerManager> input_handler_manager_;
std::unique_ptr<TestRenderWidgetInputHandler> render_widget_input_handler_;
DISALLOW_COPY_AND_ASSIGN(CompositorMusConnectionTest);
};
std::unique_ptr<ui::Event> CompositorMusConnectionTest::GenerateKeyEvent() {
return std::unique_ptr<ui::Event>(new ui::KeyEvent(
ui::ET_KEY_PRESSED, ui::KeyboardCode::VKEY_A, ui::EF_NONE));
}
void CompositorMusConnectionTest::OnWindowInputEvent(
ui::Window* window,
const ui::Event& event,
std::unique_ptr<base::Callback<void(EventResult)>>* ack_callback) {
compositor_connection_->OnWindowInputEvent(window, 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);
ui::TestWindow test_window;
std::unique_ptr<ui::Event> event(GenerateKeyEvent());
scoped_refptr<TestCallback> test_callback(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback)));
OnWindowInputEvent(&test_window, *event.get(), &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_EQ(EventResult::UNHANDLED, 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);
ui::TestWindow test_window;
std::unique_ptr<ui::Event> event(GenerateKeyEvent());
scoped_refptr<TestCallback> test_callback(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback)));
OnWindowInputEvent(&test_window, *event.get(), &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_EQ(EventResult::HANDLED, 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) {
ui::TestWindow test_window;
std::unique_ptr<ui::Event> event1(GenerateKeyEvent());
scoped_refptr<TestCallback> test_callback1(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback1(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback1)));
OnWindowInputEvent(&test_window, *event1.get(), &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);
std::unique_ptr<ui::Event> event2(GenerateKeyEvent());
scoped_refptr<TestCallback> test_callback2(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback2(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback2)));
OnWindowInputEvent(&test_window, *event2.get(), &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_EQ(EventResult::HANDLED, test_callback2->result());
}
// Tests that when an input handler consumes the event, that
// CompositorMusConnection will consume the ack, but call as UNHANDLED.
TEST_F(CompositorMusConnectionTest, InputHandlerConsumes) {
input_handler_manager()->SetHandleInputEventResult(
InputEventAckState::INPUT_EVENT_ACK_STATE_CONSUMED);
ui::TestWindow test_window;
std::unique_ptr<ui::Event> event(GenerateKeyEvent());
scoped_refptr<TestCallback> test_callback(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback)));
OnWindowInputEvent(&test_window, *event.get(), &ack_callback);
EXPECT_FALSE(ack_callback.get());
VerifyAndRunQueues(false, false);
EXPECT_TRUE(test_callback->called());
EXPECT_EQ(EventResult::UNHANDLED, test_callback->result());
}
// Tests that when the renderer will not ack an event, that
// CompositorMusConnection will consume the ack, but call as UNHANDLED.
TEST_F(CompositorMusConnectionTest, RendererWillNotSendAck) {
ui::TestWindow test_window;
ui::PointerEvent event(
ui::ET_POINTER_DOWN, gfx::Point(), gfx::Point(), ui::EF_NONE, 0, 0,
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE),
ui::EventTimeForNow());
scoped_refptr<TestCallback> test_callback(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback)));
OnWindowInputEvent(&test_window, event, &ack_callback);
EXPECT_FALSE(ack_callback.get());
VerifyAndRunQueues(true, false);
EXPECT_TRUE(test_callback->called());
EXPECT_EQ(EventResult::UNHANDLED, test_callback->result());
}
// Tests that when a touch event id provided, that CompositorMusConnection
// consumes the ack.
TEST_F(CompositorMusConnectionTest, TouchEventConsumed) {
TestRenderWidgetInputHandler* input_handler = render_widget_input_handler();
input_handler->set_delegate(connection());
input_handler->set_state(InputEventAckState::INPUT_EVENT_ACK_STATE_CONSUMED);
ui::TestWindow test_window;
ui::PointerEvent event(
ui::ET_POINTER_DOWN, gfx::Point(), gfx::Point(), ui::EF_NONE, 0, 0,
ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH),
ui::EventTimeForNow());
scoped_refptr<TestCallback> test_callback(new TestCallback);
std::unique_ptr<base::Callback<void(EventResult)>> ack_callback(
new base::Callback<void(EventResult)>(
base::Bind(&TestCallback::ResultCallback, test_callback)));
OnWindowInputEvent(&test_window, 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_EQ(EventResult::HANDLED, test_callback->result());
}
} // namespace content