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(); }();