summaryrefslogtreecommitdiffstats
path: root/widget/xremoteclient
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /widget/xremoteclient
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/xremoteclient')
-rw-r--r--widget/xremoteclient/XRemoteClient.cpp805
-rw-r--r--widget/xremoteclient/XRemoteClient.h56
-rw-r--r--widget/xremoteclient/moz.build11
-rw-r--r--widget/xremoteclient/nsRemoteClient.h62
4 files changed, 934 insertions, 0 deletions
diff --git a/widget/xremoteclient/XRemoteClient.cpp b/widget/xremoteclient/XRemoteClient.cpp
new file mode 100644
index 000000000..c4567f3cb
--- /dev/null
+++ b/widget/xremoteclient/XRemoteClient.cpp
@@ -0,0 +1,805 @@
+/* -*- 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: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Sprintf.h"
+#include "XRemoteClient.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "prsystem.h"
+#include "mozilla/Logging.h"
+#include "prenv.h"
+#include "prdtoa.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <X11/Xatom.h>
+
+#define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
+#define MOZILLA_LOCK_PROP "_MOZILLA_LOCK"
+#define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
+#define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE"
+#define MOZILLA_USER_PROP "_MOZILLA_USER"
+#define MOZILLA_PROFILE_PROP "_MOZILLA_PROFILE"
+#define MOZILLA_PROGRAM_PROP "_MOZILLA_PROGRAM"
+
+#ifdef IS_BIG_ENDIAN
+#define TO_LITTLE_ENDIAN32(x) \
+ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+#else
+#define TO_LITTLE_ENDIAN32(x) (x)
+#endif
+
+#ifndef MAX_PATH
+#ifdef PATH_MAX
+#define MAX_PATH PATH_MAX
+#else
+#define MAX_PATH 1024
+#endif
+#endif
+
+using mozilla::LogLevel;
+
+static PRLogModuleInfo *sRemoteLm = nullptr;
+
+static int (*sOldHandler)(Display *, XErrorEvent *);
+static bool sGotBadWindow;
+
+XRemoteClient::XRemoteClient()
+{
+ mDisplay = 0;
+ mInitialized = false;
+ mMozVersionAtom = 0;
+ mMozLockAtom = 0;
+ mMozCommandLineAtom = 0;
+ mMozResponseAtom = 0;
+ mMozWMStateAtom = 0;
+ mMozUserAtom = 0;
+ mMozProfileAtom = 0;
+ mMozProgramAtom = 0;
+ mLockData = 0;
+ if (!sRemoteLm)
+ sRemoteLm = PR_NewLogModule("XRemoteClient");
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::XRemoteClient"));
+}
+
+XRemoteClient::~XRemoteClient()
+{
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::~XRemoteClient"));
+ if (mInitialized)
+ Shutdown();
+}
+
+// Minimize the roundtrips to the X-server
+static const char *XAtomNames[] = {
+ MOZILLA_VERSION_PROP,
+ MOZILLA_LOCK_PROP,
+ MOZILLA_RESPONSE_PROP,
+ "WM_STATE",
+ MOZILLA_USER_PROP,
+ MOZILLA_PROFILE_PROP,
+ MOZILLA_PROGRAM_PROP,
+ MOZILLA_COMMANDLINE_PROP
+};
+static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];
+
+nsresult
+XRemoteClient::Init()
+{
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::Init"));
+
+ if (mInitialized)
+ return NS_OK;
+
+ // try to open the display
+ mDisplay = XOpenDisplay(0);
+ if (!mDisplay)
+ return NS_ERROR_FAILURE;
+
+ // get our atoms
+ XInternAtoms(mDisplay, const_cast<char**>(XAtomNames),
+ MOZ_ARRAY_LENGTH(XAtomNames), False, XAtoms);
+
+ int i = 0;
+ mMozVersionAtom = XAtoms[i++];
+ mMozLockAtom = XAtoms[i++];
+ mMozResponseAtom = XAtoms[i++];
+ mMozWMStateAtom = XAtoms[i++];
+ mMozUserAtom = XAtoms[i++];
+ mMozProfileAtom = XAtoms[i++];
+ mMozProgramAtom = XAtoms[i++];
+ mMozCommandLineAtom = XAtoms[i++];
+
+ mInitialized = true;
+
+ return NS_OK;
+}
+
+void
+XRemoteClient::Shutdown (void)
+{
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::Shutdown"));
+
+ if (!mInitialized)
+ return;
+
+ // shut everything down
+ XCloseDisplay(mDisplay);
+ mDisplay = 0;
+ mInitialized = false;
+ if (mLockData) {
+ free(mLockData);
+ mLockData = 0;
+ }
+}
+
+static int
+HandleBadWindow(Display *display, XErrorEvent *event)
+{
+ if (event->error_code == BadWindow) {
+ sGotBadWindow = true;
+ return 0; // ignored
+ }
+ else {
+ return (*sOldHandler)(display, event);
+ }
+}
+
+nsresult
+XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
+ const char *aProfile,
+ int32_t argc, char **argv,
+ const char* aDesktopStartupID,
+ char **aResponse, bool *aWindowFound)
+{
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("XRemoteClient::SendCommandLine"));
+
+ *aWindowFound = false;
+
+ // 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);
+
+ nsresult rv = NS_OK;
+
+ if (w) {
+ // ok, let the caller know that we at least found a window.
+ *aWindowFound = 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 = false;
+
+ // make sure we get the right events on that window
+ XSelectInput(mDisplay, w,
+ (PropertyChangeMask|StructureNotifyMask));
+
+ bool destroyed = false;
+
+ // get the lock on the window
+ rv = GetLock(w, &destroyed);
+
+ if (NS_SUCCEEDED(rv)) {
+ // send our command
+ rv = DoSendCommandLine(w, argc, argv, 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
+
+ }
+ }
+
+ XSetErrorHandler(sOldHandler);
+
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("SendCommandInternal returning 0x%x\n", rv));
+
+ return rv;
+}
+
+Window
+XRemoteClient::CheckWindow(Window aWindow)
+{
+ Atom type = None;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *data;
+ Window innerWindow;
+
+ XGetWindowProperty(mDisplay, aWindow, mMozWMStateAtom,
+ 0, 0, False, AnyPropertyType,
+ &type, &format, &nitems, &bytesafter, &data);
+
+ if (type) {
+ XFree(data);
+ return aWindow;
+ }
+
+ // didn't find it here so check the children of this window
+ innerWindow = CheckChildren(aWindow);
+
+ if (innerWindow)
+ return innerWindow;
+
+ return aWindow;
+}
+
+Window
+XRemoteClient::CheckChildren(Window aWindow)
+{
+ Window root, parent;
+ Window *children;
+ unsigned int nchildren;
+ unsigned int i;
+ Atom type = None;
+ int format;
+ unsigned long nitems, after;
+ unsigned char *data;
+ Window retval = None;
+
+ if (!XQueryTree(mDisplay, aWindow, &root, &parent, &children,
+ &nchildren))
+ return None;
+
+ // scan the list first before recursing into the list of windows
+ // which can get quite deep.
+ for (i=0; !retval && (i < nchildren); i++) {
+ XGetWindowProperty(mDisplay, children[i], mMozWMStateAtom,
+ 0, 0, False, AnyPropertyType, &type, &format,
+ &nitems, &after, &data);
+ if (type) {
+ XFree(data);
+ retval = children[i];
+ }
+ }
+
+ // otherwise recurse into the list
+ for (i=0; !retval && (i < nchildren); i++) {
+ retval = CheckChildren(children[i]);
+ }
+
+ if (children)
+ XFree((char *)children);
+
+ return retval;
+}
+
+nsresult
+XRemoteClient::GetLock(Window aWindow, bool *aDestroyed)
+{
+ bool locked = false;
+ bool waited = false;
+ *aDestroyed = false;
+
+ nsresult rv = NS_OK;
+
+ if (!mLockData) {
+
+ char pidstr[32];
+ char sysinfobuf[SYS_INFO_BUFFER_LENGTH];
+ SprintfLiteral(pidstr, "pid%d@", getpid());
+ PRStatus status;
+ status = PR_GetSystemInfo(PR_SI_HOSTNAME, sysinfobuf,
+ SYS_INFO_BUFFER_LENGTH);
+ if (status != PR_SUCCESS) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // allocate enough space for the string plus the terminating
+ // char
+ mLockData = (char *)malloc(strlen(pidstr) + strlen(sysinfobuf) + 1);
+ if (!mLockData)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ strcpy(mLockData, pidstr);
+ if (!strcat(mLockData, sysinfobuf))
+ return NS_ERROR_FAILURE;
+ }
+
+ do {
+ int result;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = 0;
+
+ XGrabServer(mDisplay);
+
+ result = XGetWindowProperty (mDisplay, aWindow, mMozLockAtom,
+ 0, (65536 / sizeof (long)),
+ False, /* don't delete */
+ XA_STRING,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after,
+ &data);
+
+ // 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 = 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,
+ (unsigned char *)mLockData,
+ strlen(mLockData));
+ locked = True;
+ }
+
+ XUngrabServer(mDisplay);
+ XFlush(mDisplay); // ungrab now!
+
+ 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. */
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("window 0x%x is locked by %s; waiting...\n",
+ (unsigned int) aWindow, data));
+ waited = True;
+ while (1) {
+ XEvent event;
+ int select_retval;
+ fd_set select_set;
+ struct timeval delay;
+ delay.tv_sec = 10;
+ delay.tv_usec = 0;
+
+ FD_ZERO(&select_set);
+ // add the x event queue to the select set
+ FD_SET(ConnectionNumber(mDisplay), &select_set);
+ select_retval = select(ConnectionNumber(mDisplay) + 1,
+ &select_set, nullptr, nullptr, &delay);
+ // did we time out?
+ if (select_retval == 0) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("timed out waiting for window\n"));
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("xevent...\n"));
+ XNextEvent (mDisplay, &event);
+ if (event.xany.type == DestroyNotify &&
+ event.xdestroywindow.window == aWindow) {
+ *aDestroyed = true;
+ rv = NS_ERROR_FAILURE;
+ break;
+ }
+ else if (event.xany.type == PropertyNotify &&
+ event.xproperty.state == PropertyDelete &&
+ event.xproperty.window == aWindow &&
+ event.xproperty.atom == mMozLockAtom) {
+ /* Ok! Someone deleted their lock, so now we can try
+ again. */
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("(0x%x unlocked, trying again...)\n",
+ (unsigned int) aWindow));
+ break;
+ }
+ }
+ }
+ if (data)
+ XFree(data);
+ } while (!locked && !NS_FAILED(rv));
+
+ if (waited && locked) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("obtained lock.\n"));
+ } else if (*aDestroyed) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("window 0x%x unexpectedly destroyed.\n",
+ (unsigned int) aWindow));
+ }
+
+ return rv;
+}
+
+Window
+XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
+ const char *aProfile)
+{
+ Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
+ Window bestWindow = 0;
+ Window root2, parent, *kids;
+ unsigned int nkids;
+
+ // Get a list of the children of the root window, walk the list
+ // looking for the best window that fits the criteria.
+ if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("XQueryTree failed in XRemoteClient::FindBestWindow"));
+ return 0;
+ }
+
+ if (!(kids && nkids)) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("root window has no children"));
+ return 0;
+ }
+
+ // We'll walk the list of windows looking for a window that best
+ // fits the criteria here.
+
+ for (unsigned int i = 0; i < nkids; i++) {
+ Atom type;
+ int format;
+ unsigned long nitems, bytesafter;
+ unsigned char *data_return = 0;
+ Window w;
+ w = kids[i];
+ // find the inner window with WM_STATE on it
+ w = CheckWindow(w);
+
+ int status = XGetWindowProperty(mDisplay, w, mMozVersionAtom,
+ 0, (65536 / sizeof (long)),
+ False, XA_STRING,
+ &type, &format, &nitems, &bytesafter,
+ &data_return);
+
+ if (!data_return)
+ continue;
+
+ double version = PR_strtod((char*) data_return, nullptr);
+ XFree(data_return);
+
+ if (!(version >= 5.1 && version < 6))
+ continue;
+
+ data_return = 0;
+
+ if (status != Success || type == None)
+ continue;
+
+ // If someone passed in a program name, check it against this one
+ // unless it's "any" in which case, we don't care. If someone did
+ // pass in a program name and this window doesn't support that
+ // protocol, we don't include it in our list.
+ if (aProgram && strcmp(aProgram, "any")) {
+ status = XGetWindowProperty(mDisplay, w, mMozProgramAtom,
+ 0, (65536 / sizeof(long)),
+ False, XA_STRING,
+ &type, &format, &nitems, &bytesafter,
+ &data_return);
+
+ // If the return name is not the same as what someone passed in,
+ // we don't want this window.
+ if (data_return) {
+ if (strcmp(aProgram, (const char *)data_return)) {
+ XFree(data_return);
+ continue;
+ }
+
+ // This is actually the success condition.
+ XFree(data_return);
+ }
+ else {
+ // Doesn't support the protocol, even though the user
+ // requested it. So we're not going to use this window.
+ continue;
+ }
+ }
+
+ // Check to see if it has the user atom on that window. If there
+ // is then we need to make sure that it matches what we have.
+ const char *username;
+ if (aUsername) {
+ username = aUsername;
+ }
+ else {
+ username = PR_GetEnv("LOGNAME");
+ }
+
+ if (username) {
+ status = XGetWindowProperty(mDisplay, w, mMozUserAtom,
+ 0, (65536 / sizeof(long)),
+ False, XA_STRING,
+ &type, &format, &nitems, &bytesafter,
+ &data_return);
+
+ // if there's a username compare it with what we have
+ if (data_return) {
+ // If the IDs aren't equal, we don't want this window.
+ if (strcmp(username, (const char *)data_return)) {
+ XFree(data_return);
+ continue;
+ }
+
+ XFree(data_return);
+ }
+ }
+
+ // Check to see if there's a profile name on this window. If
+ // there is, then we need to make sure it matches what someone
+ // passed in.
+ if (aProfile) {
+ status = XGetWindowProperty(mDisplay, w, mMozProfileAtom,
+ 0, (65536 / sizeof(long)),
+ False, XA_STRING,
+ &type, &format, &nitems, &bytesafter,
+ &data_return);
+
+ // If there's a profile compare it with what we have
+ if (data_return) {
+ // If the profiles aren't equal, we don't want this window.
+ if (strcmp(aProfile, (const char *)data_return)) {
+ XFree(data_return);
+ continue;
+ }
+
+ XFree(data_return);
+ }
+ }
+
+ // Check to see if the window supports the new command-line passing
+ // protocol, if that is requested.
+
+ // If we got this far, this is the best window. It passed
+ // all the tests.
+ bestWindow = w;
+ break;
+ }
+
+ if (kids)
+ XFree((char *) kids);
+
+ return bestWindow;
+}
+
+nsresult
+XRemoteClient::FreeLock(Window aWindow)
+{
+ int result;
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = 0;
+
+ result = XGetWindowProperty(mDisplay, aWindow, mMozLockAtom,
+ 0, (65536 / sizeof(long)),
+ True, /* atomic delete after */
+ XA_STRING,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after,
+ &data);
+ if (result != Success) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("unable to read and delete " MOZILLA_LOCK_PROP
+ " property\n"));
+ return NS_ERROR_FAILURE;
+ }
+ else if (!data || !*data){
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("invalid data on " MOZILLA_LOCK_PROP
+ " of window 0x%x.\n",
+ (unsigned int) aWindow));
+ return NS_ERROR_FAILURE;
+ }
+ else if (strcmp((char *)data, mLockData)) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ (MOZILLA_LOCK_PROP " was stolen! Expected \"%s\", saw \"%s\"!\n",
+ mLockData, data));
+ return NS_ERROR_FAILURE;
+ }
+
+ if (data)
+ XFree(data);
+ return NS_OK;
+}
+
+/* like strcpy, but return the char after the final null */
+static char*
+estrcpy(const char* s, char* d)
+{
+ while (*s)
+ *d++ = *s++;
+
+ *d++ = '\0';
+ return d;
+}
+
+nsresult
+XRemoteClient::DoSendCommandLine(Window aWindow, int32_t argc, char **argv,
+ const char* aDesktopStartupID,
+ char **aResponse, bool *aDestroyed)
+{
+ *aDestroyed = false;
+
+ char cwdbuf[MAX_PATH];
+ if (!getcwd(cwdbuf, MAX_PATH))
+ return NS_ERROR_UNEXPECTED;
+
+ // the commandline property is constructed as an array of int32_t
+ // followed by a series of null-terminated strings:
+ //
+ // [argc][offsetargv0][offsetargv1...]<workingdir>\0<argv[0]>\0argv[1]...\0
+ // (offset is from the beginning of the buffer)
+
+ static char desktopStartupPrefix[] = " DESKTOP_STARTUP_ID=";
+
+ int32_t argvlen = strlen(cwdbuf);
+ for (int i = 0; i < argc; ++i) {
+ int32_t len = strlen(argv[i]);
+ if (i == 0 && aDesktopStartupID) {
+ len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
+ }
+ argvlen += len;
+ }
+
+ int32_t* buffer = (int32_t*) malloc(argvlen + argc + 1 +
+ sizeof(int32_t) * (argc + 1));
+ if (!buffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ buffer[0] = TO_LITTLE_ENDIAN32(argc);
+
+ char *bufend = (char*) (buffer + argc + 1);
+
+ bufend = estrcpy(cwdbuf, bufend);
+
+ 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
+ int32_t debug_argc = TO_LITTLE_ENDIAN32(*buffer);
+ char *debug_workingdir = (char*) (buffer + argc + 1);
+
+ printf("Sending command line:\n"
+ " working dir: %s\n"
+ " argc:\t%i",
+ debug_workingdir,
+ debug_argc);
+
+ int32_t *debug_offset = buffer + 1;
+ for (int debug_i = 0; debug_i < debug_argc; ++debug_i)
+ printf(" argv[%i]:\t%s\n", debug_i,
+ ((char*) buffer) + TO_LITTLE_ENDIAN32(debug_offset[debug_i]));
+#endif
+
+ XChangeProperty (mDisplay, aWindow, mMozCommandLineAtom, XA_STRING, 8,
+ PropModeReplace, (unsigned char *) buffer,
+ bufend - ((char*) buffer));
+ free(buffer);
+
+ if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandLineAtom))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+bool
+XRemoteClient::WaitForResponse(Window aWindow, char **aResponse,
+ bool *aDestroyed, Atom aCommandAtom)
+{
+ bool done = false;
+ bool accepted = false;
+
+ while (!done) {
+ XEvent event;
+ XNextEvent (mDisplay, &event);
+ if (event.xany.type == DestroyNotify &&
+ event.xdestroywindow.window == aWindow) {
+ /* Print to warn user...*/
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("window 0x%x was destroyed.\n",
+ (unsigned int) aWindow));
+ *aResponse = strdup("Window was destroyed while reading response.");
+ *aDestroyed = true;
+ return false;
+ }
+ else if (event.xany.type == PropertyNotify &&
+ event.xproperty.state == PropertyNewValue &&
+ event.xproperty.window == aWindow &&
+ event.xproperty.atom == mMozResponseAtom) {
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data = 0;
+ Bool result;
+ result = XGetWindowProperty (mDisplay, aWindow, mMozResponseAtom,
+ 0, (65536 / sizeof (long)),
+ True, /* atomic delete after */
+ XA_STRING,
+ &actual_type, &actual_format,
+ &nitems, &bytes_after,
+ &data);
+ if (result != Success) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("failed reading " MOZILLA_RESPONSE_PROP
+ " from window 0x%0x.\n",
+ (unsigned int) aWindow));
+ *aResponse = strdup("Internal error reading response from window.");
+ done = true;
+ }
+ else if (!data || strlen((char *) data) < 5) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("invalid data on " MOZILLA_RESPONSE_PROP
+ " property of window 0x%0x.\n",
+ (unsigned int) aWindow));
+ *aResponse = strdup("Server returned invalid data in response.");
+ done = true;
+ }
+ else if (*data == '1') { /* positive preliminary reply */
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
+ /* keep going */
+ done = false;
+ }
+
+ else if (!strncmp ((char *)data, "200", 3)) { /* positive completion */
+ *aResponse = strdup((char *)data);
+ accepted = true;
+ done = true;
+ }
+
+ else if (*data == '2') { /* positive completion */
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
+ *aResponse = strdup((char *)data);
+ accepted = true;
+ done = true;
+ }
+
+ else if (*data == '3') { /* positive intermediate reply */
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("internal error: "
+ "server wants more information? (%s)\n",
+ data));
+ *aResponse = strdup((char *)data);
+ done = true;
+ }
+
+ else if (*data == '4' || /* transient negative completion */
+ *data == '5') { /* permanent negative completion */
+ MOZ_LOG(sRemoteLm, LogLevel::Debug, ("%s\n", data + 4));
+ *aResponse = strdup((char *)data);
+ done = true;
+ }
+
+ else {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("unrecognised " MOZILLA_RESPONSE_PROP
+ " from window 0x%x: %s\n",
+ (unsigned int) aWindow, data));
+ *aResponse = strdup((char *)data);
+ done = true;
+ }
+
+ if (data)
+ XFree(data);
+ }
+
+ else if (event.xany.type == PropertyNotify &&
+ event.xproperty.window == aWindow &&
+ event.xproperty.state == PropertyDelete &&
+ event.xproperty.atom == aCommandAtom) {
+ MOZ_LOG(sRemoteLm, LogLevel::Debug,
+ ("(server 0x%x has accepted "
+ MOZILLA_COMMANDLINE_PROP ".)\n",
+ (unsigned int) aWindow));
+ }
+
+ }
+
+ return accepted;
+}
diff --git a/widget/xremoteclient/XRemoteClient.h b/widget/xremoteclient/XRemoteClient.h
new file mode 100644
index 000000000..840716ad9
--- /dev/null
+++ b/widget/xremoteclient/XRemoteClient.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include "nsRemoteClient.h"
+
+class XRemoteClient : public nsRemoteClient
+{
+public:
+ XRemoteClient();
+ ~XRemoteClient();
+
+ virtual nsresult Init();
+ virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername,
+ const char *aProfile,
+ int32_t argc, char **argv,
+ const char* aDesktopStartupID,
+ char **aResponse, bool *aSucceeded);
+ void Shutdown();
+
+private:
+
+ Window CheckWindow (Window aWindow);
+ Window CheckChildren (Window aWindow);
+ nsresult GetLock (Window aWindow, bool *aDestroyed);
+ nsresult FreeLock (Window aWindow);
+ Window FindBestWindow (const char *aProgram,
+ const char *aUsername,
+ const char *aProfile);
+ nsresult DoSendCommandLine(Window aWindow,
+ int32_t argc, char **argv,
+ const char* aDesktopStartupID,
+ char **aResponse,
+ bool *aDestroyed);
+ bool WaitForResponse (Window aWindow, char **aResponse,
+ bool *aDestroyed, Atom aCommandAtom);
+
+ Display *mDisplay;
+
+ Atom mMozVersionAtom;
+ Atom mMozLockAtom;
+ Atom mMozCommandLineAtom;
+ Atom mMozResponseAtom;
+ Atom mMozWMStateAtom;
+ Atom mMozUserAtom;
+ Atom mMozProfileAtom;
+ Atom mMozProgramAtom;
+
+ char *mLockData;
+
+ bool mInitialized;
+};
diff --git a/widget/xremoteclient/moz.build b/widget/xremoteclient/moz.build
new file mode 100644
index 000000000..8d3f01c82
--- /dev/null
+++ b/widget/xremoteclient/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_LIBRARY = 'xul'
+
+SOURCES += [
+ 'XRemoteClient.cpp',
+]
diff --git a/widget/xremoteclient/nsRemoteClient.h b/widget/xremoteclient/nsRemoteClient.h
new file mode 100644
index 000000000..6d90d693f
--- /dev/null
+++ b/widget/xremoteclient/nsRemoteClient.h
@@ -0,0 +1,62 @@
+/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsRemoteClient_h__
+#define nsRemoteClient_h__
+
+#include "nscore.h"
+
+/**
+ * Pure-virtual common base class for remoting implementations.
+ */
+
+class nsRemoteClient
+{
+public:
+ /**
+ * Initializes the client
+ */
+ virtual nsresult Init() = 0;
+
+ /**
+ * Send a complete command line to a running instance.
+ *
+ * @param aProgram This is the preferred program that we want to use
+ * for this particular command.
+ *
+ * @param aUsername This allows someone to only talk to an instance
+ * of the server that's running under a particular username. If
+ * this isn't specified here it's pulled from the LOGNAME
+ * environmental variable if it's set.
+ *
+ * @param aProfile This allows you to specify a particular server
+ * running under a named profile. If it is not specified the
+ * profile is not checked.
+ *
+ * @param argc The number of command-line arguments.
+ *
+ * @param argv The command-line arguments.
+ *
+ * @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
+ * string functions, so free it with free().
+ *
+ * @return true if succeeded, false if no running instance was found.
+ *
+ */
+ virtual nsresult SendCommandLine(const char *aProgram, const char *aUsername,
+ const char *aProfile,
+ int32_t argc, char **argv,
+ const char* aDesktopStartupID,
+ char **aResponse, bool *aSucceeded) = 0;
+};
+
+#endif // nsRemoteClient_h__