|  | // 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: | 
|  | void SetUp() override { | 
|  | RenderViewTest::SetUp(); | 
|  | route_id_ = view()->GetRoutingID(); | 
|  | target_ = new MockLockTarget(); | 
|  | alternate_target_ = new MockLockTarget(); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | 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 |