diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /widget/xremoteclient | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-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.cpp | 805 | ||||
-rw-r--r-- | widget/xremoteclient/XRemoteClient.h | 56 | ||||
-rw-r--r-- | widget/xremoteclient/moz.build | 11 | ||||
-rw-r--r-- | widget/xremoteclient/nsRemoteClient.h | 62 |
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__ |