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 185288 fda0bc06b42b58397f86b4c593f848c2b2908cba
parent 185287 4073b066636a39d15a967f9ffc050b1e19bca685
child 185289 f05d51941f4c8378b40f4775e3708c5938acd4ae
push id7422
push usercbook@mozilla.com
push dateWed, 28 May 2014 12:57:41 +0000
treeherderb2g-inbound@2dde931a8034 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1003943
milestone32.0a1
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;