tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 6e1be6241390b4b4e3524f607afd300fd8309705
parent ba045db40f9da6b6e095466ee06c4cad8d458537
Author: Alexandru Marc <amarc@mozilla.com>
Date:   Tue,  6 Jan 2026 23:03:59 +0200

Revert "Bug 2001628 [Linux] Make mWindowSurface persistent r=emilio" for causing bc failures @ browser_bookmark_context_menu_contents.js

This reverts commit ac30d4d7974faeacc0816c65d88d75e016979d91.

Diffstat:
Mgfx/gl/GLContextProviderEGL.cpp | 18++++++++++++++++++
Mgfx/layers/wr/WebRenderLayerManager.cpp | 10++++++----
Mgfx/webrender_bindings/RenderCompositorEGL.cpp | 54+++++++++++++++++++++++-------------------------------
Mgfx/webrender_bindings/RenderCompositorOGLSWGL.cpp | 8++++++++
Mgfx/webrender_bindings/RenderCompositorSWGL.cpp | 2++
Mwidget/gtk/CompositorWidgetChild.cpp | 4++++
Mwidget/gtk/CompositorWidgetChild.h | 1+
Mwidget/gtk/CompositorWidgetParent.cpp | 6++++++
Mwidget/gtk/CompositorWidgetParent.h | 2++
Mwidget/gtk/GtkCompositorWidget.cpp | 17+++++++++++++++++
Mwidget/gtk/GtkCompositorWidget.h | 4++++
Mwidget/gtk/PCompositorWidget.ipdl | 1+
Mwidget/gtk/WindowSurfaceProvider.cpp | 53++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mwidget/gtk/WindowSurfaceProvider.h | 17++++++++++++++++-
Mwidget/gtk/nsWindow.cpp | 138++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mwidget/gtk/nsWindow.h | 9++++++++-
Mwidget/nsIWidget.h | 8++++++++
17 files changed, 284 insertions(+), 68 deletions(-)

diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp @@ -339,8 +339,26 @@ EGLSurface GLContextEGL::CreateEGLSurfaceForCompositorWidget( EGLNativeWindowType window = GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget); if (!window) { +#ifdef MOZ_WIDGET_GTK + // RenderCompositorEGL does not like EGL_NO_SURFACE as it fallbacks + // to SW rendering or claims itself as paused. + // In case we're missing valid native window because aCompositorWidget + // hidden, just create a fallback EGLSurface. Actual EGLSurface will be + // created by widget code later when aCompositorWidget becomes visible. + mozilla::gfx::IntSize pbSize(16, 16); +# ifdef MOZ_WAYLAND + if (GdkIsWaylandDisplay()) { + return CreateWaylandOffscreenSurface(*egl, aConfig, pbSize); + } else +# endif + { + return CreatePBufferSurfaceTryingPowerOfTwo(*egl, aConfig, LOCAL_EGL_NONE, + pbSize); + } +#else gfxCriticalNote << "window is null"; return EGL_NO_SURFACE; +#endif } return mozilla::gl::CreateSurfaceFromNativeWindow(*egl, window, aConfig); diff --git a/gfx/layers/wr/WebRenderLayerManager.cpp b/gfx/layers/wr/WebRenderLayerManager.cpp @@ -268,8 +268,9 @@ bool WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags) { mDisplayItemCache.SkipWaitingForPartialDisplayList(); - mLatestTransactionId = - mTransactionIdAllocator->GetTransactionId(/*aThrottle*/ true); + // Don't block on hidden windows on Linux as it may block all rendering. + const bool throttle = mWidget->IsMapped(); + mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(throttle); if (aFlags & EndTransactionFlags::END_NO_COMPOSITE && !mWebRenderCommandBuilder.NeedsEmptyTransaction()) { @@ -420,8 +421,9 @@ void WebRenderLayerManager::EndTransactionWithoutLayer( // an empty transaction. ClearAndNotifyOfFullTransactionPendingScrollInfoUpdate(); - mLatestTransactionId = mTransactionIdAllocator->GetTransactionId( - /*aThrottle*/ !aRenderOffscreen); + // Don't block on hidden windows on Linux as it may block all rendering. + const bool throttle = mWidget->IsMapped() && !aRenderOffscreen; + mLatestTransactionId = mTransactionIdAllocator->GetTransactionId(throttle); // Get the time of when the refresh driver start its tick (if available), // otherwise use the time of when LayerManager::BeginTransaction was called. diff --git a/gfx/webrender_bindings/RenderCompositorEGL.cpp b/gfx/webrender_bindings/RenderCompositorEGL.cpp @@ -72,24 +72,6 @@ RenderCompositorEGL::RenderCompositorEGL( : RenderCompositor(aWidget), mGL(aGL), mEGLSurface(EGL_NO_SURFACE) { MOZ_ASSERT(mGL); LOG("RenderCompositorEGL::RenderCompositorEGL()"); - -#ifdef MOZ_WIDGET_GTK - mEGLSurface = CreateEGLSurface(); - if (!mEGLSurface) { - RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); - return; - } - // We have a new EGL surface, which on wayland needs to be configured for - // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL - // context before we call eglSwapInterval, which is why we do it here - // rather than where the surface was created. - const auto& gle = gl::GLContextEGL::Cast(gl()); - const auto& egl = gle->mEgl; - MakeCurrent(); - - const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0; - egl->fSwapInterval(interval); -#endif } RenderCompositorEGL::~RenderCompositorEGL() { @@ -169,7 +151,7 @@ RenderedFrameId RenderCompositorEGL::EndFrame( gl()->SetDamage(bufferInvalid); } -#ifdef MOZ_WAYLAND +#ifdef MOZ_WIDGET_GTK // Rendering on Wayland has to be atomic (buffer attach + commit) and // wayland surface is also used by main thread so lock it before // we paint at SwapBuffers(). @@ -182,11 +164,7 @@ RenderedFrameId RenderCompositorEGL::EndFrame( return frameId; } -void RenderCompositorEGL::Pause() { - if (kIsAndroid) { - DestroyEGLSurface(); - } -} +void RenderCompositorEGL::Pause() { DestroyEGLSurface(); } bool RenderCompositorEGL::Resume() { if (kIsAndroid) { @@ -224,17 +202,31 @@ bool RenderCompositorEGL::Resume() { mHandlingNewSurfaceError = false; gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface); + } else if (kIsLinux) { + // Destroy EGLSurface if it exists and create a new one. We will set the + // swap interval after MakeCurrent() has been called. + DestroyEGLSurface(); + mEGLSurface = CreateEGLSurface(); + if (mEGLSurface != EGL_NO_SURFACE) { + // We have a new EGL surface, which on wayland needs to be configured for + // non-blocking buffer swaps. We need MakeCurrent() to set our current EGL + // context before we call eglSwapInterval, which is why we do it here + // rather than where the surface was created. + const auto& gle = gl::GLContextEGL::Cast(gl()); + const auto& egl = gle->mEgl; + MakeCurrent(); + + const int interval = gfx::gfxVars::SwapIntervalEGL() ? 1 : 0; + egl->fSwapInterval(interval); + } else { + RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); + return false; + } } return true; } -bool RenderCompositorEGL::IsPaused() { -#ifdef MOZ_WIDGET_ANDROID - return mEGLSurface == EGL_NO_SURFACE; -#else - return false; -#endif -} +bool RenderCompositorEGL::IsPaused() { return mEGLSurface == EGL_NO_SURFACE; } bool RenderCompositorEGL::MakeCurrent() { const auto& gle = gl::GLContextEGL::Cast(gl()); diff --git a/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp b/gfx/webrender_bindings/RenderCompositorOGLSWGL.cpp @@ -218,6 +218,8 @@ bool RenderCompositorOGLSWGL::RequestFullRender() { return mFullRender; } void RenderCompositorOGLSWGL::Pause() { #ifdef MOZ_WIDGET_ANDROID DestroyEGLSurface(); +#elif defined(MOZ_WIDGET_GTK) + mCompositor->Pause(); #endif } @@ -259,6 +261,12 @@ bool RenderCompositorOGLSWGL::Resume() { gl::GLContextEGL::Cast(GetGLContext())->SetEGLSurfaceOverride(mEGLSurface); mCompositor->SetDestinationSurfaceSize(size.ToUnknownSize()); +#elif defined(MOZ_WIDGET_GTK) + bool resumed = mCompositor->Resume(); + if (!resumed) { + RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE); + return false; + } #endif return true; } diff --git a/gfx/webrender_bindings/RenderCompositorSWGL.cpp b/gfx/webrender_bindings/RenderCompositorSWGL.cpp @@ -78,8 +78,10 @@ bool RenderCompositorSWGL::AllocateMappedBuffer( MOZ_ASSERT(!mDT); mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion); if (!mDT) { +#if !defined(MOZ_WAYLAND) gfxCriticalNoteOnce << "RenderCompositorSWGL failed mapping default framebuffer, no dt"; +#endif return false; } // Attempt to lock the underlying buffer directly from the draw target. diff --git a/widget/gtk/CompositorWidgetChild.cpp b/widget/gtk/CompositorWidgetChild.cpp @@ -43,5 +43,9 @@ void CompositorWidgetChild::NotifyClientSizeChanged( void CompositorWidgetChild::CleanupResources() { (void)SendCleanupResources(); } +void CompositorWidgetChild::SetRenderingSurface(const uintptr_t aXWindow) { + (void)SendSetRenderingSurface(aXWindow); +} + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/CompositorWidgetChild.h b/widget/gtk/CompositorWidgetChild.h @@ -30,6 +30,7 @@ class CompositorWidgetChild final : public PCompositorWidgetChild, void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize) override; void NotifyFullscreenChanged(bool aIsFullscreen) override {}; void CleanupResources() override; + void SetRenderingSurface(const uintptr_t aXWindow) override; private: ~CompositorWidgetChild() override; diff --git a/widget/gtk/CompositorWidgetParent.cpp b/widget/gtk/CompositorWidgetParent.cpp @@ -44,4 +44,10 @@ mozilla::ipc::IPCResult CompositorWidgetParent::RecvCleanupResources() { return IPC_OK(); } +mozilla::ipc::IPCResult CompositorWidgetParent::RecvSetRenderingSurface( + const uintptr_t& aXWindow) { + SetRenderingSurface(aXWindow); + return IPC_OK(); +} + } // namespace mozilla::widget diff --git a/widget/gtk/CompositorWidgetParent.h b/widget/gtk/CompositorWidgetParent.h @@ -30,6 +30,8 @@ class CompositorWidgetParent final : public PCompositorWidgetParent, const LayoutDeviceIntSize& aClientSize) override; mozilla::ipc::IPCResult RecvCleanupResources() override; + mozilla::ipc::IPCResult RecvSetRenderingSurface( + const uintptr_t& aXWindow) override; private: ~CompositorWidgetParent() override; diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp @@ -181,6 +181,23 @@ void GtkCompositorWidget::ConfigureX11Backend(Window aXWindow) { } #endif +void GtkCompositorWidget::SetRenderingSurface(const uintptr_t aXWindow) { + LOG("GtkCompositorWidget::SetRenderingSurface() [%p]\n", mWidget.get()); + +#if defined(MOZ_WAYLAND) + if (GdkIsWaylandDisplay()) { + LOG(" configure widget %p\n", mWidget.get()); + ConfigureWaylandBackend(); + } +#endif +#if defined(MOZ_X11) + if (GdkIsX11Display()) { + LOG(" configure XWindow %p\n", (void*)aXWindow); + ConfigureX11Backend((Window)aXWindow); + } +#endif +} + #ifdef MOZ_LOGGING bool GtkCompositorWidget::IsPopup() { return mWidget ? mWidget->IsPopup() : false; diff --git a/widget/gtk/GtkCompositorWidget.h b/widget/gtk/GtkCompositorWidget.h @@ -32,6 +32,7 @@ class PlatformCompositorWidgetDelegate : public CompositorWidgetDelegate { virtual GtkCompositorWidget* AsGtkCompositorWidget() { return nullptr; }; virtual void CleanupResources() = 0; + virtual void SetRenderingSurface(const uintptr_t aXWindow) = 0; // CompositorWidgetDelegate Overrides @@ -75,6 +76,9 @@ class GtkCompositorWidget : public CompositorWidget, // Can be used when underlying window is hidden/unmapped. void CleanupResources() override; + // Resume rendering with to given aXWindow (X11) or nsWindow (Wayland). + void SetRenderingSurface(const uintptr_t aXWindow) override; + // Set EGLWindow size to avoid rendering artifacts void SetEGLNativeWindowSize(const LayoutDeviceIntSize& aEGLWindowSize); diff --git a/widget/gtk/PCompositorWidget.ipdl b/widget/gtk/PCompositorWidget.ipdl @@ -23,6 +23,7 @@ parent: async NotifyClientSizeChanged(LayoutDeviceIntSize aClientSize); async CleanupResources(); + async SetRenderingSurface(uintptr_t aXWindow); child: diff --git 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" #include "WidgetUtilsGtk.h" #ifdef MOZ_WAYLAND @@ -41,7 +42,9 @@ namespace widget { using namespace mozilla::layers; WindowSurfaceProvider::WindowSurfaceProvider() - : mWindowSurface(nullptr) + : mWindowSurface(nullptr), + mMutex("WindowSurfaceProvider"), + mWindowSurfaceValid(false) #ifdef MOZ_X11 , mXDepth(0), @@ -63,10 +66,12 @@ WindowSurfaceProvider::~WindowSurfaceProvider() { #ifdef MOZ_WAYLAND bool WindowSurfaceProvider::Initialize(RefPtr<nsWindow> aWidget) { + mWindowSurfaceValid = false; mWidget = std::move(aWidget); return true; } bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { + mWindowSurfaceValid = false; mCompositorWidget = aCompositorWidget; mWidget = static_cast<nsWindow*>(aCompositorWidget->RealWidget()); return true; @@ -74,6 +79,8 @@ bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { #endif #ifdef MOZ_X11 bool WindowSurfaceProvider::Initialize(Window aWindow) { + mWindowSurfaceValid = false; + // Grab the window's visual and depth XWindowAttributes windowAttrs; if (!XGetWindowAttributes(DefaultXDisplay(), aWindow, &windowAttrs)) { @@ -89,6 +96,8 @@ bool WindowSurfaceProvider::Initialize(Window aWindow) { #endif void WindowSurfaceProvider::CleanupResources() { + MutexAutoLock lock(mMutex); + mWindowSurfaceValid = false; #ifdef MOZ_WAYLAND mWidget = nullptr; #endif @@ -137,6 +146,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) { @@ -144,6 +158,19 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion( return nullptr; } + // 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; + mWindowSurfaceValid = true; + } + if (!mWindowSurface) { mWindowSurface = CreateWindowSurface(); if (!mWindowSurface) { @@ -163,13 +190,37 @@ 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) { + // 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; + } +#if defined(MOZ_WAYLAND) + if (GdkIsWaylandDisplay()) { + // We're called too early or we're unmapped. + // Don't draw anything. + if (!mWidget || !mWidget->IsMapped()) { + return; + } + } +#endif mWindowSurface->Commit(aInvalidRegion); } +MOZ_POP_THREAD_SAFETY + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/WindowSurfaceProvider.h b/widget/gtk/WindowSurfaceProvider.h @@ -70,6 +70,16 @@ class WindowSurfaceProvider final { RefPtr<WindowSurface> mWindowSurface; + /* While CleanupResources() can be called from Main thread when nsWindow is + * destroyed/hidden, StartRemoteDrawingInRegion()/EndRemoteDrawingInRegion() + * is called from Compositor thread during rendering. + * + * As nsWindow CleanupResources() call comes from Gtk/X11 we can't synchronize + * that with WebRender so we use lock to synchronize the access. + */ + mozilla::Mutex mMutex MOZ_UNANNOTATED; + // WindowSurface needs to be re-created as underlying window was changed. + bool mWindowSurfaceValid; #ifdef MOZ_WAYLAND RefPtr<nsWindow> mWidget; // WindowSurfaceProvider is owned by GtkCompositorWidget so we don't need @@ -78,7 +88,12 @@ class WindowSurfaceProvider final { #endif #ifdef MOZ_X11 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/nsWindow.cpp b/widget/gtk/nsWindow.cpp @@ -859,6 +859,8 @@ void nsWindow::SetModal(bool aModal) { // nsIWidget method, which means IsShown. bool nsWindow::IsVisible() const { return mIsShown; } +bool nsWindow::IsMapped() const { return mIsMapped; } + void nsWindow::RegisterTouchWindow() { mHandleTouchEvent = true; mTouches.Clear(); @@ -1396,6 +1398,7 @@ void nsWindow::HideWaylandToplevelWindow() { popup = prev; } } + WaylandStopVsync(); gtk_widget_hide(mShell); } @@ -6079,6 +6082,22 @@ Window nsWindow::GetX11Window() { return (Window) nullptr; } +void nsWindow::ConfigureCompositor() { + LOG("nsWindow::ConfigureCompositor()"); + + if (mIsDestroyed) { + LOG(" quit, mIsDestroyed = %d", !!mIsDestroyed); + return; + } + // Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate(). + if (!mCompositorWidgetDelegate) { + LOG(" quit, missing mCompositorWidgetDelegate"); + return; + } + + ResumeCompositorImpl(); +} + void nsWindow::SetGdkWindow(GdkWindow* aGdkWindow) { LOG("nsWindow::SetGdkWindow() %p", aGdkWindow); if (!aGdkWindow) { @@ -6342,8 +6361,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, } gtk_widget_realize(container); - // mGdkWindow is set by moz_container_realize() / SetGdkWindow(). - MOZ_DIAGNOSTIC_ASSERT(mGdkWindow, "MozContainer realize failed?"); #ifdef MOZ_X11 if (GdkIsX11Display()) { @@ -6352,7 +6369,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, #endif #ifdef MOZ_WAYLAND if (GdkIsWaylandDisplay() && mIsAccelerated) { - mEGLWindow = mSurface->GetEGLWindow(mClientArea.Size()); + mEGLWindow = MOZ_WL_SURFACE(container)->GetEGLWindow(mClientArea.Size()); } #endif if (mEGLWindow) { @@ -6360,24 +6377,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, mGdkWindow, mEGLWindow); } -#ifdef MOZ_X11 - if (GdkIsX11Display()) { - mSurfaceProvider.Initialize(GetX11Window()); - - // 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 - // make sure this is the focus widget in the container gtk_widget_show(container); @@ -6508,7 +6507,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, mWaylandVsyncSource = new WaylandVsyncSource(this); mWaylandVsyncSource->Init(); mWaylandVsyncDispatcher = new VsyncDispatcher(mWaylandVsyncSource); - mWaylandVsyncSource->EnableVSyncSource(); } #endif @@ -6793,6 +6791,44 @@ void nsWindow::NativeMoveResize(bool aMoved, bool aResized) { } } +void nsWindow::ResumeCompositorImpl() { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + LOG("nsWindow::ResumeCompositorImpl()\n"); + + MOZ_DIAGNOSTIC_ASSERT(mCompositorWidgetDelegate); + mCompositorWidgetDelegate->SetRenderingSurface(GetX11Window()); + + // As WaylandStartVsync needs mCompositorWidgetDelegate this is the right + // time to start it. + WaylandStartVsync(); + + CompositorBridgeChild* remoteRenderer = GetRemoteRenderer(); + MOZ_RELEASE_ASSERT(remoteRenderer); + remoteRenderer->SendResume(); + remoteRenderer->SendForcePresent(wr::RenderReasons::WIDGET); +} + +void nsWindow::WaylandStartVsync() { +#ifdef MOZ_WAYLAND + if (!mWaylandVsyncSource) { + return; + } + LOG_VSYNC("nsWindow::WaylandStartVsync"); + mWaylandVsyncSource->EnableVSyncSource(); +#endif +} + +void nsWindow::WaylandStopVsync() { +#ifdef MOZ_WAYLAND + if (!mWaylandVsyncSource) { + return; + } + LOG_VSYNC("nsWindow::WaylandStopVsync"); + mWaylandVsyncSource->DisableVSyncSource(); +#endif +} + void nsWindow::NativeShow(bool aAction) { if (aAction) { // unset our flag now that our window has been shown @@ -8897,8 +8933,17 @@ void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) { delegate, !!mIsMapped, mCompositorWidgetDelegate); MOZ_RELEASE_ASSERT(NS_IsMainThread()); - mCompositorWidgetDelegate = - delegate ? delegate->AsPlatformSpecificDelegate() : nullptr; + if (delegate) { + mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate(); + MOZ_ASSERT(mCompositorWidgetDelegate, + "nsWindow::SetCompositorWidgetDelegate called with a " + "non-PlatformCompositorWidgetDelegate"); + if (mIsMapped) { + ConfigureCompositor(); + } + } else { + mCompositorWidgetDelegate = nullptr; + } } bool nsWindow::IsAlwaysUndecoratedWindow() const { @@ -9472,11 +9517,24 @@ nsWindow::GtkWindowDecoration nsWindow::GetSystemGtkWindowDecoration() { void nsWindow::GetCompositorWidgetInitData( mozilla::widget::CompositorWidgetInitData* aInitData) { - MOZ_DIAGNOSTIC_ASSERT(!mIsDestroyed); + nsCString displayName; LOG("nsWindow::GetCompositorWidgetInitData"); - nsCString displayName; + Window window = GetX11Window(); +#ifdef MOZ_X11 + // We're bit hackish here. Old GLX backend needs XWindow when GLContext + // is created so get XWindow now before map signal. + // We may see crashes/errors when nsWindow is unmapped (XWindow is + // invalidated) but we can't do anything about it. + if (!window && !gfxVars::UseEGL()) { + window = + gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(mContainer))); + } +#endif + *aInitData = mozilla::widget::GtkCompositorWidgetInitData( + window, displayName, GdkIsX11Display(), GetClientSize()); + #ifdef MOZ_X11 if (GdkIsX11Display()) { // Make sure the window XID is propagated to X server, we can fail otherwise @@ -9486,9 +9544,6 @@ void nsWindow::GetCompositorWidgetInitData( displayName = nsCString(XDisplayString(display)); } #endif - - *aInitData = mozilla::widget::GtkCompositorWidgetInitData( - GetX11Window(), displayName, GdkIsX11Display(), GetClientSize()); } #ifdef MOZ_X11 @@ -9776,6 +9831,24 @@ void nsWindow::OnMap() { if (mIsAlert) { gdk_window_set_override_redirect(GetToplevelGdkWindow(), TRUE); } + +#ifdef MOZ_X11 + if (GdkIsX11Display()) { + mSurfaceProvider.Initialize(GetX11Window()); + + // 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 && GdkIsX11Display()) { @@ -9791,7 +9864,12 @@ void nsWindow::OnMap() { RefreshWindowClass(); - LOG(" finished, GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window()); + // 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() { diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h @@ -194,6 +194,7 @@ class nsWindow final : public nsIWidget { mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override; void SetModal(bool aModal) override; bool IsVisible() const override; + bool IsMapped() const override; void ConstrainPosition(DesktopIntPoint&) override; void SetSizeConstraints(const SizeConstraints&) override; void LockAspectRatio(bool aShouldLock) override; @@ -530,6 +531,8 @@ class nsWindow final : public nsIWidget { LayoutDeviceIntSize GetMoveToRectPopupSize() override; #endif + void ResumeCompositorImpl(); + bool ApplyEnterLeaveMutterWorkaround(); void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override; @@ -574,6 +577,8 @@ class nsWindow final : public nsIWidget { bool DoTitlebarAction(mozilla::LookAndFeel::TitlebarEvent aEvent, GdkEventButton* aButtonEvent); + void WaylandStartVsync(); + void WaylandStopVsync(); void DestroyChildWindows(); GtkWidget* GetToplevelWidget() const; nsWindow* GetContainerWindow() const; @@ -711,7 +716,7 @@ class nsWindow final : public nsIWidget { // This track real window visibility from OS perspective. // It's set by OnMap/OnUnmap which is based on Gtk events. - bool mIsMapped; + mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped; // Has this widget been destroyed yet? mozilla::Atomic<bool, mozilla::Relaxed> mIsDestroyed; // mIsShown tracks requested visible status from browser perspective, i.e. @@ -866,6 +871,8 @@ class nsWindow final : public nsIWidget { void DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent); + void ConfigureCompositor(); + bool IsAlwaysUndecoratedWindow() const; // nsIWidget diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h @@ -727,6 +727,14 @@ class nsIWidget : public nsSupportsWeakReference { virtual bool IsVisible() const = 0; /** + * Returns whether the window has allocated resources so + * we can paint into it. + * Recently it's used on Linux/Gtk where we should not paint + * to invisible window. + */ + virtual bool IsMapped() const { return true; } + + /** * Perform platform-dependent sanity check on a potential window position. * This is guaranteed to work only for top-level windows. */