diff options
Diffstat (limited to 'content')
17 files changed, 457 insertions, 19 deletions
diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc index 77248cb..01077f7 100644 --- a/content/browser/renderer_host/render_widget_host.cc +++ b/content/browser/renderer_host/render_widget_host.cc @@ -199,6 +199,8 @@ bool RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) { OnMsgImeCancelComposition) IPC_MESSAGE_HANDLER(ViewHostMsg_DidActivateAcceleratedCompositing, OnMsgDidActivateAcceleratedCompositing) + IPC_MESSAGE_HANDLER(ViewHostMsg_LockMouse, OnMsgLockMouse) + IPC_MESSAGE_HANDLER(ViewHostMsg_UnlockMouse, OnMsgUnlockMouse) #if defined(OS_POSIX) IPC_MESSAGE_HANDLER(ViewHostMsg_GetScreenInfo, OnMsgGetScreenInfo) IPC_MESSAGE_HANDLER(ViewHostMsg_GetWindowRect, OnMsgGetWindowRect) @@ -381,6 +383,9 @@ void RenderWidgetHost::Focus() { } void RenderWidgetHost::Blur() { + if (IsMouseLocked()) + view_->UnlockMouse(); + Send(new ViewMsg_SetFocus(routing_id_, false)); } @@ -388,7 +393,14 @@ void RenderWidgetHost::LostCapture() { Send(new ViewMsg_MouseCaptureLost(routing_id_)); } +void RenderWidgetHost::LostMouseLock() { + Send(new ViewMsg_MouseLockLost(routing_id_)); +} + void RenderWidgetHost::ViewDestroyed() { + if (IsMouseLocked()) + view_->UnlockMouse(); + // TODO(evanm): tracking this may no longer be necessary; // eliminate this function if so. SetView(NULL); @@ -527,7 +539,16 @@ void RenderWidgetHost::ForwardMouseEvent(const WebMouseEvent& mouse_event) { // more WM_MOUSEMOVE events than we wish to send to the renderer. if (mouse_event.type == WebInputEvent::MouseMove) { if (mouse_move_pending_) { - next_mouse_move_.reset(new WebMouseEvent(mouse_event)); + if (!next_mouse_move_.get()) { + next_mouse_move_.reset(new WebMouseEvent(mouse_event)); + } else { + // Accumulate movement deltas. + int x = next_mouse_move_->movementX; + int y = next_mouse_move_->movementY; + *next_mouse_move_ = mouse_event; + next_mouse_move_->movementX += x; + next_mouse_move_->movementY += y; + } return; } mouse_move_pending_ = true; @@ -780,6 +801,10 @@ void RenderWidgetHost::ImeCancelComposition() { std::vector<WebKit::WebCompositionUnderline>(), 0, 0)); } +bool RenderWidgetHost::IsMouseLocked() const { + return view_ ? view_->mouse_locked() : false; +} + void RenderWidgetHost::Destroy() { NotificationService::current()->Notify( content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED, @@ -1125,6 +1150,21 @@ void RenderWidgetHost::OnMsgDidActivateAcceleratedCompositing(bool activated) { #endif } +void RenderWidgetHost::OnMsgLockMouse() { + // TODO(yzshen): Only allow to lock the mouse when in fullscreen mode, and + // make sure that the mouse is unlocked when leaving fullscreen mode. + if (!view_ || !view_->HasFocus() || !view_->LockMouse()) { + Send(new ViewMsg_LockMouse_ACK(routing_id_, false)); + } else { + Send(new ViewMsg_LockMouse_ACK(routing_id_, true)); + } +} + +void RenderWidgetHost::OnMsgUnlockMouse() { + if (IsMouseLocked()) + view_->UnlockMouse(); +} + #if defined(OS_POSIX) void RenderWidgetHost::OnMsgGetScreenInfo(gfx::NativeViewId window_id, WebKit::WebScreenInfo* results) { diff --git a/content/browser/renderer_host/render_widget_host.h b/content/browser/renderer_host/render_widget_host.h index 10463c2..9b6a14c 100644 --- a/content/browser/renderer_host/render_widget_host.h +++ b/content/browser/renderer_host/render_widget_host.h @@ -196,6 +196,9 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener, void Blur(); virtual void LostCapture(); + // Called to notify the RenderWidget that it has lost the mouse lock. + virtual void LostMouseLock(); + // Tells us whether the page is rendered directly via the GPU process. bool is_accelerated_compositing_active() { return is_accelerated_compositing_active_; @@ -465,6 +468,8 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener, virtual void NotifyRendererUnresponsive() {} virtual void NotifyRendererResponsive() {} + bool IsMouseLocked() const; + protected: // true if a renderer has once been valid. We use this flag to display a sad // tab only when we lose our renderer and not if a paint occurs during @@ -511,6 +516,9 @@ class CONTENT_EXPORT RenderWidgetHost : public IPC::Channel::Listener, void OnMsgDidActivateAcceleratedCompositing(bool activated); + void OnMsgLockMouse(); + void OnMsgUnlockMouse(); + #if defined(OS_POSIX) void OnMsgGetScreenInfo(gfx::NativeViewId view, WebKit::WebScreenInfo* results); diff --git a/content/browser/renderer_host/render_widget_host_view.cc b/content/browser/renderer_host/render_widget_host_view.cc index ba714df..94f287c 100644 --- a/content/browser/renderer_host/render_widget_host_view.cc +++ b/content/browser/renderer_host/render_widget_host_view.cc @@ -4,6 +4,7 @@ #include "content/browser/renderer_host/render_widget_host_view.h" +#include "base/logging.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h" #if defined(OS_MACOSX) @@ -39,7 +40,13 @@ void RenderWidgetHostView::GetDefaultScreenInfo( } #endif -RenderWidgetHostView::~RenderWidgetHostView() {} +RenderWidgetHostView::RenderWidgetHostView() + : popup_type_(WebKit::WebPopupTypeNone), mouse_locked_(false) { +} + +RenderWidgetHostView::~RenderWidgetHostView() { + DCHECK(!mouse_locked_); +} void RenderWidgetHostView::SetBackground(const SkBitmap& background) { background_ = background; diff --git a/content/browser/renderer_host/render_widget_host_view.h b/content/browser/renderer_host/render_widget_host_view.h index 3456a67..8859d2a 100644 --- a/content/browser/renderer_host/render_widget_host_view.h +++ b/content/browser/renderer_host/render_widget_host_view.h @@ -313,6 +313,10 @@ class RenderWidgetHostView { virtual void SetScrollOffsetPinning( bool is_pinned_to_left, bool is_pinned_to_right) = 0; + // Return value indicates whether the mouse is locked successfully or not. + virtual bool LockMouse() = 0; + virtual void UnlockMouse() = 0; + void set_popup_type(WebKit::WebPopupType popup_type) { popup_type_ = popup_type; } @@ -334,9 +338,11 @@ class RenderWidgetHostView { reserved_rect_ = reserved_rect; } + bool mouse_locked() const { return mouse_locked_; } + protected: // Interface class only, do not construct. - RenderWidgetHostView() : popup_type_(WebKit::WebPopupTypeNone) {} + RenderWidgetHostView(); // Whether this view is a popup and what kind of popup it is (select, // autofill...). @@ -350,6 +356,13 @@ class RenderWidgetHostView { // rendered to draw the resize corner, sidebar mini tabs etc. gfx::Rect reserved_rect_; + // While the mouse is locked, the cursor is hidden from the user. Mouse events + // are still generated. However, the position they report is the last known + // mouse position just as mouse lock was entered; the movement they report + // indicates what the change in position of the mouse would be had it not been + // locked. + bool mouse_locked_; + private: DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostView); }; diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.cc b/content/browser/renderer_host/render_widget_host_view_gtk.cc index d258aac..c92290c 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.cc +++ b/content/browser/renderer_host/render_widget_host_view_gtk.cc @@ -1183,6 +1183,15 @@ gfx::PluginWindowHandle RenderWidgetHostViewGtk::GetCompositingSurface() { return compositing_surface_; } +bool RenderWidgetHostViewGtk::LockMouse() { + NOTIMPLEMENTED(); + return false; +} + +void RenderWidgetHostViewGtk::UnlockMouse() { + NOTIMPLEMENTED(); +} + void RenderWidgetHostViewGtk::ForwardKeyboardEvent( const NativeWebKeyboardEvent& event) { if (!host_) diff --git a/content/browser/renderer_host/render_widget_host_view_gtk.h b/content/browser/renderer_host/render_widget_host_view_gtk.h index 5eb9ed4..93925f4 100644 --- a/content/browser/renderer_host/render_widget_host_view_gtk.h +++ b/content/browser/renderer_host/render_widget_host_view_gtk.h @@ -106,6 +106,8 @@ class RenderWidgetHostViewGtk : public RenderWidgetHostView, virtual void GetScreenInfo(WebKit::WebScreenInfo* results) OVERRIDE; virtual gfx::Rect GetRootWindowBounds() OVERRIDE; virtual gfx::PluginWindowHandle GetCompositingSurface() OVERRIDE; + virtual bool LockMouse() OVERRIDE; + virtual void UnlockMouse() OVERRIDE; // ui::AnimationDelegate implementation. virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE; diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc index 1e94b60..04f8ddb 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.cc +++ b/content/browser/renderer_host/render_widget_host_view_win.cc @@ -243,7 +243,8 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) is_loading_(false), overlay_color_(0), text_input_type_(ui::TEXT_INPUT_TYPE_NONE), - is_fullscreen_(false) { + is_fullscreen_(false), + ignore_mouse_movement_(true) { render_widget_host_->SetView(this); registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, @@ -251,6 +252,7 @@ RenderWidgetHostViewWin::RenderWidgetHostViewWin(RenderWidgetHost* widget) } RenderWidgetHostViewWin::~RenderWidgetHostViewWin() { + UnlockMouse(); ResetTooltip(); } @@ -1100,12 +1102,14 @@ LRESULT RenderWidgetHostViewWin::OnNotify(int w_param, NMHDR* header) { tooltip_hwnd_, TTM_SETMAXTIPWIDTH, 0, kTooltipMaxWidthPixels); SetMsgHandled(TRUE); break; - } + } case TTN_POP: tooltip_showing_ = false; SetMsgHandled(TRUE); break; case TTN_SHOW: + // Tooltip shouldn't be shown when the mouse is locked. + DCHECK(!mouse_locked_); tooltip_showing_ = true; SetMsgHandled(TRUE); break; @@ -1226,6 +1230,15 @@ LRESULT RenderWidgetHostViewWin::OnMouseEvent(UINT message, WPARAM wparam, LPARAM lparam, BOOL& handled) { handled = TRUE; + if (message == WM_MOUSELEAVE) + ignore_mouse_movement_ = true; + + if (mouse_locked_) { + HandleLockedMouseEvent(message, wparam, lparam); + MoveCursorToCenter(); + return 0; + } + if (::IsWindow(tooltip_hwnd_)) { // Forward mouse events through to the tooltip window MSG msg; @@ -1289,10 +1302,12 @@ LRESULT RenderWidgetHostViewWin::OnKeyEvent(UINT message, WPARAM wparam, handled = TRUE; // Force fullscreen windows to close on Escape. - if (is_fullscreen_ && (message == WM_KEYDOWN || message == WM_KEYUP) && - wparam == VK_ESCAPE) { - SendMessage(WM_CANCELMODE); - return 0; + if ((message == WM_KEYDOWN || message == WM_KEYUP) && wparam == VK_ESCAPE) { + if (mouse_locked_) + UnlockMouse(); + if (is_fullscreen_) + SendMessage(WM_CANCELMODE); + return 0; } // If we are a pop-up, forward tab related messages to our parent HWND, so @@ -1477,6 +1492,52 @@ void RenderWidgetHostViewWin::OnAccessibilityNotifications( browser_accessibility_manager_->OnAccessibilityNotifications(params); } +bool RenderWidgetHostViewWin::LockMouse() { + if (mouse_locked_) + return true; + + mouse_locked_ = true; + + // Hide the tooltip window if it is currently visible. When the mouse is + // locked, no mouse message is relayed to the tooltip window, so we don't need + // to worry that it will reappear. + if (::IsWindow(tooltip_hwnd_) && tooltip_showing_) { + ::SendMessage(tooltip_hwnd_, TTM_POP, 0, 0); + // Sending a TTM_POP message doesn't seem to actually hide the tooltip + // window, although we will receive a TTN_POP notification. As a result, + // ShowWindow() is explicitly called to hide the window. + ::ShowWindow(tooltip_hwnd_, SW_HIDE); + } + + // TODO(yzshen): Show an invisible cursor instead of using + // ::ShowCursor(FALSE), so that MoveCursorToCenter() works with Remote + // Desktop. + ::ShowCursor(FALSE); + + MoveCursorToCenter(); + + CRect rect; + GetWindowRect(&rect); + ::ClipCursor(&rect); + + return true; +} + +void RenderWidgetHostViewWin::UnlockMouse() { + if (!mouse_locked_) + return; + + mouse_locked_ = false; + + ::ClipCursor(NULL); + ::SetCursorPos(last_global_mouse_position_.x(), + last_global_mouse_position_.y()); + ::ShowCursor(TRUE); + + if (render_widget_host_) + render_widget_host_->LostMouseLock(); +} + void RenderWidgetHostViewWin::Observe(int type, const NotificationSource& source, const NotificationDetails& details) { @@ -1795,6 +1856,31 @@ void RenderWidgetHostViewWin::ForwardMouseEventToRenderer(UINT message, WebMouseEvent event( WebInputEventFactory::mouseEvent(m_hWnd, message, wparam, lparam)); + if (mouse_locked_) { + CPoint center = GetClientCenter(); + + event.movementX = event.windowX - center.x; + event.movementY = event.windowY - center.y; + event.x = last_mouse_position_.x(); + event.y = last_mouse_position_.y(); + event.windowX = last_mouse_position_.x(); + event.windowY = last_mouse_position_.y(); + event.globalX = last_global_mouse_position_.x(); + event.globalY = last_global_mouse_position_.y(); + } else { + if (ignore_mouse_movement_) { + ignore_mouse_movement_ = false; + event.movementX = 0; + event.movementY = 0; + } else { + event.movementX = event.globalX - last_global_mouse_position_.x(); + event.movementY = event.globalY - last_global_mouse_position_.y(); + } + + last_mouse_position_.SetPoint(event.windowX, event.windowY); + last_global_mouse_position_.SetPoint(event.globalX, event.globalY); + } + // Send the event to the renderer before changing mouse capture, so that the // capturelost event arrives after mouseup. render_widget_host_->ForwardMouseEvent(event); @@ -1843,3 +1929,32 @@ void RenderWidgetHostViewWin::DoPopupOrFullscreenInit(HWND parent_hwnd, EnsureTooltip(); ShowWindow(IsActivatable() ? SW_SHOW : SW_SHOWNA); } + +CPoint RenderWidgetHostViewWin::GetClientCenter() const { + CRect rect; + GetClientRect(&rect); + return rect.CenterPoint(); +} + +void RenderWidgetHostViewWin::MoveCursorToCenter() const { + CPoint center = GetClientCenter(); + ClientToScreen(¢er); + if (!::SetCursorPos(center.x, center.y)) + LOG_GETLASTERROR(WARNING) << "Failed to set cursor position."; +} + +void RenderWidgetHostViewWin::HandleLockedMouseEvent(UINT message, + WPARAM wparam, + LPARAM lparam) { + DCHECK(mouse_locked_); + + if (message == WM_MOUSEMOVE) { + CPoint center = GetClientCenter(); + // Ignore WM_MOUSEMOVE messages generated by MoveCursorToCenter(). + if (LOWORD(lparam) == center.x && HIWORD(lparam) == center.y) + return; + } + + ForwardMouseEventToRenderer(message, wparam, lparam); +} + diff --git a/content/browser/renderer_host/render_widget_host_view_win.h b/content/browser/renderer_host/render_widget_host_view_win.h index 74e58a6..6a9511f 100644 --- a/content/browser/renderer_host/render_widget_host_view_win.h +++ b/content/browser/renderer_host/render_widget_host_view_win.h @@ -26,6 +26,7 @@ #include "content/common/notification_registrar.h" #include "ui/base/win/ime_input.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/point.h" #include "webkit/glue/webcursor.h" class BackingStore; @@ -181,6 +182,8 @@ class RenderWidgetHostViewWin virtual void OnAccessibilityNotifications( const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params ) OVERRIDE; + virtual bool LockMouse() OVERRIDE; + virtual void UnlockMouse() OVERRIDE; // Implementation of NotificationObserver: virtual void Observe(int type, @@ -291,6 +294,11 @@ class RenderWidgetHostViewWin const gfx::Rect& pos, DWORD ex_style); + CPoint GetClientCenter() const; + void MoveCursorToCenter() const; + + void HandleLockedMouseEvent(UINT message, WPARAM wparam, LPARAM lparam); + // The associated Model. While |this| is being Destroyed, // |render_widget_host_| is NULL and the Windows message loop is run one last // time. Message handlers must check for a NULL |render_widget_host_|. @@ -386,6 +394,18 @@ class RenderWidgetHostViewWin // Is the widget fullscreen? bool is_fullscreen_; + // Used to record the last position of the mouse. + // While the mouse is locked, they store the last known position just as mouse + // lock was entered. + // Relative to the upper-left corner of the view. + gfx::Point last_mouse_position_; + // Relative to the upper-left corner of the screen. + gfx::Point last_global_mouse_position_; + + // In the case of the mouse being moved away from the view and then moved + // back, we regard the mouse movement as (0, 0). + bool ignore_mouse_movement_; + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewWin); }; diff --git a/content/browser/renderer_host/test_render_view_host.cc b/content/browser/renderer_host/test_render_view_host.cc index 9303f86..e15018e 100644 --- a/content/browser/renderer_host/test_render_view_host.cc +++ b/content/browser/renderer_host/test_render_view_host.cc @@ -252,6 +252,13 @@ gfx::PluginWindowHandle TestRenderWidgetHostView::GetCompositingSurface() { return gfx::kNullPluginWindow; } +bool TestRenderWidgetHostView::LockMouse() { + return false; +} + +void TestRenderWidgetHostView::UnlockMouse() { +} + TestRenderViewHostFactory::TestRenderViewHostFactory( RenderProcessHostFactory* rph_factory) : render_process_host_factory_(rph_factory) { diff --git a/content/browser/renderer_host/test_render_view_host.h b/content/browser/renderer_host/test_render_view_host.h index 3afebf7..2952242 100644 --- a/content/browser/renderer_host/test_render_view_host.h +++ b/content/browser/renderer_host/test_render_view_host.h @@ -155,6 +155,9 @@ class TestRenderWidgetHostView : public RenderWidgetHostView { virtual gfx::PluginWindowHandle GetCompositingSurface() OVERRIDE; + virtual bool LockMouse() OVERRIDE; + virtual void UnlockMouse() OVERRIDE; + bool is_showing() const { return is_showing_; } private: diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 0d4c597..f80be55 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -1335,6 +1335,14 @@ IPC_MESSAGE_ROUTED3(ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks, IPC_MESSAGE_ROUTED1(ViewMsg_UpdateRemoteAccessClientFirewallTraversal, std::string /* traversal_data */) +// Tells the render side that a ViewHostMsg_LockMouse message has been +// processed. |succeeded| indicates whether the mouse has been successfully +// locked or not. +IPC_MESSAGE_ROUTED1(ViewMsg_LockMouse_ACK, + bool /* succeeded */) +// Tells the render side that the mouse has been unlocked. +IPC_MESSAGE_ROUTED0(ViewMsg_MouseLockLost) + // These three messages are sent to the parent RenderViewHost to display the // page/widget that was created by // CreateWindow/CreateWidget/CreateFullscreenWidget. routing_id @@ -1900,8 +1908,8 @@ IPC_MESSAGE_ROUTED3(ViewHostMsg_UpdateZoomLimits, // Asks the browser to create a block of shared memory for the renderer to // fill in and pass back to the browser. IPC_SYNC_MESSAGE_CONTROL1_1(ViewHostMsg_AllocateSharedMemoryBuffer, - uint32 /* buffer size */, - base::SharedMemoryHandle /* browser handle */) + uint32 /* buffer size */, + base::SharedMemoryHandle /* browser handle */) // Notify the browser that this render process can or can't be suddenly // terminated. @@ -2086,3 +2094,13 @@ IPC_MESSAGE_ROUTED0(ViewHostMsg_RequestRemoteAccessClientFirewallTraversal) // Notifies the browser of an event occurring in the media pipeline. IPC_MESSAGE_CONTROL1(ViewHostMsg_MediaLogEvent, media::MediaLogEvent /* event */) + +// Requests to lock the mouse. Will result in a ViewMsg_LockMouse_ACK message +// being sent back. +IPC_MESSAGE_ROUTED0(ViewHostMsg_LockMouse) + +// Requests to unlock the mouse. A ViewMsg_MouseLockLost message will be sent +// whenever the mouse is unlocked (which may or may not be caused by +// ViewHostMsg_UnlockMouse). +IPC_MESSAGE_ROUTED0(ViewHostMsg_UnlockMouse) + diff --git a/content/renderer/pepper_plugin_delegate_impl.cc b/content/renderer/pepper_plugin_delegate_impl.cc index c21353f..37a71b5 100644 --- a/content/renderer/pepper_plugin_delegate_impl.cc +++ b/content/renderer/pepper_plugin_delegate_impl.cc @@ -52,8 +52,10 @@ #include "ppapi/proxy/host_dispatcher.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/ppapi_preferences.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserCompletion.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserParams.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebPluginContainer.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" @@ -663,10 +665,15 @@ PepperPluginDelegateImpl::PepperPluginDelegateImpl(RenderView* render_view) has_saved_context_menu_action_(false), saved_context_menu_action_(0), id_generator_(0), - is_pepper_plugin_focused_(false) { + is_pepper_plugin_focused_(false), + mouse_lock_owner_(NULL), + mouse_locked_(false), + pending_lock_request_(false), + pending_unlock_request_(false) { } PepperPluginDelegateImpl::~PepperPluginDelegateImpl() { + DCHECK(!mouse_lock_owner_); } scoped_refptr<webkit::ppapi::PluginModule> @@ -851,6 +858,8 @@ void PepperPluginDelegateImpl::PluginCrashed( webkit::ppapi::PluginInstance* instance) { subscribed_to_policy_updates_.erase(instance); render_view_->PluginCrashed(instance->module()->path()); + + UnlockMouse(instance); } void PepperPluginDelegateImpl::InstanceCreated( @@ -865,6 +874,14 @@ void PepperPluginDelegateImpl::InstanceDeleted( webkit::ppapi::PluginInstance* instance) { active_instances_.erase(instance); subscribed_to_policy_updates_.erase(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; + } } SkBitmap* PepperPluginDelegateImpl::GetSadPluginBitmap() { @@ -1027,7 +1044,7 @@ void PepperPluginDelegateImpl::OnAsyncFileOpened( void PepperPluginDelegateImpl::OnSetFocus(bool has_focus) { for (std::set<webkit::ppapi::PluginInstance*>::iterator i = - active_instances_.begin(); + active_instances_.begin(); i != active_instances_.end(); ++i) (*i)->SetContentAreaFocus(has_focus); } @@ -1036,6 +1053,64 @@ bool PepperPluginDelegateImpl::IsPluginFocused() const { return is_pepper_plugin_focused_; } +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::DispatchLockedMouseEvent( + const WebKit::WebMouseEvent& event) { + 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( const GURL& url, fileapi::FileSystemType type, @@ -1490,6 +1565,58 @@ ppapi::Preferences PepperPluginDelegateImpl::GetPreferences() { return ppapi::Preferences(render_view_->webkit_preferences()); } +void 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); + } + } +} + +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(); + } + } +} + int PepperPluginDelegateImpl::GetRoutingId() const { return render_view_->routing_id(); } diff --git a/content/renderer/pepper_plugin_delegate_impl.h b/content/renderer/pepper_plugin_delegate_impl.h index cf8d2c3..f8dcd56 100644 --- a/content/renderer/pepper_plugin_delegate_impl.h +++ b/content/renderer/pepper_plugin_delegate_impl.h @@ -45,6 +45,7 @@ class PluginModule; namespace WebKit { class WebFileChooserCompletion; +class WebMouseEvent; struct WebFileChooserParams; } @@ -168,6 +169,15 @@ class PepperPluginDelegateImpl // Returns whether or not a Pepper plugin is focused. bool IsPluginFocused() const; + // 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(); + // Dispatches mouse events directly to the owner of the mouse lock. + // True indicates currently the mouse is locked and the event has been + // dispatched to the owner. + bool DispatchLockedMouseEvent(const WebKit::WebMouseEvent& event); + // PluginDelegate implementation. virtual void PluginFocusChanged(bool focused) OVERRIDE; virtual void PluginCrashed(webkit::ppapi::PluginInstance* instance); @@ -305,6 +315,8 @@ class PepperPluginDelegateImpl virtual base::SharedMemory* CreateAnonymousSharedMemory(uint32_t size) OVERRIDE; virtual ::ppapi::Preferences GetPreferences() OVERRIDE; + virtual void LockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE; + virtual void UnlockMouse(webkit::ppapi::PluginInstance* instance) OVERRIDE; CONTENT_EXPORT int GetRoutingId() const; @@ -317,6 +329,10 @@ class PepperPluginDelegateImpl scoped_refptr<PpapiBrokerImpl> CreatePpapiBroker( webkit::ppapi::PluginModule* plugin_module); + bool MouseLockedOrPending() const { + return mouse_locked_ || pending_lock_request_ || pending_unlock_request_; + } + // Pointer to the RenderView that owns us. RenderView* render_view_; @@ -347,6 +363,21 @@ class PepperPluginDelegateImpl // been updated. std::set<webkit::ppapi::PluginInstance*> subscribed_to_policy_updates_; + // |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_; + DISALLOW_COPY_AND_ASSIGN(PepperPluginDelegateImpl); }; diff --git a/content/renderer/render_view.cc b/content/renderer/render_view.cc index aa45119..46e03c0 100644 --- a/content/renderer/render_view.cc +++ b/content/renderer/render_view.cc @@ -760,6 +760,8 @@ bool RenderView::OnMessageReceived(const IPC::Message& message) { OnSetHistoryLengthAndPrune) IPC_MESSAGE_HANDLER(ViewMsg_EnableViewSourceMode, OnEnableViewSourceMode) IPC_MESSAGE_HANDLER(IntentsMsg_WebIntentReply, OnWebIntentReply); + IPC_MESSAGE_HANDLER(ViewMsg_LockMouse_ACK, OnLockMouseACK) + IPC_MESSAGE_HANDLER(ViewMsg_MouseLockLost, OnMouseLockLost) // Have the super handle all other messages. IPC_MESSAGE_UNHANDLED(handled = RenderWidget::OnMessageReceived(message)) @@ -4185,6 +4187,10 @@ void RenderView::DidHandleKeyEvent() { edit_commands_.clear(); } +bool RenderView::WillHandleMouseEvent(const WebKit::WebMouseEvent& event) { + return pepper_delegate_.DispatchLockedMouseEvent(event); +} + void RenderView::DidHandleMouseEvent(const WebKit::WebMouseEvent& event) { FOR_EACH_OBSERVER(RenderViewObserver, observers_, DidHandleMouseEvent(event)); } @@ -4636,3 +4642,12 @@ void RenderView::OnEnableViewSourceMode() { return; main_frame->enableViewSourceMode(true); } + +void RenderView::OnLockMouseACK(bool succeeded) { + pepper_delegate_.OnLockMouseACK(succeeded); +} + +void RenderView::OnMouseLockLost() { + pepper_delegate_.OnMouseLockLost(); +} + diff --git a/content/renderer/render_view.h b/content/renderer/render_view.h index db207a4..8f95fb3 100644 --- a/content/renderer/render_view.h +++ b/content/renderer/render_view.h @@ -648,6 +648,8 @@ class RenderView : public RenderWidget, gfx::Rect* clip); virtual gfx::Point GetScrollOffset(); virtual void DidHandleKeyEvent(); + virtual bool WillHandleMouseEvent( + const WebKit::WebMouseEvent& event) OVERRIDE; virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event); virtual void OnSetFocus(bool enable); virtual void OnWasHidden(); @@ -829,8 +831,10 @@ class RenderView : 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 OnMouseLockLost(); void OnMoveOrResizeStarted(); void OnNavigate(const ViewMsg_Navigate_Params& params); void OnPaste(); diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 7b2f48a..3f02e07 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -446,10 +446,16 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) { if (input_event->type == WebInputEvent::RawKeyDown) message.ReadBool(&iter, &is_keyboard_shortcut); - bool processed = false; + bool prevent_default = false; + if (WebInputEvent::isMouseEventType(input_event->type)) { + prevent_default = WillHandleMouseEvent( + *(static_cast<const WebMouseEvent*>(input_event))); + } + + bool processed = prevent_default; if (input_event->type != WebInputEvent::Char || !suppress_next_char_events_) { suppress_next_char_events_ = false; - if (webwidget_) + if (!processed && webwidget_) processed = webwidget_->handleInputEvent(*input_event); } @@ -482,10 +488,12 @@ void RenderWidget::OnHandleInputEvent(const IPC::Message& message) { handling_input_event_ = false; - if (WebInputEvent::isKeyboardEventType(input_event->type)) - DidHandleKeyEvent(); - if (WebInputEvent::isMouseEventType(input_event->type)) - DidHandleMouseEvent(*(static_cast<const WebMouseEvent*>(input_event))); + if (!prevent_default) { + if (WebInputEvent::isKeyboardEventType(input_event->type)) + DidHandleKeyEvent(); + if (WebInputEvent::isMouseEventType(input_event->type)) + DidHandleMouseEvent(*(static_cast<const WebMouseEvent*>(input_event))); + } } void RenderWidget::OnMouseCaptureLost() { @@ -1393,3 +1401,7 @@ void RenderWidget::CleanupWindowInPluginMoves(gfx::PluginWindowHandle window) { } } } + +bool RenderWidget::WillHandleMouseEvent(const WebKit::WebMouseEvent& event) { + return false; +} diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index 05a7d5f..0980575 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h @@ -42,6 +42,7 @@ class PlatformCanvas; } namespace WebKit { +class WebInputEvent; class WebMouseEvent; class WebWidget; struct WebPopupMenuInfo; @@ -300,6 +301,12 @@ class RenderWidget : public IPC::Channel::Listener, // just handled. virtual void DidHandleKeyEvent() {} + // Called by OnHandleInputEvent() to notify subclasses that a mouse event is + // about to be handled. + // Returns true if no further handling is needed. In that case, the event + // won't be sent to WebKit or trigger DidHandleMouseEvent(). + virtual bool WillHandleMouseEvent(const WebKit::WebMouseEvent& event); + // Called by OnHandleInputEvent() to notify subclasses that a mouse event was // just handled. virtual void DidHandleMouseEvent(const WebKit::WebMouseEvent& event) {} |