Bug 1520502 - Set the standard cursor and the custom cursor in the same IPC message. r=jmathies
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 15 Jan 2019 14:56:52 +0100
changeset 512657 edfd8a13603c0d5ebb69362dff02bb138f616127
parent 512656 9ae7ae0acee4f8807cffaf64fef4f2e3a614b997
child 512658 da799f611f33fc367cb49193872709c2cd89fd8a
child 512670 341040a5fb8064aef7dc5c520709019716ec222b
push id10566
push userarchaeopteryx@coole-files.de
push dateMon, 28 Jan 2019 12:41:12 +0000
treeherdermozilla-beta@69a3d7c8d04b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmathies
bugs1520502, 1445844
milestone66.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 1520502 - Set the standard cursor and the custom cursor in the same IPC message. r=jmathies This cleans up a bit and allows us to be smarter about which cursors should we allow from content or what not, which will help with bug 1445844 and co. Differential Revision: https://phabricator.services.mozilla.com/D16711
docshell/base/nsDocShell.cpp
dom/events/EventStateManager.cpp
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/android/nsWindow.h
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaWindow.h
widget/cocoa/nsCocoaWindow.mm
widget/gtk/nsWindow.cpp
widget/gtk/nsWindow.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -6386,33 +6386,33 @@ nsDocShell::OnStateChange(nsIWebProgress
     mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
 
     if ((aStateFlags & STATE_RESTORING) == 0) {
       // Show the progress cursor if the pref is set
       if (nsContentUtils::UseActivityCursor()) {
         nsCOMPtr<nsIWidget> mainWidget;
         GetMainWidget(getter_AddRefs(mainWidget));
         if (mainWidget) {
-          mainWidget->SetCursor(eCursor_spinning);
+          mainWidget->SetCursor(eCursor_spinning, nullptr, 0, 0);
         }
       }
     }
   } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
     // Page is loading
     mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
   } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
     // Page has finished loading
     mBusyFlags = BUSY_FLAGS_NONE;
 
     // Hide the progress cursor if the pref is set
     if (nsContentUtils::UseActivityCursor()) {
       nsCOMPtr<nsIWidget> mainWidget;
       GetMainWidget(getter_AddRefs(mainWidget));
       if (mainWidget) {
-        mainWidget->SetCursor(eCursor_standard);
+        mainWidget->SetCursor(eCursor_standard, nullptr, 0, 0);
       }
     }
   }
   if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
     nsCOMPtr<nsIWebProgress> webProgress =
         do_QueryInterface(GetAsSupports(this));
     // Is the document stop notification for this document?
     if (aProgress == webProgress.get()) {
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3821,19 +3821,19 @@ nsresult EventStateManager::SetCursor(St
       c = eCursor_ew_resize;
       break;
     case StyleCursorKind::None:
       c = eCursor_none;
       break;
   }
 
   // First, try the imgIContainer, if non-null
-  nsresult rv = NS_ERROR_FAILURE;
+  uint32_t hotspotX = 0;
+  uint32_t hotspotY = 0;
   if (aContainer) {
-    uint32_t hotspotX, hotspotY;
 
     // css3-ui says to use the CSS-specified hotspot if present,
     // otherwise use the intrinsic hotspot, otherwise use the top left
     // corner.
     if (aHaveHotspot) {
       int32_t imgWidth, imgHeight;
       aContainer->GetWidth(&imgWidth);
       aContainer->GetHeight(&imgHeight);
@@ -3854,22 +3854,19 @@ nsresult EventStateManager::SetCursor(St
                    getter_AddRefs(hotspotXWrap));
         props->Get("hotspotY", NS_GET_IID(nsISupportsPRUint32),
                    getter_AddRefs(hotspotYWrap));
 
         if (hotspotXWrap) hotspotXWrap->GetData(&hotspotX);
         if (hotspotYWrap) hotspotYWrap->GetData(&hotspotY);
       }
     }
-
-    rv = aWidget->SetCursor(aContainer, hotspotX, hotspotY);
-  }
-
-  if (NS_FAILED(rv)) aWidget->SetCursor(c);
-
+  }
+
+  aWidget->SetCursor(c, aContainer, hotspotX, hotspotY);
   return NS_OK;
 }
 
 class MOZ_STACK_CLASS ESMEventCB : public EventDispatchingCallback {
  public:
   explicit ESMEventCB(nsIContent* aTarget) : mTarget(aTarget) {}
 
   void HandleEvent(EventChainPostVisitor& aVisitor) override {
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -381,25 +381,20 @@ parent:
 
     nested(inside_cpow) async SetInputContext(InputContext context,
                                               InputContextAction action);
 
     /**
      * Set the native cursor.
      * @param value
      *   The widget cursor to set.
-     * @param force
-     *   Invalidate any locally cached cursor settings and force an
-     *   update.
-     */
-    async SetCursor(nsCursor value, bool force);
-
-    /**
-     * Set the native cursor using a custom image.
-     * @param cursorData
+     * @param hasCustomCursor
+     *   Whether there's any custom cursor represented by cursorData and
+     *   company.
+     * @param customCursorData
      *   Serialized image data.
      * @param width
      *   Width of the image.
      * @param height
      *   Height of the image.
      * @param stride
      *   Stride used in the image data.
      * @param format
@@ -407,19 +402,22 @@ parent:
      * @param hotspotX
      *   Horizontal hotspot of the image, as specified by the css cursor property.
      * @param hotspotY
      *   Vertical hotspot of the image, as specified by the css cursor property.
      * @param force
      *   Invalidate any locally cached cursor settings and force an
      *   update.
      */
-    async SetCustomCursor(nsCString cursorData, uint32_t width, uint32_t height,
-                          uint32_t stride, SurfaceFormat format,
-                          uint32_t hotspotX, uint32_t hotspotY, bool force);
+    async SetCursor(nsCursor value,
+                    bool hasCustomCursor,
+                    nsCString customCursorData,
+                    uint32_t width, uint32_t height,
+                    uint32_t stride, SurfaceFormat format,
+                    uint32_t hotspotX, uint32_t hotspotY, bool force);
 
     /**
      * Used to set the current text of the status tooltip.
      * Nowadays this is mainly used for link locations on hover.
      */
     async SetStatus(uint32_t type, nsString status);
 
     /**
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1033,21 +1033,19 @@ void TabParent::SendRealMouseEvent(Widge
   aEvent.mRefPoint += GetChildProcessOffset();
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     // When we mouseenter the tab, the tab's cursor should
     // become the current cursor.  When we mouseexit, we stop.
     if (eMouseEnterIntoWidget == aEvent.mMessage) {
       mTabSetsCursor = true;
-      if (mCustomCursor) {
-        widget->SetCursor(mCustomCursor, mCustomCursorHotspotX,
+      if (mCursor != eCursorInvalid) {
+        widget->SetCursor(mCursor, mCustomCursor, mCustomCursorHotspotX,
                           mCustomCursorHotspotY);
-      } else if (mCursor != eCursorInvalid) {
-        widget->SetCursor(mCursor);
       }
     } else if (eMouseExitFromWidget == aEvent.mMessage) {
       mTabSetsCursor = false;
     }
   }
   if (!mIsReadyToHandleInputEvents) {
     if (eMouseEnterIntoWidget == aEvent.mMessage) {
       mIsMouseEnterIntoWidgetEventSuppressed = true;
@@ -1627,65 +1625,55 @@ mozilla::ipc::IPCResult TabParent::RecvA
 
   CrossProcessCpowHolder cpows(Manager(), aCpows);
   if (!ReceiveMessage(aMessage, false, &data, &cpows, aPrincipal, nullptr)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult TabParent::RecvSetCursor(const nsCursor& aCursor,
-                                                 const bool& aForce) {
-  mCursor = aCursor;
-  mCustomCursor = nullptr;
-
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  if (widget) {
-    if (aForce) {
-      widget->ClearCachedCursor();
-    }
-    if (mTabSetsCursor) {
-      widget->SetCursor(mCursor);
-    }
-  }
-  return IPC_OK();
-}
-
-mozilla::ipc::IPCResult TabParent::RecvSetCustomCursor(
+mozilla::ipc::IPCResult TabParent::RecvSetCursor(
+    const nsCursor& aCursor,
+    const bool& aHasCustomCursor,
     const nsCString& aCursorData, const uint32_t& aWidth,
     const uint32_t& aHeight, const uint32_t& aStride,
     const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX,
     const uint32_t& aHotspotY, const bool& aForce) {
-  mCursor = eCursorInvalid;
-
   nsCOMPtr<nsIWidget> widget = GetWidget();
-  if (widget) {
-    if (aForce) {
-      widget->ClearCachedCursor();
-    }
-
-    if (mTabSetsCursor) {
-      const gfx::IntSize size(aWidth, aHeight);
-
-      RefPtr<gfx::DataSourceSurface> customCursor =
-          gfx::CreateDataSourceSurfaceFromData(
-              size, aFormat,
-              reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()),
-              aStride);
-
-      RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size);
-      nsCOMPtr<imgIContainer> cursorImage(
-          image::ImageOps::CreateFromDrawable(drawable));
-      widget->SetCursor(cursorImage, aHotspotX, aHotspotY);
-      mCustomCursor = cursorImage;
-      mCustomCursorHotspotX = aHotspotX;
-      mCustomCursorHotspotY = aHotspotY;
-    }
+  if (!widget) {
+    return IPC_OK();
+  }
+
+  if (aForce) {
+    widget->ClearCachedCursor();
+  }
+
+  if (!mTabSetsCursor) {
+    return IPC_OK();
   }
 
+  nsCOMPtr<imgIContainer> cursorImage;
+  if (aHasCustomCursor) {
+    const gfx::IntSize size(aWidth, aHeight);
+    RefPtr<gfx::DataSourceSurface> customCursor =
+        gfx::CreateDataSourceSurfaceFromData(
+            size, aFormat,
+            reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()),
+            aStride);
+
+    RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size);
+    cursorImage = image::ImageOps::CreateFromDrawable(drawable);
+  }
+
+  widget->SetCursor(aCursor, cursorImage, aHotspotX, aHotspotY);
+  mCursor = aCursor;
+  mCustomCursor = cursorImage;
+  mCustomCursorHotspotX = aHotspotX;
+  mCustomCursorHotspotY = aHotspotY;
+
   return IPC_OK();
 }
 
 nsIXULBrowserWindow* TabParent::GetXULBrowserWindow() {
   if (!mFrameElement) {
     return nullptr;
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -255,24 +255,25 @@ class TabParent final : public PBrowserP
       const nsString& aText, nsTArray<mozilla::FontRange>&& aFontRangeArray,
       const bool& aIsVertical, const LayoutDeviceIntPoint& aPoint) override;
 
   virtual mozilla::ipc::IPCResult RecvEnableDisableCommands(
       const nsString& aAction, nsTArray<nsCString>&& aEnabledCommands,
       nsTArray<nsCString>&& aDisabledCommands) override;
 
   virtual mozilla::ipc::IPCResult RecvSetCursor(const nsCursor& aValue,
+                                                const bool& aHasCustomCursor,
+                                                const nsCString& aUri,
+                                                const uint32_t& aWidth, const uint32_t& aHeight,
+                                                const uint32_t& aStride,
+                                                const gfx::SurfaceFormat& aFormat,
+                                                const uint32_t& aHotspotX,
+                                                const uint32_t& aHotspotY,
                                                 const bool& aForce) override;
 
-  virtual mozilla::ipc::IPCResult RecvSetCustomCursor(
-      const nsCString& aUri, const uint32_t& aWidth, const uint32_t& aHeight,
-      const uint32_t& aStride, const gfx::SurfaceFormat& aFormat,
-      const uint32_t& aHotspotX, const uint32_t& aHotspotY,
-      const bool& aForce) override;
-
   virtual mozilla::ipc::IPCResult RecvSetStatus(
       const uint32_t& aType, const nsString& aStatus) override;
 
   virtual mozilla::ipc::IPCResult RecvShowTooltip(
       const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip,
       const nsString& aDirection) override;
 
   virtual mozilla::ipc::IPCResult RecvHideTooltip() override;
--- a/widget/PuppetWidget.cpp
+++ b/widget/PuppetWidget.cpp
@@ -893,80 +893,75 @@ nsresult PuppetWidget::NotifyIMEOfPositi
   if (mIMENotificationRequestsOfParent.WantPositionChanged()) {
     mTabChild->SendNotifyIMEPositionChange(mContentCache, aIMENotification);
   } else {
     mTabChild->SendUpdateContentCache(mContentCache);
   }
   return NS_OK;
 }
 
-void PuppetWidget::SetCursor(nsCursor aCursor) {
+struct CursorSurface {
+  UniquePtr<char[]> mData;
+  IntSize mSize;
+};
+
+void PuppetWidget::SetCursor(nsCursor aCursor,
+                             imgIContainer* aCursorImage,
+                             uint32_t aHotspotX,
+                             uint32_t aHotspotY) {
+  if (!mTabChild) {
+    return;
+  }
+
   // Don't cache on windows, Windowless flash breaks this via async cursor
   // updates.
 #if !defined(XP_WIN)
-  if (mCursor == aCursor && !mCustomCursor && !mUpdateCursor) {
+  if (!mUpdateCursor && mCursor == aCursor && mCustomCursor == aCursorImage &&
+      (!aCursorImage ||
+       (mCursorHotspotX == aHotspotX && mCursorHotspotY == aHotspotY))) {
     return;
   }
 #endif
 
+  bool hasCustomCursor = false;
+  UniquePtr<char[]> customCursorData;
+  size_t length = 0;
+  IntSize customCursorSize;
+  int32_t stride = 0;
+  auto format = SurfaceFormat::B8G8R8A8;
+  bool force = mUpdateCursor;
+
+  if (aCursorImage) {
+    RefPtr<SourceSurface> surface = aCursorImage->GetFrame(
+        imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
+    if (surface) {
+      if (RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface()) {
+        hasCustomCursor = true;
+        customCursorData = nsContentUtils::GetSurfaceData(
+            WrapNotNull(dataSurface), &length, &stride);
+        customCursorSize = dataSurface->GetSize();
+        format = dataSurface->GetFormat();
+      }
+    }
+  }
+
   mCustomCursor = nullptr;
 
-  if (mTabChild && !mTabChild->SendSetCursor(aCursor, mUpdateCursor)) {
+  nsDependentCString cursorData(customCursorData ? customCursorData.get() : "", length);
+  if (!mTabChild->SendSetCursor(aCursor, hasCustomCursor, cursorData,
+                                customCursorSize.width, customCursorSize.height,
+                                stride, format, aHotspotX, aHotspotY, force)) {
     return;
   }
 
   mCursor = aCursor;
-  mUpdateCursor = false;
-}
-
-nsresult PuppetWidget::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                                 uint32_t aHotspotY) {
-  if (!aCursor || !mTabChild) {
-    return NS_OK;
-  }
-
-#if !defined(XP_WIN)
-  if (mCustomCursor == aCursor && mCursorHotspotX == aHotspotX &&
-      mCursorHotspotY == aHotspotY && !mUpdateCursor) {
-    return NS_OK;
-  }
-#endif
-
-  RefPtr<mozilla::gfx::SourceSurface> surface = aCursor->GetFrame(
-      imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE);
-  if (!surface) {
-    return NS_ERROR_FAILURE;
-  }
-
-  RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
-      surface->GetDataSurface();
-  if (!dataSurface) {
-    return NS_ERROR_FAILURE;
-  }
-
-  size_t length;
-  int32_t stride;
-  mozilla::UniquePtr<char[]> surfaceData = nsContentUtils::GetSurfaceData(
-      WrapNotNull(dataSurface), &length, &stride);
-
-  nsDependentCString cursorData(surfaceData.get(), length);
-  mozilla::gfx::IntSize size = dataSurface->GetSize();
-  if (!mTabChild->SendSetCustomCursor(cursorData, size.width, size.height,
-                                      stride, dataSurface->GetFormat(),
-                                      aHotspotX, aHotspotY, mUpdateCursor)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  mCursor = eCursorInvalid;
-  mCustomCursor = aCursor;
+  mCustomCursor = aCursorImage;
   mCursorHotspotX = aHotspotX;
   mCursorHotspotY = aHotspotY;
   mUpdateCursor = false;
-
-  return NS_OK;
 }
 
 void PuppetWidget::ClearCachedCursor() {
   nsBaseWidget::ClearCachedCursor();
   mCustomCursor = nullptr;
 }
 
 nsresult PuppetWidget::Paint() {
--- a/widget/PuppetWidget.h
+++ b/widget/PuppetWidget.h
@@ -193,19 +193,18 @@ class PuppetWidget : public nsBaseWidget
                ? mNativeTextEventDispatcherListener.get()
                : this;
   }
   void SetNativeTextEventDispatcherListener(
       TextEventDispatcherListener* aListener) {
     mNativeTextEventDispatcherListener = aListener;
   }
 
-  virtual void SetCursor(nsCursor aCursor) override;
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCustomCursor,
+                         uint32_t aHotspotX, uint32_t aHotspotY) override;
 
   virtual void ClearCachedCursor() override;
 
   // Gets the DPI of the screen corresponding to this widget.
   // Contacts the parent process which gets the DPI from the
   // proper widget there. TODO: Handle DPI changes that happen
   // later on.
   virtual float GetDPI() override;
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -253,22 +253,18 @@ class nsWindow final : public nsBaseWidg
   virtual LayoutDeviceIntRect GetScreenBounds() override;
   virtual LayoutDeviceIntPoint WidgetToScreenOffset() override;
   virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
                                  nsEventStatus& aStatus) override;
   nsEventStatus DispatchEvent(mozilla::WidgetGUIEvent* aEvent);
   virtual already_AddRefed<nsIScreen> GetWidgetScreen() override;
   virtual nsresult MakeFullScreen(bool aFullScreen,
                                   nsIScreen* aTargetScreen = nullptr) override;
-
-  virtual void SetCursor(nsCursor aCursor) override {}
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
+  void SetCursor(nsCursor aDefaultCursor, imgIContainer* aImageCursor,
+                 uint32_t aHotspotX, uint32_t aHotspotY) override {}
   void* GetNativeData(uint32_t aDataType) override;
   void SetNativeData(uint32_t aDataType, uintptr_t aVal) override;
   virtual nsresult SetTitle(const nsAString& aTitle) override { return NS_OK; }
   virtual MOZ_MUST_USE nsresult GetAttention(int32_t aCycleCount) override {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override;
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -359,19 +359,18 @@ class nsChildView final : public nsBaseW
   static bool ConvertStatus(nsEventStatus aStatus) {
     return aStatus == nsEventStatus_eConsumeNoDefault;
   }
   virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override;
 
   virtual bool WidgetTypeSupportsAcceleration() override;
   virtual bool ShouldUseOffMainThreadCompositing() override;
 
-  virtual void SetCursor(nsCursor aCursor) override;
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursor,
+                         uint32_t aHotspotX, uint32_t aHotspotY) override;
 
   virtual nsresult SetTitle(const nsAString& title) override;
 
   virtual MOZ_MUST_USE nsresult GetAttention(int32_t aCycleCount) override;
 
   virtual bool HasPendingInputEvent() override;
 
   bool SendEventToNativeMenuSystem(NSEvent* aEvent);
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -757,40 +757,38 @@ nsresult nsChildView::SetFocus(bool aRai
   NSWindow* window = [mView window];
   if (window) [window makeFirstResponder:mView];
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 // Override to set the cursor on the mac
-void nsChildView::SetCursor(nsCursor aCursor) {
+void nsChildView::SetCursor(nsCursor aDefaultCursor, imgIContainer* aImageCursor,
+                            uint32_t aHotspotX, uint32_t aHotspotY) {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if ([mView isDragInProgress]) return;  // Don't change the cursor during dragging.
 
-  nsBaseWidget::SetCursor(aCursor);
-  [[nsCursorManager sharedInstance] setCursor:aCursor];
+  if (aImageCursor) {
+    nsresult rv = [[nsCursorManager sharedInstance] setCursorWithImage:aImageCursor
+                                                              hotSpotX:aHotspotX
+                                                              hotSpotY:aHotspotY
+                                                           scaleFactor:BackingScaleFactor()];
+    if (NS_SUCCEEDED(rv)) {
+      return;
+    }
+  }
+
+  nsBaseWidget::SetCursor(aDefaultCursor, nullptr, 0, 0);
+  [[nsCursorManager sharedInstance] setCursor:aDefaultCursor];
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
-// implement to fix "hidden virtual function" warning
-nsresult nsChildView::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
-  nsBaseWidget::SetCursor(aCursor, aHotspotX, aHotspotY);
-  return [[nsCursorManager sharedInstance] setCursorWithImage:aCursor
-                                                     hotSpotX:aHotspotX
-                                                     hotSpotY:aHotspotY
-                                                  scaleFactor:BackingScaleFactor()];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
 #pragma mark -
 
 // Get this component dimension
 LayoutDeviceIntRect nsChildView::GetBounds() {
   return !mView ? mBounds : CocoaPointsToDevPixels([mView frame]);
 }
 
 LayoutDeviceIntRect nsChildView::GetClientBounds() {
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -237,19 +237,18 @@ class nsCocoaWindow final : public nsBas
   }
 
   virtual void Resize(double aWidth, double aHeight, bool aRepaint) override;
   virtual void Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override;
   virtual LayoutDeviceIntRect GetClientBounds() override;
   virtual LayoutDeviceIntRect GetScreenBounds() override;
   void ReportMoveEvent();
   void ReportSizeEvent();
-  virtual void SetCursor(nsCursor aCursor) override;
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursorImage, uint32_t aHotspotX,
+                         uint32_t aHotspotY) override;
 
   CGFloat BackingScaleFactor();
   void BackingScaleFactorChanged();
   virtual double GetDefaultScaleInternal() override;
   virtual int32_t RoundsWidgetCoordinatesTo() override;
 
   mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final {
     return mozilla::DesktopToLayoutDeviceScale(BackingScaleFactor());
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -1624,26 +1624,20 @@ void nsCocoaWindow::BackingScaleFactorCh
 
 int32_t nsCocoaWindow::RoundsWidgetCoordinatesTo() {
   if (BackingScaleFactor() == 2.0) {
     return 2;
   }
   return 1;
 }
 
-void nsCocoaWindow::SetCursor(nsCursor aCursor) {
-  if (mPopupContentView) {
-    mPopupContentView->SetCursor(aCursor);
-  }
-}
-
-nsresult nsCocoaWindow::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX, uint32_t aHotspotY) {
-  if (mPopupContentView) return mPopupContentView->SetCursor(aCursor, aHotspotX, aHotspotY);
-
-  return NS_OK;
+void nsCocoaWindow::SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursorImage,
+                              uint32_t aHotspotX, uint32_t aHotspotY) {
+  if (mPopupContentView)
+    mPopupContentView->SetCursor(aDefaultCursor, aCursorImage, aHotspotX, aHotspotY);
 }
 
 nsresult nsCocoaWindow::SetTitle(const nsAString& aTitle) {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if (!mWindow) {
     return NS_OK;
   }
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -1,10 +1,10 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:expandtab:shiftwidth=4:tabstop=4:
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWindow.h"
 
 #include "mozilla/ArrayUtils.h"
@@ -1429,99 +1429,101 @@ gboolean nsWindow::OnPropertyNotifyEvent
 
   if (GetCurrentTimeGetter()->PropertyNotifyHandler(aWidget, aEvent)) {
     return TRUE;
   }
 
   return FALSE;
 }
 
-void nsWindow::SetCursor(nsCursor aCursor) {
-  // if we're not the toplevel window pass up the cursor request to
-  // the toplevel window to handle it.
-  if (!mContainer && mGdkWindow) {
-    nsWindow *window = GetContainerWindow();
-    if (!window) return;
-
-    window->SetCursor(aCursor);
-    return;
-  }
-
-  // Only change cursor if it's actually been changed
-  if (aCursor != mCursor || mUpdateCursor) {
-    GdkCursor *newCursor = nullptr;
-    mUpdateCursor = false;
-
-    newCursor = get_gtk_cursor(aCursor);
-
-    if (nullptr != newCursor) {
-      mCursor = aCursor;
-
-      if (!mContainer) return;
-
-      gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)),
-                            newCursor);
-    }
-  }
-}
-
-nsresult nsWindow::SetCursor(imgIContainer *aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) {
-  // if we're not the toplevel window pass up the cursor request to
-  // the toplevel window to handle it.
-  if (!mContainer && mGdkWindow) {
-    nsWindow *window = GetContainerWindow();
-    if (!window) return NS_ERROR_FAILURE;
-
-    return window->SetCursor(aCursor, aHotspotX, aHotspotY);
-  }
-
-  mCursor = eCursorInvalid;
-
-  // Get the image's current frame
-  GdkPixbuf *pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursor);
-  if (!pixbuf) return NS_ERROR_NOT_AVAILABLE;
+static GdkCursor *GetCursorForImage(imgIContainer *aCursorImage,
+                                    uint32_t aHotspotX, uint32_t aHotspotY) {
+  if (!aCursorImage) {
+    return nullptr;
+  }
+  GdkPixbuf *pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursorImage);
+  if (!pixbuf) {
+    return nullptr;
+  }
 
   int width = gdk_pixbuf_get_width(pixbuf);
   int height = gdk_pixbuf_get_height(pixbuf);
+
+  auto CleanupPixBuf =
+      mozilla::MakeScopeExit([&]() { g_object_unref(pixbuf); });
+
   // Reject cursors greater than 128 pixels in some direction, to prevent
   // spoofing.
   // XXX ideally we should rescale. Also, we could modify the API to
   // allow trusted content to set larger cursors.
+  //
+  // TODO(emilio, bug 1445844): Unify the solution for this with other
+  // platforms.
   if (width > 128 || height > 128) {
-    g_object_unref(pixbuf);
-    return NS_ERROR_NOT_AVAILABLE;
+    return nullptr;
   }
 
   // Looks like all cursors need an alpha channel (tested on Gtk 2.4.4). This
   // is of course not documented anywhere...
   // So add one if there isn't one yet
   if (!gdk_pixbuf_get_has_alpha(pixbuf)) {
     GdkPixbuf *alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0);
     g_object_unref(pixbuf);
+    pixbuf = alphaBuf;
     if (!alphaBuf) {
-      return NS_ERROR_OUT_OF_MEMORY;
+      return nullptr;
     }
-    pixbuf = alphaBuf;
-  }
-
-  GdkCursor *cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
-                                                 pixbuf, aHotspotX, aHotspotY);
-  g_object_unref(pixbuf);
-  nsresult rv = NS_ERROR_OUT_OF_MEMORY;
-  if (cursor) {
-    if (mContainer) {
-      gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)),
-                            cursor);
-      rv = NS_OK;
+  }
+
+  return gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf,
+                                    aHotspotX, aHotspotY);
+}
+
+void nsWindow::SetCursor(nsCursor aDefaultCursor, imgIContainer *aCursorImage,
+                         uint32_t aHotspotX, uint32_t aHotspotY) {
+  // if we're not the toplevel window pass up the cursor request to
+  // the toplevel window to handle it.
+  if (!mContainer && mGdkWindow) {
+    nsWindow *window = GetContainerWindow();
+    if (!window) return;
+
+    window->SetCursor(aDefaultCursor, aCursorImage, aHotspotX, aHotspotY);
+    return;
+  }
+
+  // Only change cursor if it's actually been changed
+  if (!aCursorImage && aDefaultCursor == mCursor && !mUpdateCursor) {
+    return;
+  }
+
+  mUpdateCursor = false;
+  mCursor = eCursorInvalid;
+
+  // Try to set the cursor image first, and fall back to the numeric cursor.
+  GdkCursor *newCursor = GetCursorForImage(aCursorImage, aHotspotX, aHotspotY);
+  if (!newCursor) {
+    newCursor = get_gtk_cursor(aDefaultCursor);
+    if (newCursor) {
+      mCursor = aDefaultCursor;
     }
-    g_object_unref(cursor);
-  }
-
-  return rv;
+  }
+
+  auto CleanupCursor = mozilla::MakeScopeExit([&]() {
+    // get_gtk_cursor returns a weak reference, which we shouldn't unref.
+    if (newCursor && mCursor == eCursorInvalid) {
+      g_object_unref(newCursor);
+    }
+  });
+
+  if (!newCursor || !mContainer) {
+    return;
+  }
+
+  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(mContainer)),
+                        newCursor);
 }
 
 void nsWindow::Invalidate(const LayoutDeviceIntRect &aRect) {
   if (!mGdkWindow) return;
 
   GdkRectangle rect = DevicePixelsToGdkRectRoundOut(aRect);
   gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
 
@@ -3477,17 +3479,17 @@ nsresult nsWindow::Create(nsIWidget *aPa
       if (mWindowType == eWindowType_popup) {
         // gdk does not automatically set the cursor for "temporary"
         // windows, which are what gtk uses for popups.
 
         mCursor = eCursor_wait;  // force SetCursor to actually set the
                                  // cursor, even though our internal state
                                  // indicates that we already have the
                                  // standard cursor.
-        SetCursor(eCursor_standard);
+        SetCursor(eCursor_standard, nullptr, 0, 0);
 
         if (aInitData->mNoAutoHide) {
           gint wmd = ConvertBorderStyles(mBorderStyle);
           if (wmd != -1)
             gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd);
         }
 
         // If the popup ignores mouse events, set an empty input shape.
--- a/widget/gtk/nsWindow.h
+++ b/widget/gtk/nsWindow.h
@@ -139,19 +139,18 @@ class nsWindow final : public nsBaseWidg
   void SetZIndex(int32_t aZIndex) override;
   virtual void SetSizeMode(nsSizeMode aMode) override;
   virtual void Enable(bool aState) override;
   virtual nsresult SetFocus(bool aRaise = false) override;
   virtual LayoutDeviceIntRect GetScreenBounds() override;
   virtual LayoutDeviceIntRect GetClientBounds() override;
   virtual LayoutDeviceIntSize GetClientSize() override;
   virtual LayoutDeviceIntPoint GetClientOffset() override;
-  virtual void SetCursor(nsCursor aCursor) override;
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursor,
+                         uint32_t aHotspotX, uint32_t aHotspotY) override;
   virtual void Invalidate(const LayoutDeviceIntRect& aRect) override;
   virtual void* GetNativeData(uint32_t aDataType) override;
   virtual nsresult SetTitle(const nsAString& aTitle) override;
   virtual void SetIcon(const nsAString& aIconSpec) override;
   virtual void SetWindowClass(const nsAString& xulWinType) override;
   virtual LayoutDeviceIntPoint WidgetToScreenOffset() override;
   virtual void CaptureMouse(bool aCapture) override;
   virtual void CaptureRollupEvents(nsIRollupListener* aListener,
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -635,21 +635,20 @@ void nsBaseWidget::SetSizeMode(nsSizeMod
 }
 
 //-------------------------------------------------------------------------
 //
 // Get this component cursor
 //
 //-------------------------------------------------------------------------
 
-void nsBaseWidget::SetCursor(nsCursor aCursor) { mCursor = aCursor; }
-
-nsresult nsBaseWidget::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                                 uint32_t aHotspotY) {
-  return NS_ERROR_NOT_IMPLEMENTED;
+void nsBaseWidget::SetCursor(nsCursor aCursor,
+                             imgIContainer*, uint32_t, uint32_t) {
+  // We don't support the cursor image.
+  mCursor = aCursor;
 }
 
 //-------------------------------------------------------------------------
 //
 // Window transparency methods
 //
 //-------------------------------------------------------------------------
 
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -163,19 +163,18 @@ class nsBaseWidget : public nsIWidget, p
   virtual void PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
                            nsIWidget* aWidget, bool aActivate) override {}
 
   virtual void SetSizeMode(nsSizeMode aMode) override;
   virtual nsSizeMode SizeMode() override { return mSizeMode; }
 
   virtual bool IsFullyOccluded() const override { return mIsFullyOccluded; }
 
-  virtual void SetCursor(nsCursor aCursor) override;
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursor,
+                         uint32_t aHotspotX, uint32_t aHotspotY) override;
   virtual void ClearCachedCursor() override { mUpdateCursor = true; }
   virtual void SetTransparencyMode(nsTransparencyMode aMode) override;
   virtual nsTransparencyMode GetTransparencyMode() override;
   virtual void GetWindowClipRegion(
       nsTArray<LayoutDeviceIntRect>* aRects) override;
   virtual void SetWindowShadowStyle(int32_t aStyle) override {}
   virtual void SetShowsToolbarButton(bool aShow) override {}
   virtual void SetShowsFullScreenButton(bool aShow) override {}
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -929,40 +929,32 @@ class nsIWidget : public nsISupports {
    *
    * @param aColor the new background color
    *
    */
 
   virtual void SetBackgroundColor(const nscolor& aColor) {}
 
   /**
-   * Set the cursor for this widget
-   *
-   * @param aCursor the new cursor for this widget
-   */
-  virtual void 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.
+   * Sets the cursor 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
+   * @param aDefaultCursor the default cursor to be set
+   * @param aCursorImage a custom cursor, maybe null.
+   * @param aX the X coordinate of the hotspot for aCursorImage (from left).
+   * @param aY the Y coordinate of the hotspot for aCursorImage (from top).
    */
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) = 0;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursorImage,
+                         uint32_t aHotspotX, uint32_t aHotspotY) = 0;
 
   /**
    * Get the window type of this widget.
    */
   nsWindowType WindowType() { return mWindowType; }
 
   /**
    * Determines if this widget is one of the three types of plugin widgets.
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -1511,17 +1511,17 @@ void nsWindow::Show(bool bState) {
     if (bState) {
       if (!wasVisible && mWindowType == eWindowType_toplevel) {
         // speed up the initial paint after show for
         // top level windows:
         syncInvalidate = true;
 
         // Set the cursor before showing the window to avoid the default wait
         // cursor.
-        SetCursor(eCursor_standard);
+        SetCursor(eCursor_standard, nullptr, 0, 0);
 
         switch (mSizeMode) {
           case nsSizeMode_Fullscreen:
             ::ShowWindow(mWnd, SW_SHOW);
             break;
           case nsSizeMode_Maximized:
             ::ShowWindow(mWnd, SW_SHOWMAXIMIZED);
             break;
@@ -2705,218 +2705,193 @@ void nsWindow::SetBackgroundColor(const 
  *
  * SECTION: nsIWidget::SetCursor
  *
  * SetCursor and related utilities for manging cursor state.
  *
  **************************************************************/
 
 // Set this component cursor
-void nsWindow::SetCursor(nsCursor aCursor) {
-  // Only change cursor if it's changing
-
-  // XXX mCursor isn't always right.  Scrollbars and others change it, too.
-  // XXX If we want this optimization we need a better way to do it.
-  // if (aCursor != mCursor) {
-  HCURSOR newCursor = nullptr;
-
+static HCURSOR CursorFor(nsCursor aCursor) {
   switch (aCursor) {
     case eCursor_select:
-      newCursor = ::LoadCursor(nullptr, IDC_IBEAM);
-      break;
-
+      return ::LoadCursor(nullptr, IDC_IBEAM);
     case eCursor_wait:
-      newCursor = ::LoadCursor(nullptr, IDC_WAIT);
-      break;
-
-    case eCursor_hyperlink: {
-      newCursor = ::LoadCursor(nullptr, IDC_HAND);
-      break;
-    }
-
+      return ::LoadCursor(nullptr, IDC_WAIT);
+    case eCursor_hyperlink:
+      return ::LoadCursor(nullptr, IDC_HAND);
     case eCursor_standard:
     case eCursor_context_menu:  // XXX See bug 258960.
-      newCursor = ::LoadCursor(nullptr, IDC_ARROW);
-      break;
+      return ::LoadCursor(nullptr, IDC_ARROW);
 
     case eCursor_n_resize:
     case eCursor_s_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZENS);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZENS);
 
     case eCursor_w_resize:
     case eCursor_e_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZEWE);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZEWE);
 
     case eCursor_nw_resize:
     case eCursor_se_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZENWSE);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZENWSE);
 
     case eCursor_ne_resize:
     case eCursor_sw_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZENESW);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZENESW);
 
     case eCursor_crosshair:
-      newCursor = ::LoadCursor(nullptr, IDC_CROSS);
-      break;
+      return ::LoadCursor(nullptr, IDC_CROSS);
 
     case eCursor_move:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZEALL);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZEALL);
 
     case eCursor_help:
-      newCursor = ::LoadCursor(nullptr, IDC_HELP);
-      break;
+      return ::LoadCursor(nullptr, IDC_HELP);
 
     case eCursor_copy:  // CSS3
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COPY));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COPY));
 
     case eCursor_alias:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ALIAS));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ALIAS));
 
     case eCursor_cell:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_CELL));
-      break;
-
+      return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_CELL));
     case eCursor_grab:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRAB));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRAB));
 
     case eCursor_grabbing:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRABBING));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance,
+                          MAKEINTRESOURCE(IDC_GRABBING));
 
     case eCursor_spinning:
-      newCursor = ::LoadCursor(nullptr, IDC_APPSTARTING);
-      break;
+      return ::LoadCursor(nullptr, IDC_APPSTARTING);
 
     case eCursor_zoom_in:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMIN));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMIN));
 
     case eCursor_zoom_out:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMOUT));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance,
+                          MAKEINTRESOURCE(IDC_ZOOMOUT));
 
     case eCursor_not_allowed:
     case eCursor_no_drop:
-      newCursor = ::LoadCursor(nullptr, IDC_NO);
-      break;
+      return ::LoadCursor(nullptr, IDC_NO);
 
     case eCursor_col_resize:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COLRESIZE));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance,
+                          MAKEINTRESOURCE(IDC_COLRESIZE));
 
     case eCursor_row_resize:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ROWRESIZE));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance,
+                          MAKEINTRESOURCE(IDC_ROWRESIZE));
 
     case eCursor_vertical_text:
-      newCursor = ::LoadCursor(nsToolkit::mDllInstance,
-                               MAKEINTRESOURCE(IDC_VERTICALTEXT));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance,
+                          MAKEINTRESOURCE(IDC_VERTICALTEXT));
 
     case eCursor_all_scroll:
       // XXX not 100% appropriate perhaps
-      newCursor = ::LoadCursor(nullptr, IDC_SIZEALL);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZEALL);
 
     case eCursor_nesw_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZENESW);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZENESW);
 
     case eCursor_nwse_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZENWSE);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZENWSE);
 
     case eCursor_ns_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZENS);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZENS);
 
     case eCursor_ew_resize:
-      newCursor = ::LoadCursor(nullptr, IDC_SIZEWE);
-      break;
+      return ::LoadCursor(nullptr, IDC_SIZEWE);
 
     case eCursor_none:
-      newCursor =
-          ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_NONE));
-      break;
+      return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_NONE));
 
     default:
       NS_ERROR("Invalid cursor type");
-      break;
-  }
-
-  if (nullptr != newCursor) {
-    mCursor = aCursor;
-    HCURSOR oldCursor = ::SetCursor(newCursor);
-
-    if (sHCursor == oldCursor) {
-      NS_IF_RELEASE(sCursorImgContainer);
-      if (sHCursor != nullptr) ::DestroyIcon(sHCursor);
-      sHCursor = nullptr;
-    }
-  }
-}
-
-// Setting the actual cursor
-nsresult nsWindow::SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) {
-  if (sCursorImgContainer == aCursor && sHCursor) {
-    ::SetCursor(sHCursor);
-    return NS_OK;
-  }
-
-  int32_t width;
-  int32_t height;
-
-  nsresult rv;
-  rv = aCursor->GetWidth(&width);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = aCursor->GetHeight(&height);
-  NS_ENSURE_SUCCESS(rv, rv);
+      return nullptr;
+  }
+}
+
+static HCURSOR CursorForImage(imgIContainer* aImageContainer,
+                              uint32_t aHotspotX, uint32_t aHotspotY,
+                              double aScale) {
+  if (!aImageContainer) {
+    return nullptr;
+  }
+
+  int32_t width = 0;
+  int32_t height = 0;
+
+  if (NS_FAILED(aImageContainer->GetWidth(&width)) ||
+      NS_FAILED(aImageContainer->GetHeight(&height))) {
+    return nullptr;
+  }
 
   // Reject cursors greater than 128 pixels in either direction, to prevent
   // spoofing.
   // XXX ideally we should rescale. Also, we could modify the API to
   // allow trusted content to set larger cursors.
-  if (width > 128 || height > 128) return NS_ERROR_NOT_AVAILABLE;
-
+  if (width > 128 || height > 128) {
+    return nullptr;
+  }
+
+  IntSize size = RoundedToInt(Size(width * aScale, height * aScale));
   HCURSOR cursor;
+  nsresult rv = nsWindowGfx::CreateIcon(aImageContainer, true, aHotspotX,
+                                        aHotspotY, size, &cursor);
+  if (NS_FAILED(rv)) {
+    return nullptr;
+  }
+
+  return cursor;
+}
+
+// Setting the actual cursor
+void nsWindow::SetCursor(nsCursor aDefaultCursor, imgIContainer* aImageCursor,
+                         uint32_t aHotspotX, uint32_t aHotspotY) {
+  if (aImageCursor && sCursorImgContainer == aImageCursor && sHCursor) {
+    ::SetCursor(sHCursor);
+    return;
+  }
+
   double scale = GetDefaultScale().scale;
-  IntSize size = RoundedToInt(Size(width * scale, height * scale));
-  rv = nsWindowGfx::CreateIcon(aCursor, true, aHotspotX, aHotspotY, size,
-                               &cursor);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mCursor = eCursorInvalid;
-  ::SetCursor(cursor);
-
-  NS_IF_RELEASE(sCursorImgContainer);
-  sCursorImgContainer = aCursor;
-  NS_ADDREF(sCursorImgContainer);
-
-  if (sHCursor != nullptr) ::DestroyIcon(sHCursor);
-  sHCursor = cursor;
-
-  return NS_OK;
+  HCURSOR cursor = CursorForImage(aImageCursor, aHotspotX, aHotspotY, scale);
+  if (cursor) {
+    mCursor = eCursorInvalid;
+    ::SetCursor(cursor);
+
+    NS_IF_RELEASE(sCursorImgContainer);
+    sCursorImgContainer = aImageCursor;
+    NS_ADDREF(sCursorImgContainer);
+
+    if (sHCursor) {
+      ::DestroyIcon(sHCursor);
+    }
+    sHCursor = cursor;
+    return;
+  }
+
+  cursor = CursorFor(aDefaultCursor);
+  if (!cursor) {
+    return;
+  }
+
+  mCursor = aDefaultCursor;
+  HCURSOR oldCursor = ::SetCursor(cursor);
+
+  if (sHCursor == oldCursor) {
+    NS_IF_RELEASE(sCursorImgContainer);
+    if (sHCursor) {
+      ::DestroyIcon(sHCursor);
+    }
+    sHCursor = nullptr;
+  }
 }
 
 /**************************************************************
  *
  * SECTION: nsIWidget::Get/SetTransparencyMode
  *
  * Manage the transparency mode of the window containing this
  * widget. Only works for popup and dialog windows when the
@@ -6925,17 +6900,19 @@ void nsWindow::OnDestroy() {
 
   // Free GDI window class objects
   if (mBrush) {
     VERIFY(::DeleteObject(mBrush));
     mBrush = nullptr;
   }
 
   // Destroy any custom cursor resources.
-  if (mCursor == eCursorInvalid) SetCursor(eCursor_standard);
+  if (mCursor == eCursorInvalid) {
+    SetCursor(eCursor_standard, nullptr, 0, 0);
+  }
 
   if (mCompositorWidgetDelegate) {
     mCompositorWidgetDelegate->OnDestroyWindow();
   }
   mBasicLayersSurface = nullptr;
 
   // Finalize panning feedback to possibly restore window displacement
   mGesture.PanFeedbackFinalize(mWnd, true);
--- a/widget/windows/nsWindow.h
+++ b/widget/windows/nsWindow.h
@@ -146,19 +146,18 @@ class nsWindow final : public nsWindowBa
   virtual nsresult SetFocus(bool aRaise) override;
   virtual LayoutDeviceIntRect GetBounds() override;
   virtual LayoutDeviceIntRect GetScreenBounds() override;
   virtual MOZ_MUST_USE nsresult
   GetRestoredBounds(LayoutDeviceIntRect& aRect) override;
   virtual LayoutDeviceIntRect GetClientBounds() override;
   virtual LayoutDeviceIntPoint GetClientOffset() override;
   void SetBackgroundColor(const nscolor& aColor) override;
-  virtual nsresult SetCursor(imgIContainer* aCursor, uint32_t aHotspotX,
-                             uint32_t aHotspotY) override;
-  virtual void SetCursor(nsCursor aCursor) override;
+  virtual void SetCursor(nsCursor aDefaultCursor, imgIContainer* aCursorImage,
+                         uint32_t aHotspotX, uint32_t aHotspotY) override;
   virtual nsresult ConfigureChildren(
       const nsTArray<Configuration>& aConfigurations) override;
   virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;
   virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage,
                                            uint16_t aDuration,
                                            nsISupports* aData,
                                            nsIRunnable* aCallback) override;
   virtual void CleanupFullscreenTransition() override;