diff options
author | scheib@chromium.org <scheib@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-26 07:13:20 +0000 |
---|---|---|
committer | scheib@chromium.org <scheib@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-26 07:13:20 +0000 |
commit | e39037d2b7adaa7c4518a49faa5ddc78ac895efa (patch) | |
tree | 1a02d96c2a42b045869e641bceca137112700ca7 | |
parent | ef4113beaa7c033fef93b9814c2bc6fb5a88b2fb (diff) | |
download | chromium_src-e39037d2b7adaa7c4518a49faa5ddc78ac895efa.zip chromium_src-e39037d2b7adaa7c4518a49faa5ddc78ac895efa.tar.gz chromium_src-e39037d2b7adaa7c4518a49faa5ddc78ac895efa.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.
Review URL: http://codereview.chromium.org/8970016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119206 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/content_tests.gypi | 1 | ||||
-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_unittest.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 | ||||
-rw-r--r-- | ppapi/examples/mouse_lock/mouse_lock.cc | 5 | ||||
-rw-r--r-- | ppapi/examples/mouse_lock/mouse_lock.html | 54 | ||||
-rw-r--r-- | webkit/plugins/ppapi/mock_plugin_delegate.cc | 8 | ||||
-rw-r--r-- | webkit/plugins/ppapi/mock_plugin_delegate.h | 3 | ||||
-rw-r--r-- | webkit/plugins/ppapi/plugin_delegate.h | 13 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppapi_plugin_instance.cc | 30 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppapi_plugin_instance.h | 7 |
16 files changed, 662 insertions, 204 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 36c0ac9..d19db89 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/content_tests.gypi b/content/content_tests.gypi index 9c380d4..1803ce4 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -285,6 +285,7 @@ 'renderer/media/video_capture_impl_unittest.cc', 'renderer/media/video_capture_message_filter_unittest.cc', 'renderer/media/webrtc_audio_device_unittest.cc', + 'renderer/mouse_lock_dispatcher_unittest.cc', 'renderer/paint_aggregator_unittest.cc', 'renderer/pepper_plugin_delegate_impl_unittest.cc', 'renderer/v8_value_converter_impl_unittest.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..1520e38 --- /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 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_unittest.cc b/content/renderer/mouse_lock_dispatcher_unittest.cc new file mode 100644 index 0000000..5605aa4 --- /dev/null +++ b/content/renderer/mouse_lock_dispatcher_unittest.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 ffc7fc1..3c4bc63 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 diff --git a/ppapi/examples/mouse_lock/mouse_lock.cc b/ppapi/examples/mouse_lock/mouse_lock.cc index 6a934ba..f6bfbb6 100644 --- a/ppapi/examples/mouse_lock/mouse_lock.cc +++ b/ppapi/examples/mouse_lock/mouse_lock.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -75,8 +75,9 @@ class MyInstance : public pp::Instance, public pp::MouseLock { LockMouse(callback_factory_.NewRequiredCallback( &MyInstance::DidLockMouse)); } + return true; } - return true; + return false; } default: return false; diff --git a/ppapi/examples/mouse_lock/mouse_lock.html b/ppapi/examples/mouse_lock/mouse_lock.html index 5ce2261..5f0e5aa 100644 --- a/ppapi/examples/mouse_lock/mouse_lock.html +++ b/ppapi/examples/mouse_lock/mouse_lock.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <!-- - Copyright (c) 2011 The Chromium Authors. All rights reserved. + 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. --> @@ -13,16 +13,6 @@ height: 100%; } </style> - <script> - function ToggleFullscreen() { - if (document.webkitIsFullScreen) { - document.webkitCancelFullScreen(); - } else { - document.getElementById('container').webkitRequestFullScreen( - Element.ALLOW_KEYBOARD_INPUT); - } - } - </script> </head> <body title="This tooltip should not be shown if the mouse is locked."> <div id="container"> @@ -61,15 +51,45 @@ </li> </ul> <div> - <button id="toggle_fullscreen" onclick="ToggleFullscreen();"> + <button onclick="ToggleFullscreen();"> Toggle Tab Fullscreen </button> + <button onclick="AddAPlugin();"> + Add A Plugin + </button> + <button onclick="RemoveAPlugin();"> + Remove A Plugin (press 'x') + </button> + </div> + <div id="plugins_container"> </div> - <object id="plugin" type="application/x-ppapi-example-mouse-lock" - width="300" height="300" border="2px"></object> - <div></div> - <object id="plugin" type="application/x-ppapi-example-mouse-lock" - width="300" height="300" border="2px"></object> </div> </body> +<script> + plugins_container = document.getElementById("plugins_container"); + AddAPlugin(); + AddAPlugin(); + + function ToggleFullscreen() { + if (document.webkitIsFullScreen) { + document.webkitCancelFullScreen(); + } else { + document.getElementById('container').webkitRequestFullScreen( + Element.ALLOW_KEYBOARD_INPUT); + } + } + function AddAPlugin() { + plugins_container.insertAdjacentHTML("BeforeEnd", + '<object type="application/x-ppapi-example-mouse-lock" ' + + 'width="300" height="300" border="2px"></object>'); + } + function RemoveAPlugin() { + if (plugins_container.firstElementChild) + plugins_container.removeChild(plugins_container.firstElementChild); + } + document.body.onkeydown = function (e) { + if (String.fromCharCode(e.keyCode) == "X") + RemoveAPlugin(); + } +</script> </html> diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.cc b/webkit/plugins/ppapi/mock_plugin_delegate.cc index da371c54..780bf4c 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.cc +++ b/webkit/plugins/ppapi/mock_plugin_delegate.cc @@ -348,13 +348,17 @@ base::SharedMemory* MockPluginDelegate::CreateAnonymousSharedMemory( return ::ppapi::Preferences(); } -void MockPluginDelegate::LockMouse(PluginInstance* instance) { - instance->OnLockMouseACK(PP_ERROR_FAILED); +bool MockPluginDelegate::LockMouse(PluginInstance* instance) { + return false; } void MockPluginDelegate::UnlockMouse(PluginInstance* instance) { } +bool MockPluginDelegate::IsMouseLocked(PluginInstance* instance) { + return false; +} + void MockPluginDelegate::DidChangeCursor(PluginInstance* instance, const WebKit::WebCursorInfo& cursor) { } diff --git a/webkit/plugins/ppapi/mock_plugin_delegate.h b/webkit/plugins/ppapi/mock_plugin_delegate.h index fbd961a..4768080 100644 --- a/webkit/plugins/ppapi/mock_plugin_delegate.h +++ b/webkit/plugins/ppapi/mock_plugin_delegate.h @@ -148,8 +148,9 @@ class MockPluginDelegate : public PluginDelegate { virtual std::string GetFlashCommandLineArgs(); virtual base::SharedMemory* CreateAnonymousSharedMemory(uint32_t size); virtual ::ppapi::Preferences GetPreferences(); - virtual void LockMouse(PluginInstance* instance); + virtual bool LockMouse(PluginInstance* instance); virtual void UnlockMouse(PluginInstance* instance); + virtual bool IsMouseLocked(PluginInstance* instance); virtual void DidChangeCursor(PluginInstance* instance, const WebKit::WebCursorInfo& cursor); virtual void DidReceiveMouseEvent(PluginInstance* instance); diff --git a/webkit/plugins/ppapi/plugin_delegate.h b/webkit/plugins/ppapi/plugin_delegate.h index 6f9a17e..d91621c 100644 --- a/webkit/plugins/ppapi/plugin_delegate.h +++ b/webkit/plugins/ppapi/plugin_delegate.h @@ -490,11 +490,11 @@ class PluginDelegate { // Returns the current preferences. virtual ::ppapi::Preferences GetPreferences() = 0; - // Locks the mouse for |instance|. It will call - // PluginInstance::OnLockMouseACK() to notify the instance when the operation - // is completed. The call to OnLockMouseACK() may be synchronous (i.e., it may - // be called when LockMouse() is still on the stack). - virtual void LockMouse(PluginInstance* instance) = 0; + // Locks the mouse for |instance|. If false is returned, the lock is not + // possible. If true is returned then the lock is pending. Success or + // failure will be delivered asynchronously via + // PluginInstance::OnLockMouseACK(). + virtual bool LockMouse(PluginInstance* instance) = 0; // Unlocks the mouse if |instance| currently owns the mouse lock. Whenever an // plugin instance has lost the mouse lock, it will be notified by @@ -504,6 +504,9 @@ class PluginDelegate { // call to the current mouse lock owner. virtual void UnlockMouse(PluginInstance* instance) = 0; + // Returns true iff |instance| currently owns the mouse lock. + virtual bool IsMouseLocked(PluginInstance* instance) = 0; + // Notifies that |instance| has changed the cursor. // This will update the cursor appearance if it is currently over the plugin // instance. diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.cc b/webkit/plugins/ppapi/ppapi_plugin_instance.cc index 7f849f6..23a1862 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc @@ -1675,13 +1675,13 @@ bool PluginInstance::IsFullPagePlugin() const { return frame->view()->mainFrame()->document().isPluginDocument(); } -void PluginInstance::OnLockMouseACK(int32_t result) { +void PluginInstance::OnLockMouseACK(bool succeeded) { if (!lock_mouse_callback_.func) { NOTREACHED(); return; } - - PP_RunAndClearCompletionCallback(&lock_mouse_callback_, result); + PP_RunAndClearCompletionCallback(&lock_mouse_callback_, + succeeded ? PP_OK : PP_ERROR_FAILED); } void PluginInstance::OnMouseLockLost() { @@ -1689,6 +1689,13 @@ void PluginInstance::OnMouseLockLost() { plugin_mouse_lock_interface_->MouseLockLost(pp_instance()); } +void PluginInstance::HandleMouseLockedInputEvent( + const WebKit::WebMouseEvent& event) { + // |cursor_info| is ignored since it is hidden when the mouse is locked. + WebKit::WebCursorInfo cursor_info; + HandleInputEvent(event, &cursor_info); +} + void PluginInstance::SimulateInputEvent(const InputEventData& input_event) { WebView* web_view = container()->element().document().frame()->view(); if (!web_view) { @@ -1974,16 +1981,21 @@ int32_t PluginInstance::LockMouse(PP_Instance instance, // Don't support synchronous call. return PP_ERROR_BLOCKS_MAIN_THREAD; } - if (lock_mouse_callback_.func) + if (lock_mouse_callback_.func) // A lock is pending. return PP_ERROR_INPROGRESS; + + if (delegate()->IsMouseLocked(this)) + return PP_OK; + if (!CanAccessMainFrame()) return PP_ERROR_NOACCESS; - lock_mouse_callback_ = callback; - // We will be notified on completion via OnLockMouseACK(), either - // synchronously or asynchronously. - delegate()->LockMouse(this); - return PP_OK_COMPLETIONPENDING; + if (delegate()->LockMouse(this)) { + lock_mouse_callback_ = callback; + return PP_OK_COMPLETIONPENDING; + } else { + return PP_ERROR_FAILED; + } } void PluginInstance::UnlockMouse(PP_Instance instance) { diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.h b/webkit/plugins/ppapi/ppapi_plugin_instance.h index 086125f..c8a6332 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.h +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.h @@ -52,6 +52,7 @@ class TransportDIB; namespace WebKit { class WebInputEvent; +class WebMouseEvent; class WebPluginContainer; struct WebCompositionUnderline; struct WebCursorInfo; @@ -305,8 +306,12 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : // or embedded in a page). bool IsFullPagePlugin() const; - void OnLockMouseACK(int32_t result); + // A mouse lock request was pending and this reports success or failure. + void OnLockMouseACK(bool succeeded); + // A mouse lock was in place, but has been lost. void OnMouseLockLost(); + // A mouse lock is enabled and mouse events are being delievered. + void HandleMouseLockedInputEvent(const WebKit::WebMouseEvent& event); // Simulates an input event to the plugin by passing it down to WebKit, // which sends it back up to the plugin as if it came from the user. |