mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-01 21:07:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
805
widget/xremoteclient/XRemoteClient.cpp
Normal file
805
widget/xremoteclient/XRemoteClient.cpp
Normal file
|
|
@ -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;
|
||||
}
|
||||
56
widget/xremoteclient/XRemoteClient.h
Normal file
56
widget/xremoteclient/XRemoteClient.h
Normal file
|
|
@ -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;
|
||||
};
|
||||
11
widget/xremoteclient/moz.build
Normal file
11
widget/xremoteclient/moz.build
Normal file
|
|
@ -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',
|
||||
]
|
||||
62
widget/xremoteclient/nsRemoteClient.h
Normal file
62
widget/xremoteclient/nsRemoteClient.h
Normal file
|
|
@ -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__
|
||||
Loading…
Add table
Add a link
Reference in a new issue