commit f5f68709dddcf4abbe3b59b58ab275c3633a8cc9
parent 83b903c46e796318bba0fd29023293bea92fb122
Author: Sandor Molnar <smolnar@mozilla.com>
Date: Thu, 13 Nov 2025 17:40:59 +0200
Revert "Bug 1998657 [Wayland] Create EGLWindow over offscreen wl_surface and make it always available r=emilio" for causing valgrind failures
This reverts commit de6ac364421c381d9ada823225e57595427ba88a.
Revert "Bug 1998657 [Wayland] Make WaylandSurface ready to draw right after its created r=emilio"
This reverts commit b39b0ef973df2a60d669595203a471330aa9fc7d.
Diffstat:
8 files changed, 410 insertions(+), 70 deletions(-)
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
@@ -117,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;
@@ -135,7 +233,7 @@ void WaylandSurface::FrameCallbackHandler(struct wl_callback* aCallback,
WaylandSurfaceLock lock(this);
// Don't run emulated callbacks on hidden surfaces
- if ((emulatedCallback || aRoutedFromChildSurface) && !mIsVisible) {
+ if ((emulatedCallback || aRoutedFromChildSurface) && !mIsReadyToDraw) {
return;
}
@@ -161,7 +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) {
- mIsVisible = true;
mBufferAttached = true;
}
@@ -381,7 +478,8 @@ 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?");
@@ -421,6 +519,14 @@ 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);
@@ -450,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(
@@ -502,11 +609,11 @@ void WaylandSurface::UnmapLocked(WaylandSurfaceLock& aSurfaceLock) {
return;
}
mIsMapped = false;
- mIsVisible = false;
LOGWAYLAND("WaylandSurface::UnmapLocked()");
RemoveAttachedBufferLocked(aSurfaceLock);
+ ClearReadyToDrawCallbacksLocked(aSurfaceLock);
ClearFrameCallbackLocked(aSurfaceLock);
ClearScaleLocked(aSurfaceLock);
@@ -526,6 +633,9 @@ void WaylandSurface::UnmapLocked(WaylandSurfaceLock& aSurfaceLock) {
MozClearPointer(mPendingOpaqueRegion, wl_region_destroy);
MozClearPointer(mOpaqueRegionFrameCallback, 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.
ReleaseAllWaylandTransactionsLocked(aSurfaceLock);
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,12 +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; }
-
// 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
@@ -119,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,
@@ -274,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);
@@ -296,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};
@@ -374,6 +385,11 @@ class WaylandSurface final {
bool mBufferTransformFlippedX = false;
bool mBufferTransformFlippedY = false;
+ // 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),
@@ -734,14 +735,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;
-
- // mGdkWindow is owned by mContainer, will be deleted with mShell/mContainer.
- g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr);
- mGdkWindow = nullptr;
-
gtk_widget_destroy(mShell);
mShell = nullptr;
mContainer = nullptr;
@@ -749,6 +742,9 @@ void nsWindow::Destroy() {
mSurface = nullptr;
#endif
+ MOZ_ASSERT(!mGdkWindow,
+ "mGdkWindow should be NULL when mContainer is destroyed");
+
#ifdef ACCESSIBILITY
if (mRootAccessible) {
mRootAccessible = nullptr;
@@ -1391,6 +1387,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
}
}
@@ -1451,7 +1456,8 @@ 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) {
@@ -3760,8 +3766,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;
@@ -4090,10 +4129,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 (!GetListener()) {
LOG("quit, !GetListener()");
@@ -4125,8 +4171,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;
}
@@ -4279,7 +4325,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));
@@ -4305,6 +4351,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
@@ -4575,6 +4625,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) {
@@ -4591,8 +4645,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()) {
@@ -5027,6 +5083,10 @@ void nsWindow::OnButtonReleaseEvent(GdkEventButton* aEvent) {
SetLastPointerDownEvent(nullptr);
mLastMouseCoordinates.Set(aEvent);
+ if (!mGdkWindow) {
+ return;
+ }
+
if (mAspectResizer) {
mAspectResizer = Nothing();
return;
@@ -5206,7 +5266,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();
}
@@ -5242,6 +5302,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);
}
@@ -5697,7 +5758,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;
}
@@ -5715,6 +5776,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;
@@ -5761,7 +5823,7 @@ void nsWindow::SetDragPopupSurface(
mDragPopupSurface = aDragPopupSurface;
mDragPopupSurfaceRegion = aInvalidRegion;
- if (!mIsDestroyed) {
+ if (mGdkWindow) {
gdk_window_invalidate_rect(mGdkWindow, nullptr, false);
}
}
@@ -6172,26 +6234,51 @@ 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::EnsureGdkWindow() {
+ MOZ_DIAGNOSTIC_ASSERT(mIsMapped);
+ if (!mGdkWindow) {
+ mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer));
+ g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
+ }
+}
+
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;
+ }
- if (mIsDestroyed) {
- LOG(" quit, mIsDestroyed = %d", !!mIsDestroyed);
- return;
- }
- // Compositor will be resumed at nsWindow::SetCompositorWidgetDelegate().
- if (!mCompositorWidgetDelegate) {
- LOG(" quit, missing mCompositorWidgetDelegate");
- return;
- }
+ ResumeCompositorImpl();
+ };
- ResumeCompositorImpl();
+ if (GdkIsWaylandDisplay()) {
+#ifdef MOZ_WAYLAND
+ moz_container_wayland_add_or_fire_initial_draw_callback(mContainer,
+ startCompositing);
+#endif
+ } else {
+ startCompositing();
+ }
}
nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
@@ -6445,24 +6532,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
gtk_widget_realize(container);
- mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer));
- g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this);
-
-#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);
@@ -9902,7 +9971,7 @@ bool nsWindow::SetEGLNativeWindowSize(
// SetEGLNativeWindowSize() is Wayland only call.
MOZ_ASSERT(GdkIsWaylandDisplay());
- if (mIsDestroyed) {
+ if (!mIsMapped) {
return true;
}
@@ -9931,6 +10000,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() {
@@ -9939,8 +10017,10 @@ void nsWindow::OnMap() {
MaybeCreatePipResources();
{
+ MutexAutoLock lock(mWindowVisibilityMutex);
mIsMapped = true;
+ EnsureGdkWindow();
RefreshScale(/* aRefreshScreen */ false);
if (mIsAlert) {
@@ -9992,6 +10072,7 @@ void nsWindow::OnUnmap() {
ClearPipResources();
{
+ MutexAutoLock lock(mWindowVisibilityMutex);
mIsMapped = false;
mHasReceivedSizeAllocate = false;
@@ -10005,8 +10086,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,
@@ -10017,6 +10113,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();
+ }
}
}
diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
@@ -501,6 +501,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;
@@ -551,6 +555,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);
@@ -595,8 +600,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
@@ -705,6 +708,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;