diff --git a/widget/src/xremoteclient/XRemoteClient.cpp b/widget/src/xremoteclient/XRemoteClient.cpp index 0e91d7fdaaf..add2b669258 100644 --- a/widget/src/xremoteclient/XRemoteClient.cpp +++ b/widget/src/xremoteclient/XRemoteClient.cpp @@ -1,5 +1,5 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim:expandtab:shiftwidth=4:tabstop=4: +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=8: */ /* vim:set ts=8 sw=2 et cindent: */ /* ***** BEGIN LICENSE BLOCK ***** @@ -88,6 +88,9 @@ static PRLogModuleInfo *sRemoteLm = NULL; +static int (*sOldHandler)(Display *, XErrorEvent *); +static PRBool sGotBadWindow; + XRemoteClient::XRemoteClient() { mDisplay = 0; @@ -183,40 +186,10 @@ XRemoteClient::SendCommand (const char *aProgram, const char *aUsername, { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand")); - *aWindowFound = PR_FALSE; - - Window w = FindBestWindow(aProgram, aUsername, aProfile, PR_FALSE); - - nsresult rv = NS_OK; - - if (w) { - // ok, let the caller know that we at least found a window. - *aWindowFound = PR_TRUE; - - // make sure we get the right events on that window - XSelectInput(mDisplay, w, - (PropertyChangeMask|StructureNotifyMask)); - - PRBool destroyed = PR_FALSE; - - // get the lock on the window - rv = GetLock(w, &destroyed); - - if (NS_SUCCEEDED(rv)) { - // send our command - rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, &destroyed); - - // if the window was destroyed, don't bother trying to free the - // lock. - if (!destroyed) - FreeLock(w); // doesn't really matter what this returns - - } - } - - PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommand returning 0x%x\n", rv)); - - return rv; + return SendCommandInternal(aProgram, aUsername, aProfile, + aCommand, 0, nsnull, + aDesktopStartupID, + aResponse, aWindowFound); } nsresult @@ -228,9 +201,39 @@ XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine")); - *aWindowFound = PR_FALSE; + return SendCommandInternal(aProgram, aUsername, aProfile, + nsnull, argc, argv, + aDesktopStartupID, + aResponse, aWindowFound); +} - Window w = FindBestWindow(aProgram, aUsername, aProfile, PR_TRUE); +static int +HandleBadWindow(Display *display, XErrorEvent *event) +{ + if (event->error_code == BadWindow) { + sGotBadWindow = PR_TRUE; + return 0; // ignored + } + else { + return (*sOldHandler)(display, event); + } +} + +nsresult +XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername, + const char *aProfile, const char *aCommand, + PRInt32 argc, char **argv, + const char* aDesktopStartupID, + char **aResponse, PRBool *aWindowFound) +{ + *aWindowFound = PR_FALSE; + PRBool isCommandLine = !aCommand; + + // FindBestWindow() iterates down the window hierarchy, so catch X errors + // when windows get destroyed before being accessed. + sOldHandler = XSetErrorHandler(HandleBadWindow); + + Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine); nsresult rv = NS_OK; @@ -238,10 +241,15 @@ XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, // ok, let the caller know that we at least found a window. *aWindowFound = PR_TRUE; + // Ignore BadWindow errors up to this point. The last request from + // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to + // Sync. Leave the error handler installed to detect if w gets destroyed. + sGotBadWindow = PR_FALSE; + // make sure we get the right events on that window XSelectInput(mDisplay, w, (PropertyChangeMask|StructureNotifyMask)); - + PRBool destroyed = PR_FALSE; // get the lock on the window @@ -249,7 +257,14 @@ XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, if (NS_SUCCEEDED(rv)) { // send our command - rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, &destroyed); + if (isCommandLine) { + rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, + &destroyed); + } + else { + rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, + &destroyed); + } // if the window was destroyed, don't bother trying to free the // lock. @@ -259,7 +274,9 @@ XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername, } } - PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandLine returning 0x%x\n", rv)); + XSetErrorHandler(sOldHandler); + + PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandInternal returning 0x%x\n", rv)); return rv; } @@ -338,6 +355,8 @@ XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed) PRBool waited = PR_FALSE; *aDestroyed = PR_FALSE; + nsresult rv = NS_OK; + if (!mLockData) { char pidstr[32]; @@ -377,7 +396,16 @@ XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed) &actual_type, &actual_format, &nitems, &bytes_after, &data); - if (result != Success || actual_type == None) { + + // aWindow may have been destroyed before XSelectInput was processed, in + // which case there may not be any DestroyNotify event in the queue to + // tell us. XGetWindowProperty() was synchronous so error responses have + // now been processed, setting sGotBadWindow. + if (sGotBadWindow) { + *aDestroyed = PR_TRUE; + rv = NS_ERROR_FAILURE; + } + else if (result != Success || actual_type == None) { /* It's not now locked - lock it. */ XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8, PropModeReplace, @@ -387,9 +415,9 @@ XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed) } XUngrabServer(mDisplay); - XSync(mDisplay, False); + XFlush(mDisplay); // ungrab now! - if (!locked) { + if (!locked && !NS_FAILED(rv)) { /* We tried to grab the lock this time, and failed because someone else is holding it already. So, wait for a PropertyDelete event to come in, and try again. */ @@ -420,17 +448,16 @@ XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed) // did we time out? if (select_retval == 0) { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("timed out waiting for window\n")); - return NS_ERROR_FAILURE; + rv = NS_ERROR_FAILURE; + break; } PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("xevent...\n")); XNextEvent (mDisplay, &event); if (event.xany.type == DestroyNotify && event.xdestroywindow.window == aWindow) { - PR_LOG(sRemoteLm, PR_LOG_DEBUG, - ("window 0x%x unexpectedly destroyed.\n", - (unsigned int) aWindow)); *aDestroyed = PR_TRUE; - return NS_ERROR_FAILURE; + rv = NS_ERROR_FAILURE; + break; } else if (event.xany.type == PropertyNotify && event.xproperty.state == PropertyDelete && @@ -447,13 +474,17 @@ XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed) } if (data) XFree(data); - } while (!locked); + } while (!locked && !NS_FAILED(rv)); - if (waited) { + if (waited && locked) { PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("obtained lock.\n")); + } else if (*aDestroyed) { + PR_LOG(sRemoteLm, PR_LOG_DEBUG, + ("window 0x%x unexpectedly destroyed.\n", + (unsigned int) aWindow)); } - return NS_OK; + return rv; } Window diff --git a/widget/src/xremoteclient/XRemoteClient.h b/widget/src/xremoteclient/XRemoteClient.h index 8a163263bbb..ef940ec8602 100644 --- a/widget/src/xremoteclient/XRemoteClient.h +++ b/widget/src/xremoteclient/XRemoteClient.h @@ -1,3 +1,4 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -67,6 +68,11 @@ private: const char *aUsername, const char *aProfile, PRBool aSupportsCommandLine); + nsresult SendCommandInternal(const char *aProgram, const char *aUsername, + const char *aProfile, const char *aCommand, + PRInt32 argc, char **argv, + const char* aDesktopStartupID, + char **aResponse, PRBool *aWindowFound); nsresult DoSendCommand (Window aWindow, const char *aCommand, const char* aDesktopStartupID,