CommandLine

Handling command line arguments with XULRunner

For Multiple Instances Application

It's fairly easy to retrieve application specific command line arguments in XULRunner when it's not a single instance application. An nsICommandLine object is passed as the first argument of the launched window:

Example

var cmdLine = window.arguments[0];
cmdLine = cmdLine.QueryInterface(Components.interfaces.nsICommandLine);
alert(cmdLine.handleFlagWithParam("test", false));

For single instance applications

Of course, for a single instance application (see toolkit.singletonWindowType for more information), the last example still applies the first time your application is launched. However, if you'd like to retrieve the latest command line arguments (to open a file for example), a possible solution is to create your own command line handler.

For the sake of simplicity, the proposed solution involves the observer service to notify observers that the updated arguments are available. A similar and better way to implement this solution is by defining argument specific handlers with registering and unregistering functions in your command line handler service. This has the advantage to accumulate unhandled arguments until a specific handler is added. This approach has been used in Songbird.

Example

Notice the changes since Gecko 2.0 (Component registration has moved into the manifest file).

Define your own Command Line Handler Component: components/clh.js

const nsISupports              = Components.interfaces.nsISupports;

const nsICategoryManager       = Components.interfaces.nsICategoryManager;
const nsIComponentRegistrar    = Components.interfaces.nsIComponentRegistrar;
const nsICommandLine           = Components.interfaces.nsICommandLine;
const nsICommandLineHandler    = Components.interfaces.nsICommandLineHandler;
const nsIFactory               = Components.interfaces.nsIFactory;
const nsIModule                = Components.interfaces.nsIModule;

const CLASS_ID = Components.ID("178cfbb6-503c-11dc-8314-0800200c9a66");
const CLASS_NAME = "ApplicationNameCLH";
const CONTRACT_ID = "@example.com/applicationname/clh;1";
const CLD_CATEGORY = "m-applicationname";

var appHandler = {
  /* nsISupports */
  QueryInterface : function clh_QI(aIID)
  {
    if (aIID.equals(nsICommandLineHandler) ||
        aIID.equals(nsIFactory) ||
        aIID.equals(nsISupports))
      return this;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  /* nsICommandLineHandler */
  handle : function clh_handle(aCmdLine)
  {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                    .getService(Components.interfaces.nsIObserverService);
    observerService.notifyObservers(aCmdLine, "commandline-args-changed", null);
  },

  helpInfo : "  -test <value>        A test attribute\n",

  /* nsIFactory */
  createInstance : function mdh_CI(aOuter, aIID)
  {
    if (aOuter != null) {
      throw Components.results.NS_ERROR_NO_AGGREGATION;
    }

    return this.QueryInterface(aIID);
  },

  lockFactory : function mdh_lock(aLock)
  {
    /* no-op */
  }
};

var appHandlerModule = {
  /* nsISupports */
  QueryInterface : function mod_QI(aIID)
  {
    if (aIID.equals(nsIModule) ||
        aIID.equals(nsISupports))
      return this;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  /* nsIModule */
  getClassObject : function mod_gch(aCompMgr, aCID, aIID)
  {
    if (aCID.equals(CLASS_ID))
      return appHandler.QueryInterface(aIID);

    throw components.results.NS_ERROR_FAILURE;
  },

  registerSelf : function mod_regself(aCompMgr, aFileSpec, aLocation, aType)
  {
    var compReg = aCompMgr.QueryInterface(nsIComponentRegistrar);

    compReg.registerFactoryLocation(CLASS_ID, CLASS_NAME, CONTRACT_ID,
                                    aFileSpec, aLocation, aType);

    var catMan = Components.classes["@mozilla.org/categorymanager;1"]
                           .getService(nsICategoryManager);
    catMan.addCategoryEntry("command-line-handler",
                            CLD_CATEGORY, CONTRACT_ID, true, true);
  },

  unregisterSelf : function mod_unreg(aCompMgr, aLocation, aType)
  {
    var compReg = aCompMgr.QueryInterface(nsIComponentRegistrar);
    compReg.unregisterFactoryLocation(CLASS_ID, aLocation);

    var catMan = Components.classes["@mozilla.org/categorymanager;1"]
                           .getService(nsICategoryManager);
    catMan.deleteCategoryEntry("command-line-handler", CLD_CATEGORY);
  },

  canUnload : function (aCompMgr)
  {
    return true;
  }
};

function NSGetModule(aCompMgr, aFileSpec)
{
  return appHandlerModule;
}

Create an observer that will get notified when arguments change: chrome/content/cmdline.js

function CommandLineObserver() {
  this.register();
}
CommandLineObserver.prototype = {
  observe: function(aSubject, aTopic, aData) {
     var cmdLine = aSubject.QueryInterface(Components.interfaces.nsICommandLine);
     var test = cmdLine.handleFlagWithParam("test", false);
     alert("test = " + test);
  },

  register: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                    .getService(Components.interfaces.nsIObserverService);
    observerService.addObserver(this, "commandline-args-changed", false);
  },

  unregister: function() {
    var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                    .getService(Components.interfaces.nsIObserverService);
    observerService.removeObserver(this, "commandline-args-changed");
  }
}

var observer = new CommandLineObserver();


// Because we haven't yet registered a CommandLineObserver when the application is
// launched the first time, we simulate a notification here.
var observerService = Components.classes["@mozilla.org/observer-service;1"]
                                 .getService(Components.interfaces.nsIObserverService);
observerService.notifyObservers(window.arguments[0], "commandline-args-changed", null);

addEventListener("unload", observer.unregister, false);

Finally, add a reference in your application window to the observer: chrome/content/window.xul

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>

<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        id="main" title="&window.title;" windowtype="xulmine"
        style="width: 300px; height: 350px;"
        persist="screenX screenY width height sizemode">
  <script type="application/javascript" src="cmdline.js" />
  ...
</window>

Original Document Information