summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorscheib@chromium.org <scheib@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-27 07:33:11 +0000
committerscheib@chromium.org <scheib@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-27 07:33:11 +0000
commit217690df70c213c482039242dd935786c37dcd3c (patch)
tree8d0a579099bf69e51866cffb682c28ba44330b4f /content
parent15f08dde26dbb02c8e4441492ab967cb48fcd4e4 (diff)
downloadchromium_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.gypi2
-rw-r--r--content/renderer/mouse_lock_dispatcher.cc117
-rw-r--r--content/renderer/mouse_lock_dispatcher.h87
-rw-r--r--content/renderer/mouse_lock_dispatcher_browsertest.cc232
-rw-r--r--content/renderer/pepper_plugin_delegate_impl.cc177
-rw-r--r--content/renderer/pepper_plugin_delegate_impl.h39
-rw-r--r--content/renderer/render_view_impl.cc75
-rw-r--r--content/renderer/render_view_impl.h16
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