From 075294349b5b7ce54d2525efa12aad39ae61ad2e Mon Sep 17 00:00:00 2001 From: stransky Date: Thu, 1 Aug 2024 13:24:14 +0000 Subject: [PATCH] Bug 1904424 [Linux/X11] Don't steal focus on X11 r=emilio a=RyanVM The patch comes from https://gitlab.com/redhat/centos-stream/rpms/firefox/-/merge_requests/94 Firefox isn't currently handling window focusing right on X11... There's a few problems: If no startup id is provided, it tries to get user time from g_get_monotonic_time when there's no guarantee that timestamp is synchronized or even in a compatible unit. This merge request addressed that by dropping the code that uses g_get_monotonic_time entirely, and instead adding a heuristic to synthesize a startup id based on the time of the last user interaction on the display. This makes an assumption that firefox was started as a result of user interaction, which might not always hold true, but it's more likely than not, and is basically the same assumption firefox is already making by trying to use a timestamp of "now" (g_get_monotonic_time) when focusing the window. If a startup id is provided, it fails to tell gtk to use it when focusing the window because it calls gtk_window_present_with_time with some other random timestamp instead of passing GDK_CURRENT_TIME (which is the magic value that means "use startup id for timestamp"). This commit tries to detect when a startup id is available, and the display is X11, and if so use a timestamp of GDK_CURRENT_TIME. Differential Revision: https://phabricator.services.mozilla.com/D217174 --- .../components/remote/nsDBusRemoteServer.cpp | 6 +- toolkit/xre/nsAppRunner.cpp | 6 +- widget/gtk/WidgetUtilsGtk.cpp | 71 +++++++++++++++++++ widget/gtk/WidgetUtilsGtk.h | 8 ++- widget/gtk/nsWindow.cpp | 12 ++++ 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/toolkit/components/remote/nsDBusRemoteServer.cpp b/toolkit/components/remote/nsDBusRemoteServer.cpp index 4ed053e3cc6c..0f4e32c79ede 100644 --- a/toolkit/components/remote/nsDBusRemoteServer.cpp +++ b/toolkit/components/remote/nsDBusRemoteServer.cpp @@ -47,11 +47,7 @@ bool nsDBusRemoteServer::HandleOpenURL(const gchar* aInterfaceName, return false; } - guint32 timestamp = gtk_get_current_event_time(); - if (timestamp == GDK_CURRENT_TIME) { - timestamp = guint32(g_get_monotonic_time() / 1000); - } - HandleCommandLine(aParam, timestamp); + HandleCommandLine(aParam, gtk_get_current_event_time()); return true; } diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 9161237fd440..e186689f619f 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -4943,8 +4943,12 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) { // Try to remote the entire command line. If this fails, start up // normally. # ifdef MOZ_WIDGET_GTK - const auto& startupToken = + auto& startupToken = GdkIsWaylandDisplay() ? mXDGActivationToken : mDesktopStartupID; +# ifdef MOZ_X11 + if (GdkIsX11Display() && startupToken.IsEmpty()) + startupToken = SynthesizeStartupToken(); +# endif /* MOZ_X11 */ # else const nsCString startupToken; # endif diff --git a/widget/gtk/WidgetUtilsGtk.cpp b/widget/gtk/WidgetUtilsGtk.cpp index 8228a4530beb..26ba66b27402 100644 --- a/widget/gtk/WidgetUtilsGtk.cpp +++ b/widget/gtk/WidgetUtilsGtk.cpp @@ -28,6 +28,11 @@ #include #include +#ifdef MOZ_X11 +# include +# include +#endif /* MOZ_X11 */ + #undef LOGW #ifdef MOZ_LOGGING # include "mozilla/Logging.h" @@ -498,4 +503,70 @@ bool IsCancelledGError(GError* aGError) { return g_error_matches(aGError, G_IO_ERROR, G_IO_ERROR_CANCELLED); } +#if defined(MOZ_X11) +static unsigned long GetWindowUserTime(GdkDisplay* aDisplay, + uintptr_t aWindow) { + Atom actualType; + int actualFormat; + unsigned long numberOfItems; + unsigned long bytesAfter; + unsigned char* property = nullptr; + unsigned long userTime = 0; + + Display* xDisplay = GDK_DISPLAY_XDISPLAY(aDisplay); + Atom atom = + gdk_x11_get_xatom_by_name_for_display(aDisplay, "_NET_WM_USER_TIME"); + + if (XGetWindowProperty(xDisplay, aWindow, atom, 0, 1, false, XA_CARDINAL, + &actualType, &actualFormat, &numberOfItems, + &bytesAfter, &property) == Success && + property) { + if (numberOfItems == 1) { + userTime = *((unsigned long*)property); + } + XFree(property); + } + + return userTime; +} + +void FindLatestUserTime(GdkDisplay* aDisplay, uintptr_t aWindow, + unsigned long* aLatestTime) { + Window rootReturn; + Window parentReturn; + Window* children; + unsigned int numberOfChildren; + unsigned long userTime; + + Display* xDisplay = GDK_DISPLAY_XDISPLAY(aDisplay); + + if (XQueryTree(xDisplay, aWindow, &rootReturn, &parentReturn, &children, + &numberOfChildren)) { + for (unsigned int i = 0; i < numberOfChildren; i++) { + userTime = GetWindowUserTime(aDisplay, children[i]); + if (userTime > *aLatestTime) { + *aLatestTime = userTime; + } + FindLatestUserTime(aDisplay, children[i], aLatestTime); + } + + XFree(children); + } +} + +// Assume we're started from user interaction and infer user time if its missing +nsCString SynthesizeStartupToken() { + unsigned long latestUserTime = 0; + FindLatestUserTime(gdk_display_get_default(), + GDK_WINDOW_XID(gdk_get_default_root_window()), + &latestUserTime); + + if (latestUserTime == 0) { + return nsCString(); + } + + return nsPrintfCString("%s_TIME%lu", g_get_host_name(), latestUserTime); +} +#endif + } // namespace mozilla::widget diff --git a/widget/gtk/WidgetUtilsGtk.h b/widget/gtk/WidgetUtilsGtk.h index ffd7425ae5cb..5cf6604b3c11 100644 --- a/widget/gtk/WidgetUtilsGtk.h +++ b/widget/gtk/WidgetUtilsGtk.h @@ -9,7 +9,6 @@ #include "nsString.h" #include "nsTArray.h" #include "mozilla/MozPromise.h" - #include typedef struct _GdkDisplay GdkDisplay; @@ -78,6 +77,13 @@ RefPtr RequestWaylandFocusPromise(); bool IsCancelledGError(GError* aGError); +#if defined(MOZ_X11) +// Used by startup notifications +nsCString SynthesizeStartupToken(); +void FindLatestUserTime(GdkDisplay* aDisplay, uintptr_t aWindow, + unsigned long* aLatestTime); +#endif + } // namespace mozilla::widget #endif // WidgetUtilsGtk_h__ diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 28c0cb2641e2..e3f3cf40f5e6 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -3102,6 +3102,18 @@ void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) { return t; } } +#if defined(MOZ_X11) + // If it's X11 and there's a startup token, use GDK_CURRENT_TIME, so + // gtk_window_present_with_time will pull the timestamp from the startup + // token. + if (GdkIsX11Display()) { + nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit(); + const auto& startupToken = toolkit->GetStartupToken(); + if (!startupToken.IsEmpty()) { + return static_cast(GDK_CURRENT_TIME); + } + } +#endif return GetLastUserInputTime(); }();