diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 7bac1c734cae..eaae3b0d0424 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -6391,7 +6391,7 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest, nsCOMPtr mainWidget; GetMainWidget(getter_AddRefs(mainWidget)); if (mainWidget) { - mainWidget->SetCursor(eCursor_spinning); + mainWidget->SetCursor(eCursor_spinning, nullptr, 0, 0); } } } @@ -6407,7 +6407,7 @@ nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest, nsCOMPtr mainWidget; GetMainWidget(getter_AddRefs(mainWidget)); if (mainWidget) { - mainWidget->SetCursor(eCursor_standard); + mainWidget->SetCursor(eCursor_standard, nullptr, 0, 0); } } } diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index ed77895c5505..42254a3dfd97 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -3826,9 +3826,9 @@ nsresult EventStateManager::SetCursor(StyleCursorKind aCursor, } // 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 @@ -3859,12 +3859,9 @@ nsresult EventStateManager::SetCursor(StyleCursorKind aCursor, 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; } diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 32f4debcdf79..b4c894376090 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -386,15 +386,10 @@ parent: * 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. @@ -412,9 +407,12 @@ parent: * 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. diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 6a9785d11f45..e2172a8598d8 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1038,11 +1038,9 @@ void TabParent::SendRealMouseEvent(WidgetMouseEvent& aEvent) { // 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; @@ -1632,55 +1630,45 @@ mozilla::ipc::IPCResult TabParent::RecvAsyncMessage( return IPC_OK(); } -mozilla::ipc::IPCResult TabParent::RecvSetCursor(const nsCursor& aCursor, - const bool& aForce) { - mCursor = aCursor; - mCustomCursor = nullptr; - - nsCOMPtr 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 widget = GetWidget(); - if (widget) { - if (aForce) { - widget->ClearCachedCursor(); - } - - if (mTabSetsCursor) { - const gfx::IntSize size(aWidth, aHeight); - - RefPtr customCursor = - gfx::CreateDataSourceSurfaceFromData( - size, aFormat, - reinterpret_cast(aCursorData.BeginReading()), - aStride); - - RefPtr drawable = new gfxSurfaceDrawable(customCursor, size); - nsCOMPtr 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 cursorImage; + if (aHasCustomCursor) { + const gfx::IntSize size(aWidth, aHeight); + RefPtr customCursor = + gfx::CreateDataSourceSurfaceFromData( + size, aFormat, + reinterpret_cast(aCursorData.BeginReading()), + aStride); + + RefPtr 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(); } diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 5445dfa6939b..a772e11f9f5b 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -260,14 +260,15 @@ class TabParent final : public PBrowserParent, nsTArray&& 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; diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 76d8d5efc1cd..fe84925483f7 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -898,70 +898,65 @@ nsresult PuppetWidget::NotifyIMEOfPositionChange( return NS_OK; } -void PuppetWidget::SetCursor(nsCursor aCursor) { +struct CursorSurface { + UniquePtr 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 customCursorData; + size_t length = 0; + IntSize customCursorSize; + int32_t stride = 0; + auto format = SurfaceFormat::B8G8R8A8; + bool force = mUpdateCursor; + + if (aCursorImage) { + RefPtr surface = aCursorImage->GetFrame( + imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE); + if (surface) { + if (RefPtr 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 surface = aCursor->GetFrame( - imgIContainer::FRAME_CURRENT, imgIContainer::FLAG_SYNC_DECODE); - if (!surface) { - return NS_ERROR_FAILURE; - } - - RefPtr dataSurface = - surface->GetDataSurface(); - if (!dataSurface) { - return NS_ERROR_FAILURE; - } - - size_t length; - int32_t stride; - mozilla::UniquePtr 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() { diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index efc5409f6e33..bd92d102880f 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -198,9 +198,8 @@ class PuppetWidget : public nsBaseWidget, 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; diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 7ae60a258d78..0be873c62082 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -258,12 +258,8 @@ class nsWindow final : public nsBaseWidget { virtual already_AddRefed 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; } diff --git a/widget/cocoa/nsChildView.h b/widget/cocoa/nsChildView.h index 33f123460322..43dba285c16b 100644 --- a/widget/cocoa/nsChildView.h +++ b/widget/cocoa/nsChildView.h @@ -364,9 +364,8 @@ class nsChildView final : public nsBaseWidget { 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; diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index d1bb5567339b..4fb893576b30 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -762,30 +762,28 @@ nsresult nsChildView::SetFocus(bool aRaise) { } // 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 diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index 2ea2a42cb5b0..55b53157564a 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -242,9 +242,8 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { 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(); diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index c657f994085d..e9fe4368bd64 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -1629,16 +1629,10 @@ int32_t nsCocoaWindow::RoundsWidgetCoordinatesTo() { 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) { diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index bd32d4f08cf5..95757710897a 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -1,5 +1,5 @@ -/* -*- 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 @@ -1434,61 +1434,31 @@ gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget *aWidget, 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; +static GdkCursor *GetCursorForImage(imgIContainer *aCursorImage, + uint32_t aHotspotX, uint32_t aHotspotY) { + if (!aCursorImage) { + return nullptr; } - - // 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); - } + GdkPixbuf *pixbuf = nsImageToPixbuf::ImageToPixbuf(aCursorImage); + if (!pixbuf) { + return nullptr; } -} - -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; 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 @@ -1497,26 +1467,58 @@ nsresult nsWindow::SetCursor(imgIContainer *aCursor, uint32_t aHotspotX, if (!gdk_pixbuf_get_has_alpha(pixbuf)) { GdkPixbuf *alphaBuf = gdk_pixbuf_add_alpha(pixbuf, FALSE, 0, 0, 0); g_object_unref(pixbuf); - if (!alphaBuf) { - return NS_ERROR_OUT_OF_MEMORY; - } 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; + if (!alphaBuf) { + return nullptr; } - g_object_unref(cursor); } - return rv; + 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; + } + } + + 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) { @@ -3482,7 +3484,7 @@ nsresult nsWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent, // 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); diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index 61fb37c3fa09..d1bc1f89f82a 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -144,9 +144,8 @@ class nsWindow final : public nsBaseWidget { 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; diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 0fde5d1619ff..70aae518c79a 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -640,11 +640,10 @@ void nsBaseWidget::SetSizeMode(nsSizeMode aMode) { // //------------------------------------------------------------------------- -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; } //------------------------------------------------------------------------- diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 54a58d4b8ae6..d36f086889c5 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -168,9 +168,8 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { 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; diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index f9f0dc428cf6..62c6a95aa7ec 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -933,13 +933,6 @@ class nsIWidget : public nsISupports { 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. @@ -948,16 +941,15 @@ class nsIWidget : public nsISupports { 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. diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 0cb433104dd3..fe50b9986cff 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -1516,7 +1516,7 @@ void nsWindow::Show(bool bState) { // 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: @@ -2710,208 +2710,183 @@ void nsWindow::SetBackgroundColor(const nscolor& aColor) { **************************************************************/ // 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; - } + return 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; +static HCURSOR CursorForImage(imgIContainer* aImageContainer, + uint32_t aHotspotX, uint32_t aHotspotY, + double aScale) { + if (!aImageContainer) { + return nullptr; } - int32_t width; - int32_t height; + int32_t width = 0; + int32_t height = 0; - nsresult rv; - rv = aCursor->GetWidth(&width); - NS_ENSURE_SUCCESS(rv, rv); - rv = aCursor->GetHeight(&height); - NS_ENSURE_SUCCESS(rv, rv); + 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); + HCURSOR cursor = CursorForImage(aImageCursor, aHotspotX, aHotspotY, scale); + if (cursor) { + mCursor = eCursorInvalid; + ::SetCursor(cursor); - mCursor = eCursorInvalid; - ::SetCursor(cursor); + NS_IF_RELEASE(sCursorImgContainer); + sCursorImgContainer = aImageCursor; + NS_ADDREF(sCursorImgContainer); - NS_IF_RELEASE(sCursorImgContainer); - sCursorImgContainer = aCursor; - NS_ADDREF(sCursorImgContainer); + if (sHCursor) { + ::DestroyIcon(sHCursor); + } + sHCursor = cursor; + return; + } - if (sHCursor != nullptr) ::DestroyIcon(sHCursor); - sHCursor = cursor; + cursor = CursorFor(aDefaultCursor); + if (!cursor) { + return; + } - return NS_OK; + mCursor = aDefaultCursor; + HCURSOR oldCursor = ::SetCursor(cursor); + + if (sHCursor == oldCursor) { + NS_IF_RELEASE(sCursorImgContainer); + if (sHCursor) { + ::DestroyIcon(sHCursor); + } + sHCursor = nullptr; + } } /************************************************************** @@ -6930,7 +6905,9 @@ void nsWindow::OnDestroy() { } // Destroy any custom cursor resources. - if (mCursor == eCursorInvalid) SetCursor(eCursor_standard); + if (mCursor == eCursorInvalid) { + SetCursor(eCursor_standard, nullptr, 0, 0); + } if (mCompositorWidgetDelegate) { mCompositorWidgetDelegate->OnDestroyWindow(); diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index e260ee73203b..09376f2f39d1 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -151,9 +151,8 @@ class nsWindow final : public nsWindowBase { 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& aConfigurations) override; virtual bool PrepareForFullscreenTransition(nsISupports** aData) override;