From: Robert O'Callahan Subject: implement startup-notification References: https://bugzilla.novell.com/show_bug.cgi?id=115417 https://bugzilla.mozilla.org/show_bug.cgi?id=223492 Index: configure.in ================================================================================ --- config/autoconf.mk.in +++ config/autoconf.mk.in @@ -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@ --- configure.in +++ configure.in @@ -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 ======================================================== @@ -4088,6 +4089,42 @@ 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) --- toolkit/components/remote/Makefile.in +++ toolkit/components/remote/Makefile.in @@ -56,8 +56,9 @@ string \ appcomps \ toolkitcomps \ - appcomps \ + appshell \ xulapp \ + layout \ widget \ gfx \ dom \ --- toolkit/components/remote/nsGTKRemoteService.cpp +++ toolkit/components/remote/nsGTKRemoteService.cpp @@ -50,7 +50,7 @@ #include "nsIBaseWindow.h" #include "nsIDocShell.h" -#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" #include "nsIGenericFactory.h" #include "nsILocalFile.h" #include "nsIObserverService.h" @@ -58,6 +58,8 @@ #include "nsIServiceManager.h" #include "nsIWeakReference.h" #include "nsIWidget.h" +#include "nsIAppShellService.h" +#include "nsAppShellCID.h" #include "nsCOMPtr.h" #include "nsString.h" @@ -72,6 +74,10 @@ #include "nsISuiteRemoteService.h" #endif +#ifdef MOZ_WIDGET_GTK2 +#include "nsGTKToolkit.h" +#endif + #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION" #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK" #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND" @@ -155,20 +161,44 @@ return PL_DHASH_NEXT; } -NS_IMETHODIMP -nsGTKRemoteService::RegisterWindow(nsIDOMWindow* aWindow) +static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) { // get the native window for this instance nsCOMPtr scriptObject (do_QueryInterface(aWindow)); - NS_ENSURE_TRUE(scriptObject, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(scriptObject, nsnull); nsCOMPtr baseWindow (do_QueryInterface(scriptObject->GetDocShell())); - NS_ENSURE_TRUE(baseWindow, NS_ERROR_FAILURE); + 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) +{ + nsIWidget* mainWidget = GetMainWidget(aWindow); NS_ENSURE_TRUE(mainWidget, NS_ERROR_FAILURE); // walk up the widget tree and find the toplevel window in the @@ -260,7 +290,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 +313,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 +396,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 +410,8 @@ if (aWindow) cmdline->SetWindowContext(aWindow); + SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); + rv = cmdline->Run(); if (NS_ERROR_ABORT == rv) return "500 command not parseable"; @@ -333,7 +423,8 @@ } const char* -nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow) +nsGTKRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow, + PRUint32 aTimestamp) { nsresult rv; @@ -364,6 +455,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 +465,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 +485,10 @@ if (aWindow) cmdline->SetWindowContext(aWindow); + SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp); + rv = cmdline->Run(); + if (NS_ERROR_ABORT == rv) return "500 command not parseable"; @@ -486,7 +588,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 +633,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 +++ toolkit/components/remote/nsGTKRemoteService.h @@ -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/library/Makefile.in +++ toolkit/library/Makefile.in @@ -344,6 +344,10 @@ EXTRA_DSO_LDOPTS += $(XLDFLAGS) $(XLIBS) $(MOZ_GTK_LDFLAGS) $(MOZ_XFT_LIBS) $(MOZ_GTK2_LIBS) $(XT_LIBS) endif +ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +EXTRA_DSO_LDOPTS += $(MOZ_STARTUP_NOTIFICATION_LIBS) +endif + ifdef MOZ_ENABLE_XPRINT EXTRA_DSO_LDOPTS += $(MOZ_XPRINT_LDFLAGS) endif --- toolkit/xre/Makefile.in +++ toolkit/xre/Makefile.in @@ -69,6 +69,7 @@ shellservice \ string \ uriloader \ + layout \ widget \ windowwatcher \ xpcom \ --- widget/src/gtk2/Makefile.in +++ widget/src/gtk2/Makefile.in @@ -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/nsGTKToolkit.h +++ widget/src/gtk2/nsGTKToolkit.h @@ -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 --- widget/src/gtk2/nsToolkit.cpp +++ widget/src/gtk2/nsToolkit.cpp @@ -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/XRemoteClient.cpp +++ widget/src/xremoteclient/XRemoteClient.cpp @@ -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 += strlen(desktopStartupPrefix) + 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/XRemoteClient.h +++ widget/src/xremoteclient/XRemoteClient.h @@ -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, --- widget/src/xremoteclient/mozilla-xremote-client.cpp +++ widget/src/xremoteclient/mozilla-xremote-client.cpp @@ -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/nsRemoteClient.h +++ widget/src/xremoteclient/nsRemoteClient.h @@ -85,6 +85,7 @@ */ virtual nsresult SendCommand(const char *aProgram, const char *aUsername, const char *aProfile, const char *aCommand, + const char* aDesktopStartupID, char **aResponse, PRBool *aSucceeded) = 0; /** @@ -97,6 +98,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; }; Index: widget/src/gtk2/nsWindow.cpp =================================================================== RCS file: /cvsroot/mozilla/widget/src/gtk2/nsWindow.cpp,v retrieving revision 1.145.2.12 diff -u -p -6 -r1.145.2.12 nsWindow.cpp --- widget/src/gtk2/nsWindow.cpp 25 Jan 2007 02:22:26 -0000 1.145.2.12 +++ widget/src/gtk2/nsWindow.cpp 19 Feb 2007 09:39:54 -0000 @@ -37,13 +37,13 @@ * * ***** END LICENSE BLOCK ***** */ #include "prlink.h" #include "nsWindow.h" -#include "nsToolkit.h" +#include "nsGTKToolkit.h" #include "nsIRenderingContext.h" #include "nsIRegion.h" #include "nsIRollupListener.h" #include "nsIMenuRollup.h" #include "nsIDOMNode.h" @@ -55,12 +55,17 @@ #include "nsGtkCursors.h" #include #include #include +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +#define SN_API_NOT_YET_FROZEN +#include +#endif + #include "gtk2xtbin.h" #include "nsIPrefService.h" #include "nsIPrefBranch.h" #include "nsIServiceManager.h" #include "nsGfxCIID.h" @@ -657,12 +662,76 @@ nsWindow::SetSizeMode(PRInt32 aMode) NS_IMETHODIMP nsWindow::Enable(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; } +typedef void (* SetUserTimeFunc)(GdkWindow* aWindow, guint32 aTimestamp); + +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()) { + 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) { // Make sure that our owning widget has focus. If it doesn't try to // grab it. Note that we don't set our focus flag in this case. @@ -677,12 +746,16 @@ nsWindow::SetFocus(PRBool aRaise) return NS_ERROR_FAILURE; // Raise the window if someone passed in PR_TRUE and the prefs are // 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)) { GtkWidget* top_window = nsnull; GetToplevelWidget(&top_window); if (top_window && (GTK_WIDGET_VISIBLE(top_window))) @@ -1164,13 +1237,13 @@ nsWindow::GetNativeData(PRUint32 aDataTy case NS_NATIVE_DISPLAY: return GDK_DISPLAY(); break; 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; } case NS_NATIVE_SHELLWIDGET: return (void *) mShell; @@ -2799,22 +2872,27 @@ nsWindow::NativeShow (PRBool aAction) // going to need it, because GTK won't let us unset the mask properly // later. // So, we delay setting the mask until the last moment: when the window // 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) { moz_drawingarea_set_visibility(mDrawingarea, aAction); gtk_widget_show(GTK_WIDGET(mContainer)); gtk_widget_show(mShell); + + // Set up usertime/startupID metadata for the created window. + if (mWindowType != eWindowType_invisible) { + SetUserTimeAndStartupIDForActivatedWindow(mShell); + } } else if (mContainer) { moz_drawingarea_set_visibility(mDrawingarea, TRUE); gtk_widget_show(GTK_WIDGET(mContainer)); } else if (mDrawingarea) { Index: toolkit/xre/nsAppRunner.cpp =================================================================== RCS file: /cvsroot/mozilla/toolkit/xre/nsAppRunner.cpp,v retrieving revision 1.113.2.23 diff -u -p -6 -r1.113.2.23 nsAppRunner.cpp --- toolkit/xre/nsAppRunner.cpp 9 Sep 2007 06:08:37 -0000 1.113.2.23 +++ toolkit/xre/nsAppRunner.cpp 15 Oct 2007 08:01:52 -0000 @@ -96,12 +96,13 @@ #include "nsIWindowWatcher.h" #include "nsIXULAppInfo.h" #include "nsIXULRuntime.h" #ifdef XP_WIN #include "nsIWinAppHelper.h" #endif +#include "nsIScriptGlobalObject.h" #include "nsCRT.h" #include "nsCOMPtr.h" #include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsEmbedCID.h" @@ -112,12 +113,18 @@ #include "nsXPFEComponentsCID.h" #include "nsAppDirectoryServiceDefs.h" #include "nsXULAppAPI.h" #include "nsXREDirProvider.h" #include "nsToolkitCompsCID.h" +#include "nsPIDOMWindow.h" +#include "nsIDOMWindowInternal.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIDocShell.h" +#include "nsAppShellCID.h" #include "nsINIParser.h" #include "InstallCleanupDefines.h" #include @@ -260,12 +267,16 @@ static int gRestartArgc; static char **gRestartArgv; #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 #endif // Save the path of the given file to the specified environment variable. static void @@ -1121,13 +1132,13 @@ DumpVersion() } #ifdef MOZ_ENABLE_XREMOTE // 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; const char *profile = 0; nsCAutoString program(gAppData->name); @@ -1162,13 +1173,13 @@ HandleRemoteArgument(const char* remote) return 1; } 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", response ? response.get() : "No response included"); return 1; } @@ -1179,13 +1190,13 @@ HandleRemoteArgument(const char* remote) } return 0; } static RemoteResult -RemoteCommandLine() +RemoteCommandLine(const char* aDesktopStartupID) { nsresult rv; ArgResult ar; nsCAutoString program(gAppData->name); ToLowerCase(program); @@ -1211,13 +1222,13 @@ RemoteCommandLine() if (NS_FAILED(rv)) return REMOTE_NOT_FOUND; 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) return REMOTE_NOT_FOUND; return REMOTE_FOUND; @@ -2101,13 +2112,48 @@ public: }; #endif #ifdef MOZ_WIDGET_GTK2 #include "prlink.h" typedef void (*_g_set_application_name_fn)(const gchar *application_name); -#endif + +static nsIWidget* GetMainWidget(nsIDOMWindow* aWindow) +{ + // get the native window for this instance + nsCOMPtr scriptObject + (do_QueryInterface(aWindow)); + NS_ENSURE_TRUE(scriptObject, nsnull); + + nsCOMPtr baseWindow + (do_QueryInterface(scriptObject->GetDocShell())); + 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 // MOZ_WIDGET_GTK2 int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) { nsresult rv; ArgResult ar; @@ -2285,12 +2331,25 @@ XRE_main(int argc, char* argv[], const n chromeReg->CheckForNewChrome(); } return 0; } +#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) || defined(MOZ_ENABLE_XREMOTE) + // Stash DESKTOP_STARTUP_ID becaus gtk_init will clear it. +#define HAVE_DESKTOP_STARTUP_ID + char* desktopStartupID = PR_GetEnv("DESKTOP_STARTUP_ID"); + if (desktopStartupID) { + if (desktopStartupID[0] == 0) { + desktopStartupID = nsnull; + } else { + desktopStartupID = strdup(desktopStartupID); + } + } +#endif + #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2) // setup for private colormap. Ideally we'd like to do this // in nsAppShell::Create, but we need to get in before gtk // has been initialized to make sure everything is running // consistently. #if defined(MOZ_WIDGET_GTK2) @@ -2313,12 +2372,13 @@ XRE_main(int argc, char* argv[], const n if (_g_set_application_name) { _g_set_application_name(gAppData->name); } if (glib2) { PR_UnloadLibrary(glib2); } + gtk_window_set_auto_startup_notification(PR_FALSE); #endif gtk_widget_set_default_visual(gdk_rgb_get_visual()); gtk_widget_set_default_colormap(gdk_rgb_get_cmap()); #endif /* MOZ_WIDGET_GTK || MOZ_WIDGET_GTK2 */ @@ -2377,18 +2437,18 @@ XRE_main(int argc, char* argv[], const n ar = CheckArg("remote", PR_TRUE, &xremotearg); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n"); return 1; } if (ar) { - return HandleRemoteArgument(xremotearg); + return HandleRemoteArgument(xremotearg, desktopStartupID); } if (!PR_GetEnv("MOZ_NO_REMOTE")) { // Try to remote the entire command line. If this fails, start up normally. - RemoteResult rr = RemoteCommandLine(); + RemoteResult rr = RemoteCommandLine(desktopStartupID); if (rr == REMOTE_FOUND) return 0; else if (rr == REMOTE_ARG_BAD) return 1; } #endif @@ -2597,12 +2657,19 @@ XRE_main(int argc, char* argv[], const n NS_TIMELINE_ENTER("appStartup->CreateHiddenWindow"); rv = appStartup->CreateHiddenWindow(); 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) { + toolkit->SetDesktopStartupID(nsDependentCString(desktopStartupID)); + } +#endif + // Extension Compatibility Checking and Startup if (gAppData->flags & NS_XRE_ENABLE_EXTENSION_MANAGER) { nsCOMPtr em(do_GetService("@mozilla.org/extensions/manager;1")); NS_ENSURE_TRUE(em, 1); ar = CheckArg("install-global-extension", PR_TRUE); @@ -2791,12 +2858,31 @@ XRE_main(int argc, char* argv[], const n static char kEnvVar[MAXPATHLEN]; sprintf(kEnvVar, "XRE_BINARY_PATH=%s", gBinaryPath); PR_SetEnv(kEnvVar); } #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; } +#ifdef HAVE_DESKTOP_STARTUP_ID + free(desktopStartupID); +#endif + return NS_FAILED(rv) ? 1 : 0; } Index: config/system-headers =================================================================== RCS file: /cvsroot/mozilla/config/system-headers,v retrieving revision 3.4.4.3 diff -u -r3.4.4.3 system-headers --- config/system-headers 18 Dec 2006 23:48:58 -0000 3.4.4.3 +++ config/system-headers 14 Nov 2007 09:20:50 -0000 @@ -931,3 +934,11 @@ #if MOZ_NATIVE_ZLIB==1 zlib.h #endif +#ifdef MOZ_ENABLE_STARTUP_NOTIFICATION +libsn/sn.h +libsn/sn-common.h +libsn/sn-launchee.h +libsn/sn-launcher.h +libsn/sn-monitor.h +libsn/sn-util.h +#endif