https://bugzilla.mozilla.org/show_bug.cgi?id=223492 --- config/autoconf.mk.in 2006-09-14 14:07:03.000000000 -0400 +++ config/autoconf.mk.in 2007-07-03 18:01:36.000000000 -0400 @@ -223,6 +223,10 @@ MOZ_GNOMEUI_CFLAGS = @MOZ_GNOMEUI_CFLAGS@ MOZ_GNOMEUI_LIBS = @MOZ_GNOMEUI_LIBS@ +MOZ_ENABLE_STARTUP_NOTIFICATION = @MOZ_ENABLE_STARTUP_NOTIFICATION@ +MOZ_STARTUP_NOTIFICATION_CFLAGS = @MOZ_STARTUP_NOTIFICATION_CFLAGS@ +MOZ_STARTUP_NOTIFICATION_LIBS = @MOZ_STARTUP_NOTIFICATION_LIBS@ + MOZ_GNOMEVFS_CFLAGS = @MOZ_GNOMEVFS_CFLAGS@ MOZ_GNOMEVFS_LIBS = @MOZ_GNOMEVFS_LIBS@ --- toolkit/components/remote/nsGTKRemoteService.cpp 2006-01-05 22:19:20.000000000 -0500 +++ toolkit/components/remote/nsGTKRemoteService.cpp 2007-07-05 17:34:41.000000000 -0400 @@ -50,7 +50,9 @@ #include "nsIBaseWindow.h" #include "nsIDocShell.h" +#include "nsIDocument.h" #include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" #include "nsIGenericFactory.h" #include "nsILocalFile.h" #include "nsIObserverService.h" @@ -58,6 +60,8 @@ #include "nsIServiceManager.h" #include "nsIWeakReference.h" #include "nsIWidget.h" +#include "nsIAppShellService.h" +#include "nsAppShellCID.h" #include "nsCOMPtr.h" #include "nsString.h" @@ -65,6 +69,10 @@ #include "prenv.h" #include "nsCRT.h" +#ifdef MOZ_WIDGET_GTK2 +#include "nsGTKToolkit.h" +#endif + #ifdef MOZ_XUL_APP #include "nsICommandLineRunner.h" #include "nsXULAppAPI.h" @@ -155,20 +163,46 @@ return PL_DHASH_NEXT; } +static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) +{ + // get the native window for this instance + nsCOMPtr window(do_QueryInterface(aWindow)); + NS_ENSURE_TRUE(window, nsnull); + nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + NS_ENSURE_TRUE(doc, nsnull); + nsCOMPtr container = doc->GetContainer(); + nsCOMPtr baseWindow(do_QueryInterface(container)); + NS_ENSURE_TRUE(baseWindow, nsnull); + + nsCOMPtr mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; +} + +static nsGTKToolkit* GetGTKToolkit() +{ + nsCOMPtr svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); + if (!svc) + return nsnull; + nsCOMPtr window; + svc->GetHiddenDOMWindow(getter_AddRefs(window)); + if (!window) + return nsnull; + nsIWidget* widget = GetMainWidget(window); + if (!widget) + return nsnull; + nsIToolkit* toolkit = widget->GetToolkit(); + if (!toolkit) + return nsnull; + return NS_STATIC_CAST(nsGTKToolkit*, toolkit); +} + + NS_IMETHODIMP nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow) { // get the native window for this instance - nsCOMPtr scriptObject - (do_QueryInterface(aWindow)); - NS_ENSURE_TRUE(scriptObject, NS_ERROR_FAILURE); - - nsCOMPtr baseWindow - (do_QueryInterface(scriptObject->GetDocShell())); - NS_ENSURE_TRUE(baseWindow, NS_ERROR_FAILURE); - - nsCOMPtr mainWidget; - baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + nsIWidget* mainWidget = GetMainWidget(aWindow); NS_ENSURE_TRUE(mainWidget, NS_ERROR_FAILURE); // walk up the widget tree and find the toplevel window in the @@ -201,7 +235,6 @@ return NS_OK; } - NS_IMETHODIMP nsGTKRemoteService::Shutdown() { @@ -260,7 +293,7 @@ #ifndef MOZ_XUL_APP const char* -nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow, PRUint32 aTimestamp) { nsresult rv; @@ -283,8 +316,60 @@ } #else //MOZ_XUL_APP + +// Set desktop startup ID to the passed ID, if there is one, so that any created +// windows get created with the right window manager metadata, and any windows +// that get new tabs and are activated also get the right WM metadata. +// If there is no desktop startup ID, then use the X event's timestamp +// for _NET_ACTIVE_WINDOW when the window gets focused or shown. +static void +SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID, + PRUint32 aTimestamp) { +#ifdef MOZ_WIDGET_GTK2 + nsGTKToolkit* toolkit = GetGTKToolkit(); + if (!toolkit) + return; + if (!aDesktopStartupID.IsEmpty()) { + toolkit->SetDesktopStartupID(aDesktopStartupID); + } else { + toolkit->SetFocusTimestamp(aTimestamp); + } +#endif +} + +static PRBool +FindExtensionParameterInCommand(const char* aParameterName, + const nsACString& aCommand, + char aSeparator, + nsACString* aValue) +{ + nsCAutoString searchFor; + searchFor.Append(aSeparator); + searchFor.Append(aParameterName); + searchFor.Append('='); + + nsACString::const_iterator start, end; + aCommand.BeginReading(start); + aCommand.EndReading(end); + if (!FindInReadable(searchFor, start, end)) + return PR_FALSE; + + nsACString::const_iterator charStart, charEnd; + charStart = end; + aCommand.EndReading(charEnd); + nsACString::const_iterator idStart = charStart, idEnd; + if (FindCharInReadable(aSeparator, charStart, charEnd)) { + idEnd = charStart; + } else { + idEnd = charEnd; + } + *aValue = nsDependentCSubstring(idStart, idEnd); + return PR_TRUE; +} + const char* -nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow, + PRUint32 aTimestamp) { nsresult rv; @@ -314,6 +399,12 @@ #endif if (!command.EqualsLiteral("ping")) { + nsCAutoString desktopStartupID; + nsDependentCString cmd(aCommand); + FindExtensionParameterInCommand("DESKTOP_STARTUP_ID", + cmd, '\n', + &desktopStartupID); + char* argv[3] = {"dummyappname", "-remote", aCommand}; rv = cmdline->Init(3, argv, nsnull, nsICommandLine::STATE_REMOTE_EXPLICIT); if (NS_FAILED(rv)) @@ -322,6 +413,8 @@ if (aWindow) cmdline->SetWindowContext(aWindow); + SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); + rv = cmdline->Run(); if (NS_ERROR_ABORT == rv) return "500 command not parseable"; @@ -333,7 +426,8 @@ } const char* -nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow, + PRUint32 aTimestamp) { nsresult rv; @@ -364,6 +458,8 @@ if (NS_FAILED(rv)) return "509 internal error"; + nsCAutoString desktopStartupID; + char **argv = (char**) malloc(sizeof(char*) * argc); if (!argv) return "509 internal error"; @@ -372,6 +468,12 @@ for (int i = 0; i < argc; ++i) { argv[i] = aBuffer + TO_LITTLE_ENDIAN32(offset[i]); + if (i == 0) { + nsDependentCString cmd(argv[0]); + FindExtensionParameterInCommand("DESKTOP_STARTUP_ID", + cmd, ' ', + &desktopStartupID); + } #ifdef DEBUG_bsmedberg printf(" argv[%i]:\t%s\n", i, argv[i]); #endif @@ -386,7 +488,10 @@ if (aWindow) cmdline->SetWindowContext(aWindow); + SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); + rv = cmdline->Run(); + if (NS_ERROR_ABORT == rv) return "500 command not parseable"; @@ -486,7 +591,7 @@ return FALSE; // cool, we got the property data. - const char *response = HandleCommand(data, window); + const char *response = HandleCommand(data, window, pevent->time); // put the property onto the window as the response XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window), @@ -531,7 +636,7 @@ return FALSE; // cool, we got the property data. - const char *response = HandleCommandLine(data, window); + const char *response = HandleCommandLine(data, window, pevent->time); // put the property onto the window as the response XChangeProperty (GDK_DISPLAY(), GDK_WINDOW_XWINDOW(pevent->window), --- toolkit/components/remote/nsGTKRemoteService.h 2005-04-04 19:11:42.000000000 -0400 +++ toolkit/components/remote/nsGTKRemoteService.h 2007-07-03 18:01:36.000000000 -0400 @@ -80,10 +80,12 @@ nsIWeakReference* aData, void* aClosure); - static const char* HandleCommand(char* aCommand, nsIDOMWindow* aWindow); + static const char* HandleCommand(char* aCommand, nsIDOMWindow* aWindow, + PRUint32 aTimestamp); #ifdef MOZ_XUL_APP - static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow); + static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow, + PRUint32 aTimestamp); #endif static gboolean HandlePropertyChange(GtkWidget *widget, --- toolkit/components/remote/Makefile.in 2005-04-08 00:59:36.000000000 -0400 +++ toolkit/components/remote/Makefile.in 2007-07-05 17:45:55.000000000 -0400 @@ -56,7 +56,9 @@ string \ appcomps \ toolkitcomps \ - appcomps \ + appshell \ + layout \ + content \ xulapp \ widget \ gfx \ --- toolkit/library/Makefile.in 2007-04-03 10:32:27.000000000 -0400 +++ toolkit/library/Makefile.in 2007-07-03 18:01:36.000000000 -0400 @@ -357,6 +357,10 @@ EXTRA_DSO_LDOPTS += $(MOZ_XPRINT_LDFLAGS) endif +ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +EXTRA_DSO_LDOPTS += $(MOZ_STARTUP_NOTIFICATION_LIBS) +endif + ifdef MOZ_ENABLE_PANGO EXTRA_DSO_LDOPTS += $(MOZ_PANGO_LIBS) endif --- toolkit/xre/nsAppRunner.cpp 2007-04-30 13:26:58.000000000 -0400 +++ toolkit/xre/nsAppRunner.cpp 2007-07-05 17:48:51.000000000 -0400 @@ -72,6 +72,7 @@ #include "nsIComponentRegistrar.h" #include "nsIContentHandler.h" #include "nsIDialogParamBlock.h" +#include "nsIDocument.h" #include "nsIDOMWindow.h" #include "nsIEventQueueService.h" #include "nsIExtensionManager.h" @@ -99,6 +100,11 @@ #ifdef XP_WIN #include "nsIWinAppHelper.h" #endif +#include "nsPIDOMWindow.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIDocShell.h" +#include "nsAppShellCID.h" #include "nsCRT.h" #include "nsCOMPtr.h" @@ -262,6 +268,9 @@ #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) #include #endif //MOZ_WIDGET_GTK || MOZ_WIDGET_GTK2 +#if defined(MOZ_WIDGET_GTK2) +#include "nsGTKToolkit.h" +#endif #if defined(MOZ_WIDGET_QT) #include @@ -1105,7 +1114,7 @@ // use int here instead of a PR type since it will be returned // from main - just to keep types consistent static int -HandleRemoteArgument(const char* remote) +HandleRemoteArgument(const char* remote, const char* aDesktopStartupID) { nsresult rv; ArgResult ar; @@ -1146,7 +1155,7 @@ nsXPIDLCString response; PRBool success = PR_FALSE; rv = client.SendCommand(program.get(), username, profile, remote, - getter_Copies(response), &success); + aDesktopStartupID, getter_Copies(response), &success); // did the command fail? if (NS_FAILED(rv)) { PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n", @@ -1163,7 +1172,7 @@ } static PRBool -RemoteCommandLine() +RemoteCommandLine(const char* aDesktopStartupID) { nsresult rv; ArgResult ar; @@ -1195,7 +1204,7 @@ nsXPIDLCString response; PRBool success = PR_FALSE; rv = client.SendCommandLine(program.get(), username, nsnull, - gArgc, gArgv, + gArgc, gArgv, aDesktopStartupID, getter_Copies(response), &success); // did the command fail? if (NS_FAILED(rv) || !success) @@ -2059,6 +2068,53 @@ #ifdef MOZ_WIDGET_GTK2 #include "prlink.h" typedef void (*_g_set_application_name_fn)(const gchar *application_name); +typedef void (*_gtk_window_set_auto_startup_notification_fn)(gboolean setting); + +static PRFuncPtr FindFunction(const char* aName) +{ + PRLibrary *lib = nsnull; + PRFuncPtr result = PR_FindFunctionSymbolAndLibrary(aName, &lib); + // Since the library was already loaded, we can safely unload it here. + if (lib) { + PR_UnloadLibrary(lib); + } + return result; +} + +static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) +{ + // get the native window for this instance + nsCOMPtr window(do_QueryInterface(aWindow)); + NS_ENSURE_TRUE(window, nsnull); + nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + NS_ENSURE_TRUE(doc, nsnull); + nsCOMPtr container = doc->GetContainer(); + nsCOMPtr baseWindow(do_QueryInterface(container)); + NS_ENSURE_TRUE(baseWindow, nsnull); + + nsCOMPtr mainWidget; + baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); + return mainWidget; +} + +static nsGTKToolkit* GetGTKToolkit() +{ + nsCOMPtr svc = do_GetService(NS_APPSHELLSERVICE_CONTRACTID); + if (!svc) + return nsnull; + nsCOMPtr window; + svc->GetHiddenDOMWindow(getter_AddRefs(window)); + if (!window) + return nsnull; + nsIWidget* widget = GetMainWidget(window); + if (!widget) + return nsnull; + nsIToolkit* toolkit = widget->GetToolkit(); + if (!toolkit) + return nsnull; + return NS_STATIC_CAST(nsGTKToolkit*, toolkit); +} + #endif int @@ -2235,6 +2291,16 @@ if (CheckArg("install")) gdk_rgb_set_install(TRUE); +#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) || defined(MOZ_ENABLE_XREMOTE) + // Stash DESKTOP_STARTUP_ID in malloc'ed memory becaus gtk_init will clear it. +#define HAVE_DESKTOP_STARTUP_ID + const char* desktopStartupIDEnv = PR_GetEnv("DESKTOP_STARTUP_ID"); + nsCAutoString desktopStartupID; + if (desktopStartupIDEnv) { + desktopStartupID.Assign(desktopStartupIDEnv); + } +#endif + // Initialize GTK+1/2 here for splash #if defined(MOZ_WIDGET_GTK) gtk_set_locale(); @@ -2243,15 +2309,15 @@ #if defined(MOZ_WIDGET_GTK2) // g_set_application_name () is only defined in glib2.2 and higher. - PRLibrary *glib2 = nsnull; - _g_set_application_name_fn _g_set_application_name = - (_g_set_application_name_fn)PR_FindFunctionSymbolAndLibrary("g_set_application_name", &glib2); + _g_set_application_name_fn _g_set_application_name = + (_g_set_application_name_fn)FindFunction("g_set_application_name"); if (_g_set_application_name) { _g_set_application_name(gAppData->name); } - if (glib2) { - PR_UnloadLibrary(glib2); - } + _gtk_window_set_auto_startup_notification_fn _gtk_window_set_auto_startup_notification = + (_gtk_window_set_auto_startup_notification_fn)FindFunction("gtk_window_set_auto_startup_notification"); + if (_gtk_window_set_auto_startup_notification) + _gtk_window_set_auto_startup_notification(PR_FALSE); #endif gtk_widget_set_default_visual(gdk_rgb_get_visual()); @@ -2315,13 +2381,15 @@ PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n"); return 1; } + const char* desktopStartupIDPtr = + desktopStartupID.IsEmpty() ? nsnull : desktopStartupID.get(); if (ar) { - return HandleRemoteArgument(xremotearg); + return HandleRemoteArgument(xremotearg, desktopStartupIDPtr); } if (!PR_GetEnv("MOZ_NO_REMOTE")) { // Try to remote the entire command line. If this fails, start up normally. - if (RemoteCommandLine()) + if (RemoteCommandLine(desktopStartupIDPtr)) return 0; } #endif @@ -2533,6 +2601,13 @@ NS_TIMELINE_LEAVE("appStartup->CreateHiddenWindow"); NS_ENSURE_SUCCESS(rv, 1); +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) + nsRefPtr toolkit = GetGTKToolkit(); + if (toolkit && !desktopStartupID.IsEmpty()) { + toolkit->SetDesktopStartupID(desktopStartupID); + } +#endif + // Extension Compatibility Checking and Startup if (gAppData->flags & NS_XRE_ENABLE_EXTENSION_MANAGER) { nsCOMPtr em(do_GetService("@mozilla.org/extensions/manager;1")); @@ -2713,6 +2788,21 @@ } #endif +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_TOOLKIT_GTK2) + nsGTKToolkit* toolkit = GetGTKToolkit(); + if (toolkit) { + nsCAutoString currentDesktopStartupID; + toolkit->GetDesktopStartupID(¤tDesktopStartupID); + if (!currentDesktopStartupID.IsEmpty()) { + nsCAutoString desktopStartupEnv; + desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); + desktopStartupEnv.Append(currentDesktopStartupID); + // Leak it with extreme prejudice! + PR_SetEnv(ToNewCString(desktopStartupEnv)); + } + } +#endif + rv = LaunchChild(nativeApp, appInitiatedRestart, upgraded ? -1 : 0); return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1; } --- toolkit/xre/Makefile.in 2007-02-06 02:13:20.000000000 -0500 +++ toolkit/xre/Makefile.in 2007-07-03 18:01:36.000000000 -0400 @@ -69,6 +69,7 @@ shellservice \ string \ uriloader \ + layout \ widget \ windowwatcher \ xpcom \ --- configure.in 2007-04-03 11:40:02.000000000 -0400 +++ configure.in 2007-07-03 18:01:36.000000000 -0400 @@ -125,6 +125,7 @@ GNOMEUI_VERSION=2.2.0 GCONF_VERSION=1.2.1 LIBGNOME_VERSION=2.0 +STARTUP_NOTIFICATION_VERSION=0.8 dnl Set various checks dnl ======================================================== @@ -4156,6 +4157,41 @@ AC_SUBST(MOZ_DEFAULT_TOOLKIT) +dnl ======================================================== +dnl = startup-notification support module +dnl ======================================================== + +if test "$MOZ_ENABLE_GTK2" +then + MOZ_ENABLE_STARTUP_NOTIFICATION= + + MOZ_ARG_ENABLE_BOOL(startup-notification, + [ --enable-startup-notification Enable startup-notification support (default: disabled) ], + MOZ_ENABLE_STARTUP_NOTIFICATION=force, + MOZ_ENABLE_STARTUP_NOTIFICATION=) + if test "$MOZ_ENABLE_STARTUP_NOTIFICATION" + then + PKG_CHECK_MODULES(MOZ_STARTUP_NOTIFICATION, + libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_VERSION, + [MOZ_ENABLE_STARTUP_NOTIFICATION=1], [ + if test "$MOZ_ENABLE_STARTUP_NOTIFICATION" = "force" + then + AC_MSG_ERROR([* * * Could not find startup-notification >= $STARTUP_NOTIFICATION_VERSION]) + fi + MOZ_ENABLE_STARTUP_NOTIFICATION= + ]) + fi + + if test "$MOZ_ENABLE_STARTUP_NOTIFICATION"; then + AC_DEFINE(MOZ_ENABLE_STARTUP_NOTIFICATION) + fi + + TK_LIBS="$TK_LIBS $MOZ_STARTUP_NOTIFICATION_LIBS" +fi +AC_SUBST(MOZ_ENABLE_STARTUP_NOTIFICATION) +AC_SUBST(MOZ_STARTUP_NOTIFICATION_CFLAGS) +AC_SUBST(MOZ_STARTUP_NOTIFICATION_LIBS) + AC_SUBST(GTK_CONFIG) AC_SUBST(TK_CFLAGS) AC_SUBST(TK_LIBS) --- widget/src/gtk2/nsWindow.cpp 2007-04-19 14:46:03.000000000 -0400 +++ widget/src/gtk2/nsWindow.cpp 2007-07-03 18:01:36.000000000 -0400 @@ -40,7 +40,7 @@ #include "prlink.h" #include "nsWindow.h" -#include "nsToolkit.h" +#include "nsGTKToolkit.h" #include "nsIRenderingContext.h" #include "nsIRegion.h" #include "nsIRollupListener.h" @@ -58,6 +58,11 @@ #include #include +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +#define SN_API_NOT_YET_FROZEN +#include +#endif + #include "gtk2xtbin.h" #include "nsIPrefService.h" @@ -660,6 +665,75 @@ return NS_ERROR_NOT_IMPLEMENTED; } +typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp); + +// This will become obsolete when new GTK APIs are widely supported, +// as described here: http://bugzilla.gnome.org/show_bug.cgi?id=347375 +static void +SetUserTimeAndStartupIDForActivatedWindow(GtkWidget* aWindow) +{ + nsCOMPtr toolkit; + NS_GetCurrentToolkit(getter_AddRefs(toolkit)); + if (!toolkit) + return; + + nsGTKToolkit* GTKToolkit = NS_STATIC_CAST(nsGTKToolkit*, + NS_STATIC_CAST(nsIToolkit*, toolkit)); + nsCAutoString desktopStartupID; + GTKToolkit->GetDesktopStartupID(&desktopStartupID); + if (desktopStartupID.IsEmpty()) { + // We don't have the data we need. Fall back to an + // approximation ... using the timestamp of the remote command + // being received as a guess for the timestamp of the user event + // that triggered it. + PRUint32 timestamp = GTKToolkit->GetFocusTimestamp(); + if (timestamp) { + gdk_window_focus(aWindow->window, timestamp); + GTKToolkit->SetFocusTimestamp(0); + } + return; + } + +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION + GdkDrawable* drawable = GDK_DRAWABLE(aWindow->window); + GtkWindow* win = GTK_WINDOW(aWindow); + if (!win) { + NS_WARNING("Passed in widget was not a GdkWindow!"); + return; + } + GdkScreen* screen = gtk_window_get_screen(win); + SnDisplay* snd = + sn_display_new(gdk_x11_drawable_get_xdisplay(drawable), nsnull, nsnull); + if (!snd) + return; + SnLauncheeContext* ctx = + sn_launchee_context_new(snd, gdk_screen_get_number(screen), + desktopStartupID.get()); + if (!ctx) { + sn_display_unref(snd); + return; + } + + if (sn_launchee_context_get_id_has_timestamp(ctx)) { + PRLibrary* gtkLibrary; + SetUserTimeFunc setUserTimeFunc = (SetUserTimeFunc) + PR_FindFunctionSymbolAndLibrary("gdk_x11_window_set_user_time", >kLibrary); + if (setUserTimeFunc) { + setUserTimeFunc(aWindow->window, sn_launchee_context_get_timestamp(ctx)); + PR_UnloadLibrary(gtkLibrary); + } + } + + sn_launchee_context_setup_window(ctx, gdk_x11_drawable_get_xid(drawable)); + sn_launchee_context_complete(ctx); + + sn_launchee_context_unref(ctx); + sn_display_unref(snd); +#endif + + GTKToolkit->SetDesktopStartupID(EmptyCString()); +} + NS_IMETHODIMP nsWindow::SetFocus(PRBool aRaise) { @@ -680,6 +754,10 @@ // set properly. GtkWidget *toplevelWidget = gtk_widget_get_toplevel(owningWidget); + if (toplevelWidget && aRaise) { + SetUserTimeAndStartupIDForActivatedWindow(toplevelWidget); + } + if (gRaiseWindows && aRaise && toplevelWidget && !GTK_WIDGET_HAS_FOCUS(owningWidget) && !GTK_WIDGET_HAS_FOCUS(toplevelWidget)) { @@ -1167,7 +1245,7 @@ case NS_NATIVE_GRAPHIC: { NS_ASSERTION(nsnull != mToolkit, "NULL toolkit, unable to get a GC"); - return (void *)NS_STATIC_CAST(nsToolkit *, mToolkit)->GetSharedGC(); + return (void *)NS_STATIC_CAST(nsGTKToolkit *, mToolkit)->GetSharedGC(); break; } @@ -2802,13 +2880,18 @@ // is shown. // XXX that may or may not be true for GTK+ 2.x if (mTransparencyBitmap) { - ApplyTransparencyBitmap(); + ApplyTransparencyBitmap(); } // unset our flag now that our window has been shown mNeedsShow = PR_FALSE; if (mIsTopLevel) { + // Set up usertime/startupID metadata for the created window. + if (mWindowType != eWindowType_invisible) { + SetUserTimeAndStartupIDForActivatedWindow(mShell); + } + moz_drawingarea_set_visibility(mDrawingarea, aAction); gtk_widget_show(GTK_WIDGET(mContainer)); gtk_widget_show(mShell); --- widget/src/gtk2/Makefile.in 2006-06-17 11:16:14.000000000 -0400 +++ widget/src/gtk2/Makefile.in 2007-07-03 18:01:36.000000000 -0400 @@ -97,6 +97,7 @@ $(MOZ_COMPONENT_LIBS) \ -lgkgfx \ -lgtkxtbin \ + $(MOZ_STARTUP_NOTIFICATION_LIBS) \ $(XLDFLAGS) \ $(XLIBS) \ $(MOZ_GTK2_LIBS) @@ -107,14 +108,15 @@ EXPORTS = \ nsIGdkPixbufImage.h \ + nsGTKToolkit.h \ mozdrawingarea.h \ mozcontainer.h \ $(NULL) include $(topsrcdir)/config/rules.mk -CFLAGS += $(MOZ_GTK2_CFLAGS) -CXXFLAGS += $(MOZ_GTK2_CFLAGS) +CFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) +CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) DEFINES += -DUSE_XIM --- widget/src/gtk2/nsToolkit.cpp 2004-04-18 18:00:17.000000000 -0400 +++ widget/src/gtk2/nsToolkit.cpp 2007-07-03 18:01:36.000000000 -0400 @@ -38,7 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nscore.h" // needed for 'nsnull' -#include "nsToolkit.h" +#include "nsGTKToolkit.h" // // Static thread local storage index of the Toolkit @@ -51,9 +51,10 @@ // constructor // //------------------------------------------------------------------------- -nsToolkit::nsToolkit() +nsGTKToolkit::nsGTKToolkit() { mSharedGC = nsnull; + mFocusTimestamp = 0; } //------------------------------------------------------------------------- @@ -61,7 +62,7 @@ // destructor // //------------------------------------------------------------------------- -nsToolkit::~nsToolkit() +nsGTKToolkit::~nsGTKToolkit() { if (mSharedGC) { gdk_gc_unref(mSharedGC); @@ -77,9 +78,9 @@ // //------------------------------------------------------------------------- -NS_IMPL_ISUPPORTS1(nsToolkit, nsIToolkit) +NS_IMPL_ISUPPORTS1(nsGTKToolkit, nsIToolkit) -void nsToolkit::CreateSharedGC(void) +void nsGTKToolkit::CreateSharedGC(void) { GdkPixmap *pixmap; @@ -91,7 +92,7 @@ gdk_pixmap_unref(pixmap); } -GdkGC *nsToolkit::GetSharedGC(void) +GdkGC *nsGTKToolkit::GetSharedGC(void) { return gdk_gc_ref(mSharedGC); } @@ -100,7 +101,7 @@ // // //------------------------------------------------------------------------- -NS_IMETHODIMP nsToolkit::Init(PRThread *aThread) +NS_IMETHODIMP nsGTKToolkit::Init(PRThread *aThread) { CreateSharedGC(); @@ -135,7 +136,7 @@ // Create a new toolkit for this thread... // if (!toolkit) { - toolkit = new nsToolkit(); + toolkit = new nsGTKToolkit(); if (!toolkit) { rv = NS_ERROR_OUT_OF_MEMORY; --- widget/src/xremoteclient/mozilla-xremote-client.cpp 2005-04-04 15:08:51.000000000 -0400 +++ widget/src/xremoteclient/mozilla-xremote-client.cpp 2007-07-03 18:01:36.000000000 -0400 @@ -40,6 +40,7 @@ #include #include #include +#include #ifdef MOZ_WIDGET_PHOTON #include "PhRemoteClient.h" #else @@ -99,7 +100,7 @@ // send the command - it doesn't get any easier than this PRBool success = PR_FALSE; char *error = 0; - rv = client.SendCommand(browser, username, profile, command, + rv = client.SendCommand(browser, username, profile, command, nsnull, &error, &success); // failed to send command --- widget/src/xremoteclient/XRemoteClient.cpp 2006-03-30 03:01:13.000000000 -0500 +++ widget/src/xremoteclient/XRemoteClient.cpp 2007-07-03 18:01:36.000000000 -0400 @@ -173,6 +173,7 @@ nsresult XRemoteClient::SendCommand (const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aWindowFound) { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand")); @@ -198,7 +199,7 @@ if (NS_SUCCEEDED(rv)) { // send our command - rv = DoSendCommand(w, aCommand, aResponse, &destroyed); + rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, &destroyed); // if the window was destroyed, don't bother trying to free the // lock. @@ -217,6 +218,7 @@ XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, const char *aProfile, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aWindowFound) { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine")); @@ -242,7 +244,7 @@ if (NS_SUCCEEDED(rv)) { // send our command - rv = DoSendCommandLine(w, argc, argv, aResponse, &destroyed); + rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, &destroyed); // if the window was destroyed, don't bother trying to free the // lock. @@ -643,6 +645,7 @@ nsresult XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed) { *aDestroyed = PR_FALSE; @@ -651,9 +654,28 @@ ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n", aCommand, (unsigned int) aWindow)); + // We add the DESKTOP_STARTUP_ID setting as an extra line of + // the command string. Firefox ignores all lines but the first. + static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID="; + + PRInt32 len = strlen(aCommand); + if (aDesktopStartupID) { + len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); + } + char* buffer = (char*)malloc(len + 1); + if (!buffer) + return NS_ERROR_OUT_OF_MEMORY; + + strcpy(buffer, aCommand); + if (aDesktopStartupID) { + strcat(buffer, desktopStartupPrefix); + strcat(buffer, aDesktopStartupID); + } + XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8, - PropModeReplace, (unsigned char *)aCommand, - strlen(aCommand)); + PropModeReplace, (unsigned char *)buffer, len); + + free(buffer); if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom)) return NS_ERROR_FAILURE; @@ -663,7 +685,7 @@ /* like strcpy, but return the char after the final null */ static char* -estrcpy(char* s, char* d) +estrcpy(const char* s, char* d) { while (*s) *d++ = *s++; @@ -674,6 +696,7 @@ nsresult XRemoteClient::DoSendCommandLine(Window aWindow, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed) { int i; @@ -690,9 +713,16 @@ // [argc][offsetargv0][offsetargv1...]\0\0argv[1]...\0 // (offset is from the beginning of the buffer) + static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID="; + PRInt32 argvlen = strlen(cwdbuf); - for (i = 0; i < argc; ++i) - argvlen += strlen(argv[i]); + for (i = 0; i < argc; ++i) { + PRInt32 len = strlen(argv[i]); + if (i == 0 && aDesktopStartupID) { + len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID); + } + argvlen += len; + } PRInt32* buffer = (PRInt32*) malloc(argvlen + argc + 1 + sizeof(PRInt32) * (argc + 1)); @@ -708,6 +738,10 @@ for (int i = 0; i < argc; ++i) { buffer[i + 1] = TO_LITTLE_ENDIAN32(bufend - ((char*) buffer)); bufend = estrcpy(argv[i], bufend); + if (i == 0 && aDesktopStartupID) { + bufend = estrcpy(desktopStartupPrefix, bufend - 1); + bufend = estrcpy(aDesktopStartupID, bufend - 1); + } } #ifdef DEBUG_bsmedberg --- widget/src/xremoteclient/nsRemoteClient.h 2005-04-04 15:08:51.000000000 -0400 +++ widget/src/xremoteclient/nsRemoteClient.h 2007-07-03 18:01:36.000000000 -0400 @@ -76,6 +76,10 @@ * @param aCommand This is the command that is passed to the server. * Please see the additional information located at: * http://www.mozilla.org/unix/remote.html + * + * @param aDesktopStartupID the contents of the DESKTOP_STARTUP_ID environment + * variable defined by the Startup Notification specification + * http://standards.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt * * @param aResponse If there is a response, it will be here. This * includes error messages. The string is allocated using stdlib @@ -85,11 +89,16 @@ */ virtual nsresult SendCommand(const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded) = 0; /** * Send a complete command line to a running instance. * + * @param aDesktopStartupID the contents of the DESKTOP_STARTUP_ID environment + * variable defined by the Startup Notification specification + * http://standards.freedesktop.org/startup-notification-spec/startup-notification-0.1.txt + * * @see sendCommand * @param argc The number of command-line arguments. * @@ -97,6 +106,7 @@ virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername, const char *aProfile, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded) = 0; }; --- widget/src/xremoteclient/XRemoteClient.h 2006-03-30 03:01:13.000000000 -0500 +++ widget/src/xremoteclient/XRemoteClient.h 2007-07-03 18:01:36.000000000 -0400 @@ -48,10 +48,12 @@ virtual nsresult Init(); virtual nsresult SendCommand(const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded); virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername, const char *aProfile, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded); void Shutdown(); @@ -67,10 +69,12 @@ PRBool aSupportsCommandLine); nsresult DoSendCommand (Window aWindow, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed); nsresult DoSendCommandLine(Window aWindow, PRInt32 argc, char **argv, + const char* aDesktopStartupID, char **aResponse, PRBool *aDestroyed); PRBool WaitForResponse (Window aWindow, char **aResponse, --- /dev/null 2007-07-05 17:03:04.116204904 -0400 +++ widget/src/gtk2/nsGTKToolkit.h 2007-07-03 18:01:36.000000000 -0400 @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GTKTOOLKIT_H +#define GTKTOOLKIT_H + +#include "nsIToolkit.h" +#include "nsString.h" +#include + +/** + * Wrapper around the thread running the message pump. + * The toolkit abstraction is necessary because the message pump must + * execute within the same thread that created the widget under Win32. + */ + +class nsGTKToolkit : public nsIToolkit +{ +public: + nsGTKToolkit(); + virtual ~nsGTKToolkit(); + + NS_DECL_ISUPPORTS + + NS_IMETHOD Init(PRThread *aThread); + + void CreateSharedGC(void); + GdkGC *GetSharedGC(void); + + /** + * Get/set our value of DESKTOP_STARTUP_ID. When non-empty, this is applied + * to the next toplevel window to be shown or focused (and then immediately + * cleared). + */ + void SetDesktopStartupID(const nsACString& aID) { mDesktopStartupID = aID; } + void GetDesktopStartupID(nsACString* aID) { *aID = mDesktopStartupID; } + + /** + * Get/set the timestamp value to be used, if non-zero, to focus the + * next top-level window to be shown or focused (upon which it is cleared). + */ + void SetFocusTimestamp(PRUint32 aTimestamp) { mFocusTimestamp = aTimestamp; } + PRUint32 GetFocusTimestamp() { return mFocusTimestamp; } + +private: + GdkGC *mSharedGC; + nsCString mDesktopStartupID; + PRUint32 mFocusTimestamp; +}; + +#endif // GTKTOOLKIT_H