commit b73e692a64e85ed83a71bb9269768e3d5c963b26
parent a70a0ebf9aa5b868ae15c06fdbbbc92244c743b6
Author: Atila Butkovits <abutkovits@mozilla.com>
Date: Thu, 20 Nov 2025 14:17:45 +0200
Revert "Bug 1998657 [Linux] Set/Unset mGdkWindow on container realize/unrealize to make sure we have recent GdkWindow r=jhorak" for causing bustages at WaylandSurface.cpp.
This reverts commit 3a006d1c8f2a58b5e6796683609690ebbf67a996.
Revert "Bug 1998657 [Wayland] Set mIsVisible from frame callback r=emilio"
This reverts commit 7cf41c4eb1616dad42f3e66e7dd4d04fed1da602.
Revert "Bug 1998657 [Wayland] Update fractional scale & popup logging r=emilio"
This reverts commit 1997d8a78dcdddb450018c21840c5b8662ff485c.
Revert "Bug 1998657 [Linux] Update gdk_x11 window resource leak as GdkWindow is created from nsWindow::Create() r=emilio"
This reverts commit 51937e8e4515185103bd8e48a685f692aa85d94a.
Revert "Bug 1998657 [Wayland] Create EGLWindow over offscreen wl_surface and make it always available r=emilio"
This reverts commit 6cb5ce2f04df8f791ad18b6cbe1f6a914697a773.
Revert "Bug 1998657 [Wayland] Make WaylandSurface ready to draw right after its created r=emilio"
This reverts commit 915de2a8936abd806de54f8052d40c0a988af314.
Diffstat:
10 files changed, 426 insertions(+), 147 deletions(-)
diff --git a/build/valgrind/x86_64-pc-linux-gnu.sup b/build/valgrind/x86_64-pc-linux-gnu.sup
@@ -266,7 +266,15 @@
fun:_gdk_x11_display_create_window_impl
fun:gdk_window_ensure_native
fun:gdk_x11_window_get_xid
- fun:_ZN8nsWindow6CreateEP9nsIWidgetRKN7mozilla3gfx12IntRectTypedINS2_17LayoutDevicePixelEEERKNS2_6widget8InitDataE
+ fun:GetX11Window
+ ...
+ fun:gtk_widget_map
+ fun:gtk_window_map
+ ...
+ fun:gtk_widget_map
+ fun:gtk_window_show
+ ...
+ fun:gtk_widget_show
...
}
diff --git a/widget/gtk/MozContainer.cpp b/widget/gtk/MozContainer.cpp
@@ -189,28 +189,20 @@ void moz_container_realize(GtkWidget* widget) {
attributes.visual = gtk_widget_get_visual(widget);
window = gdk_window_new(parent, &attributes, attributes_mask);
- auto w = moz_container_get_nsWindow(container);
- LOGCONTAINER(
- ("moz_container_realize() [%p] GdkWindow %p\n", (void*)w, (void*)window));
+ LOGCONTAINER(("moz_container_realize() [%p] GdkWindow %p\n",
+ (void*)moz_container_get_nsWindow(container), (void*)window));
gtk_widget_register_window(widget, window);
gtk_widget_set_window(widget, window);
-
- MOZ_DIAGNOSTIC_ASSERT(w);
- w->SetGdkWindow(window);
}
void moz_container_unrealize(GtkWidget* widget) {
GdkWindow* window = gtk_widget_get_window(widget);
- auto w = moz_container_get_nsWindow(MOZ_CONTAINER(widget));
-
- LOGCONTAINER(("moz_container_unrealize() [%p] GdkWindow %p\n", (void*)w,
+ LOGCONTAINER(("moz_container_unrealize() [%p] GdkWindow %p\n",
+ (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)),
(void*)window));
- MOZ_DIAGNOSTIC_ASSERT(w);
- w->SetGdkWindow(nullptr);
-
if (gtk_widget_get_mapped(widget)) {
gtk_widget_unmap(widget);
}
diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp
@@ -99,6 +99,11 @@ static void moz_container_wayland_invalidate(MozContainer* container) {
gdk_window_invalidate_rect(window, nullptr, true);
}
+void moz_container_wayland_add_or_fire_initial_draw_callback(
+ MozContainer* container, const std::function<void(void)>& initial_draw_cb) {
+ MOZ_WL_SURFACE(container)->AddOrFireReadyToDrawCallback(initial_draw_cb);
+}
+
void moz_container_wayland_unmap(GtkWidget* widget) {
g_return_if_fail(IS_MOZ_CONTAINER(widget));
@@ -138,9 +143,23 @@ gboolean moz_container_wayland_map_event(GtkWidget* widget,
// below.
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ // Set waiting_to_show flag. It means the mozcontainer is cofigured/mapped
+ // and it's supposed to be visible. *But* it's really visible when we get
+ // moz_container_wayland_add_or_fire_initial_draw_callback() which means
+ // wayland compositor makes it live.
+ MOZ_WL_CONTAINER(widget)->waiting_to_show = true;
+ MozContainer* container = MOZ_CONTAINER(widget);
+ MOZ_WL_SURFACE(container)->AddOrFireReadyToDrawCallback(
+ [container]() -> void {
+ LOGCONTAINER(
+ "[%p] moz_container_wayland_add_or_fire_initial_draw_callback set "
+ "visible",
+ moz_container_get_nsWindow(container));
+ moz_container_wayland_clear_waiting_to_show_flag(container);
+ });
+
// Don't create wl_subsurface in map_event when it's already created or
// if we create it for the first time.
- MozContainer* container = MOZ_CONTAINER(widget);
if (MOZ_WL_SURFACE(container)->IsMapped() ||
MOZ_WL_CONTAINER(container)->before_first_size_alloc) {
return false;
@@ -262,7 +281,40 @@ static bool moz_container_wayland_ensure_surface(MozContainer* container,
return true;
}
+struct wl_egl_window* moz_container_wayland_get_egl_window(
+ MozContainer* container) {
+ LOGCONTAINER("%s [%p] mapped %d eglwindow %d", __FUNCTION__,
+ (void*)moz_container_get_nsWindow(container),
+ MOZ_WL_SURFACE(container)->IsMapped(),
+ MOZ_WL_SURFACE(container)->HasEGLWindow());
+
+ if (!MOZ_WL_SURFACE(container)->IsMapped()) {
+ return nullptr;
+ }
+
+ // TODO: Get size from bounds instead of GdkWindow?
+ // We may be in rendering/compositor thread here.
+ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container));
+ DesktopIntSize size(gdk_window_get_width(window),
+ gdk_window_get_height(window));
+ return MOZ_WL_SURFACE(container)->GetEGLWindow(size);
+}
+
+gboolean moz_container_wayland_can_draw(MozContainer* container) {
+ return MOZ_WL_SURFACE(container)->IsReadyToDraw();
+}
+
double moz_container_wayland_get_scale(MozContainer* container) {
nsWindow* window = moz_container_get_nsWindow(container);
return window ? window->FractionalScaleFactor() : 1.0;
}
+
+bool moz_container_wayland_is_waiting_to_show(MozContainer* container) {
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ return MOZ_WL_CONTAINER(container)->waiting_to_show;
+}
+
+void moz_container_wayland_clear_waiting_to_show_flag(MozContainer* container) {
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+ MOZ_WL_CONTAINER(container)->waiting_to_show = false;
+}
diff --git a/widget/gtk/MozContainerWayland.h b/widget/gtk/MozContainerWayland.h
@@ -43,13 +43,24 @@ struct MozContainerWayland {
RefPtr<mozilla::widget::WaylandSurface> mSurface;
gboolean before_first_size_alloc = false;
+ gboolean waiting_to_show = false;
};
void moz_container_wayland_map(GtkWidget*);
gboolean moz_container_wayland_map_event(GtkWidget*, GdkEventAny*);
void moz_container_wayland_size_allocate(GtkWidget*, GtkAllocation*);
void moz_container_wayland_unmap(GtkWidget*);
+
+struct wl_egl_window* moz_container_wayland_get_egl_window(
+ MozContainer* container);
+
+void moz_container_wayland_add_or_fire_initial_draw_callback(
+ MozContainer* container, const std::function<void(void)>& initial_draw_cb);
+
wl_surface* moz_gtk_widget_get_wl_surface(GtkWidget* aWidget);
+gboolean moz_container_wayland_can_draw(MozContainer* container);
double moz_container_wayland_get_scale(MozContainer* container);
+bool moz_container_wayland_is_waiting_to_show(MozContainer* container);
+void moz_container_wayland_clear_waiting_to_show_flag(MozContainer* container);
#endif /* __MOZ_CONTAINER_WAYLAND_H__ */
diff --git a/widget/gtk/WaylandSurface.cpp b/widget/gtk/WaylandSurface.cpp
@@ -38,15 +38,12 @@ extern mozilla::LazyLogModule gWidgetWaylandLog;
MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
# define LOGS_VERBOSE(...) \
MOZ_LOG(gWidgetWaylandLog, mozilla::LogLevel::Verbose, (__VA_ARGS__))
-# define LOG_ENABLED_VERBOSE() \
- MOZ_LOG_TEST(gWidgetWaylandLog, mozilla::LogLevel::Verbose)
#else
# define LOGWAYLAND(...)
# undef LOGVERBOSE
# define LOGVERBOSE(...)
# define LOGS(...)
# define LOGS_VERBOSE(...)
-# define LOG_ENABLED_VERBOSE(...)
#endif /* MOZ_LOGGING */
using namespace mozilla;
@@ -120,6 +117,104 @@ WaylandSurface::~WaylandSurface() {
"We can't release WaylandSurface with numap callback!");
}
+void WaylandSurface::ReadyToDrawFrameCallbackHandler(
+ struct wl_callback* callback) {
+ LOGWAYLAND(
+ "WaylandSurface::ReadyToDrawFrameCallbackHandler() "
+ "mReadyToDrawFrameCallback %p mIsReadyToDraw %d initial_draw callback "
+ "%zd\n",
+ (void*)mReadyToDrawFrameCallback, (bool)mIsReadyToDraw,
+ mReadyToDrawCallbacks.size());
+
+ // We're supposed to run on main thread only.
+ AssertIsOnMainThread();
+
+ // mReadyToDrawFrameCallback/callback can be nullptr when redering directly
+ // to GtkWidget and ReadyToDrawFrameCallbackHandler is called by us from main
+ // thread by WaylandSurface::Map().
+ MOZ_RELEASE_ASSERT(mReadyToDrawFrameCallback == callback);
+
+ std::vector<std::function<void(void)>> cbs;
+ {
+ WaylandSurfaceLock lock(this);
+ MozClearPointer(mReadyToDrawFrameCallback, wl_callback_destroy);
+ // It's possible that we're already unmapped so quit in such case.
+ if (!mIsMapped) {
+ LOGWAYLAND(" WaylandSurface is unmapped, quit.");
+ if (!mReadyToDrawCallbacks.empty()) {
+ NS_WARNING("Unmapping WaylandSurface with active draw callback!");
+ mReadyToDrawCallbacks.clear();
+ }
+ return;
+ }
+ if (mIsReadyToDraw) {
+ return;
+ }
+ mIsReadyToDraw = true;
+ cbs = std::move(mReadyToDrawCallbacks);
+
+ RequestFrameCallbackLocked(lock);
+ }
+
+ // We can't call the callbacks under lock
+#ifdef MOZ_LOGGING
+ int callbackNum = 0;
+#endif
+ for (auto const& cb : cbs) {
+ LOGWAYLAND(" initial callback fire [%d]", callbackNum++);
+ cb();
+ }
+}
+
+static void ReadyToDrawFrameCallbackHandler(void* aWaylandSurface,
+ struct wl_callback* callback,
+ uint32_t time) {
+ auto* waylandSurface = static_cast<WaylandSurface*>(aWaylandSurface);
+ waylandSurface->ReadyToDrawFrameCallbackHandler(callback);
+}
+
+static const struct wl_callback_listener
+ sWaylandSurfaceReadyToDrawFrameListener = {
+ ::ReadyToDrawFrameCallbackHandler};
+
+void WaylandSurface::AddReadyToDrawCallbackLocked(
+ const WaylandSurfaceLock& aProofOfLock,
+ const std::function<void(void)>& aDrawCB) {
+ LOGVERBOSE("WaylandSurface::AddReadyToDrawCallbackLocked()");
+ MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
+ mReadyToDrawCallbacks.push_back(aDrawCB);
+}
+
+void WaylandSurface::AddOrFireReadyToDrawCallback(
+ const std::function<void(void)>& aDrawCB) {
+ {
+ WaylandSurfaceLock lock(this);
+ if (!mIsReadyToDraw) {
+ LOGVERBOSE(
+ "WaylandSurface::AddOrFireReadyToDrawCallback() callback stored");
+ mReadyToDrawCallbacks.push_back(aDrawCB);
+ return;
+ }
+ }
+
+ LOGWAYLAND("WaylandSurface::AddOrFireReadyToDrawCallback() callback fire");
+
+ // We're ready to draw and we have a surface to draw into.
+ aDrawCB();
+}
+
+void WaylandSurface::ClearReadyToDrawCallbacksLocked(
+ const WaylandSurfaceLock& aProofOfLock) {
+ MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
+ MozClearPointer(mReadyToDrawFrameCallback, wl_callback_destroy);
+ mReadyToDrawCallbacks.clear();
+}
+
+void WaylandSurface::ClearReadyToDrawCallbacks() {
+ WaylandSurfaceLock lock(this);
+ ClearReadyToDrawCallbacksLocked(lock);
+}
+
bool WaylandSurface::HasEmulatedFrameCallbackLocked(
const WaylandSurfaceLock& aProofOfLock) const {
return mFrameCallbackHandler.IsSet() && mFrameCallbackHandler.mEmulated;
@@ -138,11 +233,7 @@ void WaylandSurface::FrameCallbackHandler(struct wl_callback* aCallback,
WaylandSurfaceLock lock(this);
// Don't run emulated callbacks on hidden surfaces
- if ((emulatedCallback || aRoutedFromChildSurface) && !mIsVisible) {
- LOGVERBOSE(
- "WaylandSurface::FrameCallbackHandler() quit, emulatedCallback %d "
- "aRoutedFromChildSurface %d mIsVisible %d",
- emulatedCallback, aRoutedFromChildSurface, !mIsVisible);
+ if ((emulatedCallback || aRoutedFromChildSurface) && !mIsReadyToDraw) {
return;
}
@@ -168,10 +259,6 @@ void WaylandSurface::FrameCallbackHandler(struct wl_callback* aCallback,
// We're getting regular frame callback from this surface so we must
// have buffer attached.
if (!emulatedCallback && !aRoutedFromChildSurface) {
- LOGVERBOSE(
- "WaylandSurface::FrameCallbackHandler() marked as visible & has "
- "buffer");
- mIsVisible = true;
mBufferAttached = true;
}
@@ -387,23 +474,12 @@ void WaylandSurface::DisableDMABufFormatsLocked(
mFormats = nullptr;
}
-void WaylandSurface::VisibleCallbackHandler() {
- WaylandSurfaceLock lock(this);
- LOGVERBOSE("WaylandSurface::VisibleCallbackHandler()");
- MozClearPointer(mVisibleFrameCallback, wl_callback_destroy);
- // We can get frame callback after unmap due to queue sync.
- // In this case just ignore it.
- if (mIsMapped) {
- mIsVisible = true;
- mBufferAttached = true;
- }
-}
-
bool WaylandSurface::MapLocked(const WaylandSurfaceLock& aProofOfLock,
wl_surface* aParentWLSurface,
WaylandSurfaceLock* aParentWaylandSurfaceLock,
gfx::IntPoint aSubsurfacePosition,
- bool aSubsurfaceDesync) {
+ bool aSubsurfaceDesync,
+ bool aUseReadyToDrawCallback) {
LOGWAYLAND("WaylandSurface::MapLocked()");
MOZ_DIAGNOSTIC_ASSERT(&aProofOfLock == mSurfaceLock);
MOZ_DIAGNOSTIC_ASSERT(!mIsMapped, "Already mapped?");
@@ -443,18 +519,17 @@ bool WaylandSurface::MapLocked(const WaylandSurfaceLock& aProofOfLock,
LOGWAYLAND(" subsurface position [%d,%d]", (int)mSubsurfacePosition.x,
(int)mSubsurfacePosition.y);
+ if (aUseReadyToDrawCallback) {
+ mReadyToDrawFrameCallback = wl_surface_frame(mParentSurface);
+ wl_callback_add_listener(mReadyToDrawFrameCallback,
+ &sWaylandSurfaceReadyToDrawFrameListener, this);
+ LOGWAYLAND(" created ready to draw frame callback ID %d\n",
+ wl_proxy_get_id((struct wl_proxy*)mReadyToDrawFrameCallback));
+ }
+
LOGWAYLAND(" register frame callback");
RequestFrameCallbackLocked(aProofOfLock);
- MOZ_DIAGNOSTIC_ASSERT(!mVisibleFrameCallback);
- static const struct wl_callback_listener listener{
- [](void* aData, struct wl_callback* callback, uint32_t time) {
- RefPtr waylandSurface = static_cast<WaylandSurface*>(aData);
- waylandSurface->VisibleCallbackHandler();
- }};
- mVisibleFrameCallback = wl_surface_frame(mSurface);
- wl_callback_add_listener(mVisibleFrameCallback, &listener, this);
-
CommitLocked(aProofOfLock, /* aForceCommit */ true,
/* aForceDisplayFlush */ true);
@@ -481,7 +556,8 @@ bool WaylandSurface::MapLocked(const WaylandSurfaceLock& aProofOfLock,
gfx::IntPoint aSubsurfacePosition) {
return MapLocked(aProofOfLock, nullptr, aParentWaylandSurfaceLock,
aSubsurfacePosition,
- /* aSubsurfaceDesync */ false);
+ /* aSubsurfaceDesync */ false,
+ /* aUseReadyToDrawCallback */ false);
}
void WaylandSurface::SetUnmapCallbackLocked(
@@ -533,11 +609,11 @@ void WaylandSurface::UnmapLocked(WaylandSurfaceLock& aSurfaceLock) {
return;
}
mIsMapped = false;
- mIsVisible = false;
LOGWAYLAND("WaylandSurface::UnmapLocked()");
RemoveAttachedBufferLocked(aSurfaceLock);
+ ClearReadyToDrawCallbacksLocked(aSurfaceLock);
ClearFrameCallbackLocked(aSurfaceLock);
ClearScaleLocked(aSurfaceLock);
@@ -557,7 +633,8 @@ void WaylandSurface::UnmapLocked(WaylandSurfaceLock& aSurfaceLock) {
MozClearPointer(mPendingOpaqueRegion, wl_region_destroy);
MozClearPointer(mOpaqueRegionFrameCallback, wl_callback_destroy);
- MozClearPointer(mVisibleFrameCallback, wl_callback_destroy);
+ mIsReadyToDraw = false;
+ mBufferAttached = false;
// Remove references to WaylandBuffers attached to mSurface,
// we don't want to get any buffer release callback when we're unmapped.
@@ -1173,30 +1250,16 @@ GdkWindow* WaylandSurface::GetGdkWindow() const {
}
double WaylandSurface::GetScale() {
-#ifdef MOZ_LOGGING
- static float lastLoggedScale = 0.0;
-#endif
-
if (mScreenScale != sNoScale) {
-#ifdef MOZ_LOGGING
- if (LOG_ENABLED_VERBOSE() && lastLoggedScale != mScreenScale) {
- lastLoggedScale = mScreenScale;
- LOGVERBOSE("WaylandSurface::GetScale() fractional scale %f",
- (double)mScreenScale);
- }
-#endif
+ LOGVERBOSE("WaylandSurface::GetScale() fractional scale %f",
+ (double)mScreenScale);
return mScreenScale;
}
// We don't have scale yet - query parent surface if there's any.
if (mParent) {
auto scale = mParent->GetScale();
-#ifdef MOZ_LOGGING
- if (LOG_ENABLED_VERBOSE() && lastLoggedScale != scale) {
- lastLoggedScale = scale;
- LOGVERBOSE("WaylandSurface::GetScale() parent scale %f", scale);
- }
-#endif
+ LOGVERBOSE("WaylandSurface::GetScale() parent scale %f", scale);
return scale;
}
diff --git a/widget/gtk/WaylandSurface.h b/widget/gtk/WaylandSurface.h
@@ -43,6 +43,10 @@ class WaylandSurface final {
void SetLoggingWidget(void* aWidget) { mLoggingWidget = aWidget; }
#endif
+ void ReadyToDrawFrameCallbackHandler(struct wl_callback* aCallback);
+ void AddOrFireReadyToDrawCallback(const std::function<void(void)>& aDrawCB);
+ void ClearReadyToDrawCallbacks();
+
void FrameCallbackHandler(struct wl_callback* aCallback, uint32_t aTime,
bool aRoutedFromChildSurface);
@@ -74,15 +78,11 @@ class WaylandSurface final {
bool SetEGLWindowSize(LayoutDeviceIntSize aSize);
bool HasEGLWindow() const { return !!mEGLWindow; }
+ // Read to draw means we got frame callback from parent surface
+ // where we attached to.
+ bool IsReadyToDraw() const { return mIsReadyToDraw; }
// Mapped means we have all internals created.
bool IsMapped() const { return mIsMapped; }
-
- // We've got first frame callback so we're really visible now.
- bool IsVisible() const { return mIsVisible; }
-
- // Called from frame callback and sets the visible flag
- void VisibleCallbackHandler();
-
// Indicate that Wayland surface uses Gdk resources which
// need to be released on main thread by GdkCleanUpLocked().
// It may be called after Unmap() to make sure
@@ -122,6 +122,10 @@ class WaylandSurface final {
bool CreateViewportLocked(const WaylandSurfaceLock& aProofOfLock,
bool aFollowsSizeChanges);
+ void AddReadyToDrawCallbackLocked(
+ const WaylandSurfaceLock& aProofOfLock,
+ const std::function<void(void)>& aInitialDrawCB);
+
// Attach WaylandBuffer which shows WaylandBuffer content
// on screen.
bool AttachLocked(const WaylandSurfaceLock& aSurfaceLock,
@@ -277,7 +281,8 @@ class WaylandSurface final {
bool MapLocked(const WaylandSurfaceLock& aProofOfLock,
wl_surface* aParentWLSurface,
WaylandSurfaceLock* aParentWaylandSurfaceLock,
- gfx::IntPoint aSubsurfacePosition, bool aSubsurfaceDesync);
+ gfx::IntPoint aSubsurfacePosition, bool aSubsurfaceDesync,
+ bool aUseReadyToDrawCallback = true);
void SetSizeLocked(const WaylandSurfaceLock& aProofOfLock,
gfx::IntSize aSizeScaled, gfx::IntSize aUnscaledSize);
@@ -299,18 +304,21 @@ class WaylandSurface final {
bool HasEmulatedFrameCallbackLocked(
const WaylandSurfaceLock& aProofOfLock) const;
+ void ClearReadyToDrawCallbacksLocked(const WaylandSurfaceLock& aProofOfLock);
+
void ClearScaleLocked(const WaylandSurfaceLock& aProofOfLock);
// Weak ref to owning widget (nsWindow or NativeLayerWayland),
// used for diagnostics/logging only.
void* mLoggingWidget = nullptr;
- // mIsMapped means we're supposed to be visible
- // (or not if Wayland compositor decides so).
+ // WaylandSurface mapped - we have valid wl_surface where we can paint to.
mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped{false};
- // mIsVisible means we're really visible as we've got frame callback.
- mozilla::Atomic<bool, mozilla::Relaxed> mIsVisible{false};
+ // Wayland shows only subsurfaces of visible parent surfaces.
+ // mIsReadyToDraw means our parent wl_surface has content so
+ // this WaylandSurface can be visible on screen and get get frame callback.
+ mozilla::Atomic<bool, mozilla::Relaxed> mIsReadyToDraw{false};
// We used Gdk functions which needs clean up in main thread.
mozilla::Atomic<bool, mozilla::Relaxed> mIsPendingGdkCleanup{false};
@@ -377,8 +385,10 @@ class WaylandSurface final {
bool mBufferTransformFlippedX = false;
bool mBufferTransformFlippedY = false;
- // Frame callback for mIsVisible flag
- wl_callback* mVisibleFrameCallback = nullptr;
+ // Frame callback registered to parent surface. When we get it we know
+ // parent surface is ready and we can paint.
+ wl_callback* mReadyToDrawFrameCallback = nullptr;
+ std::vector<std::function<void(void)>> mReadyToDrawCallbacks;
// Frame callbacks of this surface
wl_callback* mFrameCallback = nullptr;
diff --git a/widget/gtk/WindowSurfaceWaylandMultiBuffer.cpp b/widget/gtk/WindowSurfaceWaylandMultiBuffer.cpp
@@ -156,7 +156,9 @@ WindowSurfaceWaylandMB::WindowSurfaceWaylandMB(
RefPtr<nsWindow> aWindow, GtkCompositorWidget* aCompositorWidget)
: mSurfaceLock("WindowSurfaceWayland lock"),
mWindow(std::move(aWindow)),
- mCompositorWidget(aCompositorWidget) {}
+ mCompositorWidget(aCompositorWidget),
+ mFrameInProcess(false),
+ mCallbackRequested(false) {}
bool WindowSurfaceWaylandMB::MaybeUpdateWindowSize() {
// We want to get window size from compositor widget as it matches window
@@ -187,6 +189,7 @@ already_AddRefed<DrawTarget> WindowSurfaceWaylandMB::Lock(
if (mWindow->GetWindowType() == WindowType::Invisible) {
return nullptr;
}
+ mFrameInProcess = true;
CollectPendingSurfaces(lock);
@@ -278,10 +281,30 @@ void WindowSurfaceWaylandMB::Commit(
// invisible window
return;
}
+ mFrameInProcess = false;
MozContainer* container = mWindow->GetMozContainer();
WaylandSurface* waylandSurface = MOZ_WL_SURFACE(container);
WaylandSurfaceLock lock(waylandSurface);
+ if (!waylandSurface->IsMapped()) {
+ LOGWAYLAND(
+ "WindowSurfaceWaylandMB::Commit [%p] frame queued: can't lock "
+ "wl_surface\n",
+ (void*)mWindow.get());
+ if (!mCallbackRequested) {
+ RefPtr<WindowSurfaceWaylandMB> self(this);
+ waylandSurface->AddReadyToDrawCallbackLocked(
+ lock, [self, aInvalidRegion]() -> void {
+ MutexAutoLock lock(self->mSurfaceLock);
+ if (!self->mFrameInProcess) {
+ self->Commit(lock, aInvalidRegion);
+ }
+ self->mCallbackRequested = false;
+ });
+ mCallbackRequested = true;
+ }
+ return;
+ }
waylandSurface->InvalidateRegionLocked(lock,
aInvalidRegion.ToUnknownRegion());
diff --git a/widget/gtk/WindowSurfaceWaylandMultiBuffer.h b/widget/gtk/WindowSurfaceWaylandMultiBuffer.h
@@ -73,6 +73,10 @@ class WindowSurfaceWaylandMB : public WindowSurface {
nsTArray<RefPtr<WaylandBufferSHM>> mInUseBuffers;
nsTArray<RefPtr<WaylandBufferSHM>> mPendingBuffers;
nsTArray<RefPtr<WaylandBufferSHM>> mAvailableBuffers;
+
+ // delayed commits
+ bool mFrameInProcess;
+ bool mCallbackRequested;
};
} // namespace mozilla::widget
diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
@@ -411,7 +411,8 @@ static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) {
}
nsWindow::nsWindow()
- : mIsMapped(false),
+ : mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"),
+ mIsMapped(false),
mIsDestroyed(false),
mIsShown(false),
mNeedsShow(false),
@@ -718,10 +719,6 @@ void nsWindow::Destroy() {
gtk_accessible_set_widget(GTK_ACCESSIBLE(ac), nullptr);
}
- // Owned by WaylandSurface or it's X11 ID,
- // just drop the reference here.
- mEGLWindow = nullptr;
-
gtk_widget_destroy(mShell);
mShell = nullptr;
mContainer = nullptr;
@@ -729,6 +726,9 @@ void nsWindow::Destroy() {
mSurface = nullptr;
#endif
+ MOZ_ASSERT(!mGdkWindow,
+ "mGdkWindow should be NULL when mContainer is destroyed");
+
#ifdef ACCESSIBILITY
if (mRootAccessible) {
mRootAccessible = nullptr;
@@ -966,7 +966,7 @@ bool nsWindow::ToplevelUsesCSD() const {
#ifdef MOZ_WAYLAND
if (GdkIsWaylandDisplay()) {
static auto sGdkWaylandDisplayPrefersSsd =
- (gboolean (*)(const GdkWaylandDisplay*))dlsym(
+ (gboolean(*)(const GdkWaylandDisplay*))dlsym(
RTLD_DEFAULT, "gdk_wayland_display_prefers_ssd");
// NOTE(emilio): Not using GDK_WAYLAND_DISPLAY to avoid bug 1946088.
return !sGdkWaylandDisplayPrefersSsd ||
@@ -1371,6 +1371,15 @@ void nsWindow::HideWaylandPopupWindow(bool aTemporaryHide,
if (mPopupClosed) {
LOG(" Clearing mMoveToRectPopupSize\n");
mMoveToRectPopupSize = {};
+#ifdef MOZ_WAYLAND
+ if (moz_container_wayland_is_waiting_to_show(mContainer)) {
+ // We need to clear rendering queue, see Bug 1782948.
+ LOG(" popup failed to show by Wayland compositor, clear rendering "
+ "queue.");
+ moz_container_wayland_clear_waiting_to_show_flag(mContainer);
+ ClearRenderingQueue();
+ }
+#endif
}
}
@@ -1431,11 +1440,11 @@ void nsWindow::WaylandPopupCloseOrphanedPopups() {
nsWindow* popup = mWaylandPopupNext;
bool dangling = false;
while (popup) {
- if (!dangling && !MOZ_WL_SURFACE(popup->GetMozContainer())->IsVisible()) {
+ if (!dangling &&
+ moz_container_wayland_is_waiting_to_show(popup->GetMozContainer())) {
LOG(" popup [%p] is waiting to show, close all child popups", popup);
dangling = true;
} else if (dangling) {
- LOG(" popup [%p] is dangling, hide it", popup);
popup->WaylandPopupMarkAsClosed();
}
popup = popup->mWaylandPopupNext;
@@ -3741,8 +3750,41 @@ void* nsWindow::GetNativeData(uint32_t aDataType) {
}
case NS_NATIVE_OPENGL_CONTEXT:
return nullptr;
- case NS_NATIVE_EGL_WINDOW:
- return mIsDestroyed ? nullptr : mEGLWindow;
+ 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;
+ if (mIsMapped && !mIsDestroyed) {
+#ifdef MOZ_X11
+ if (GdkIsX11Display()) {
+ eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow);
+ }
+#endif
+#ifdef MOZ_WAYLAND
+ if (GdkIsWaylandDisplay()) {
+ eglWindow = moz_container_wayland_get_egl_window(mContainer);
+ }
+#endif
+ }
+ LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p",
+ mGdkWindow, eglWindow);
+ return eglWindow;
+ }
default:
NS_WARNING("nsWindow::GetNativeData called with bad value");
return nullptr;
@@ -4071,10 +4113,17 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
}
// Windows that are not visible will be painted after they become visible.
- if (!mHasMappedToplevel) {
- LOG("quit, !mHasMappedToplevel");
+ if (!mGdkWindow || !mHasMappedToplevel) {
+ LOG("quit, !mGdkWindow || !mHasMappedToplevel");
+ return FALSE;
+ }
+#ifdef MOZ_WAYLAND
+ if (!mIsDragPopup && GdkIsWaylandDisplay() &&
+ !moz_container_wayland_can_draw(mContainer)) {
+ LOG("quit, !moz_container_wayland_can_draw()");
return FALSE;
}
+#endif
if (!GetPaintListener()) {
LOG("quit, !GetPaintListener()");
@@ -4106,8 +4155,8 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) {
// If the window has been destroyed during the will paint notification,
// there is nothing left to do.
- if (mIsDestroyed) {
- LOG("quit, mIsDestroyed");
+ if (!mGdkWindow || mIsDestroyed) {
+ LOG("quit, !mGdkWindow || mIsDestroyed");
return TRUE;
}
@@ -4260,7 +4309,7 @@ gboolean nsWindow::OnShellConfigureEvent(GdkEventConfigure* aEvent) {
// Don't fire configure event for scale changes, we handle that
// OnScaleEvent event. Skip that for toplevel windows only.
- if (IsTopLevelWidget() &&
+ if (mGdkWindow && IsTopLevelWidget() &&
mCeiledScaleFactor != gdk_window_get_scale_factor(mGdkWindow)) {
LOG(" scale factor changed to %d, return early",
gdk_window_get_scale_factor(mGdkWindow));
@@ -4286,6 +4335,10 @@ void nsWindow::OnContainerSizeAllocate(GtkAllocation* aAllocation) {
mReceivedClientArea = DesktopIntRect(aAllocation->x, aAllocation->y,
aAllocation->width, aAllocation->height);
+ if (!mGdkWindow) {
+ return;
+ }
+
// Bounds will get updated on the main configure.
// Gecko permits running nested event loops during processing of events,
// GtkWindow callers of gtk_widget_size_allocate expect the signal handlers
@@ -4556,6 +4609,10 @@ void nsWindow::EmulateResizeDrag(GdkEventMotion* aEvent) {
void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
mLastMouseCoordinates.Set(aEvent);
+ if (!mGdkWindow) {
+ return;
+ }
+
// Emulate gdk_window_begin_resize_drag() for windows
// with fixed aspect ratio on Wayland.
if (mAspectResizer && mAspectRatio != 0.0f) {
@@ -4572,8 +4629,10 @@ void nsWindow::OnMotionNotifyEvent(GdkEventMotion* aEvent) {
GdkWindow* dragWindow = nullptr;
// find the top-level window
- dragWindow = gdk_window_get_toplevel(mGdkWindow);
- MOZ_ASSERT(dragWindow, "gdk_window_get_toplevel should not return null");
+ if (mGdkWindow) {
+ dragWindow = gdk_window_get_toplevel(mGdkWindow);
+ MOZ_ASSERT(dragWindow, "gdk_window_get_toplevel should not return null");
+ }
#ifdef MOZ_X11
if (dragWindow && GdkIsX11Display()) {
@@ -5008,6 +5067,10 @@ void nsWindow::OnButtonReleaseEvent(GdkEventButton* aEvent) {
SetLastPointerDownEvent(nullptr);
mLastMouseCoordinates.Set(aEvent);
+ if (!mGdkWindow) {
+ return;
+ }
+
if (mAspectResizer) {
mAspectResizer = Nothing();
return;
@@ -5185,7 +5248,7 @@ WidgetEventTime nsWindow::GetWidgetEventTime(guint32 aEventTime) {
}
TimeStamp nsWindow::GetEventTimeStamp(guint32 aEventTime) {
- if (MOZ_UNLIKELY(mIsDestroyed)) {
+ if (MOZ_UNLIKELY(!mGdkWindow)) {
// nsWindow has been Destroy()ed.
return TimeStamp::Now();
}
@@ -5221,6 +5284,7 @@ TimeStamp nsWindow::GetEventTimeStamp(guint32 aEventTime) {
#ifdef MOZ_X11
mozilla::CurrentX11TimeGetter* nsWindow::GetCurrentTimeGetter() {
+ MOZ_ASSERT(mGdkWindow, "Expected mGdkWindow to be set");
if (MOZ_UNLIKELY(!mCurrentTimeGetter)) {
mCurrentTimeGetter = MakeUnique<CurrentX11TimeGetter>(mGdkWindow);
}
@@ -5283,7 +5347,7 @@ void nsWindow::OnScrollEvent(GdkEventScroll* aEvent) {
if (StaticPrefs::apz_gtk_pangesture_enabled() &&
gtk_check_version(3, 20, 0) == nullptr) {
static auto sGdkEventIsScrollStopEvent =
- (gboolean (*)(const GdkEvent*))dlsym(
+ (gboolean(*)(const GdkEvent*))dlsym(
RTLD_DEFAULT, "gdk_event_is_scroll_stop_event");
LOG("[%d] pan smooth event dx=%.2f dy=%.2f inprogress=%d\n",
@@ -5674,7 +5738,7 @@ void nsWindow::OnCompositedChanged() {
// Let's follow the working scenario for now to avoid complexity
// and maybe fix that later.
void nsWindow::OnScaleEvent() {
- if (!IsTopLevelWidget()) {
+ if (!mGdkWindow || !IsTopLevelWidget()) {
return;
}
@@ -5692,6 +5756,7 @@ void nsWindow::RefreshScale(bool aRefreshScreen, bool aForceRefresh) {
LOG("nsWindow::RefreshScale() GdkWindow scale %d refresh %d",
gdk_window_get_scale_factor(mGdkWindow), aRefreshScreen);
+ MOZ_DIAGNOSTIC_ASSERT(mIsMapped && mGdkWindow);
int ceiledScale = gdk_window_get_scale_factor(mGdkWindow);
const bool scaleChanged =
aForceRefresh || GdkCeiledScaleFactor() != ceiledScale;
@@ -5736,7 +5801,7 @@ void nsWindow::SetDragPopupSurface(
mDragPopupSurface = aDragPopupSurface;
mDragPopupSurfaceRegion = aInvalidRegion;
- if (!mIsDestroyed) {
+ if (mGdkWindow) {
gdk_window_invalidate_rect(mGdkWindow, nullptr, false);
}
}
@@ -6147,38 +6212,50 @@ nsCString nsWindow::GetPopupTypeName() {
Window nsWindow::GetX11Window() {
#ifdef MOZ_X11
if (GdkIsX11Display()) {
- return gdk_x11_window_get_xid(mGdkWindow);
+ return mGdkWindow ? gdk_x11_window_get_xid(mGdkWindow) : X11None;
}
#endif
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;
+void nsWindow::EnsureGdkWindow() {
+ MOZ_DIAGNOSTIC_ASSERT(mIsMapped);
+ if (!mGdkWindow) {
+ mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer));
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
}
-
- ResumeCompositorImpl();
}
-void nsWindow::SetGdkWindow(GdkWindow* aGdkWindow) {
- LOG("nsWindow::SetGdkWindow() %p", aGdkWindow);
- if (!aGdkWindow) {
- if (mGdkWindow) {
- g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
+void nsWindow::ConfigureCompositor() {
+ MOZ_DIAGNOSTIC_ASSERT(mIsMapped);
+
+ LOG("nsWindow::ConfigureCompositor()");
+ auto startCompositing = [self = RefPtr{this}, this]() -> void {
+ LOG(" moz_container_wayland_add_or_fire_initial_draw_callback "
+ "ConfigureCompositor");
+
+ // too late
+ if (mIsDestroyed || !mIsMapped) {
+ LOG(" quit, mIsDestroyed = %d mIsMapped = %d", !!mIsDestroyed,
+ !!mIsMapped);
+ return;
+ }
+ // Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate().
+ if (!mCompositorWidgetDelegate) {
+ LOG(" quit, missing mCompositorWidgetDelegate");
+ return;
}
- mGdkWindow = nullptr;
+
+ ResumeCompositorImpl();
+ };
+
+ if (GdkIsWaylandDisplay()) {
+#ifdef MOZ_WAYLAND
+ moz_container_wayland_add_or_fire_initial_draw_callback(mContainer,
+ startCompositing);
+#endif
} else {
- mGdkWindow = aGdkWindow;
- g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
+ startCompositing();
}
}
@@ -6433,21 +6510,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
gtk_widget_realize(container);
-#ifdef MOZ_X11
- if (GdkIsX11Display()) {
- mEGLWindow = (void*)GDK_WINDOW_XID(mGdkWindow);
- }
-#endif
-#ifdef MOZ_WAYLAND
- if (GdkIsWaylandDisplay() && mIsAccelerated) {
- mEGLWindow = MOZ_WL_SURFACE(container)->GetEGLWindow(mClientArea.Size());
- }
-#endif
- if (mEGLWindow) {
- LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned mEGLWindow %p",
- mGdkWindow, mEGLWindow);
- }
-
// make sure this is the focus widget in the container
gtk_widget_show(container);
@@ -9890,7 +9952,7 @@ bool nsWindow::SetEGLNativeWindowSize(
// SetEGLNativeWindowSize() is Wayland only call.
MOZ_ASSERT(GdkIsWaylandDisplay());
- if (mIsDestroyed) {
+ if (!mIsMapped) {
return true;
}
@@ -9919,6 +9981,15 @@ nsWindow* nsWindow::GetWindow(GdkWindow* window) {
return get_window_for_gdk_window(window);
}
+void nsWindow::ClearRenderingQueue() {
+ LOG("nsWindow::ClearRenderingQueue()");
+
+ if (mWidgetListener) {
+ mWidgetListener->RequestWindowClose(this);
+ }
+ DestroyLayerManager();
+}
+
// nsWindow::OnMap() / nsWindow::OnUnmap() is called from map/unmap mContainer
// handlers directly as we paint to mContainer.
void nsWindow::OnMap() {
@@ -9927,8 +9998,10 @@ void nsWindow::OnMap() {
MaybeCreatePipResources();
{
+ MutexAutoLock lock(mWindowVisibilityMutex);
mIsMapped = true;
+ EnsureGdkWindow();
RefreshScale(/* aRefreshScreen */ false);
if (mIsAlert) {
@@ -9980,6 +10053,7 @@ void nsWindow::OnUnmap() {
ClearPipResources();
{
+ MutexAutoLock lock(mWindowVisibilityMutex);
mIsMapped = false;
mHasReceivedSizeAllocate = false;
@@ -9993,8 +10067,23 @@ void nsWindow::OnUnmap() {
}
}
+ if (mGdkWindow) {
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
+ mGdkWindow = nullptr;
+ }
+
// Reset scale for hidden windows
mCeiledScaleFactor = sNoScale;
+
+ // 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,
@@ -10005,6 +10094,28 @@ void nsWindow::OnUnmap() {
// see bug 1958695.
if (mWindowType == WindowType::Popup && !mPopupTemporaryHidden) {
DestroyLayerManager();
+ } else {
+ // 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).
+ //
+ // 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();
+ }
}
}
@@ -10093,7 +10204,7 @@ RefPtr<nsWindow::ExportHandlePromise> nsWindow::ExportHandle() {
auto* toplevel = GetToplevelGdkWindow();
#ifdef MOZ_WAYLAND
if (GdkIsWaylandDisplay()) {
- static auto sGdkWaylandWindowExportHandle = (gboolean (*)(
+ static auto sGdkWaylandWindowExportHandle = (gboolean(*)(
const GdkWindow*, GdkWaylandWindowExported, gpointer,
GDestroyNotify))dlsym(RTLD_DEFAULT, "gdk_wayland_window_export_handle");
if (!sGdkWaylandWindowExportHandle || !toplevel) {
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -340,8 +340,7 @@ class nsWindow final : public nsIWidget {
static guint32 sLastButtonPressTime;
MozContainer* GetMozContainer() { return mContainer; }
- GdkWindow* GetGdkWindow() const { return mGdkWindow; }
- void SetGdkWindow(GdkWindow* aGdkWindow);
+ GdkWindow* GetGdkWindow() const { return mGdkWindow; };
GdkWindow* GetToplevelGdkWindow() const;
GtkWidget* GetGtkWidget() const { return mShell; }
nsWindow* GetEffectiveParent();
@@ -499,6 +498,10 @@ class nsWindow final : public nsIWidget {
void ResumeCompositorImpl();
+ // Force hide this window, remove compositor etc. to avoid
+ // rendering queue blocking (see Bug 1782948).
+ void ClearRenderingQueue();
+
bool ApplyEnterLeaveMutterWorkaround();
void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override;
@@ -549,6 +552,7 @@ class nsWindow final : public nsIWidget {
GtkWidget* GetToplevelWidget() const;
nsWindow* GetContainerWindow() const;
Window GetX11Window();
+ void EnsureGdkWindow();
void SetUrgencyHint(GtkWidget* top_window, bool state);
void SetDefaultIcon(void);
void SetWindowDecoration(BorderStyle aStyle);
@@ -592,8 +596,6 @@ class nsWindow final : public nsIWidget {
GtkWidget* mShell = nullptr;
MozContainer* mContainer = nullptr;
GdkWindow* mGdkWindow = nullptr;
- // mEGLWindow is owned by WaylandSurface or it's X11 ID.
- void* mEGLWindow = nullptr;
#ifdef MOZ_WAYLAND
RefPtr<mozilla::widget::WaylandSurface> mSurface;
#endif
@@ -702,6 +704,9 @@ class nsWindow final : public nsIWidget {
// If true, draw our own window titlebar.
bool mDrawInTitlebar = false;
+ // 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;