Bug 1003943 - Properly update widget cursors when the mouse passes between remote frames. r=smaug
authorJim Mathies <jmathies@mozilla.com>
Tue, 27 May 2014 20:12:29 -0500
changeset 204425 fda0bc06b42b58397f86b4c593f848c2b2908cba
parent 204424 4073b066636a39d15a967f9ffc050b1e19bca685
child 204426 f05d51941f4c8378b40f4775e3708c5938acd4ae
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1003943
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1003943 - Properly update widget cursors when the mouse passes between remote frames. r=smaug
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
widget/gtk/nsWindow.cpp
widget/nsIWidget.h
widget/qt/nsWindow.cpp
widget/xpwidgets/PuppetWidget.cpp
widget/xpwidgets/nsBaseWidget.cpp
widget/xpwidgets/nsBaseWidget.h
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -553,16 +553,27 @@ EventStateManager::PreHandleEvent(nsPres
       case WidgetMouseEvent::eRightButton:
       case WidgetMouseEvent::eMiddleButton:
         SetClickCount(aPresContext, mouseEvent, aStatus);
         break;
     }
     break;
   }
   case NS_MOUSE_EXIT:
+    // If this is a remote frame, we receive NS_MOUSE_EXIT from the parent
+    // the mouse exits our content. Since the parent may update the cursor
+    // while the mouse is outside our frame, and since PuppetWidget caches the
+    // current cursor internally, re-entering our content (say from over a
+    // window edge) wont update the cursor if the cached value and the current
+    // cursor match. So when the mouse exits a remote frame, clear the cached
+    // widget cursor so a proper update will occur when the mouse re-enters.
+    if (XRE_GetProcessType() == GeckoProcessType_Content) {
+      ClearCachedWidgetCursor(mCurrentTarget);
+    }
+
     // If the event is not a top-level window exit, then it's not
     // really an exit --- we may have traversed widget boundaries but
     // we're still in our toplevel window.
     if (mouseEvent->exit != WidgetMouseEvent::eTopLevel) {
       // Treat it as a synthetic move so we don't generate spurious
       // "exit" or "move" events.  Any necessary "out" or "over" events
       // will be generated by GenerateMouseEnterExit
       mouseEvent->message = NS_MOUSE_MOVE;
@@ -3324,16 +3335,29 @@ EventStateManager::UpdateCursor(nsPresCo
               aTargetFrame->GetNearestWidget(), false);
   }
 
   if (mLockCursor || NS_STYLE_CURSOR_AUTO != cursor) {
     *aStatus = nsEventStatus_eConsumeDoDefault;
   }
 }
 
+void
+EventStateManager::ClearCachedWidgetCursor(nsIFrame* aTargetFrame)
+{
+  if (!aTargetFrame) {
+    return;
+  }
+  nsIWidget* aWidget = aTargetFrame->GetNearestWidget();
+  if (!aWidget) {
+    return;
+  }
+  aWidget->ClearCachedCursor();
+}
+
 nsresult
 EventStateManager::SetCursor(int32_t aCursor, imgIContainer* aContainer,
                              bool aHaveHotspot,
                              float aHotspotX, float aHotspotY,
                              nsIWidget* aWidget, bool aLockCursor)
 {
   EnsureDocument(mPresContext);
   NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -266,16 +266,25 @@ protected:
   };
 
   /**
    * Get appropriate access modifier mask for the aDocShell.  Returns -1 if
    * access key isn't available.
    */
   static int32_t GetAccessModifierMaskFor(nsISupports* aDocShell);
 
+  /*
+   * If aTargetFrame's widget has a cached cursor value, resets the cursor
+   * such that the next call to SetCursor on the widget will force an update
+   * of the native cursor. For use in getting puppet widget to update its
+   * cursor between mouse exit / enter transitions. This call basically wraps
+   * nsIWidget ClearCachedCursor.
+   */
+  void ClearCachedWidgetCursor(nsIFrame* aTargetFrame);
+
   void UpdateCursor(nsPresContext* aPresContext,
                     WidgetEvent* aEvent,
                     nsIFrame* aTargetFrame,
                     nsEventStatus* aStatus);
   /**
    * Turn a GUI mouse/pointer event into a mouse/pointer event targeted at the specified
    * content.  This returns the primary frame for the content (or null
    * if it goes away during the event).
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -221,17 +221,26 @@ parent:
      */
     sync GetDefaultScale() returns (double value);
 
     /**
      * Return native data of root widget
      */
     sync GetWidgetNativeData() returns (WindowsHandle value);
 
-    SetCursor(uint32_t value);
+    /**
+     * Set the native cursor.
+     * @param value
+     *   The widget cursor to set.
+     * @param force
+     *   Invalidate any locally cached cursor settings and force an
+     *   update.
+     */
+    SetCursor(uint32_t value, bool force);
+
     SetBackgroundColor(nscolor color);
 
     /**
      * Used to set the current text of the status tooltip.
      * Nowadays this is mainly used for link locations on hover.
      */
     SetStatus(uint32_t type, nsString status);
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1012,20 +1012,23 @@ TabParent::RecvAsyncMessage(const nsStri
   }
 
   StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
   CpowIdHolder cpows(parent->GetCPOWManager(), aCpows);
   return ReceiveMessage(aMessage, false, &cloneData, &cpows, aPrincipal, nullptr);
 }
 
 bool
-TabParent::RecvSetCursor(const uint32_t& aCursor)
+TabParent::RecvSetCursor(const uint32_t& aCursor, const bool& aForce)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
+    if (aForce) {
+      widget->ClearCachedCursor();
+    }
     widget->SetCursor((nsCursor) aCursor);
   }
   return true;
 }
 
 bool
 TabParent::RecvSetBackgroundColor(const nscolor& aColor)
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -163,17 +163,17 @@ public:
     virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
                                      const int32_t& aIMEOpen,
                                      const nsString& aType,
                                      const nsString& aInputmode,
                                      const nsString& aActionHint,
                                      const int32_t& aCause,
                                      const int32_t& aFocusChange) MOZ_OVERRIDE;
     virtual bool RecvRequestFocus(const bool& aCanRaise) MOZ_OVERRIDE;
-    virtual bool RecvSetCursor(const uint32_t& aValue) MOZ_OVERRIDE;
+    virtual bool RecvSetCursor(const uint32_t& aValue, const bool& aForce) MOZ_OVERRIDE;
     virtual bool RecvSetBackgroundColor(const nscolor& aValue) MOZ_OVERRIDE;
     virtual bool RecvSetStatus(const uint32_t& aType, const nsString& aStatus) MOZ_OVERRIDE;
     virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible);
     virtual bool RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip);
     virtual bool RecvHideTooltip();
     virtual bool RecvGetDPI(float* aValue) MOZ_OVERRIDE;
     virtual bool RecvGetDefaultScale(double* aValue) MOZ_OVERRIDE;
     virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) MOZ_OVERRIDE;
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1502,18 +1502,19 @@ nsWindow::SetCursor(nsCursor aCursor)
         nsWindow *window = GetContainerWindow();
         if (!window)
             return NS_ERROR_FAILURE;
 
         return window->SetCursor(aCursor);
     }
 
     // Only change cursor if it's actually been changed
-    if (aCursor != mCursor) {
+    if (aCursor != mCursor || mUpdateCursor) {
         GdkCursor *newCursor = nullptr;
+        mUpdateCursor = false;
 
         newCursor = get_gtk_cursor(aCursor);
 
         if (nullptr != newCursor) {
             mCursor = aCursor;
 
             if (!mContainer)
                 return NS_OK;
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -95,18 +95,18 @@ typedef void* nsNativeWidget;
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102
 #define NS_NATIVE_ICOREWINDOW          103 // winrt specific
 #endif
 
 #define NS_IWIDGET_IID \
-{ 0x91944a4b, 0xbc29, 0x44aa, \
-  { 0x99, 0x21, 0x42, 0xeb, 0x1f, 0xbb, 0xa6, 0x89 } }
+{ 0x5b27abd6, 0x9e53, 0x4a0a, \
+  { 0x86, 0xf, 0x77, 0x5c, 0xc5, 0x69, 0x35, 0xf } };
 
 /*
  * Window shadow styles
  * Also used for the -moz-window-shadow CSS property
  */
 
 #define NS_STYLE_WINDOW_SHADOW_NONE             0
 #define NS_STYLE_WINDOW_SHADOW_DEFAULT          1
@@ -1161,16 +1161,23 @@ class nsIWidget : public nsISupports {
      * Set the cursor for this widget
      *
      * @param aCursor the new cursor for this widget
      */
 
     NS_IMETHOD SetCursor(nsCursor aCursor) = 0;
 
     /**
+     * If a cursor type is currently cached locally for this widget, clear the
+     * cached cursor to force an update on the next SetCursor call.
+     */
+
+    virtual void ClearCachedCursor() = 0;
+
+    /**
      * Sets an image as the cursor for this widget.
      *
      * @param aCursor the cursor to set
      * @param aX the X coordinate of the hotspot (from left).
      * @param aY the Y coordinate of the hotspot (from top).
      * @retval NS_ERROR_NOT_IMPLEMENTED if setting images as cursors is not
      *         supported
      */
--- a/widget/qt/nsWindow.cpp
+++ b/widget/qt/nsWindow.cpp
@@ -804,20 +804,20 @@ nsWindow::GetGLFrameBufferFormat()
         return LOCAL_GL_RGB;
     }
     return LOCAL_GL_NONE;
 }
 
 NS_IMETHODIMP
 nsWindow::SetCursor(nsCursor aCursor)
 {
-    if (mCursor == aCursor) {
+    if (mCursor == aCursor && !mUpdateCursor) {
         return NS_OK;
     }
-
+    mUpdateCursor = false;
     mCursor = aCursor;
     if (mWidget) {
         mWidget->SetCursor(mCursor);
     }
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -644,25 +644,27 @@ PuppetWidget::NotifyIMEOfSelectionChange
       aIMENotification.mSelectionChangeData.mCausedByComposition);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PuppetWidget::SetCursor(nsCursor aCursor)
 {
-  if (mCursor == aCursor) {
+  if (mCursor == aCursor && !mUpdateCursor) {
     return NS_OK;
   }
 
-  if (mTabChild && !mTabChild->SendSetCursor(aCursor)) {
+  if (mTabChild &&
+      !mTabChild->SendSetCursor(aCursor, mUpdateCursor)) {
     return NS_ERROR_FAILURE;
   }
 
   mCursor = aCursor;
+  mUpdateCursor = false;
 
   return NS_OK;
 }
 
 nsresult
 PuppetWidget::Paint()
 {
   NS_ABORT_IF_FALSE(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
--- a/widget/xpwidgets/nsBaseWidget.cpp
+++ b/widget/xpwidgets/nsBaseWidget.cpp
@@ -106,16 +106,17 @@ nsAutoRollup::~nsAutoRollup()
 //
 //-------------------------------------------------------------------------
 
 nsBaseWidget::nsBaseWidget()
 : mWidgetListener(nullptr)
 , mAttachedWidgetListener(nullptr)
 , mContext(nullptr)
 , mCursor(eCursor_standard)
+, mUpdateCursor(true)
 , mBorderStyle(eBorderStyle_none)
 , mUseLayersAcceleration(false)
 , mForceLayersAcceleration(false)
 , mTemporarilyUseBasicLayerManager(false)
 , mUseAttachedEvents(false)
 , mContextInitialized(false)
 , mBounds(0,0,0,0)
 , mOriginalBounds(nullptr)
--- a/widget/xpwidgets/nsBaseWidget.h
+++ b/widget/xpwidgets/nsBaseWidget.h
@@ -110,16 +110,17 @@ public:
   {
     return mSizeMode;
   }
 
   virtual nsCursor        GetCursor();
   NS_IMETHOD              SetCursor(nsCursor aCursor);
   NS_IMETHOD              SetCursor(imgIContainer* aCursor,
                                     uint32_t aHotspotX, uint32_t aHotspotY);
+  virtual void            ClearCachedCursor() { mUpdateCursor = true; }
   virtual void            SetTransparencyMode(nsTransparencyMode aMode);
   virtual nsTransparencyMode GetTransparencyMode();
   virtual void            GetWindowClipRegion(nsTArray<nsIntRect>* aRects);
   NS_IMETHOD              SetWindowShadowStyle(int32_t aStyle);
   virtual void            SetShowsToolbarButton(bool aShow) {}
   virtual void            SetShowsFullScreenButton(bool aShow) {}
   virtual void            SetWindowAnimationType(WindowAnimationType aType) {}
   NS_IMETHOD              HideWindowChrome(bool aShouldHide);
@@ -397,16 +398,17 @@ protected:
   nsIWidgetListener* mAttachedWidgetListener;
   nsDeviceContext* mContext;
   nsRefPtr<LayerManager> mLayerManager;
   nsRefPtr<LayerManager> mBasicLayerManager;
   nsRefPtr<CompositorChild> mCompositorChild;
   nsRefPtr<CompositorParent> mCompositorParent;
   nsRefPtr<WidgetShutdownObserver> mShutdownObserver;
   nsCursor          mCursor;
+  bool              mUpdateCursor;
   nsBorderStyle     mBorderStyle;
   bool              mUseLayersAcceleration;
   bool              mForceLayersAcceleration;
   bool              mTemporarilyUseBasicLayerManager;
   // Windows with out-of-process tabs always require OMTC. This flag designates
   // such windows.
   bool              mRequireOffMainThreadCompositing;
   bool              mUseAttachedEvents;