1
diff --git a/widget/src/xremoteclient/XRemoteClient.cpp b/widget/src/xremoteclient/XRemoteClient.cpp
2
--- a/widget/src/xremoteclient/XRemoteClient.cpp
3
+++ b/widget/src/xremoteclient/XRemoteClient.cpp
5
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
6
-/* vim:expandtab:shiftwidth=4:tabstop=4:
7
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
8
+/* vim:expandtab:shiftwidth=2:tabstop=8:
10
/* vim:set ts=8 sw=2 et cindent: */
11
/* ***** BEGIN LICENSE BLOCK *****
12
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
14
* The contents of this file are subject to the Mozilla Public License Version
15
* 1.1 (the "License"); you may not use this file except in compliance with
16
* the License. You may obtain a copy of the License at
22
#define ARRAY_LENGTH(array_) (sizeof(array_)/sizeof(array_[0]))
24
static PRLogModuleInfo *sRemoteLm = NULL;
26
+static int (*sOldHandler)(Display *, XErrorEvent *);
27
+static PRBool sGotBadWindow;
29
XRemoteClient::XRemoteClient()
32
mInitialized = PR_FALSE;
37
@@ -178,93 +181,107 @@ XRemoteClient::Shutdown (void)
39
XRemoteClient::SendCommand (const char *aProgram, const char *aUsername,
40
const char *aProfile, const char *aCommand,
41
const char* aDesktopStartupID,
42
char **aResponse, PRBool *aWindowFound)
44
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
46
- *aWindowFound = PR_FALSE;
48
- Window w = FindBestWindow(aProgram, aUsername, aProfile, PR_FALSE);
50
- nsresult rv = NS_OK;
53
- // ok, let the caller know that we at least found a window.
54
- *aWindowFound = PR_TRUE;
56
- // make sure we get the right events on that window
57
- XSelectInput(mDisplay, w,
58
- (PropertyChangeMask|StructureNotifyMask));
60
- PRBool destroyed = PR_FALSE;
62
- // get the lock on the window
63
- rv = GetLock(w, &destroyed);
65
- if (NS_SUCCEEDED(rv)) {
67
- rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse, &destroyed);
69
- // if the window was destroyed, don't bother trying to free the
72
- FreeLock(w); // doesn't really matter what this returns
77
- PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommand returning 0x%x\n", rv));
80
+ return SendCommandInternal(aProgram, aUsername, aProfile,
81
+ aCommand, 0, nsnull,
83
+ aResponse, aWindowFound);
87
XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
89
PRInt32 argc, char **argv,
90
const char* aDesktopStartupID,
91
char **aResponse, PRBool *aWindowFound)
93
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
95
+ return SendCommandInternal(aProgram, aUsername, aProfile,
98
+ aResponse, aWindowFound);
102
+HandleBadWindow(Display *display, XErrorEvent *event)
104
+ if (event->error_code == BadWindow) {
105
+ sGotBadWindow = PR_TRUE;
106
+ return 0; // ignored
109
+ return (*sOldHandler)(display, event);
114
+XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername,
115
+ const char *aProfile, const char *aCommand,
116
+ PRInt32 argc, char **argv,
117
+ const char* aDesktopStartupID,
118
+ char **aResponse, PRBool *aWindowFound)
120
*aWindowFound = PR_FALSE;
121
+ PRBool isCommandLine = !aCommand;
123
- Window w = FindBestWindow(aProgram, aUsername, aProfile, PR_TRUE);
124
+ // FindBestWindow() iterates down the window hierarchy, so catch X errors
125
+ // when windows get destroyed before being accessed.
126
+ sOldHandler = XSetErrorHandler(HandleBadWindow);
128
+ Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine);
133
// ok, let the caller know that we at least found a window.
134
*aWindowFound = PR_TRUE;
136
+ // Ignore BadWindow errors up to this point. The last request from
137
+ // FindBestWindow() was a synchronous XGetWindowProperty(), so no need to
138
+ // Sync. Leave the error handler installed to detect if w gets destroyed.
139
+ sGotBadWindow = PR_FALSE;
141
// make sure we get the right events on that window
142
XSelectInput(mDisplay, w,
143
(PropertyChangeMask|StructureNotifyMask));
146
PRBool destroyed = PR_FALSE;
148
// get the lock on the window
149
rv = GetLock(w, &destroyed);
151
if (NS_SUCCEEDED(rv)) {
153
- rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse, &destroyed);
154
+ if (isCommandLine) {
155
+ rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
159
+ rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse,
163
// if the window was destroyed, don't bother trying to free the
166
FreeLock(w); // doesn't really matter what this returns
171
- PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandLine returning 0x%x\n", rv));
172
+ XSetErrorHandler(sOldHandler);
174
+ PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommandInternal returning 0x%x\n", rv));
180
XRemoteClient::CheckWindow(Window aWindow)
183
@@ -333,16 +350,18 @@ XRemoteClient::CheckChildren(Window aWin
186
XRemoteClient::GetLock(Window aWindow, PRBool *aDestroyed)
188
PRBool locked = PR_FALSE;
189
PRBool waited = PR_FALSE;
190
*aDestroyed = PR_FALSE;
192
+ nsresult rv = NS_OK;
197
char sysinfobuf[SYS_INFO_BUFFER_LENGTH];
198
PR_snprintf(pidstr, sizeof(pidstr), "pid%d@", getpid());
200
status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf,
201
SYS_INFO_BUFFER_LENGTH);
202
@@ -372,29 +391,38 @@ XRemoteClient::GetLock(Window aWindow, P
204
result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom,
205
0, (65536 / sizeof (long)),
206
False, /* don't delete */
208
&actual_type, &actual_format,
209
&nitems, &bytes_after,
211
- if (result != Success || actual_type == None) {
213
+ // aWindow may have been destroyed before XSelectInput was processed, in
214
+ // which case there may not be any DestroyNotify event in the queue to
215
+ // tell us. XGetWindowProperty() was synchronous so error responses have
216
+ // now been processed, setting sGotBadWindow.
217
+ if (sGotBadWindow) {
218
+ *aDestroyed = PR_TRUE;
219
+ rv = NS_ERROR_FAILURE;
221
+ else if (result != Success || actual_type == None) {
222
/* It's not now locked - lock it. */
223
XChangeProperty (mDisplay, aWindow, mMozLockAtom, XA_STRING, 8,
225
(unsigned char *)mLockData,
230
XUngrabServer(mDisplay);
231
- XSync(mDisplay, False);
232
+ XFlush(mDisplay); // ungrab now!
235
+ if (!locked && !NS_FAILED(rv)) {
236
/* We tried to grab the lock this time, and failed because someone
237
else is holding it already. So, wait for a PropertyDelete event
238
to come in, and try again. */
239
PR_LOG(sRemoteLm, PR_LOG_DEBUG,
240
("window 0x%x is locked by %s; waiting...\n",
241
(unsigned int) aWindow, data));
244
@@ -415,50 +443,53 @@ XRemoteClient::GetLock(Window aWindow, P
245
// add the x event queue to the select set
246
FD_SET(ConnectionNumber(mDisplay), &select_set);
247
select_retval = select(ConnectionNumber(mDisplay) + 1,
248
&select_set, NULL, NULL, &delay);
251
if (select_retval == 0) {
252
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("timed out waiting for window\n"));
253
- return NS_ERROR_FAILURE;
254
+ rv = NS_ERROR_FAILURE;
257
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("xevent...\n"));
258
XNextEvent (mDisplay, &event);
259
if (event.xany.type == DestroyNotify &&
260
event.xdestroywindow.window == aWindow) {
261
- PR_LOG(sRemoteLm, PR_LOG_DEBUG,
262
- ("window 0x%x unexpectedly destroyed.\n",
263
- (unsigned int) aWindow));
264
*aDestroyed = PR_TRUE;
265
- return NS_ERROR_FAILURE;
266
+ rv = NS_ERROR_FAILURE;
269
else if (event.xany.type == PropertyNotify &&
270
event.xproperty.state == PropertyDelete &&
271
event.xproperty.window == aWindow &&
272
event.xproperty.atom == mMozLockAtom) {
273
/* Ok! Someone deleted their lock, so now we can try
275
PR_LOG(sRemoteLm, PR_LOG_DEBUG,
276
("(0x%x unlocked, trying again...)\n",
277
(unsigned int) aWindow));
285
+ } while (!locked && !NS_FAILED(rv));
288
+ if (waited && locked) {
289
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("obtained lock.\n"));
290
+ } else if (*aDestroyed) {
291
+ PR_LOG(sRemoteLm, PR_LOG_DEBUG,
292
+ ("window 0x%x unexpectedly destroyed.\n",
293
+ (unsigned int) aWindow));
301
XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
302
const char *aProfile,
303
PRBool aSupportsCommandLine)
305
Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
306
diff --git a/widget/src/xremoteclient/XRemoteClient.h b/widget/src/xremoteclient/XRemoteClient.h
307
--- a/widget/src/xremoteclient/XRemoteClient.h
308
+++ b/widget/src/xremoteclient/XRemoteClient.h
310
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
311
/* ***** BEGIN LICENSE BLOCK *****
312
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
314
* The contents of this file are subject to the Mozilla Public License Version
315
* 1.1 (the "License"); you may not use this file except in compliance with
316
* the License. You may obtain a copy of the License at
317
* http://www.mozilla.org/MPL/
319
@@ -62,16 +63,21 @@ private:
320
Window CheckWindow (Window aWindow);
321
Window CheckChildren (Window aWindow);
322
nsresult GetLock (Window aWindow, PRBool *aDestroyed);
323
nsresult FreeLock (Window aWindow);
324
Window FindBestWindow (const char *aProgram,
325
const char *aUsername,
326
const char *aProfile,
327
PRBool aSupportsCommandLine);
328
+ nsresult SendCommandInternal(const char *aProgram, const char *aUsername,
329
+ const char *aProfile, const char *aCommand,
330
+ PRInt32 argc, char **argv,
331
+ const char* aDesktopStartupID,
332
+ char **aResponse, PRBool *aWindowFound);
333
nsresult DoSendCommand (Window aWindow,
334
const char *aCommand,
335
const char* aDesktopStartupID,
338
nsresult DoSendCommandLine(Window aWindow,
339
PRInt32 argc, char **argv,
340
const char* aDesktopStartupID,