diff options
Diffstat (limited to 'widget/gtk')
-rw-r--r-- | widget/gtk/CompositorWidgetChild.cpp | 10 | ||||
-rw-r--r-- | widget/gtk/CompositorWidgetChild.h | 5 | ||||
-rw-r--r-- | widget/gtk/CompositorWidgetParent.cpp | 8 | ||||
-rw-r--r-- | widget/gtk/CompositorWidgetParent.h | 6 | ||||
-rw-r--r-- | widget/gtk/GtkCompositorWidget.cpp | 50 | ||||
-rw-r--r-- | widget/gtk/GtkCompositorWidget.h | 21 | ||||
-rw-r--r-- | widget/gtk/MPRISServiceHandler.cpp | 6 | ||||
-rw-r--r-- | widget/gtk/MozContainer.cpp | 11 | ||||
-rw-r--r-- | widget/gtk/MozContainerWayland.cpp | 15 | ||||
-rw-r--r-- | widget/gtk/PCompositorWidget.ipdl | 4 | ||||
-rw-r--r-- | widget/gtk/ScreenHelperGTK.cpp | 2 | ||||
-rw-r--r-- | widget/gtk/WindowSurfaceProvider.cpp | 30 | ||||
-rw-r--r-- | widget/gtk/WindowSurfaceProvider.h | 9 | ||||
-rw-r--r-- | widget/gtk/nsDragService.cpp | 55 | ||||
-rw-r--r-- | widget/gtk/nsLookAndFeel.cpp | 4 | ||||
-rw-r--r-- | widget/gtk/nsNativeThemeGTK.cpp | 16 | ||||
-rw-r--r-- | widget/gtk/nsWindow.cpp | 493 | ||||
-rw-r--r-- | widget/gtk/nsWindow.h | 29 |
18 files changed, 396 insertions, 378 deletions
diff --git a/widget/gtk/CompositorWidgetChild.cpp b/widget/gtk/CompositorWidgetChild.cpp index b7908a43d4..3b6af872ed 100644 --- a/widget/gtk/CompositorWidgetChild.cpp +++ b/widget/gtk/CompositorWidgetChild.cpp @@ -38,13 +38,13 @@ void CompositorWidgetChild::NotifyClientSizeChanged( Unused << SendNotifyClientSizeChanged(aClientSize); } -void CompositorWidgetChild::DisableRendering() { - Unused << SendDisableRendering(); +void CompositorWidgetChild::CleanupResources() { + Unused << SendCleanupResources(); } -void CompositorWidgetChild::EnableRendering(const uintptr_t aXWindow, - const bool aShaped) { - Unused << SendEnableRendering(aXWindow, aShaped); +void CompositorWidgetChild::SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) { + Unused << SendSetRenderingSurface(aXWindow, aShaped); } } // namespace widget diff --git a/widget/gtk/CompositorWidgetChild.h b/widget/gtk/CompositorWidgetChild.h index b1cad75da3..f46cf63bfb 100644 --- a/widget/gtk/CompositorWidgetChild.h +++ b/widget/gtk/CompositorWidgetChild.h @@ -27,8 +27,9 @@ class CompositorWidgetChild final : public PCompositorWidgetChild, mozilla::ipc::IPCResult RecvUnobserveVsync() override; void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize) override; - void DisableRendering() override; - void EnableRendering(const uintptr_t aXWindow, const bool aShaped) override; + void CleanupResources() override; + void SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) override; private: RefPtr<CompositorVsyncDispatcher> mVsyncDispatcher; diff --git a/widget/gtk/CompositorWidgetParent.cpp b/widget/gtk/CompositorWidgetParent.cpp index 998614622e..7f576f35e7 100644 --- a/widget/gtk/CompositorWidgetParent.cpp +++ b/widget/gtk/CompositorWidgetParent.cpp @@ -40,14 +40,14 @@ mozilla::ipc::IPCResult CompositorWidgetParent::RecvNotifyClientSizeChanged( return IPC_OK(); } -mozilla::ipc::IPCResult CompositorWidgetParent::RecvDisableRendering() { - DisableRendering(); +mozilla::ipc::IPCResult CompositorWidgetParent::RecvCleanupResources() { + CleanupResources(); return IPC_OK(); } -mozilla::ipc::IPCResult CompositorWidgetParent::RecvEnableRendering( +mozilla::ipc::IPCResult CompositorWidgetParent::RecvSetRenderingSurface( const uintptr_t& aXWindow, const bool& aShaped) { - EnableRendering(aXWindow, aShaped); + SetRenderingSurface(aXWindow, aShaped); return IPC_OK(); } diff --git a/widget/gtk/CompositorWidgetParent.h b/widget/gtk/CompositorWidgetParent.h index 2bbc70af3e..8c0a6e8c26 100644 --- a/widget/gtk/CompositorWidgetParent.h +++ b/widget/gtk/CompositorWidgetParent.h @@ -28,9 +28,9 @@ class CompositorWidgetParent final : public PCompositorWidgetParent, mozilla::ipc::IPCResult RecvNotifyClientSizeChanged( const LayoutDeviceIntSize& aClientSize) override; - mozilla::ipc::IPCResult RecvDisableRendering() override; - mozilla::ipc::IPCResult RecvEnableRendering(const uintptr_t& aXWindow, - const bool& aShaped) override; + mozilla::ipc::IPCResult RecvCleanupResources() override; + mozilla::ipc::IPCResult RecvSetRenderingSurface(const uintptr_t& aXWindow, + const bool& aShaped) override; private: RefPtr<VsyncObserver> mVsyncObserver; diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp index 36559cfd54..073ad5248f 100644 --- a/widget/gtk/GtkCompositorWidget.cpp +++ b/widget/gtk/GtkCompositorWidget.cpp @@ -7,6 +7,7 @@ #include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/CompositorThread.h" +#include "mozilla/WidgetUtilsGtk.h" #include "mozilla/widget/InProcessCompositorWidget.h" #include "mozilla/widget/PlatformWidgetTypes.h" #include "nsWindow.h" @@ -40,25 +41,22 @@ GtkCompositorWidget::GtkCompositorWidget( #if defined(MOZ_X11) if (GdkIsX11Display()) { ConfigureX11Backend((Window)aInitData.XWindow(), aInitData.Shaped()); - LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mXWindow %p " - "mIsRenderingSuspended %d\n", - (void*)mWidget.get(), (void*)aInitData.XWindow(), - !!mIsRenderingSuspended); + LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mXWindow %p\n", + (void*)mWidget.get(), (void*)aInitData.XWindow()); } #endif #if defined(MOZ_WAYLAND) if (GdkIsWaylandDisplay()) { ConfigureWaylandBackend(); - LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mWidget %p " - "mIsRenderingSuspended %d\n", - (void*)mWidget.get(), (void*)mWidget, !!mIsRenderingSuspended); + LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mWidget %p\n", + (void*)mWidget.get(), (void*)mWidget); } #endif } GtkCompositorWidget::~GtkCompositorWidget() { LOG("GtkCompositorWidget::~GtkCompositorWidget [%p]\n", (void*)mWidget.get()); - DisableRendering(); + CleanupResources(); RefPtr<nsIWidget> widget = mWidget.forget(); NS_ReleaseOnMainThread("GtkCompositorWidget::mWidget", widget.forget()); } @@ -169,57 +167,47 @@ GtkCompositorWidget::GetNativeLayerRoot() { } #endif -void GtkCompositorWidget::DisableRendering() { - LOG("GtkCompositorWidget::DisableRendering [%p]\n", (void*)mWidget.get()); - mIsRenderingSuspended = true; +void GtkCompositorWidget::CleanupResources() { + LOG("GtkCompositorWidget::CleanupResources [%p]\n", (void*)mWidget.get()); mProvider.CleanupResources(); } #if defined(MOZ_WAYLAND) -bool GtkCompositorWidget::ConfigureWaylandBackend() { +void GtkCompositorWidget::ConfigureWaylandBackend() { mProvider.Initialize(this); - return true; } #endif #if defined(MOZ_X11) -bool GtkCompositorWidget::ConfigureX11Backend(Window aXWindow, bool aShaped) { +void GtkCompositorWidget::ConfigureX11Backend(Window aXWindow, bool aShaped) { // We don't have X window yet. if (!aXWindow) { - mIsRenderingSuspended = true; - return false; + mProvider.CleanupResources(); + return; } // Initialize the window surface provider - return mProvider.Initialize(aXWindow, aShaped); + mProvider.Initialize(aXWindow, aShaped); } #endif -void GtkCompositorWidget::EnableRendering(const uintptr_t aXWindow, - const bool aShaped) { - LOG("GtkCompositorWidget::EnableRendering() [%p]\n", mWidget.get()); +void GtkCompositorWidget::SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) { + LOG("GtkCompositorWidget::SetRenderingSurface() [%p]\n", mWidget.get()); - if (!mIsRenderingSuspended) { - LOG(" quit, mIsRenderingSuspended = false\n"); - return; - } #if defined(MOZ_WAYLAND) if (GdkIsWaylandDisplay()) { LOG(" configure widget %p\n", mWidget.get()); - if (!ConfigureWaylandBackend()) { - return; - } + ConfigureWaylandBackend(); } #endif #if defined(MOZ_X11) if (GdkIsX11Display()) { LOG(" configure XWindow %p shaped %d\n", (void*)aXWindow, aShaped); - if (!ConfigureX11Backend((Window)aXWindow, aShaped)) { - return; - } + ConfigureX11Backend((Window)aXWindow, aShaped); } #endif - mIsRenderingSuspended = false; } + #ifdef MOZ_LOGGING bool GtkCompositorWidget::IsPopup() { return mWidget ? mWidget->IsPopup() : false; diff --git a/widget/gtk/GtkCompositorWidget.h b/widget/gtk/GtkCompositorWidget.h index bde88bde6c..d4834247f1 100644 --- a/widget/gtk/GtkCompositorWidget.h +++ b/widget/gtk/GtkCompositorWidget.h @@ -28,9 +28,9 @@ class PlatformCompositorWidgetDelegate : public CompositorWidgetDelegate { const LayoutDeviceIntSize& aClientSize) = 0; virtual GtkCompositorWidget* AsGtkCompositorWidget() { return nullptr; }; - virtual void DisableRendering() = 0; - virtual void EnableRendering(const uintptr_t aXWindow, - const bool aShaped) = 0; + virtual void CleanupResources() = 0; + virtual void SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) = 0; // CompositorWidgetDelegate Overrides @@ -74,10 +74,11 @@ class GtkCompositorWidget : public CompositorWidget, // Suspend rendering of this remote widget and clear all resources. // Can be used when underlying window is hidden/unmapped. - void DisableRendering() override; + void CleanupResources() override; // Resume rendering with to given aXWindow (X11) or nsWindow (Wayland). - void EnableRendering(const uintptr_t aXWindow, const bool aShaped) override; + void SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) override; // If we fail to set window size (due to different screen scale or so) // we can't paint the frame by compositor. @@ -90,11 +91,6 @@ class GtkCompositorWidget : public CompositorWidget, RefPtr<mozilla::layers::NativeLayerRoot> GetNativeLayerRoot() override; #endif - bool PreRender(WidgetRenderingContext* aContext) override { - return !mIsRenderingSuspended; - } - bool IsHidden() const override { return mIsRenderingSuspended; } - // PlatformCompositorWidgetDelegate Overrides void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize) override; @@ -102,10 +98,10 @@ class GtkCompositorWidget : public CompositorWidget, private: #if defined(MOZ_WAYLAND) - bool ConfigureWaylandBackend(); + void ConfigureWaylandBackend(); #endif #if defined(MOZ_X11) - bool ConfigureX11Backend(Window aXWindow, bool aShaped); + void ConfigureX11Backend(Window aXWindow, bool aShaped); #endif #ifdef MOZ_LOGGING bool IsPopup(); @@ -129,7 +125,6 @@ class GtkCompositorWidget : public CompositorWidget, #ifdef MOZ_WAYLAND RefPtr<mozilla::layers::NativeLayerRootWayland> mNativeLayerRoot; #endif - Atomic<bool> mIsRenderingSuspended{true}; }; } // namespace widget diff --git a/widget/gtk/MPRISServiceHandler.cpp b/widget/gtk/MPRISServiceHandler.cpp index ae1ef8654d..847bf3d78d 100644 --- a/widget/gtk/MPRISServiceHandler.cpp +++ b/widget/gtk/MPRISServiceHandler.cpp @@ -439,10 +439,10 @@ const char* MPRISServiceHandler::DesktopEntry() const { bool MPRISServiceHandler::PressKey(dom::MediaControlKey aKey) const { MOZ_ASSERT(mInitialized); if (!IsMediaKeySupported(aKey)) { - LOGMPRIS("%s is not supported", ToMediaControlKeyStr(aKey)); + LOGMPRIS("%s is not supported", dom::GetEnumString(aKey).get()); return false; } - LOGMPRIS("Press %s", ToMediaControlKeyStr(aKey)); + LOGMPRIS("Press %s", dom::GetEnumString(aKey).get()); EmitEvent(aKey); return true; } @@ -861,7 +861,7 @@ bool MPRISServiceHandler::EmitSupportedKeyChanged(dom::MediaControlKey aKey, bool aSupported) const { auto it = gKeyProperty.find(aKey); if (it == gKeyProperty.end()) { - LOGMPRIS("No property for %s", ToMediaControlKeyStr(aKey)); + LOGMPRIS("No property for %s", dom::GetEnumString(aKey).get()); return false; } diff --git a/widget/gtk/MozContainer.cpp b/widget/gtk/MozContainer.cpp index 775ae0488f..446dc97a66 100644 --- a/widget/gtk/MozContainer.cpp +++ b/widget/gtk/MozContainer.cpp @@ -87,19 +87,18 @@ void moz_container_class_init(MozContainerClass* klass) { GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + widget_class->map = moz_container_map; widget_class->realize = moz_container_realize; widget_class->unrealize = moz_container_unrealize; widget_class->destroy = moz_container_destroy; #ifdef MOZ_WAYLAND if (mozilla::widget::GdkIsWaylandDisplay()) { - widget_class->map = moz_container_wayland_map; widget_class->size_allocate = moz_container_wayland_size_allocate; widget_class->map_event = moz_container_wayland_map_event; widget_class->unmap = moz_container_wayland_unmap; } else { #endif - widget_class->map = moz_container_map; widget_class->size_allocate = moz_container_size_allocate; widget_class->unmap = moz_container_unmap; #ifdef MOZ_WAYLAND @@ -130,6 +129,10 @@ void moz_container_map(GtkWidget* widget) { if (gtk_widget_get_has_window(widget)) { gdk_window_show(gtk_widget_get_window(widget)); } + + // Enable rendering to nsWindow/MozContainer + nsWindow* window = moz_container_get_nsWindow(MOZ_CONTAINER(widget)); + window->OnMap(); } void moz_container_unmap(GtkWidget* widget) { @@ -138,9 +141,9 @@ void moz_container_unmap(GtkWidget* widget) { LOGCONTAINER(("moz_container_unmap() [%p]", (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)))); - // Disable rendering to MozContainer before we unmap it. + // Disable rendering to nsWindow/MozContainer before we really unmap it. nsWindow* window = moz_container_get_nsWindow(MOZ_CONTAINER(widget)); - window->DisableRendering(); + window->OnUnmap(); gtk_widget_set_mapped(widget, FALSE); diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp index 8490f25599..39e8a48390 100644 --- a/widget/gtk/MozContainerWayland.cpp +++ b/widget/gtk/MozContainerWayland.cpp @@ -419,21 +419,6 @@ gboolean moz_container_wayland_map_event(GtkWidget* widget, return FALSE; } -void moz_container_wayland_map(GtkWidget* widget) { - LOGCONTAINER("%s [%p]\n", __FUNCTION__, - (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget))); - - g_return_if_fail(IS_MOZ_CONTAINER(widget)); - - // We need to mark MozContainer as mapped to make sure - // moz_container_wayland_unmap() is called on hide/withdraw. - gtk_widget_set_mapped(widget, TRUE); - - if (gtk_widget_get_has_window(widget)) { - gdk_window_show(gtk_widget_get_window(widget)); - } -} - void moz_container_wayland_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { GtkAllocation tmp_allocation; diff --git a/widget/gtk/PCompositorWidget.ipdl b/widget/gtk/PCompositorWidget.ipdl index d554a33144..e083e8d4ef 100644 --- a/widget/gtk/PCompositorWidget.ipdl +++ b/widget/gtk/PCompositorWidget.ipdl @@ -22,8 +22,8 @@ parent: async __delete__(); async NotifyClientSizeChanged(LayoutDeviceIntSize aClientSize); - async DisableRendering(); - async EnableRendering(uintptr_t aXWindow, bool aShaped); + async CleanupResources(); + async SetRenderingSurface(uintptr_t aXWindow, bool aShaped); child: diff --git a/widget/gtk/ScreenHelperGTK.cpp b/widget/gtk/ScreenHelperGTK.cpp index 60be234c60..313bf54b1e 100644 --- a/widget/gtk/ScreenHelperGTK.cpp +++ b/widget/gtk/ScreenHelperGTK.cpp @@ -269,7 +269,7 @@ static already_AddRefed<Screen> MakeScreenGtk(GdkScreen* aScreen, contentsScale.scale, defaultCssScale.scale, dpi, refreshRate); return MakeAndAddRef<Screen>(rect, availRect, pixelDepth, pixelDepth, refreshRate, contentsScale, defaultCssScale, dpi, - Screen::IsPseudoDisplay::No); + Screen::IsPseudoDisplay::No, Screen::IsHDR::No); } void ScreenGetterGtk::RefreshScreens() { diff --git a/widget/gtk/WindowSurfaceProvider.cpp b/widget/gtk/WindowSurfaceProvider.cpp index 82f9029315..c8b2c5a7d6 100644 --- a/widget/gtk/WindowSurfaceProvider.cpp +++ b/widget/gtk/WindowSurfaceProvider.cpp @@ -11,6 +11,7 @@ #include "mozilla/gfx/Logging.h" #include "mozilla/layers/LayersTypes.h" #include "nsWindow.h" +#include "mozilla/ScopeExit.h" #ifdef MOZ_WAYLAND # include "mozilla/StaticPrefs_widget.h" @@ -129,13 +130,13 @@ RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() { // 2. XPutImage # ifdef MOZ_HAVE_SHMIMAGE if (!mIsShaped && nsShmImage::UseShm()) { - LOG(("Drawing to Window 0x%lx will use MIT-SHM\n", mXWindow)); + LOG(("Drawing to Window 0x%lx will use MIT-SHM\n", (Window)mXWindow)); return MakeRefPtr<WindowSurfaceX11SHM>(DefaultXDisplay(), mXWindow, mXVisual, mXDepth); } # endif // MOZ_HAVE_SHMIMAGE - LOG(("Drawing to Window 0x%lx will use XPutImage\n", mXWindow)); + LOG(("Drawing to Window 0x%lx will use XPutImage\n", (Window)mXWindow)); return MakeRefPtr<WindowSurfaceX11Image>(DefaultXDisplay(), mXWindow, mXVisual, mXDepth, mIsShaped); } @@ -143,6 +144,11 @@ RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() { MOZ_RELEASE_ASSERT(false); } +// We need to ignore thread safety checks here. We need to hold mMutex +// between StartRemoteDrawingInRegion()/EndRemoteDrawingInRegion() calls +// which confuses it. +MOZ_PUSH_IGNORE_THREAD_SAFETY + already_AddRefed<gfx::DrawTarget> WindowSurfaceProvider::StartRemoteDrawingInRegion( const LayoutDeviceIntRegion& aInvalidRegion, @@ -151,7 +157,13 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion( return nullptr; } - MutexAutoLock lock(mMutex); + // We return a reference to mWindowSurface inside draw target so we need to + // hold the mutex untill EndRemoteDrawingInRegion() call where draw target + // is returned. + // If we return null dt, EndRemoteDrawingInRegion() won't be called to + // release mutex. + mMutex.Lock(); + auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); }); if (!mWindowSurfaceValid) { mWindowSurface = nullptr; @@ -178,12 +190,20 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion( dt = mWindowSurface->Lock(aInvalidRegion); } #endif + if (dt) { + // We have valid dt, mutex will be released in EndRemoteDrawingInRegion(). + unlockMutex.release(); + } + return dt.forget(); } void WindowSurfaceProvider::EndRemoteDrawingInRegion( gfx::DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) { - MutexAutoLock lock(mMutex); + // Unlock mutex from StartRemoteDrawingInRegion(). + mMutex.AssertCurrentThreadOwns(); + auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); }); + // Commit to mWindowSurface only if we have a valid one. if (!mWindowSurface || !mWindowSurfaceValid) { return; @@ -218,5 +238,7 @@ void WindowSurfaceProvider::EndRemoteDrawingInRegion( mWindowSurface->Commit(aInvalidRegion); } +MOZ_POP_THREAD_SAFETY + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/WindowSurfaceProvider.h b/widget/gtk/WindowSurfaceProvider.h index 6aaf50e2da..44a0d78ec6 100644 --- a/widget/gtk/WindowSurfaceProvider.h +++ b/widget/gtk/WindowSurfaceProvider.h @@ -81,7 +81,7 @@ class WindowSurfaceProvider final { */ mozilla::Mutex mMutex MOZ_UNANNOTATED; // WindowSurface needs to be re-created as underlying window was changed. - mozilla::Atomic<bool> mWindowSurfaceValid; + bool mWindowSurfaceValid; #ifdef MOZ_WAYLAND RefPtr<nsWindow> mWidget; // WindowSurfaceProvider is owned by GtkCompositorWidget so we don't need @@ -91,7 +91,12 @@ class WindowSurfaceProvider final { #ifdef MOZ_X11 bool mIsShaped; int mXDepth; - Window mXWindow; + // Make mXWindow atomic to allow it read from different threads + // and make tsan happy. + // We don't care much about actual mXWindow value (it may be valid XWindow or + // nullptr) because we invalidate mXWindow at compositor/renderer thread + // before it's release in unmap handler. + Atomic<Window, Relaxed> mXWindow; Visual* mXVisual; #endif }; diff --git a/widget/gtk/nsDragService.cpp b/widget/gtk/nsDragService.cpp index 0135f97a4e..f435cdf2a0 100644 --- a/widget/gtk/nsDragService.cpp +++ b/widget/gtk/nsDragService.cpp @@ -607,6 +607,14 @@ nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) { mTargetDragContextForRemote = nullptr; mTargetWindow = nullptr; mPendingWindow = nullptr; + mPendingDragContext = nullptr; + mPendingWindowPoint = {}; + mScheduledTask = eDragTaskNone; + if (mTaskSource) { + g_source_remove(mTaskSource); + mTaskSource = 0; + } + mPendingTime = 0; mCachedDragContext = 0; return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers); @@ -1208,19 +1216,57 @@ void nsDragService::TargetDataReceived(GtkWidget* aWidget, GdkAtom target = gtk_selection_data_get_target(aSelectionData); GUniquePtr<gchar> name(gdk_atom_name(target)); nsDependentCString flavor(name.get()); - if (gtk_targets_include_uri(&target, 1)) { - GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData)); + // For the vnd.portal.filetransfer and vnd.portal.files we receive numeric + // id when it's a local file. The numeric id is then used by + // gtk_selection_data_get_uris implementation to get the actual file + // available in the flatpak environment. + // + // However due to GTK implementation also for example the uris like https + // are also provided by the vnd.portal.filetransfer target. In this case the + // call gtk_selection_data_get_uris fails. This is a bug in the gtk. + // To workaround it we try to create the valid uri and only if we fail + // we try to use the gtk_selection_data_get_uris. We ignore the valid uris + // for the vnd.portal.file* targets. + // See: https://gitlab.gnome.org/GNOME/gtk/-/issues/6563 + if (flavor.Equals(gPortalFile) || flavor.Equals(gPortalFileTransfer)) { + const guchar* data = gtk_selection_data_get_data(aSelectionData); + if (!data || data[0] == '\0') { + LOGDRAGSERVICE(" Empty data!\n"); + return; + } + nsCOMPtr<nsIURI> sourceURI; + nsresult rv = + NS_NewURI(getter_AddRefs(sourceURI), (const gchar*)data, nullptr); + if (NS_FAILED(rv)) { + // We're unable to get the URI, we'll use the + // gtk_selection_data_get_uris to get the actual file location + // accessible from the Firefox runtime. + GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData)); + uris.swap(mTargetDragUris); + } else { + LOGDRAGSERVICE( + " got valid uri for MIME %s - this is bug in GTK - expected " + "numeric value for portal, got %s\n", + flavor.get(), data); + return; + } + + } else { + GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData)); + uris.swap(mTargetDragUris); + } #ifdef MOZ_LOGGING if (MOZ_LOG_TEST(gWidgetDragLog, mozilla::LogLevel::Debug)) { - gchar** uri = uris.get(); + gchar** uri = mTargetDragUris.get(); while (uri && *uri) { LOGDRAGSERVICE(" got uri %s, MIME %s", *uri, flavor.get()); uri++; } } + #endif - uris.swap(mTargetDragUris); + if (mTargetDragUris) { mCachedUris.InsertOrUpdate( flavor, GUniquePtr<gchar*>(g_strdupv(mTargetDragUris.get()))); @@ -2557,6 +2603,7 @@ gboolean nsDragService::RunScheduledTask() { // Nothing more to do // Returning false removes the task source from the event loop. mTaskSource = 0; + mPendingDragContext = nullptr; return FALSE; } diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 040d942cdf..8702c154d6 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -800,6 +800,7 @@ nsresult nsLookAndFeel::PerThemeData::GetColor(ColorID aID, case ColorID::SpellCheckerUnderline: case ColorID::Mark: case ColorID::Marktext: + case ColorID::MozAutofillBackground: aColor = GetStandinForNativeColor( aID, mIsDark ? ColorScheme::Dark : ColorScheme::Light); break; @@ -863,9 +864,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::CaretWidth: aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; case IntID::SelectTextfieldsOnKeyFocus: { GtkSettings* settings; gboolean select_on_focus; diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 16945349bb..78d4e925fe 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -167,9 +167,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, ElementState elementState = GetContentState(aFrame, aAppearance); if (aState) { memset(aState, 0, sizeof(GtkWidgetState)); - - // For XUL checkboxes and radio buttons, the state of the parent - // determines our state. if (aWidgetFlags) { if (elementState.HasState(ElementState::CHECKED)) { *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; @@ -241,7 +238,8 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aAppearance == StyleAppearance::MozWindowButtonMinimize || aAppearance == StyleAppearance::MozWindowButtonMaximize || aAppearance == StyleAppearance::MozWindowButtonRestore) { - aState->backdrop = !nsWindow::GetTopLevelWindowActiveState(aFrame); + aState->backdrop = aFrame->PresContext()->Document()->State().HasState( + dom::DocumentState::WINDOW_INACTIVE); } } @@ -884,11 +882,6 @@ LayoutDeviceIntMargin nsNativeThemeGTK::GetWidgetBorder( CSSIntMargin result; GtkTextDirection direction = GetTextDirection(aFrame); switch (aAppearance) { - case StyleAppearance::Toolbox: - // gtk has no toolbox equivalent. So, although we map toolbox to - // gtk's 'toolbar' for purposes of painting the widget background, - // we don't use the toolbar border for toolbox. - break; case StyleAppearance::Dualbutton: // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw // around the entire button + dropdown, and also an inner border if you're @@ -1169,9 +1162,7 @@ nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, } // Some widget types just never change state. - if (aAppearance == StyleAppearance::Toolbox || - aAppearance == StyleAppearance::Toolbar || - aAppearance == StyleAppearance::Progresschunk || + if (aAppearance == StyleAppearance::Progresschunk || aAppearance == StyleAppearance::ProgressBar || aAppearance == StyleAppearance::Tooltip || aAppearance == StyleAppearance::MozWindowDecorations) { @@ -1244,7 +1235,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Button: case StyleAppearance::Radio: case StyleAppearance::Checkbox: - case StyleAppearance::Toolbox: // N/A case StyleAppearance::Toolbarbutton: case StyleAppearance::Dualbutton: // so we can override the border with 0 case StyleAppearance::ToolbarbuttonDropdown: diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index e84044990c..0a78d0c8ec 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -187,8 +187,6 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor); /* callbacks from widgets */ static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr); static gboolean configure_event_cb(GtkWidget* widget, GdkEventConfigure* event); -static void widget_map_cb(GtkWidget* widget); -static void widget_unmap_cb(GtkWidget* widget); static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation); static void toplevel_window_size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation); @@ -392,11 +390,11 @@ static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) { nsWindow::nsWindow() : mTitlebarRectMutex("nsWindow::mTitlebarRectMutex"), - mDestroyMutex("nsWindow::mDestroyMutex"), + mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"), + mIsMapped(false), mIsDestroyed(false), mIsShown(false), mNeedsShow(false), - mIsMapped(false), mEnabled(true), mCreated(false), mHandleTouchEvent(false), @@ -581,7 +579,6 @@ void nsWindow::Destroy() { LOG("nsWindow::Destroy\n"); - MutexAutoLock lock(mDestroyMutex); mIsDestroyed = true; mCreated = false; @@ -612,6 +609,9 @@ void nsWindow::Destroy() { NativeShow(false); + MOZ_ASSERT(!gtk_widget_get_mapped(mShell)); + MOZ_ASSERT(!gtk_widget_get_mapped(GTK_WIDGET(mContainer))); + ClearTransparencyBitmap(); DestroyLayerManager(); @@ -3165,23 +3165,34 @@ void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) { LOG(" widget now has focus in SetFocus()"); } +void nsWindow::ResetScreenBounds() { + mGdkWindowOrigin.reset(); + mGdkWindowRootOrigin.reset(); +} + LayoutDeviceIntRect nsWindow::GetScreenBounds() { if (!mGdkWindow) { return mBounds; } const LayoutDeviceIntPoint origin = [&] { - gint x, y; - gdk_window_get_root_origin(mGdkWindow, &x, &y); + GdkPoint origin; + + if (mGdkWindowRootOrigin.isSome()) { + origin = mGdkWindowRootOrigin.value(); + } else { + gdk_window_get_root_origin(mGdkWindow, &origin.x, &origin.y); + mGdkWindowRootOrigin = Some(origin); + } // Workaround for https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4820 // Bug 1775017 Gtk < 3.24.35 returns scaled values for // override redirected window on X11. if (gtk_check_version(3, 24, 35) != nullptr && GdkIsX11Display() && gdk_window_get_window_type(mGdkWindow) == GDK_WINDOW_TEMP) { - return LayoutDeviceIntPoint(x, y); + return LayoutDeviceIntPoint(origin.x, origin.y); } - return GdkPointToDevicePixels({x, y}); + return GdkPointToDevicePixels(origin); }(); // mBounds.Size() is the window bounds, not the window-manager frame @@ -3237,6 +3248,7 @@ void nsWindow::RecomputeClientOffset(bool aNotify) { gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget, GdkEventProperty* aEvent) { if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) { + ResetScreenBounds(); RecomputeClientOffset(/* aNotify = */ true); return FALSE; } @@ -3395,40 +3407,37 @@ void* nsWindow::GetNativeData(uint32_t aDataType) { case NS_NATIVE_OPENGL_CONTEXT: return nullptr; case NS_NATIVE_EGL_WINDOW: { + // On X11 we call it: + // 1) If window is mapped on OnMap() by nsWindow::ResumeCompositorImpl(), + // new EGLSurface/XWindow is created. + // 2) If window is hidden on OnUnmap(), we replace EGLSurface/XWindow + // by offline surface and release XWindow. + + // On Wayland it: + // 1) If window is mapped on OnMap(), we request frame callback + // at MozContainer. If we get frame callback at MozContainer, + // nsWindow::ResumeCompositorImpl() is called from it + // and EGLSurface/wl_surface is created. + // 2) If window is hidden on OnUnmap(), we replace EGLSurface/wl_surface + // by offline surface and release XWindow. + + // If nsWindow is already destroyed, don't try to get EGL window at all, + // we're going to be deleted anyway. + MutexAutoLock lock(mWindowVisibilityMutex); void* eglWindow = nullptr; - - // We can't block on mutex here as it leads to a deadlock: - // 1) mutex is taken at nsWindow::Destroy() - // 2) NS_NATIVE_EGL_WINDOW is called from compositor/rendering thread, - // blocking on mutex. - // 3) DestroyCompositor() is called by nsWindow::Destroy(). As a sync - // call it waits to compositor/rendering threads, - // but they're blocked at 2). - // It's fine if we return null EGL window during DestroyCompositor(), - // in such case compositor painting is skipped. - if (mDestroyMutex.TryLock()) { - if (mGdkWindow && !mIsDestroyed) { + if (mIsMapped && !mIsDestroyed) { #ifdef MOZ_X11 - if (GdkIsX11Display()) { - eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow); - } + if (GdkIsX11Display()) { + eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow); + } #endif #ifdef MOZ_WAYLAND - if (GdkIsWaylandDisplay()) { - bool hiddenWindow = - mCompositorWidgetDelegate && - mCompositorWidgetDelegate->AsGtkCompositorWidget() && - mCompositorWidgetDelegate->AsGtkCompositorWidget()->IsHidden(); - if (!hiddenWindow) { - eglWindow = moz_container_wayland_get_egl_window( - mContainer, FractionalScaleFactor()); - } - } -#endif + if (GdkIsWaylandDisplay()) { + eglWindow = moz_container_wayland_get_egl_window( + mContainer, FractionalScaleFactor()); } - mDestroyMutex.Unlock(); +#endif } - LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p", mGdkWindow, eglWindow); return eglWindow; @@ -3556,11 +3565,16 @@ LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() { if (IsWaylandPopup() && !mPopupUseMoveToRect) { return mBounds.TopLeft(); } - nsIntPoint origin(0, 0); - if (mGdkWindow) { - gdk_window_get_origin(mGdkWindow, &origin.x.value, &origin.y.value); + + GdkPoint origin{}; + if (mGdkWindowOrigin.isSome()) { + origin = mGdkWindowOrigin.value(); + } else if (mGdkWindow) { + gdk_window_get_origin(mGdkWindow, &origin.x, &origin.y); + mGdkWindowOrigin = Some(origin); } - return GdkPointToDevicePixels({origin.x, origin.y}); + + return GdkPointToDevicePixels(origin); } void nsWindow::CaptureRollupEvents(bool aDoCapture) { @@ -3749,6 +3763,8 @@ void nsWindow::RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion) { KnowsCompositor* knowsCompositor = renderer->AsKnowsCompositor(); if (knowsCompositor && layerManager && mCompositorSession) { + LOG("nsWindow::RequestRepaint()"); + if (!mConfiguredClearColor && !IsPopup()) { layerManager->WrBridge()->SendSetDefaultClearColor(LookAndFeel::Color( LookAndFeel::ColorID::Window, PreferenceSheet::ColorSchemeForChrome(), @@ -3764,9 +3780,13 @@ void nsWindow::RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion) { } gboolean nsWindow::OnExposeEvent(cairo_t* cr) { + LOG("nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]", mGdkWindow, + GetX11Window()); + // This might destroy us. NotifyOcclusionState(OcclusionState::VISIBLE); if (mIsDestroyed) { + LOG("destroyed after NotifyOcclusionState()"); return FALSE; } @@ -3774,26 +3794,27 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { // May run event loop and destroy us. MaybeDispatchResized(); if (mIsDestroyed) { + LOG("destroyed after MaybeDispatchResized()"); return FALSE; } // Windows that are not visible will be painted after they become visible. if (!mGdkWindow || !mHasMappedToplevel) { + LOG("quit, !mGdkWindow || !mHasMappedToplevel"); return FALSE; } #ifdef MOZ_WAYLAND if (GdkIsWaylandDisplay() && !moz_container_wayland_can_draw(mContainer)) { + LOG("quit, !moz_container_wayland_can_draw()"); return FALSE; } #endif if (!GetListener()) { + LOG("quit, !GetListener()"); return FALSE; } - LOG("nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]", mGdkWindow, - GetX11Window()); - LayoutDeviceIntRegion exposeRegion; if (!ExtractExposeRegion(exposeRegion, cr)) { LOG(" no rects, quit"); @@ -3816,19 +3837,24 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { // If the window has been destroyed during the will paint notification, // there is nothing left to do. if (!mGdkWindow || mIsDestroyed) { + LOG("quit, !mGdkWindow || mIsDestroyed"); return TRUE; } // Re-get all rendering components since the will paint notification // might have killed it. nsIWidgetListener* listener = GetListener(); - if (!listener) return FALSE; + if (!listener) { + LOG("quit, !listener"); + return FALSE; + } WindowRenderer* renderer = GetWindowRenderer(); WebRenderLayerManager* layerManager = renderer->AsWebRender(); KnowsCompositor* knowsCompositor = renderer->AsKnowsCompositor(); if (knowsCompositor && layerManager && layerManager->NeedsComposite()) { + LOG("needs composite, ScheduleComposite() call"); layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); layerManager->SetNeedsComposite(false); } @@ -3859,11 +3885,13 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { } if (region.IsEmpty()) { + LOG("quit, region.IsEmpty()"); return TRUE; } // If this widget uses OMTC... if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) { + LOG("redirect painting to OMTC rendering..."); listener->PaintWindow(this, region); // Re-get the listener since the will paint notification might have @@ -4048,6 +4076,8 @@ gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget, mPendingConfigures--; } + ResetScreenBounds(); + // Don't fire configure event for scale changes, we handle that // OnScaleChanged event. Skip that for toplevel windows only. if (mGdkWindow && IsTopLevelWindowType()) { @@ -4103,45 +4133,12 @@ gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget, return FALSE; } -void nsWindow::OnMap() { - LOG("nsWindow::OnMap"); - // Gtk mapped widget to screen. Configure underlying GdkWindow properly - // as our rendering target. - // This call means we have X11 (or Wayland) window we can render to by GL - // so we need to notify compositor about it. - mIsMapped = true; - ConfigureGdkWindow(); -} - -void nsWindow::OnUnmap() { - LOG("nsWindow::OnUnmap"); - - mIsMapped = false; - - if (mSourceDragContext) { - static auto sGtkDragCancel = - (void (*)(GdkDragContext*))dlsym(RTLD_DEFAULT, "gtk_drag_cancel"); - if (sGtkDragCancel) { - sGtkDragCancel(mSourceDragContext); - mSourceDragContext = nullptr; - } - } - - // We don't have valid XWindow any more, - // so clear stored ones at GtkCompositorWidget() for OMTC rendering - // and mSurfaceProvider for legacy rendering. - if (GdkIsX11Display()) { - mSurfaceProvider.CleanupResources(); - if (mCompositorWidgetDelegate) { - mCompositorWidgetDelegate->DisableRendering(); - } - } -} - void nsWindow::OnSizeAllocate(GtkAllocation* aAllocation) { LOG("nsWindow::OnSizeAllocate %d,%d -> %d x %d\n", aAllocation->x, aAllocation->y, aAllocation->width, aAllocation->height); + ResetScreenBounds(); + // Client offset are updated by _NET_FRAME_EXTENTS on X11 when system titlebar // is enabled. In either cases (Wayland or system titlebar is off on X11) // we don't get _NET_FRAME_EXTENTS X11 property notification so we derive @@ -4650,9 +4647,16 @@ bool nsWindow::DoTitlebarAction(LookAndFeel::TitlebarEvent aEvent, LOG(" action menu"); TryToShowNativeWindowMenu(aButtonEvent); break; - // Lower is part of gtk_surface1 protocol which we can't support - // as Gtk keeps it private. So emulate it by minimize. case LookAndFeel::TitlebarAction::WindowLower: + LOG(" action lower"); + // Lower is part of gtk_surface1 protocol which we can't support + // as Gtk keeps it private. So emulate it by minimize. + if (GdkIsWaylandDisplay()) { + SetSizeMode(nsSizeMode_Minimized); + } else { + gdk_window_lower(GetToplevelGdkWindow()); + } + break; case LookAndFeel::TitlebarAction::WindowMinimize: LOG(" action minimize"); SetSizeMode(nsSizeMode_Minimized); @@ -5425,8 +5429,8 @@ void nsWindow::OnScaleChanged(bool aNotify) { NotifyAPZOfDPIChange(); - LOG("OnScaleChanged %d, %f -> %d, %f\n", int(mCeiledScaleFactor), - mFractionalScaleFactor, newCeiled, newFractional); + LOG("OnScaleChanged %d, %f -> %d, %f Notify %d\n", int(mCeiledScaleFactor), + mFractionalScaleFactor, newCeiled, newFractional, aNotify); mCeiledScaleFactor = newCeiled; mFractionalScaleFactor = newFractional; @@ -5822,8 +5826,8 @@ bool nsWindow::GetShapedState() { } void nsWindow::ConfigureCompositor() { - MOZ_DIAGNOSTIC_ASSERT(mCompositorState == COMPOSITOR_ENABLED); MOZ_DIAGNOSTIC_ASSERT(mIsMapped); + MOZ_DIAGNOSTIC_ASSERT(mCompositorState == COMPOSITOR_ENABLED); LOG("nsWindow::ConfigureCompositor()"); auto startCompositing = [self = RefPtr{this}, this]() -> void { @@ -5833,7 +5837,7 @@ void nsWindow::ConfigureCompositor() { // too late if (mIsDestroyed || !mIsMapped) { LOG(" quit, mIsDestroyed = %d mIsMapped = %d", !!mIsDestroyed, - mIsMapped); + !!mIsMapped); return; } // Compositor will be resumed later by ResumeCompositorFlickering(). @@ -5860,73 +5864,6 @@ void nsWindow::ConfigureCompositor() { } } -void nsWindow::ConfigureGdkWindow() { - LOG("nsWindow::ConfigureGdkWindow()"); - - EnsureGdkWindow(); - OnScaleChanged(/* aNotify = */ false); - - if (mIsAlert) { - gdk_window_set_override_redirect(mGdkWindow, TRUE); - } - -#ifdef MOZ_X11 - if (GdkIsX11Display()) { - mSurfaceProvider.Initialize(GetX11Window(), GetShapedState()); - - // Set window manager hint to keep fullscreen windows composited. - // - // If the window were to get unredirected, there could be visible - // tearing because Gecko does not align its framebuffer updates with - // vblank. - SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); - } -#endif -#ifdef MOZ_WAYLAND - if (GdkIsWaylandDisplay()) { - mSurfaceProvider.Initialize(this); - } -#endif - - if (mIsDragPopup) { - if (GdkIsWaylandDisplay()) { - // Disable painting to the widget on Wayland as we paint directly to the - // widget. Wayland compositors does not paint wl_subsurface - // of D&D widget. - if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { - GtkWidgetDisableUpdates(parent); - } - GtkWidgetDisableUpdates(mShell); - GtkWidgetDisableUpdates(GTK_WIDGET(mContainer)); - } else { - // Disable rendering of parent container on X11 to avoid flickering. - if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { - gtk_widget_set_opacity(parent, 0.0); - } - } - } - - if (mWindowType == WindowType::Popup) { - if (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. - SetInputRegion(mInputRegion); - } - - RefreshWindowClass(); - - // We're not mapped yet but we have already created compositor. - if (mCompositorWidgetDelegate) { - ConfigureCompositor(); - } - - LOG(" finished, new GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window()); -} - nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, const LayoutDeviceIntRect& aRect, widget::InitData* aInitData) { @@ -6355,8 +6292,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, G_CALLBACK(settings_xft_dpi_changed_cb), this); // Widget signals - g_signal_connect(mContainer, "map", G_CALLBACK(widget_map_cb), nullptr); - g_signal_connect(mContainer, "unmap", G_CALLBACK(widget_unmap_cb), nullptr); g_signal_connect_after(mContainer, "size_allocate", G_CALLBACK(size_allocate_cb), nullptr); g_signal_connect(mContainer, "hierarchy-changed", @@ -6567,6 +6502,10 @@ void nsWindow::NativeMoveResize(bool aMoved, bool aResized) { LOG("nsWindow::NativeMoveResize move %d resize %d to %d,%d -> %d x %d\n", aMoved, aResized, topLeft.x, topLeft.y, size.width, size.height); + if (aMoved) { + ResetScreenBounds(); + } + if (aResized && !AreBoundsSane()) { LOG(" bounds are insane, hidding the window"); // We have been resized but to incorrect size. @@ -6648,8 +6587,8 @@ void nsWindow::PauseCompositorFlickering() { CompositorBridgeChild* remoteRenderer = GetRemoteRenderer(); if (remoteRenderer) { - remoteRenderer->SendPause(); mCompositorState = COMPOSITOR_PAUSED_FLICKERING; + remoteRenderer->SendPause(); mCompositorPauseTimeoutID = (int)g_timeout_add( COMPOSITOR_PAUSE_TIMEOUT, [](void* data) -> gint { @@ -6703,7 +6642,8 @@ void nsWindow::ResumeCompositorImpl() { LOG("nsWindow::ResumeCompositorImpl()\n"); MOZ_DIAGNOSTIC_ASSERT(mCompositorWidgetDelegate); - mCompositorWidgetDelegate->EnableRendering(GetX11Window(), GetShapedState()); + mCompositorWidgetDelegate->SetRenderingSurface(GetX11Window(), + GetShapedState()); // As WaylandStartVsync needs mCompositorWidgetDelegate this is the right // time to start it. @@ -8287,25 +8227,6 @@ static gboolean configure_event_cb(GtkWidget* widget, return window->OnConfigureEvent(widget, event); } -// Some Gtk widget code may call gtk_widget_unrealize() which destroys -// mGdkWindow. We need to listen on this signal and re-create -// mGdkWindow when we're already mapped. -static void widget_map_cb(GtkWidget* widget) { - RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); - if (!window) { - return; - } - window->OnMap(); -} - -static void widget_unmap_cb(GtkWidget* widget) { - RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); - if (!window) { - return; - } - window->OnUnmap(); -} - static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation) { RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); if (!window) { @@ -9124,7 +9045,7 @@ void nsWindow::DidGetNonBlankPaint() { void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) { LOG("nsWindow::SetCompositorWidgetDelegate %p mIsMapped %d " "mCompositorWidgetDelegate %p\n", - delegate, mIsMapped, mCompositorWidgetDelegate); + delegate, !!mIsMapped, mCompositorWidgetDelegate); MOZ_RELEASE_ASSERT(NS_IsMainThread()); if (delegate) { @@ -9962,26 +9883,6 @@ void nsWindow::UnlockNativePointer() { } #endif -bool nsWindow::GetTopLevelWindowActiveState(nsIFrame* aFrame) { - // Used by window frame and button box rendering. We can end up in here in - // the content process when rendering one of these moz styles freely in a - // page. Fail in this case, there is no applicable window focus state. - if (!XRE_IsParentProcess()) { - return false; - } - // All headless windows are considered active so they are painted. - if (gfxPlatform::IsHeadless()) { - return true; - } - // Get the widget. nsIFrame's GetNearestWidget walks up the view chain - // until it finds a real window. - nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget()); - if (!window) { - return false; - } - return !window->mTitlebarBackdropState; -} - static nsIFrame* FindTitlebarFrame(nsIFrame* aFrame) { for (nsIFrame* childFrame : aFrame->PrincipalChildList()) { StyleAppearance appearance = @@ -10072,41 +9973,34 @@ nsWindow* nsWindow::GetFocusedWindow() { return gFocusWindow; } #ifdef MOZ_WAYLAND bool nsWindow::SetEGLNativeWindowSize( const LayoutDeviceIntSize& aEGLWindowSize) { - if (!GdkIsWaylandDisplay()) { + if (!GdkIsWaylandDisplay() || !mIsMapped) { return true; } - // SetEGLNativeWindowSize() is called from renderer/compositor thread, - // make sure nsWindow is not destroyed. - bool paint = false; + if (mCompositorState == COMPOSITOR_PAUSED_FLICKERING) { + LOG("nsWindow::SetEGLNativeWindowSize() return, " + "COMPOSITOR_PAUSED_FLICKERING is set"); + return false; + } - // See NS_NATIVE_EGL_WINDOW why we can't block here. - if (mDestroyMutex.TryLock()) { - if (!mIsDestroyed) { - gint scale = GdkCeiledScaleFactor(); + gint scale = GdkCeiledScaleFactor(); # ifdef MOZ_LOGGING - if (LOG_ENABLED()) { - static uintptr_t lastSizeLog = 0; - uintptr_t sizeLog = uintptr_t(this) + aEGLWindowSize.width + - aEGLWindowSize.height + scale + - aEGLWindowSize.width / scale + - aEGLWindowSize.height / scale; - if (lastSizeLog != sizeLog) { - lastSizeLog = sizeLog; - LOG("nsWindow::SetEGLNativeWindowSize() %d x %d scale %d (unscaled " - "%d x " - "%d)", - aEGLWindowSize.width, aEGLWindowSize.height, scale, - aEGLWindowSize.width / scale, aEGLWindowSize.height / scale); - } - } -# endif - paint = moz_container_wayland_egl_window_set_size( - mContainer, aEGLWindowSize.ToUnknownSize(), scale); + if (LOG_ENABLED()) { + static uintptr_t lastSizeLog = 0; + uintptr_t sizeLog = + uintptr_t(this) + aEGLWindowSize.width + aEGLWindowSize.height + scale + + aEGLWindowSize.width / scale + aEGLWindowSize.height / scale; + if (lastSizeLog != sizeLog) { + lastSizeLog = sizeLog; + LOG("nsWindow::SetEGLNativeWindowSize() %d x %d scale %d (unscaled " + "%d x %d)", + aEGLWindowSize.width, aEGLWindowSize.height, scale, + aEGLWindowSize.width / scale, aEGLWindowSize.height / scale); } - mDestroyMutex.Unlock(); } - return paint; +# endif + return moz_container_wayland_egl_window_set_size( + mContainer, aEGLWindowSize.ToUnknownSize(), scale); } #endif @@ -10123,45 +10017,138 @@ void nsWindow::ClearRenderingQueue() { DestroyLayerManager(); } -void nsWindow::DisableRendering() { - LOG("nsWindow::DisableRendering()"); +// nsWindow::OnMap() / nsWindow::OnUnmap() is called from map/unmap mContainer +// handlers directly as we paint to mContainer. +void nsWindow::OnMap() { + LOG("nsWindow::OnMap"); - if (mGdkWindow) { - g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); - mGdkWindow = nullptr; + { + MutexAutoLock lock(mWindowVisibilityMutex); + mIsMapped = true; + + EnsureGdkWindow(); + OnScaleChanged(/* aNotify = */ false); + + if (mIsAlert) { + gdk_window_set_override_redirect(mGdkWindow, TRUE); + } + +#ifdef MOZ_X11 + if (GdkIsX11Display()) { + mSurfaceProvider.Initialize(GetX11Window(), GetShapedState()); + + // Set window manager hint to keep fullscreen windows composited. + // + // If the window were to get unredirected, there could be visible + // tearing because Gecko does not align its framebuffer updates with + // vblank. + SetCompositorHint(GTK_WIDGET_COMPOSITED_ENABLED); + } +#endif +#ifdef MOZ_WAYLAND + if (GdkIsWaylandDisplay()) { + mSurfaceProvider.Initialize(this); + } +#endif + } + + if (mIsDragPopup) { + if (GdkIsWaylandDisplay()) { + // Disable painting to the widget on Wayland as we paint directly to the + // widget. Wayland compositors does not paint wl_subsurface + // of D&D widget. + if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { + GtkWidgetDisableUpdates(parent); + } + GtkWidgetDisableUpdates(mShell); + GtkWidgetDisableUpdates(GTK_WIDGET(mContainer)); + } else { + // Disable rendering of parent container on X11 to avoid flickering. + if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { + gtk_widget_set_opacity(parent, 0.0); + } + } + } + + if (mWindowType == WindowType::Popup) { + if (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. + SetInputRegion(mInputRegion); + } + + RefreshWindowClass(); + + // We're not mapped yet but we have already created compositor. + if (mCompositorWidgetDelegate) { + ConfigureCompositor(); + } + + LOG(" finished, new GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window()); +} + +void nsWindow::OnUnmap() { + LOG("nsWindow::OnUnmap"); + + { + MutexAutoLock lock(mWindowVisibilityMutex); + mIsMapped = false; + + if (mSourceDragContext) { + static auto sGtkDragCancel = + (void (*)(GdkDragContext*))dlsym(RTLD_DEFAULT, "gtk_drag_cancel"); + if (sGtkDragCancel) { + sGtkDragCancel(mSourceDragContext); + mSourceDragContext = nullptr; + } + } + + if (mGdkWindow) { + g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); + mGdkWindow = nullptr; + } + + // Clear resources (mainly XWindow) stored at GtkCompositorWidget. + // It makes sure we don't paint to it when nsWindow becomes hiden/deleted + // and XWindow is released. + if (mCompositorWidgetDelegate) { + mCompositorWidgetDelegate->CleanupResources(); + } + + // Clear nsWindow resources used for old (in-thread) rendering. + mSurfaceProvider.CleanupResources(); } // Until Bug 1654938 is fixed we delete layer manager for hidden popups, // otherwise it can easily hold 1GB+ memory for long time. if (mWindowType == WindowType::Popup) { DestroyLayerManager(); - mSurfaceProvider.CleanupResources(); } else { -#ifdef MOZ_WAYLAND - // Widget is backed by OpenGL EGLSurface created over wl_surface - // owned by mContainer. - // RenderCompositorEGL::Resume() deletes recent EGLSurface based on - // wl_surface owned by mContainer and creates a new fallback EGLSurface. - // Then we can delete wl_surface in moz_container_wayland_unmap(). + // Widget is backed by OpenGL EGLSurface created over wl_surface/XWindow. + // + // RenderCompositorEGL::Resume() deletes recent EGLSurface, + // calls nsWindow::GetNativeData(NS_NATIVE_EGL_WINDOW) from compositor + // thread to get new native rendering surface. + // + // For hidden/unmapped windows we return nullptr NS_NATIVE_EGL_WINDOW at + // nsWindow::GetNativeData() so RenderCompositorEGL::Resume() creates + // offscreen fallback EGLSurface to avoid compositor pause. + // // We don't want to pause compositor as it may lead to whole // browser freeze (Bug 1777664). - /// - // We don't need to do such operation for SW backend as - // WindowSurfaceWaylandMB::Commit() gets wl_surface from - // MozContainer every commit. - if (moz_container_wayland_has_egl_window(mContainer) && - mCompositorWidgetDelegate) { - if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { - // Call DisableRendering() to make GtkCompositorWidget hidden. - // Then SendResume() will create fallback EGLSurface, see - // GLContextEGL::CreateEGLSurfaceForCompositorWidget(). - mCompositorWidgetDelegate->DisableRendering(); - remoteRenderer->SendResume(); - mCompositorWidgetDelegate->EnableRendering(GetX11Window(), - GetShapedState()); - } + // + // If RenderCompositorSWGL compositor is used (SW fallback) + // RenderCompositorSWGL::Resume() only requests full render for next paint + // as wl_surface/XWindow is managed by WindowSurfaceProvider owned + // directly by GtkCompositorWidget and that's covered by + // mCompositorWidgetDelegate->CleanupResources() call above. + if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { + remoteRenderer->SendResume(); } -#endif } } diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index f8fe344f09..25d68129d8 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -175,6 +175,7 @@ class nsWindow final : public nsBaseWidget { void MoveToWorkspace(const nsAString& workspaceID) override; void Enable(bool aState) override; void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; + void ResetScreenBounds(); LayoutDeviceIntRect GetScreenBounds() override; LayoutDeviceIntRect GetClientBounds() override; LayoutDeviceIntSize GetClientSize() override; @@ -411,7 +412,6 @@ class nsWindow final : public nsBaseWidget { */ static GtkWindowDecoration GetSystemGtkWindowDecoration(); - static bool GetTopLevelWindowActiveState(nsIFrame* aFrame); static bool TitlebarUseShapeMask(); bool IsRemoteContent() { return HasRemoteContent(); } void NativeMoveResizeWaylandPopupCallback(const GdkRectangle* aFinalSize, @@ -461,8 +461,6 @@ class nsWindow final : public nsBaseWidget { // rendering queue blocking (see Bug 1782948). void ClearRenderingQueue(); - void DisableRendering(); - bool ApplyEnterLeaveMutterWorkaround(); void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override; @@ -553,6 +551,9 @@ class nsWindow final : public nsBaseWidget { GtkWidget* mShell = nullptr; MozContainer* mContainer = nullptr; GdkWindow* mGdkWindow = nullptr; + mozilla::Maybe<GdkPoint> mGdkWindowOrigin; + mozilla::Maybe<GdkPoint> mGdkWindowRootOrigin; + PlatformCompositorWidgetDelegate* mCompositorWidgetDelegate = nullptr; mozilla::Atomic<WindowCompositorState, mozilla::Relaxed> mCompositorState{ COMPOSITOR_ENABLED}; @@ -633,10 +634,14 @@ class nsWindow final : public nsBaseWidget { mozilla::Mutex mTitlebarRectMutex; LayoutDeviceIntRect mTitlebarRect MOZ_GUARDED_BY(mTitlebarRectMutex); - mozilla::Mutex mDestroyMutex; + // This mutex protect window visibility changes. + mozilla::Mutex mWindowVisibilityMutex; + // This track real window visibility from OS perspective. + // It's set by OnMap/OnUnmap which is based on Gtk events. + mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped; // Has this widget been destroyed yet? - bool mIsDestroyed : 1; + mozilla::Atomic<bool, mozilla::Relaxed> mIsDestroyed; // mIsShown tracks requested visible status from browser perspective, i.e. // if the window should be visible or now. bool mIsShown : 1; @@ -646,9 +651,6 @@ class nsWindow final : public nsBaseWidget { // that the window is not actually visible but we report to browser that // it is visible (mIsShown == true). bool mNeedsShow : 1; - // This track real window visibility from OS perspective. - // It's set by OnMap/OnUnmap which is based on Gtk events. - bool mIsMapped : 1; // is this widget enabled? bool mEnabled : 1; // has the native window for this been created yet? @@ -802,11 +804,6 @@ class nsWindow final : public nsBaseWidget { void DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent); - // When window widget gets mapped/unmapped we need to configure - // underlying GdkWindow properly. Otherwise we'll end up with - // rendering to released window. - void ConfigureGdkWindow(); - void ReleaseGdkWindow(); void ConfigureCompositor(); bool IsAlwaysUndecoratedWindow() const; @@ -996,9 +993,9 @@ class nsWindow final : public nsBaseWidget { void RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion); #ifdef MOZ_X11 - typedef enum {GTK_WIDGET_COMPOSIDED_DEFAULT = 0, - GTK_WIDGET_COMPOSIDED_DISABLED = 1, - GTK_WIDGET_COMPOSIDED_ENABLED = 2} WindowComposeRequest; + typedef enum {GTK_WIDGET_COMPOSITED_DEFAULT = 0, + GTK_WIDGET_COMPOSITED_DISABLED = 1, + GTK_WIDGET_COMPOSITED_ENABLED = 2} WindowComposeRequest; void SetCompositorHint(WindowComposeRequest aState); bool ConfigureX11GLVisual(); #endif |