// Copyright (c) 2012 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 <string>
#include "content/common/view_messages.h"
#include "content/public/test/render_view_test.h"
#include "content/renderer/mouse_lock_dispatcher.h"
#include "content/renderer/render_view_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
namespace content {
namespace {
class MockLockTarget : public MouseLockDispatcher::LockTarget {
public:
MOCK_METHOD1(OnLockMouseACK, void(bool));
MOCK_METHOD0(OnMouseLockLost, void());
MOCK_METHOD1(HandleMouseLockedInputEvent,
bool(const blink::WebMouseEvent&));
};
// MouseLockDispatcher is a RenderViewObserver, and we test it by creating a
// fixture containing a RenderViewImpl view() and interacting to that interface.
class MouseLockDispatcherTest : public RenderViewTest {
public:
virtual void SetUp() {
RenderViewTest::SetUp();
route_id_ = view()->GetRoutingID();
target_ = new MockLockTarget();
alternate_target_ = new MockLockTarget();
}
virtual void TearDown() {
RenderViewTest::TearDown();
delete target_;
delete alternate_target_;
}
protected:
RenderViewImpl* view() { return static_cast<RenderViewImpl*>(view_); }
MouseLockDispatcher* dispatcher() { return view()->mouse_lock_dispatcher(); }
int route_id_;
MockLockTarget* target_;
MockLockTarget* alternate_target_;
};
} // namespace
// Test simple use of RenderViewImpl interface to WebKit for pointer lock.
TEST_F(MouseLockDispatcherTest, BasicWebWidget) {
// Start unlocked.
EXPECT_FALSE(view()->isPointerLocked());
// Lock.
EXPECT_TRUE(view()->requestPointerLock());
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, true));
EXPECT_TRUE(view()->isPointerLocked());
// Unlock.
view()->requestPointerUnlock();
view()->OnMessageReceived(ViewMsg_MouseLockLost(route_id_));
EXPECT_FALSE(view()->isPointerLocked());
// Attempt a lock, and have it fail.
EXPECT_TRUE(view()->requestPointerLock());
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, false));
EXPECT_FALSE(view()->isPointerLocked());
}
// Test simple use of MouseLockDispatcher with a mock LockTarget.
TEST_F(MouseLockDispatcherTest, BasicMockLockTarget) {
::testing::InSequence expect_calls_in_sequence;
EXPECT_CALL(*target_, OnLockMouseACK(true));
EXPECT_CALL(*target_, HandleMouseLockedInputEvent(_));
EXPECT_CALL(*target_, OnMouseLockLost());
EXPECT_CALL(*target_, OnLockMouseACK(false));
// Start unlocked.
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(NULL));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
// Lock.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, true));
EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
// Receive mouse event.
dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
// Unlock.
dispatcher()->UnlockMouse(target_);
view()->OnMessageReceived(ViewMsg_MouseLockLost(route_id_));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
// Attempt a lock, and have it fail.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, false));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
}
// Test deleting a target while it is in use by MouseLockDispatcher.
TEST_F(MouseLockDispatcherTest, DeleteAndUnlock) {
::testing::InSequence expect_calls_in_sequence;
EXPECT_CALL(*target_, OnLockMouseACK(true));
EXPECT_CALL(*target_, HandleMouseLockedInputEvent(_)).Times(0);
EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
// Lock.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, true));
EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
// Unlock, with a deleted target.
// Don't receive mouse events or lock lost.
dispatcher()->OnLockTargetDestroyed(target_);
delete target_;
target_ = NULL;
dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
view()->OnMessageReceived(ViewMsg_MouseLockLost(route_id_));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
}
// Test deleting a target that is pending a lock request response.
TEST_F(MouseLockDispatcherTest, DeleteWithPendingLockSuccess) {
::testing::InSequence expect_calls_in_sequence;
EXPECT_CALL(*target_, OnLockMouseACK(true)).Times(0);
EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
// Lock request.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
// Before receiving response delete the target.
dispatcher()->OnLockTargetDestroyed(target_);
delete target_;
target_ = NULL;
// Lock response.
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, true));
}
// Test deleting a target that is pending a lock request failure response.
TEST_F(MouseLockDispatcherTest, DeleteWithPendingLockFail) {
::testing::InSequence expect_calls_in_sequence;
EXPECT_CALL(*target_, OnLockMouseACK(true)).Times(0);
EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
// Lock request.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
// Before receiving response delete the target.
dispatcher()->OnLockTargetDestroyed(target_);
delete target_;
target_ = NULL;
// Lock response.
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, false));
}
// Test not receiving mouse events when a target is not locked.
TEST_F(MouseLockDispatcherTest, MouseEventsNotReceived) {
::testing::InSequence expect_calls_in_sequence;
EXPECT_CALL(*target_, HandleMouseLockedInputEvent(_)).Times(0);
EXPECT_CALL(*target_, OnLockMouseACK(true));
EXPECT_CALL(*target_, HandleMouseLockedInputEvent(_));
EXPECT_CALL(*target_, OnMouseLockLost());
EXPECT_CALL(*target_, HandleMouseLockedInputEvent(_)).Times(0);
// (Don't) receive mouse event.
dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
// Lock.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, true));
EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
// Receive mouse event.
dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
// Unlock.
dispatcher()->UnlockMouse(target_);
view()->OnMessageReceived(ViewMsg_MouseLockLost(route_id_));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
// (Don't) receive mouse event.
dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
}
// Test multiple targets
TEST_F(MouseLockDispatcherTest, MultipleTargets) {
::testing::InSequence expect_calls_in_sequence;
EXPECT_CALL(*target_, OnLockMouseACK(true));
EXPECT_CALL(*target_, HandleMouseLockedInputEvent(_));
EXPECT_CALL(*alternate_target_, HandleMouseLockedInputEvent(_)).Times(0);
EXPECT_CALL(*target_, OnMouseLockLost()).Times(0);
EXPECT_CALL(*alternate_target_, OnMouseLockLost()).Times(0);
EXPECT_CALL(*target_, OnMouseLockLost());
// Lock request for target.
EXPECT_TRUE(dispatcher()->LockMouse(target_));
// Fail attempt to lock alternate.
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(alternate_target_));
EXPECT_FALSE(dispatcher()->LockMouse(alternate_target_));
// Lock completion for target.
view()->OnMessageReceived(ViewMsg_LockMouse_ACK(route_id_, true));
EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
// Fail attempt to lock alternate.
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(alternate_target_));
EXPECT_FALSE(dispatcher()->LockMouse(alternate_target_));
// Receive mouse event to only one target.
dispatcher()->WillHandleMouseEvent(blink::WebMouseEvent());
// Unlock alternate target has no effect.
dispatcher()->UnlockMouse(alternate_target_);
EXPECT_TRUE(dispatcher()->IsMouseLockedTo(target_));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(alternate_target_));
// Though the call to UnlockMouse should not unlock any target, we will
// cause an unlock (as if e.g. user escaped mouse lock) and verify the
// correct target is unlocked.
view()->OnMessageReceived(ViewMsg_MouseLockLost(route_id_));
EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_));
}
} // namespace content