diff options
author | scheib@chromium.org <scheib@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-27 07:33:11 +0000 |
---|---|---|
committer | scheib@chromium.org <scheib@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-27 07:33:11 +0000 |
commit | 217690df70c213c482039242dd935786c37dcd3c (patch) | |
tree | 8d0a579099bf69e51866cffb682c28ba44330b4f /content | |
parent | 15f08dde26dbb02c8e4441492ab967cb48fcd4e4 (diff) | |
download | chromium_src-217690df70c213c482039242dd935786c37dcd3c.zip chromium_src-217690df70c213c482039242dd935786c37dcd3c.tar.gz chromium_src-217690df70c213c482039242dd935786c37dcd3c.tar.bz2 |
Mouse Lock is currently supported in Pepper, but not yet supported from WebKit.
Move the render thread logic for managing the mouse lock state out of the pepper_plugin_delegate_impl, and into a higher level dispatcher for render_view_impl.
Handle mouse lock / pointer lock requests from both pepper and webkit (WebKit API not yet landed, small TODOs left in this code to enable once that lands).
BUG=109957
TEST=Pepper examples/mouse_lock and NaCl mouse lock examples still work.
Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=119206
Review URL: http://codereview.chromium.org/8970016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119406 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/renderer/mouse_lock_dispatcher.cc | 117 | ||||
-rw-r--r-- | content/renderer/mouse_lock_dispatcher.h | 87 | ||||
-rw-r--r-- | content/renderer/mouse_lock_dispatcher_browsertest.cc | 232 | ||||
-rw-r--r-- | content/renderer/pepper_plugin_delegate_impl.cc | 177 | ||||
-rw-r--r-- | content/renderer/pepper_plugin_delegate_impl.h | 39 | ||||
-rw-r--r-- | content/renderer/render_view_impl.cc | 75 | ||||
-rw-r--r-- | content/renderer/render_view_impl.h | 16 |
8 files changed, 578 insertions, 167 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 37ee955..7c12475 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -125,6 +125,8 @@ 'renderer/media/video_capture_message_filter.h', 'renderer/mhtml_generator.cc', 'renderer/mhtml_generator.h', + 'renderer/mouse_lock_dispatcher.cc', + 'renderer/mouse_lock_dispatcher.h', 'renderer/notification_provider.cc', 'renderer/notification_provider.h', 'renderer/paint_aggregator.cc', diff --git a/content/renderer/mouse_lock_dispatcher.cc b/content/renderer/mouse_lock_dispatcher.cc new file mode 100644 index 0000000..41ed654 --- /dev/null +++ b/content/renderer/mouse_lock_dispatcher.cc @@ -0,0 +1,117 @@ +// 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 "content/renderer/mouse_lock_dispatcher.h" + +#include "content/common/view_messages.h" +#include "content/renderer/render_view_impl.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebWidget.h" + +MouseLockDispatcher::MouseLockDispatcher(RenderViewImpl* render_view_impl) + : content::RenderViewObserver(render_view_impl), + render_view_impl_(render_view_impl), + mouse_locked_(false), + pending_lock_request_(false), + pending_unlock_request_(false), + target_(NULL) { +} + +MouseLockDispatcher::~MouseLockDispatcher() { +} + +bool MouseLockDispatcher::LockMouse(LockTarget* target) { + if (MouseLockedOrPendingAction()) + return false; + + pending_lock_request_ = true; + target_ = target; + + Send(new ViewHostMsg_LockMouse(routing_id())); + return true; +} + +void MouseLockDispatcher::UnlockMouse(LockTarget* target) { + if (target && target == target_ && !pending_unlock_request_) { + pending_unlock_request_ = true; + Send(new ViewHostMsg_UnlockMouse(routing_id())); + } +} + +void MouseLockDispatcher::OnLockTargetDestroyed(LockTarget* target) { + if (target == target_) { + UnlockMouse(target); + target_ = NULL; + } +} + +bool MouseLockDispatcher::IsMouseLockedTo(LockTarget* target) { + return mouse_locked_ && target_ == target; +} + +bool MouseLockDispatcher::WillHandleMouseEvent( + const WebKit::WebMouseEvent& event) { + if (mouse_locked_ && target_) + return target_->HandleMouseLockedInputEvent(event); + return false; +} + +bool MouseLockDispatcher::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(MouseLockDispatcher, message) + IPC_MESSAGE_HANDLER(ViewMsg_LockMouse_ACK, OnLockMouseACK) + IPC_MESSAGE_HANDLER(ViewMsg_MouseLockLost, OnMouseLockLost) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void MouseLockDispatcher::OnLockMouseACK(bool succeeded) { + DCHECK(!mouse_locked_ && pending_lock_request_); + + mouse_locked_ = succeeded; + pending_lock_request_ = false; + if (pending_unlock_request_ && !succeeded) { + // We have sent an unlock request after the lock request. However, since + // the lock request has failed, the unlock request will be ignored by the + // browser side and there won't be any response to it. + pending_unlock_request_ = false; + } + + LockTarget* last_target = target_; + if (!succeeded) + target_ = NULL; + + // Callbacks made after all state modification to prevent reentrant errors + // such as OnLockMouseACK() synchronously calling LockMouse(). + + if (last_target) + last_target->OnLockMouseACK(succeeded); + + // Mouse Lock removes the system cursor and provides all mouse motion as + // .movementX/Y values on events all sent to a fixed target. This requires + // content to specifically request the mode to be entered. + // Mouse Capture is implicitly given for the duration of a drag event, and + // sends all mouse events to the initial target of the drag. + // If Lock is entered it supercedes any in progress Capture. + if (succeeded && render_view_impl_->webwidget()) + render_view_impl_->webwidget()->mouseCaptureLost(); +} + +void MouseLockDispatcher::OnMouseLockLost() { + DCHECK(mouse_locked_ && !pending_lock_request_); + + mouse_locked_ = false; + pending_unlock_request_ = false; + + LockTarget* last_target = target_; + target_ = NULL; + + // Callbacks made after all state modification to prevent reentrant errors + // such as OnMouseLockLost() synchronously calling LockMouse(). + + if (last_target) + last_target->OnMouseLockLost(); +} + diff --git a/content/renderer/mouse_lock_dispatcher.h b/content/renderer/mouse_lock_dispatcher.h new file mode 100644 index 0000000..8035d09 --- /dev/null +++ b/content/renderer/mouse_lock_dispatcher.h @@ -0,0 +1,87 @@ +// 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. + +#ifndef CONTENT_RENDERER_MOUSE_LOCK_DISPATCHER_H_ +#define CONTENT_RENDERER_MOUSE_LOCK_DISPATCHER_H_ +#pragma once + +#include "base/basictypes.h" +#include "content/public/renderer/render_view_observer.h" + +class RenderViewImpl; + +namespace WebKit { +class WebMouseEvent; +class WebWidget; +} // namespace WebKit + +namespace webkit{ +namespace ppapi { +class PluginInstance; +} // namespace ppapi +} // namespace webkit + +// MouseLockDispatcher is owned by RenderViewImpl. +class CONTENT_EXPORT MouseLockDispatcher : public content::RenderViewObserver { + public: + explicit MouseLockDispatcher(RenderViewImpl* render_view_impl); + virtual ~MouseLockDispatcher(); + + class LockTarget { + public: + virtual ~LockTarget() {} + // A mouse lock request was pending and this reports success or failure. + virtual void OnLockMouseACK(bool succeeded) = 0; + // A mouse lock was in place, but has been lost. + virtual void OnMouseLockLost() = 0; + // A mouse lock is enabled and mouse events are being delievered. + virtual bool HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent& event) = 0; + }; + + // Locks the mouse to the |target|. If true is returned, an asynchronous + // response to target->OnLockMouseACK() will follow. + bool LockMouse(LockTarget* target); + // Request to unlock the mouse. An asynchronous response to + // target->OnMouseLockLost() will follow. + void UnlockMouse(LockTarget* target); + // Clears out the reference to the |target| because it has or is being + // destroyed. Unlocks if locked. The pointer will not be accessed. + void OnLockTargetDestroyed(LockTarget* target); + bool IsMouseLockedTo(LockTarget* target); + + // Allow lock target to consumed a mouse event, if it does return true. + bool WillHandleMouseEvent(const WebKit::WebMouseEvent& event); + + private: + // RenderView::Observer implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + // IPC handlers. + void OnLockMouseACK(bool succeeded); + void OnMouseLockLost(); + + bool MouseLockedOrPendingAction() const { + return mouse_locked_ || pending_lock_request_ || pending_unlock_request_; + } + + RenderViewImpl* render_view_impl_; + + bool mouse_locked_; + // If both |pending_lock_request_| and |pending_unlock_request_| are true, + // it means a lock request was sent before an unlock request and we haven't + // received responses for them. The logic in LockMouse() makes sure that a + // lock request won't be sent when there is a pending unlock request. + bool pending_lock_request_; + bool pending_unlock_request_; + + // |target_| is the pending or current owner of mouse lock. We retain a non + // owning reference here that must be cleared by |OnLockTargetDestroyed| + // when it is destroyed. + LockTarget* target_; + + DISALLOW_COPY_AND_ASSIGN(MouseLockDispatcher); +}; + +#endif // CONTENT_RENDERER_MOUSE_LOCK_DISPATCHER_H_ diff --git a/content/renderer/mouse_lock_dispatcher_browsertest.cc b/content/renderer/mouse_lock_dispatcher_browsertest.cc new file mode 100644 index 0000000..5605aa4 --- /dev/null +++ b/content/renderer/mouse_lock_dispatcher_browsertest.cc @@ -0,0 +1,232 @@ +// 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/renderer/mouse_lock_dispatcher.h" +#include "content/renderer/render_view_impl.h" +#include "content/test/render_view_test.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; + +namespace { + +class MockLockTarget : public MouseLockDispatcher::LockTarget { + public: + MOCK_METHOD1(OnLockMouseACK, void(bool)); + MOCK_METHOD0(OnMouseLockLost, void()); + MOCK_METHOD1(HandleMouseLockedInputEvent, + bool(const WebKit::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 content::RenderViewTest { + public: + virtual void SetUp() { + content::RenderViewTest::SetUp(); + route_id_ = view()->GetRoutingId(); + target_ = new MockLockTarget(); + alternate_target_ = new MockLockTarget(); + } + + virtual void TearDown() { + content::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(WebKit::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(WebKit::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(WebKit::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(WebKit::WebMouseEvent()); + + // Unlock. + dispatcher()->UnlockMouse(target_); + view()->OnMessageReceived(ViewMsg_MouseLockLost(route_id_)); + EXPECT_FALSE(dispatcher()->IsMouseLockedTo(target_)); + + // (Don't) receive mouse event. + dispatcher()->WillHandleMouseEvent(WebKit::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(WebKit::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_)); +} + diff --git a/content/renderer/pepper_plugin_delegate_impl.cc b/content/renderer/pepper_plugin_delegate_impl.cc index f2152b1..063ff69 100644 --- a/content/renderer/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper_plugin_delegate_impl.cc @@ -644,6 +644,29 @@ class PlatformVideoCaptureImpl media::VideoCapture* video_capture_; }; +class PluginInstanceLockTarget : public MouseLockDispatcher::LockTarget { + public: + PluginInstanceLockTarget(webkit::ppapi::PluginInstance* plugin) + : plugin_(plugin) {} + + virtual void OnLockMouseACK(bool succeeded) OVERRIDE { + plugin_->OnLockMouseACK(succeeded); + } + + virtual void OnMouseLockLost() OVERRIDE { + plugin_->OnMouseLockLost(); + } + + virtual bool HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent &event) OVERRIDE { + plugin_->HandleMouseLockedInputEvent(event); + return true; + } + + private: + webkit::ppapi::PluginInstance* plugin_; +}; + } // namespace BrokerDispatcherWrapper::BrokerDispatcherWrapper() { @@ -850,15 +873,11 @@ PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderViewImpl* render_view) has_saved_context_menu_action_(false), saved_context_menu_action_(0), focused_plugin_(NULL), - mouse_lock_owner_(NULL), - mouse_locked_(false), - pending_lock_request_(false), - pending_unlock_request_(false), last_mouse_event_target_(NULL) { } PepperPluginDelegateImpl::~PepperPluginDelegateImpl() { - DCHECK(!mouse_lock_owner_); + DCHECK(mouse_lock_instances_.empty()); } scoped_refptr<webkit::ppapi::PluginModule> @@ -1146,8 +1165,7 @@ bool PepperPluginDelegateImpl::CanComposeInline() const { void PepperPluginDelegateImpl::PluginCrashed( webkit::ppapi::PluginInstance* instance) { render_view_->PluginCrashed(instance->module()->path()); - - UnlockMouse(instance); + UnSetAndDeleteLockTargetAdapter(instance); } void PepperPluginDelegateImpl::InstanceCreated( @@ -1161,14 +1179,8 @@ void PepperPluginDelegateImpl::InstanceCreated( void PepperPluginDelegateImpl::InstanceDeleted( webkit::ppapi::PluginInstance* instance) { active_instances_.erase(instance); + UnSetAndDeleteLockTargetAdapter(instance); - if (mouse_lock_owner_ && mouse_lock_owner_ == instance) { - // UnlockMouse() will determine whether a ViewHostMsg_UnlockMouse needs to - // be sent, and set internal state properly. We only need to forget about - // the current |mouse_lock_owner_|. - UnlockMouse(mouse_lock_owner_); - mouse_lock_owner_ = NULL; - } if (last_mouse_event_target_ == instance) last_mouse_event_target_ = NULL; if (focused_plugin_ == instance) @@ -1359,50 +1371,7 @@ bool PepperPluginDelegateImpl::IsPluginFocused() const { return focused_plugin_ != NULL; } -void PepperPluginDelegateImpl::OnLockMouseACK(bool succeeded) { - DCHECK(!mouse_locked_ && pending_lock_request_); - - mouse_locked_ = succeeded; - pending_lock_request_ = false; - if (pending_unlock_request_ && !succeeded) { - // We have sent an unlock request after the lock request. However, since - // the lock request has failed, the unlock request will be ignored by the - // browser side and there won't be any response to it. - pending_unlock_request_ = false; - } - // If the PluginInstance has been deleted, |mouse_lock_owner_| can be NULL. - if (mouse_lock_owner_) { - webkit::ppapi::PluginInstance* last_mouse_lock_owner = mouse_lock_owner_; - if (!succeeded) { - // Reset |mouse_lock_owner_| to NULL before calling OnLockMouseACK(), so - // that if OnLockMouseACK() results in calls to any mouse lock method - // (e.g., LockMouse()), the method will see consistent internal state. - mouse_lock_owner_ = NULL; - } - - last_mouse_lock_owner->OnLockMouseACK(succeeded ? PP_OK : PP_ERROR_FAILED); - } -} - -void PepperPluginDelegateImpl::OnMouseLockLost() { - DCHECK(mouse_locked_ && !pending_lock_request_); - - mouse_locked_ = false; - pending_unlock_request_ = false; - // If the PluginInstance has been deleted, |mouse_lock_owner_| can be NULL. - if (mouse_lock_owner_) { - // Reset |mouse_lock_owner_| to NULL before calling OnMouseLockLost(), so - // that if OnMouseLockLost() results in calls to any mouse lock method - // (e.g., LockMouse()), the method will see consistent internal state. - webkit::ppapi::PluginInstance* last_mouse_lock_owner = mouse_lock_owner_; - mouse_lock_owner_ = NULL; - - last_mouse_lock_owner->OnMouseLockLost(); - } -} - -bool PepperPluginDelegateImpl::HandleMouseEvent( - const WebKit::WebMouseEvent& event) { +void PepperPluginDelegateImpl::WillHandleMouseEvent() { // This method is called for every mouse event that the render view receives. // And then the mouse event is forwarded to WebKit, which dispatches it to the // event target. Potentially a Pepper plugin will receive the event. @@ -1411,19 +1380,6 @@ bool PepperPluginDelegateImpl::HandleMouseEvent( // event, it will notify us via DidReceiveMouseEvent() and set itself as // |last_mouse_event_target_|. last_mouse_event_target_ = NULL; - - if (mouse_locked_) { - if (mouse_lock_owner_) { - // |cursor_info| is ignored since it is hidden when the mouse is locked. - WebKit::WebCursorInfo cursor_info; - mouse_lock_owner_->HandleInputEvent(event, &cursor_info); - } - - // If the mouse is locked, only the current owner of the mouse lock can - // process mouse events. - return true; - } - return false; } bool PepperPluginDelegateImpl::OpenFileSystem( @@ -1961,56 +1917,23 @@ ppapi::Preferences PepperPluginDelegateImpl::GetPreferences() { return ppapi::Preferences(render_view_->webkit_preferences()); } -void PepperPluginDelegateImpl::LockMouse( +bool PepperPluginDelegateImpl::LockMouse( webkit::ppapi::PluginInstance* instance) { - DCHECK(instance); - if (!MouseLockedOrPending()) { - DCHECK(!mouse_lock_owner_); - pending_lock_request_ = true; - mouse_lock_owner_ = instance; - - render_view_->Send( - new ViewHostMsg_LockMouse(render_view_->routing_id())); - } else if (instance != mouse_lock_owner_) { - // Another plugin instance is using mouse lock. Fail immediately. - instance->OnLockMouseACK(PP_ERROR_FAILED); - } else { - if (mouse_locked_) { - instance->OnLockMouseACK(PP_OK); - } else if (pending_lock_request_) { - instance->OnLockMouseACK(PP_ERROR_INPROGRESS); - } else { - // The only case left here is - // !mouse_locked_ && !pending_lock_request_ && pending_unlock_request_, - // which is not possible. - NOTREACHED(); - instance->OnLockMouseACK(PP_ERROR_FAILED); - } - } + + return render_view_->mouse_lock_dispatcher()->LockMouse( + GetOrCreateLockTargetAdapter(instance)); } void PepperPluginDelegateImpl::UnlockMouse( webkit::ppapi::PluginInstance* instance) { - DCHECK(instance); - - // If no one is using mouse lock or the user is not |instance|, ignore - // the unlock request. - if (MouseLockedOrPending() && mouse_lock_owner_ == instance) { - if (mouse_locked_ || pending_lock_request_) { - DCHECK(!mouse_locked_ || !pending_lock_request_); - if (!pending_unlock_request_) { - pending_unlock_request_ = true; - - render_view_->Send( - new ViewHostMsg_UnlockMouse(render_view_->routing_id())); - } - } else { - // The only case left here is - // !mouse_locked_ && !pending_lock_request_ && pending_unlock_request_, - // which is not possible. - NOTREACHED(); - } - } + render_view_->mouse_lock_dispatcher()->UnlockMouse( + GetOrCreateLockTargetAdapter(instance)); +} + +bool PepperPluginDelegateImpl::IsMouseLocked( + webkit::ppapi::PluginInstance* instance) { + return render_view_->mouse_lock_dispatcher()->IsMouseLockedTo( + GetOrCreateLockTargetAdapter(instance)); } void PepperPluginDelegateImpl::DidChangeCursor( @@ -2170,3 +2093,25 @@ bool PepperPluginDelegateImpl::CanUseSocketAPIs() { return content::GetContentClient()->renderer()->AllowSocketAPI(url); } + +MouseLockDispatcher::LockTarget* + PepperPluginDelegateImpl::GetOrCreateLockTargetAdapter( + webkit::ppapi::PluginInstance* instance) { + MouseLockDispatcher::LockTarget* target = mouse_lock_instances_[instance]; + if (target) + return target; + + return mouse_lock_instances_[instance] = + new PluginInstanceLockTarget(instance); +} + +void PepperPluginDelegateImpl::UnSetAndDeleteLockTargetAdapter( + webkit::ppapi::PluginInstance* instance) { + LockTargetMap::iterator it = mouse_lock_instances_.find(instance); + if (it != mouse_lock_instances_.end()) { + MouseLockDispatcher::LockTarget* target = it->second; + render_view_->mouse_lock_dispatcher()->OnLockTargetDestroyed(target); + delete target; + mouse_lock_instances_.erase(it); + } +} diff --git a/content/renderer/pepper_plugin_delegate_impl.h b/content/renderer/pepper_plugin_delegate_impl.h index 22f5ab2..22de0d5 100644 --- a/content/renderer/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper_plugin_delegate_impl.h @@ -16,6 +16,7 @@ #include "base/memory/weak_ptr.h" #include "content/common/content_export.h" #include "content/public/renderer/render_view_observer.h" +#include "content/renderer/mouse_lock_dispatcher.h" #include "content/renderer/pepper_parent_context_provider.h" #include "ppapi/proxy/broker_dispatcher.h" #include "ppapi/proxy/proxy_channel.h" @@ -197,15 +198,8 @@ class PepperPluginDelegateImpl int selection_end); void OnImeConfirmComposition(const string16& text); - // Notification that the request to lock the mouse has completed. - void OnLockMouseACK(bool succeeded); - // Notification that the plugin instance has lost the mouse lock. - void OnMouseLockLost(); // Notification that a mouse event has arrived at the render view. - // Returns true if no further handling is needed. For example, if the mouse is - // currently locked, this method directly dispatches the event to the owner of - // the mouse lock and returns true. - bool HandleMouseEvent(const WebKit::WebMouseEvent& event); + void WillHandleMouseEvent(); // PluginDelegate implementation. virtual void PluginFocusChanged(webkit::ppapi::PluginInstance* instance, @@ -383,8 +377,9 @@ class PepperPluginDelegateImpl virtual base::SharedMemory* CreateAnonymousSharedMemory(uint32_t size) OVERRIDE; virtual ::ppapi::Preferences GetPreferences() OVERRIDE; - virtual void LockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE; + virtual bool LockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE; virtual void UnlockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE; + virtual bool IsMouseLocked(webkit::ppapi::PluginInstance* instance) OVERRIDE; virtual void DidChangeCursor(webkit::ppapi::PluginInstance* instance, const WebKit::WebCursorInfo& cursor) OVERRIDE; virtual void DidReceiveMouseEvent( @@ -433,10 +428,6 @@ class PepperPluginDelegateImpl scoped_refptr<PpapiBrokerImpl> CreatePpapiBroker( webkit::ppapi::PluginModule* plugin_module); - bool MouseLockedOrPending() const { - return mouse_locked_ || pending_lock_request_ || pending_unlock_request_; - } - // Implementation of PepperParentContextProvider. virtual RendererGLContext* GetParentContextForPlatformContext3D() OVERRIDE; @@ -450,10 +441,17 @@ class PepperPluginDelegateImpl // cases and do the check during socket creation in the browser process. bool CanUseSocketAPIs(); + MouseLockDispatcher::LockTarget* GetOrCreateLockTargetAdapter( + webkit::ppapi::PluginInstance* instance); + void UnSetAndDeleteLockTargetAdapter(webkit::ppapi::PluginInstance* instance); + // Pointer to the RenderView that owns us. RenderViewImpl* render_view_; std::set<webkit::ppapi::PluginInstance*> active_instances_; + typedef std::map<webkit::ppapi::PluginInstance*, + MouseLockDispatcher::LockTarget*> LockTargetMap; + LockTargetMap mouse_lock_instances_; // Used to send a single context menu "completion" upon menu close. bool has_saved_context_menu_action_; @@ -481,21 +479,6 @@ class PepperPluginDelegateImpl // progress. string16 composition_text_; - // |mouse_lock_owner_| is not owned by this class. We can know about when it - // is destroyed via InstanceDeleted(). - // |mouse_lock_owner_| being non-NULL doesn't indicate that currently the - // mouse has been locked. It is possible that a request to lock the mouse has - // been sent, but the response hasn't arrived yet. - webkit::ppapi::PluginInstance* mouse_lock_owner_; - bool mouse_locked_; - // If both |pending_lock_request_| and |pending_unlock_request_| are true, - // it means a lock request was sent before an unlock request and we haven't - // received responses for them. - // The logic in LockMouse() makes sure that a lock request won't be sent when - // there is a pending unlock request. - bool pending_lock_request_; - bool pending_unlock_request_; - // The plugin instance that received the last mouse event. It is set to NULL // if the last mouse event went to elements other than Pepper plugins. // |last_mouse_event_target_| is not owned by this class. We can know about diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 99088a8..b76e2a8 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -64,6 +64,7 @@ #include "content/renderer/media/media_stream_impl.h" #include "content/renderer/media/render_media_log.h" #include "content/renderer/mhtml_generator.h" +#include "content/renderer/mouse_lock_dispatcher.h" #include "content/renderer/notification_provider.h" #include "content/renderer/p2p/socket_dispatcher.h" #include "content/renderer/plugin_channel_host.h" @@ -335,6 +336,36 @@ struct RenderViewImpl::PendingFileChooser { WebFileChooserCompletion* completion; // MAY BE NULL to skip callback. }; +namespace { + +class WebWidgetLockTarget : public MouseLockDispatcher::LockTarget { + public: + WebWidgetLockTarget (WebKit::WebWidget* webwidget) + : webwidget_(webwidget) {} + + virtual void OnLockMouseACK(bool succeeded) OVERRIDE { + if (succeeded) + webwidget_->didAcquirePointerLock(); + else + webwidget_->didNotAcquirePointerLock(); + } + + virtual void OnMouseLockLost() OVERRIDE { + webwidget_->didLosePointerLock(); + } + + virtual bool HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent &event) OVERRIDE { + // The WebWidget handles mouse lock in WebKit's handleInputEvent(). + return false; + } + + private: + WebKit::WebWidget* webwidget_; +}; + +} // namespace + RenderViewImpl::RenderViewImpl( gfx::NativeViewId parent_hwnd, int32 opener_id, @@ -374,6 +405,7 @@ RenderViewImpl::RenderViewImpl( p2p_socket_dispatcher_(NULL), devtools_agent_(NULL), renderer_accessibility_(NULL), + mouse_lock_dispatcher_(NULL), session_storage_namespace_id_(session_storage_namespace_id), handling_select_range_(false), #if defined(OS_WIN) @@ -395,6 +427,7 @@ RenderViewImpl::RenderViewImpl( #endif webwidget_ = WebView::create(this); + webwidget_mouse_lock_target_.reset(new WebWidgetLockTarget(webwidget_)); if (counter) { shared_popup_counter_ = counter; @@ -445,9 +478,11 @@ RenderViewImpl::RenderViewImpl( new TextInputClientObserver(this); #endif // defined(OS_MACOSX) + // The next group of objects all implement RenderViewObserver, so are deleted + // along with the RenderView automatically. devtools_agent_ = new DevToolsAgent(this); - renderer_accessibility_ = new RendererAccessibility(this); + mouse_lock_dispatcher_ = new MouseLockDispatcher(this); new IdleUserDetector(this); @@ -723,8 +758,6 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER(ViewMsg_SetHistoryLengthAndPrune, OnSetHistoryLengthAndPrune) IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode) - IPC_MESSAGE_HANDLER(ViewMsg_LockMouse_ACK, OnLockMouseACK) - IPC_MESSAGE_HANDLER(ViewMsg_MouseLockLost, OnMouseLockLost) IPC_MESSAGE_HANDLER(JavaBridgeMsg_Init, OnJavaBridgeInit) // Have the super handle all other messages. @@ -1928,6 +1961,19 @@ void RenderViewImpl::exitFullScreen() { Send(new ViewHostMsg_ToggleFullscreen(routing_id_, false)); } +bool RenderViewImpl::requestPointerLock() { + return mouse_lock_dispatcher_->LockMouse(webwidget_mouse_lock_target_.get()); +} + +void RenderViewImpl::requestPointerUnlock() { + mouse_lock_dispatcher_->UnlockMouse(webwidget_mouse_lock_target_.get()); +} + +bool RenderViewImpl::isPointerLocked() { + return mouse_lock_dispatcher_->IsMouseLockedTo( + webwidget_mouse_lock_target_.get()); +} + // WebKit::WebFrameClient ----------------------------------------------------- WebPlugin* RenderViewImpl::createPlugin(WebFrame* frame, @@ -4386,7 +4432,11 @@ void RenderViewImpl::DidHandleKeyEvent() { } bool RenderViewImpl::WillHandleMouseEvent(const WebKit::WebMouseEvent& event) { - return pepper_delegate_.HandleMouseEvent(event); + pepper_delegate_.WillHandleMouseEvent(); + + // If the mouse is locked, only the current owner of the mouse lock can + // process mouse events. + return mouse_lock_dispatcher_->WillHandleMouseEvent(event); } void RenderViewImpl::DidHandleMouseEvent(const WebKit::WebMouseEvent& event) { @@ -4895,23 +4945,6 @@ void RenderViewImpl::OnEnableViewSourceMode() { main_frame->enableViewSourceMode(true); } -void RenderViewImpl::OnLockMouseACK(bool succeeded) { - // Mouse Lock removes the system cursor and provides all mouse motion as - // .movementX/Y values on events all sent to a fixed target. This requires - // content to specifically request the mode to be entered. - // Mouse Capture is implicitly given for the duration of a drag event, and - // sends all mouse events to the initial target of the drag. - // If Lock is entered it supercedes any in progress Capture. - if (succeeded) - OnMouseCaptureLost(); - - pepper_delegate_.OnLockMouseACK(succeeded); -} - -void RenderViewImpl::OnMouseLockLost() { - pepper_delegate_.OnMouseLockLost(); -} - bool RenderViewImpl::WebWidgetHandlesCompositorScheduling() const { return !!RenderThreadImpl::current()->compositor_thread(); } diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index cf82759..e8728f4 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -67,6 +67,7 @@ class JavaBridgeDispatcher; class LoadProgressTracker; class MediaStreamDispatcher; class MediaStreamImpl; +class MouseLockDispatcher; class NotificationProvider; class PepperDeviceTest; struct PP_NetAddress_Private; @@ -213,6 +214,10 @@ class RenderViewImpl : public RenderWidget, return p2p_socket_dispatcher_; } + MouseLockDispatcher* mouse_lock_dispatcher() { + return mouse_lock_dispatcher_; + } + WebKit::WebPeerConnectionHandler* CreatePeerConnectionHandler( WebKit::WebPeerConnectionHandlerClient* client); @@ -317,6 +322,9 @@ class RenderViewImpl : public RenderWidget, virtual void runModal(); virtual bool enterFullScreen(); virtual void exitFullScreen(); + virtual bool requestPointerLock(); + virtual void requestPointerUnlock(); + virtual bool isPointerLocked(); // WebKit::WebViewClient implementation -------------------------------------- @@ -810,12 +818,10 @@ class RenderViewImpl : public RenderWidget, const std::vector<GURL>& links, const std::vector<FilePath>& local_paths, const FilePath& local_directory_name); - void OnLockMouseACK(bool succeeded); void OnMediaPlayerActionAt(const gfx::Point& location, const WebKit::WebMediaPlayerAction& action); void OnPluginActionAt(const gfx::Point& location, const WebKit::WebPluginAction& action); - void OnMouseLockLost(); void OnMoveOrResizeStarted(); CONTENT_EXPORT void OnNavigate(const ViewMsg_Navigate_Params& params); void OnPaste(); @@ -1168,6 +1174,9 @@ class RenderViewImpl : public RenderWidget, // Java Bridge dispatcher attached to this view; lazily initialized. scoped_ptr<JavaBridgeDispatcher> java_bridge_dispatcher_; + // Mouse Lock dispatcher attached to this view. + MouseLockDispatcher* mouse_lock_dispatcher_; + // Misc ---------------------------------------------------------------------- // The current and pending file chooser completion objects. If the queue is @@ -1226,6 +1235,9 @@ class RenderViewImpl : public RenderWidget, // of handling a ViewMsg_SelectRange IPC. bool handling_select_range_; + // Wraps the |webwidget_| as a MouseLockDispatcher::LockTarget interface. + scoped_ptr<MouseLockDispatcher::LockTarget> webwidget_mouse_lock_target_; + // Plugins ------------------------------------------------------------------- // All the currently active plugin delegates for this RenderView; kept so |