Backout the part of changeset 8044e5199fe2 (bug 1080319) that removed -remote. a=sledru
authorMike Hommey <mh+mozilla@glandium.org>
Thu, 05 Mar 2015 16:43:34 +0900
changeset 250412 29eac8276b62
parent 250407 edb24ca59d13
child 250413 5a8085d3a0fe
push id4580
push userryanvm@gmail.com
push date2015-03-17 14:49 +0000
treeherdermozilla-beta@5a8085d3a0fe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssledru
bugs1080319
milestone37.0
Backout the part of changeset 8044e5199fe2 (bug 1080319) that removed -remote. a=sledru
browser/components/nsBrowserContentHandler.js
toolkit/components/remote/nsXRemoteService.cpp
toolkit/components/remote/nsXRemoteService.h
toolkit/xre/nsAppRunner.cpp
widget/xremoteclient/XRemoteClient.cpp
widget/xremoteclient/XRemoteClient.h
widget/xremoteclient/nsRemoteClient.h
--- a/browser/components/nsBrowserContentHandler.js
+++ b/browser/components/nsBrowserContentHandler.js
@@ -337,16 +337,96 @@ nsBrowserContentHandler.prototype = {
     if (cmdLine.handleFlag("browser", false)) {
       // Passing defaultArgs, so use NO_EXTERNAL_URIS
       openWindow(null, this.chromeURL, "_blank",
                  "chrome,dialog=no,all" + this.getFeatures(cmdLine),
                  this.defaultArgs, NO_EXTERNAL_URIS);
       cmdLine.preventDefault = true;
     }
 
+    try {
+      var remoteCommand = cmdLine.handleFlagWithParam("remote", true);
+    }
+    catch (e) {
+      throw NS_ERROR_ABORT;
+    }
+
+    if (remoteCommand != null) {
+      try {
+        var a = /^\s*(\w+)\(([^\)]*)\)\s*$/.exec(remoteCommand);
+        var remoteVerb;
+        if (a) {
+          remoteVerb = a[1].toLowerCase();
+          var remoteParams = [];
+          var sepIndex = a[2].lastIndexOf(",");
+          if (sepIndex == -1)
+            remoteParams[0] = a[2];
+          else {
+            remoteParams[0] = a[2].substring(0, sepIndex);
+            remoteParams[1] = a[2].substring(sepIndex + 1);
+          }
+        }
+
+        switch (remoteVerb) {
+        case "openurl":
+        case "openfile":
+          // openURL(<url>)
+          // openURL(<url>,new-window)
+          // openURL(<url>,new-tab)
+
+          // First param is the URL, second param (if present) is the "target"
+          // (tab, window)
+          var url = remoteParams[0];
+          var target = nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW;
+          if (remoteParams[1]) {
+            var targetParam = remoteParams[1].toLowerCase()
+                                             .replace(/^\s*|\s*$/g, "");
+            if (targetParam == "new-tab")
+              target = nsIBrowserDOMWindow.OPEN_NEWTAB;
+            else if (targetParam == "new-window")
+              target = nsIBrowserDOMWindow.OPEN_NEWWINDOW;
+            else {
+              // The "target" param isn't one of our supported values, so
+              // assume it's part of a URL that contains commas.
+              url += "," + remoteParams[1];
+            }
+          }
+
+          var uri = resolveURIInternal(cmdLine, url);
+          handURIToExistingBrowser(uri, target, cmdLine);
+          break;
+
+        case "xfedocommand":
+          // xfeDoCommand(openBrowser)
+          if (remoteParams[0].toLowerCase() != "openbrowser")
+            throw NS_ERROR_ABORT;
+
+          // Passing defaultArgs, so use NO_EXTERNAL_URIS
+          openWindow(null, this.chromeURL, "_blank",
+                     "chrome,dialog=no,all" + this.getFeatures(cmdLine),
+                     this.defaultArgs, NO_EXTERNAL_URIS);
+          break;
+
+        default:
+          // Somebody sent us a remote command we don't know how to process:
+          // just abort.
+          throw "Unknown remote command.";
+        }
+
+        cmdLine.preventDefault = true;
+      }
+      catch (e) {
+        Components.utils.reportError(e);
+        // If we had a -remote flag but failed to process it, throw
+        // NS_ERROR_ABORT so that the xremote code knows to return a failure
+        // back to the handling code.
+        throw NS_ERROR_ABORT;
+      }
+    }
+
     var uriparam;
     try {
       while ((uriparam = cmdLine.handleFlagWithParam("new-window", false))) {
         var uri = resolveURIInternal(cmdLine, uriparam);
         if (!shouldLoadURI(uri))
           continue;
         openWindow(null, this.chromeURL, "_blank",
                    "chrome,dialog=no,all" + this.getFeatures(cmdLine),
--- a/toolkit/components/remote/nsXRemoteService.cpp
+++ b/toolkit/components/remote/nsXRemoteService.cpp
@@ -35,16 +35,17 @@
 
 #include <X11/Xlib.h>
 #include <X11/Xatom.h>
 
 using namespace mozilla;
 
 #define MOZILLA_VERSION_PROP   "_MOZILLA_VERSION"
 #define MOZILLA_LOCK_PROP      "_MOZILLA_LOCK"
+#define MOZILLA_COMMAND_PROP   "_MOZILLA_COMMAND"
 #define MOZILLA_RESPONSE_PROP  "_MOZILLA_RESPONSE"
 #define MOZILLA_USER_PROP      "_MOZILLA_USER"
 #define MOZILLA_PROFILE_PROP   "_MOZILLA_PROFILE"
 #define MOZILLA_PROGRAM_PROP   "_MOZILLA_PROGRAM"
 #define MOZILLA_COMMANDLINE_PROP "_MOZILLA_COMMANDLINE"
 
 const unsigned char kRemoteVersion[] = "5.1";
 
@@ -55,26 +56,28 @@ const unsigned char kRemoteVersion[] = "
 #else
 #define TO_LITTLE_ENDIAN32(x) (x)
 #endif
 
 // Minimize the roundtrips to the X server by getting all the atoms at once
 static const char *XAtomNames[] = {
   MOZILLA_VERSION_PROP,
   MOZILLA_LOCK_PROP,
+  MOZILLA_COMMAND_PROP,
   MOZILLA_RESPONSE_PROP,
   MOZILLA_USER_PROP,
   MOZILLA_PROFILE_PROP,
   MOZILLA_PROGRAM_PROP,
   MOZILLA_COMMANDLINE_PROP
 };
 static Atom XAtoms[MOZ_ARRAY_LENGTH(XAtomNames)];
 
 Atom nsXRemoteService::sMozVersionAtom;
 Atom nsXRemoteService::sMozLockAtom;
+Atom nsXRemoteService::sMozCommandAtom;
 Atom nsXRemoteService::sMozResponseAtom;
 Atom nsXRemoteService::sMozUserAtom;
 Atom nsXRemoteService::sMozProfileAtom;
 Atom nsXRemoteService::sMozProgramAtom;
 Atom nsXRemoteService::sMozCommandLineAtom;
 
 nsXRemoteService * nsXRemoteService::sRemoteImplementation = 0;
 
@@ -174,17 +177,17 @@ bool
 nsXRemoteService::HandleNewProperty(XID aWindowId, Display* aDisplay,
                                     Time aEventTime,
                                     Atom aChangedAtom,
                                     nsIWeakReference* aDomWindow)
 {
 
   nsCOMPtr<nsIDOMWindow> window (do_QueryReferent(aDomWindow));
 
-  if (aChangedAtom == sMozCommandLineAtom) {
+  if (aChangedAtom == sMozCommandAtom || aChangedAtom == sMozCommandLineAtom) {
     // We got a new command atom.
     int result;
     Atom actual_type;
     int actual_format;
     unsigned long nitems, bytes_after;
     char *data = 0;
 
     result = XGetWindowProperty (aDisplay,
@@ -206,17 +209,21 @@ nsXRemoteService::HandleNewProperty(XID 
     if (result != Success)
       return false;
 
     // Failed to get the data off the window or it was the wrong type?
     if (!data || !TO_LITTLE_ENDIAN32(*reinterpret_cast<int32_t*>(data)))
       return false;
 
     // cool, we got the property data.
-    const char *response = HandleCommandLine(data, window, aEventTime);
+    const char *response = nullptr;
+    if (aChangedAtom == sMozCommandAtom)
+      response = HandleCommand(data, window, aEventTime);
+    else if (aChangedAtom == sMozCommandLineAtom)
+      response = HandleCommandLine(data, window, aEventTime);
 
     // put the property onto the window as the response
     XChangeProperty (aDisplay, aWindowId,
                      sMozResponseAtom, XA_STRING,
                      8, PropModeReplace,
                      (const unsigned char *)response,
                      strlen (response));
     XFree(data);
@@ -232,16 +239,71 @@ nsXRemoteService::HandleNewProperty(XID 
     // someone locked the window
     return true;
   }
 
   return false;
 }
 
 const char*
+nsXRemoteService::HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
+                                uint32_t aTimestamp)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsICommandLineRunner> cmdline
+    (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
+  if (NS_FAILED(rv))
+    return "509 internal error";
+
+  // 1) Make sure that it looks remotely valid with parens
+  // 2) Treat ping() immediately and specially
+
+  nsAutoCString command(aCommand);
+  int32_t p1, p2;
+  p1 = command.FindChar('(');
+  p2 = command.FindChar(')');
+
+  if (p1 == kNotFound || p2 == kNotFound || p1 == 0 || p2 < p1) {
+    return "500 command not parseable";
+  }
+
+  command.Truncate(p1);
+  command.Trim(" ", true, true);
+  ToLowerCase(command);
+
+  if (!command.EqualsLiteral("ping")) {
+    nsAutoCString desktopStartupID;
+    nsDependentCString cmd(aCommand);
+    FindExtensionParameterInCommand("DESKTOP_STARTUP_ID",
+                                    cmd, '\n',
+                                    &desktopStartupID);
+
+    const char* argv[3] = {"dummyappname", "-remote", aCommand};
+    rv = cmdline->Init(3, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
+    if (NS_FAILED(rv))
+      return "509 internal error";
+
+    if (aWindow)
+      cmdline->SetWindowContext(aWindow);
+
+    if (sRemoteImplementation)
+      sRemoteImplementation->SetDesktopStartupIDOrTimestamp(desktopStartupID, aTimestamp);
+
+    rv = cmdline->Run();
+    if (NS_ERROR_ABORT == rv)
+      return "500 command not parseable";
+    if (NS_FAILED(rv))
+      return "509 internal error";
+  }
+
+  return "200 executed command";
+}
+
+const char*
 nsXRemoteService::HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
                                     uint32_t aTimestamp)
 {
   nsresult rv;
 
   nsCOMPtr<nsICommandLineRunner> cmdline
     (do_CreateInstance("@mozilla.org/toolkit/command-line;1", &rv));
   if (NS_FAILED(rv))
@@ -311,14 +373,15 @@ nsXRemoteService::EnsureAtoms(void)
     return;
 
   XInternAtoms(mozilla::DefaultXDisplay(), const_cast<char**>(XAtomNames),
                ArrayLength(XAtomNames), False, XAtoms);
 
   int i = 0;
   sMozVersionAtom     = XAtoms[i++];
   sMozLockAtom        = XAtoms[i++];
+  sMozCommandAtom     = XAtoms[i++];
   sMozResponseAtom    = XAtoms[i++];
   sMozUserAtom        = XAtoms[i++];
   sMozProfileAtom     = XAtoms[i++];
   sMozProgramAtom     = XAtoms[i++];
   sMozCommandLineAtom = XAtoms[i++];
 }
--- a/toolkit/components/remote/nsXRemoteService.h
+++ b/toolkit/components/remote/nsXRemoteService.h
@@ -36,27 +36,31 @@ protected:
                                     nsIWeakReference* aDomWindow);
     
     void XRemoteBaseStartup(const char *aAppName, const char *aProfileName);
 
     void HandleCommandsFor(Window aWindowId);
     static nsXRemoteService *sRemoteImplementation;
 private:
     void EnsureAtoms();
+    static const char* HandleCommand(char* aCommand, nsIDOMWindow* aWindow,
+                                     uint32_t aTimestamp);
+
     static const char* HandleCommandLine(char* aBuffer, nsIDOMWindow* aWindow,
                                          uint32_t aTimestamp);
 
     virtual void SetDesktopStartupIDOrTimestamp(const nsACString& aDesktopStartupID,
                                                 uint32_t aTimestamp) = 0;
 
     nsCString mAppName;
     nsCString mProfileName;
 
     static Atom sMozVersionAtom;
     static Atom sMozLockAtom;
+    static Atom sMozCommandAtom;
     static Atom sMozResponseAtom;
     static Atom sMozUserAtom;
     static Atom sMozProfileAtom;
     static Atom sMozProgramAtom;
     static Atom sMozCommandLineAtom;
 };
 
 #endif // NSXREMOTESERVICE_H
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -1604,16 +1604,76 @@ DumpVersion()
     printf("%s ", gAppData->vendor);
   printf("%s %s", gAppData->name, gAppData->version);
   if (gAppData->copyright)
       printf(", %s", gAppData->copyright);
   printf("\n");
 }
 
 #ifdef MOZ_ENABLE_XREMOTE
+// use int here instead of a PR type since it will be returned
+// from main - just to keep types consistent
+static int
+HandleRemoteArgument(const char* remote, const char* aDesktopStartupID)
+{
+  nsresult rv;
+  ArgResult ar;
+
+  const char *profile = 0;
+  nsAutoCString program(gAppData->name);
+  ToLowerCase(program);
+  const char *username = getenv("LOGNAME");
+
+  ar = CheckArg("p", false, &profile);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: argument -p requires a profile name\n");
+    return 1;
+  }
+
+  const char *temp = nullptr;
+  ar = CheckArg("a", false, &temp);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n");
+    return 1;
+  } else if (ar == ARG_FOUND) {
+    program.Assign(temp);
+  }
+
+  ar = CheckArg("u", false, &username);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n");
+    return 1;
+  }
+
+  XRemoteClient client;
+  rv = client.Init();
+  if (NS_FAILED(rv)) {
+    PR_fprintf(PR_STDERR, "Error: Failed to connect to X server.\n");
+    return 1;
+  }
+
+  nsXPIDLCString response;
+  bool success = false;
+  rv = client.SendCommand(program.get(), username, profile, remote,
+                          aDesktopStartupID, getter_Copies(response), &success);
+  // did the command fail?
+  if (NS_FAILED(rv)) {
+    PR_fprintf(PR_STDERR, "Error: Failed to send command: %s\n",
+               response ? response.get() : "No response included");
+    return 1;
+  }
+
+  if (!success) {
+    PR_fprintf(PR_STDERR, "Error: No running window found\n");
+    return 2;
+  }
+
+  return 0;
+}
+
 static RemoteResult
 RemoteCommandLine(const char* aDesktopStartupID)
 {
   nsresult rv;
   ArgResult ar;
 
   nsAutoCString program(gAppData->remotingName);
   ToLowerCase(program);
@@ -3591,21 +3651,31 @@ XREMain::XRE_mainStartup(bool* aExitFlag
     if (mDisableRemote) {
       newInstance = true;
     } else {
       e = PR_GetEnv("MOZ_NEW_INSTANCE");
       newInstance = (e && *e);
     }
   }
 
+  const char* xremotearg;
+  ArgResult ar = CheckArg("remote", true, &xremotearg);
+  if (ar == ARG_BAD) {
+    PR_fprintf(PR_STDERR, "Error: -remote requires an argument\n");
+    return 1;
+  }
+  const char* desktopStartupIDPtr =
+    mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get();
+  if (ar) {
+    *aExitFlag = true;
+    return HandleRemoteArgument(xremotearg, desktopStartupIDPtr);
+  }
+
   if (!newInstance) {
     // Try to remote the entire command line. If this fails, start up normally.
-    const char* desktopStartupIDPtr =
-      mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get();
-
     RemoteResult rr = RemoteCommandLine(desktopStartupIDPtr);
     if (rr == REMOTE_FOUND) {
       *aExitFlag = true;
       return 0;
     }
     else if (rr == REMOTE_ARG_BAD)
       return 1;
   }
--- a/widget/xremoteclient/XRemoteClient.cpp
+++ b/widget/xremoteclient/XRemoteClient.cpp
@@ -21,16 +21,17 @@
 #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_COMMAND_PROP   "_MOZILLA_COMMAND"
 #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) \
@@ -56,16 +57,17 @@ static int (*sOldHandler)(Display *, XEr
 static bool sGotBadWindow;
 
 XRemoteClient::XRemoteClient()
 {
   mDisplay = 0;
   mInitialized = false;
   mMozVersionAtom = 0;
   mMozLockAtom = 0;
+  mMozCommandAtom = 0;
   mMozResponseAtom = 0;
   mMozWMStateAtom = 0;
   mMozUserAtom = 0;
   mLockData = 0;
   if (!sRemoteLm)
     sRemoteLm = PR_NewLogModule("XRemoteClient");
   PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::XRemoteClient"));
 }
@@ -76,16 +78,17 @@ XRemoteClient::~XRemoteClient()
   if (mInitialized)
     Shutdown();
 }
 
 // Minimize the roundtrips to the X-server
 static const char *XAtomNames[] = {
   MOZILLA_VERSION_PROP,
   MOZILLA_LOCK_PROP,
+  MOZILLA_COMMAND_PROP,
   MOZILLA_RESPONSE_PROP,
   "WM_STATE",
   MOZILLA_USER_PROP,
   MOZILLA_PROFILE_PROP,
   MOZILLA_PROGRAM_PROP,
   MOZILLA_COMMANDLINE_PROP
 };
 static Atom XAtoms[ARRAY_LENGTH(XAtomNames)];
@@ -105,16 +108,17 @@ XRemoteClient::Init()
 
   // get our atoms
   XInternAtoms(mDisplay, const_cast<char**>(XAtomNames),
                ARRAY_LENGTH(XAtomNames), False, XAtoms);
 
   int i = 0;
   mMozVersionAtom  = XAtoms[i++];
   mMozLockAtom     = XAtoms[i++];
+  mMozCommandAtom  = XAtoms[i++];
   mMozResponseAtom = XAtoms[i++];
   mMozWMStateAtom  = XAtoms[i++];
   mMozUserAtom     = XAtoms[i++];
   mMozProfileAtom  = XAtoms[i++];
   mMozProgramAtom  = XAtoms[i++];
   mMozCommandLineAtom = XAtoms[i++];
 
   mInitialized = true;
@@ -135,44 +139,72 @@ XRemoteClient::Shutdown (void)
   mDisplay = 0;
   mInitialized = false;
   if (mLockData) {
     free(mLockData);
     mLockData = 0;
   }
 }
 
+nsresult
+XRemoteClient::SendCommand (const char *aProgram, const char *aUsername,
+                            const char *aProfile, const char *aCommand,
+                            const char* aDesktopStartupID,
+                            char **aResponse, bool *aWindowFound)
+{
+  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
+
+  return SendCommandInternal(aProgram, aUsername, aProfile,
+                             aCommand, 0, nullptr,
+                             aDesktopStartupID,
+                             aResponse, aWindowFound);
+}
+
+nsresult
+XRemoteClient::SendCommandLine (const char *aProgram, const char *aUsername,
+                                const char *aProfile,
+                                int32_t argc, char **argv,
+                                const char* aDesktopStartupID,
+                                char **aResponse, bool *aWindowFound)
+{
+  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
+
+  return SendCommandInternal(aProgram, aUsername, aProfile,
+                             nullptr, argc, argv,
+                             aDesktopStartupID,
+                             aResponse, aWindowFound);
+}
+
 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)
+XRemoteClient::SendCommandInternal(const char *aProgram, const char *aUsername,
+                                   const char *aProfile, const char *aCommand,
+                                   int32_t argc, char **argv,
+                                   const char* aDesktopStartupID,
+                                   char **aResponse, bool *aWindowFound)
 {
-  PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommandLine"));
-
   *aWindowFound = false;
+  bool isCommandLine = !aCommand;
 
   // FindBestWindow() iterates down the window hierarchy, so catch X errors
   // when windows get destroyed before being accessed.
   sOldHandler = XSetErrorHandler(HandleBadWindow);
 
-  Window w = FindBestWindow(aProgram, aUsername, aProfile);
+  Window w = FindBestWindow(aProgram, aUsername, aProfile, isCommandLine);
 
   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
@@ -186,18 +218,24 @@ XRemoteClient::SendCommandLine (const ch
 
     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 (isCommandLine) {
+        rv = DoSendCommandLine(w, argc, argv, aDesktopStartupID, aResponse,
+                               &destroyed);
+      }
+      else {
+        rv = DoSendCommand(w, aCommand, aDesktopStartupID, aResponse,
+                           &destroyed);
+      }
 
       // if the window was destroyed, don't bother trying to free the
       // lock.
       if (!destroyed)
           FreeLock(w); // doesn't really matter what this returns
 
     }
   }
@@ -405,17 +443,18 @@ XRemoteClient::GetLock(Window aWindow, b
             (unsigned int) aWindow));
   }
 
   return rv;
 }
 
 Window
 XRemoteClient::FindBestWindow(const char *aProgram, const char *aUsername,
-                              const char *aProfile)
+                              const char *aProfile,
+                              bool aSupportsCommandLine)
 {
   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.
@@ -450,17 +489,17 @@ XRemoteClient::FindBestWindow(const char
                                     &data_return);
 
     if (!data_return)
       continue;
 
     double version = PR_strtod((char*) data_return, nullptr);
     XFree(data_return);
 
-    if (!(version >= 5.1 && version < 6))
+    if (aSupportsCommandLine && !(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
@@ -594,16 +633,56 @@ XRemoteClient::FreeLock(Window aWindow)
       return NS_ERROR_FAILURE;
   }
 
   if (data)
       XFree(data);
   return NS_OK;
 }
 
+nsresult
+XRemoteClient::DoSendCommand(Window aWindow, const char *aCommand,
+                             const char* aDesktopStartupID,
+                             char **aResponse, bool *aDestroyed)
+{
+  *aDestroyed = false;
+
+  PR_LOG(sRemoteLm, PR_LOG_DEBUG,
+     ("(writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n",
+      aCommand, (unsigned int) aWindow));
+
+  // We add the DESKTOP_STARTUP_ID setting as an extra line of
+  // the command string. Firefox ignores all lines but the first.
+  static char desktopStartupPrefix[] = "\nDESKTOP_STARTUP_ID=";
+
+  int32_t len = strlen(aCommand);
+  if (aDesktopStartupID) {
+    len += sizeof(desktopStartupPrefix) - 1 + strlen(aDesktopStartupID);
+  }
+  char* buffer = (char*)malloc(len + 1);
+  if (!buffer)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  strcpy(buffer, aCommand);
+  if (aDesktopStartupID) {
+    strcat(buffer, desktopStartupPrefix);
+    strcat(buffer, aDesktopStartupID);
+  }
+
+  XChangeProperty (mDisplay, aWindow, mMozCommandAtom, XA_STRING, 8,
+           PropModeReplace, (unsigned char *)buffer, len);
+
+  free(buffer);
+
+  if (!WaitForResponse(aWindow, aResponse, aDestroyed, mMozCommandAtom))
+    return NS_ERROR_FAILURE;
+  
+  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';
@@ -786,16 +865,16 @@ XRemoteClient::WaitForResponse(Window aW
     }
 
     else if (event.xany.type == PropertyNotify &&
              event.xproperty.window == aWindow &&
              event.xproperty.state == PropertyDelete &&
              event.xproperty.atom == aCommandAtom) {
       PR_LOG(sRemoteLm, PR_LOG_DEBUG,
              ("(server 0x%x has accepted "
-              MOZILLA_COMMANDLINE_PROP ".)\n",
+              MOZILLA_COMMAND_PROP ".)\n",
               (unsigned int) aWindow));
     }
     
   }
 
   return accepted;
 }
--- a/widget/xremoteclient/XRemoteClient.h
+++ b/widget/xremoteclient/XRemoteClient.h
@@ -10,44 +10,60 @@
 
 class XRemoteClient : public nsRemoteClient
 {
 public:
   XRemoteClient();
   ~XRemoteClient();
 
   virtual nsresult Init();
+  virtual nsresult SendCommand(const char *aProgram, const char *aUsername,
+                               const char *aProfile, const char *aCommand,
+                               const char* aDesktopStartupID,
+                               char **aResponse, bool *aSucceeded);
   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);
+                                   const char *aProfile,
+                                   bool aSupportsCommandLine);
+  nsresult     SendCommandInternal(const char *aProgram, const char *aUsername,
+                                   const char *aProfile, const char *aCommand,
+                                   int32_t argc, char **argv,
+                                   const char* aDesktopStartupID,
+                                   char **aResponse, bool *aWindowFound);
+  nsresult       DoSendCommand    (Window aWindow,
+                                   const char *aCommand,
+                                   const char* aDesktopStartupID,
+                                   char **aResponse,
+                                   bool *aDestroyed);
   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           mMozCommandAtom;
   Atom           mMozCommandLineAtom;
   Atom           mMozResponseAtom;
   Atom           mMozWMStateAtom;
   Atom           mMozUserAtom;
   Atom           mMozProfileAtom;
   Atom           mMozProgramAtom;
 
   char          *mLockData;
--- a/widget/xremoteclient/nsRemoteClient.h
+++ b/widget/xremoteclient/nsRemoteClient.h
@@ -18,43 +18,62 @@ class nsRemoteClient
 {
 public:
   /**
    * Initializes the client
    */
   virtual nsresult Init() = 0;
 
   /**
-   * Send a complete command line to a running instance.
+   * Sends a command to a running instance.
    *
    * @param aProgram This is the preferred program that we want to use
    * for this particular command.
    *
+   * @param aNoProgramFallback This boolean attribute tells the client
+   * code that if the preferred program isn't found that it should
+   * fail not send the command to another server.
+   *
    * @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 aCommand This is the command that is passed to the server.
+   * Please see the additional information located at:
+   * http://www.mozilla.org/unix/remote.html
+   * 
    * @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 SendCommand(const char *aProgram, const char *aUsername,
+                               const char *aProfile, const char *aCommand,
+                               const char* aDesktopStartupID,
+                               char **aResponse, bool *aSucceeded) = 0;
+
+  /**
+   * Send a complete command line to a running instance.
+   *
+   * @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
+   *
+   * @see sendCommand
+   * @param argc The number of command-line arguments.
    * 
    */
   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;
 };