XUL:Command Line Handling: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
m (Page was broken by the table. I have changed the table with the wiki syntax)
 
Line 93: Line 93:
Instead of treating the command-line handler as static information about which XUL window to open, it is a dynamic type that can respond to the "event" of starting the application. Arguments can start with -arg, --arg on *nix, /arg on windows; they will be normalized to -arg before handlers are called. If a unix arg is the form --arg=param, it will be split into two arguments -arg <param>. Handlers are registered with the category manager (currently, you have to register with a contractID prefix and a category, which is kinda silly) and would be run in alpha-order thusly:
Instead of treating the command-line handler as static information about which XUL window to open, it is a dynamic type that can respond to the "event" of starting the application. Arguments can start with -arg, --arg on *nix, /arg on windows; they will be normalized to -arg before handlers are called. If a unix arg is the form --arg=param, it will be split into two arguments -arg <param>. Handlers are registered with the category manager (currently, you have to register with a contractID prefix and a category, which is kinda silly) and would be run in alpha-order thusly:


<table class="data">
{| class="data"
<tr>
|-
   <th>category <th>entry <th>value <th>(description)
   !category !!entry !!value !!(description)
<tr>
|-
   <td rowspan="5">command-line-handler
   |rowspan="5"|command-line-handler
   <td>b-jsdebug <td>@mozilla.org/venkman/clh;1
   |b-jsdebug
   <td>Handles -venkman and -venkman-sync flags to debug JS, even during startup
  |@mozilla.org/venkman/clh;1
<tr>
   |Handles -venkman and -venkman-sync flags to debug JS, even during startup
   <td>c-extensions</td> <td>@mozilla.org/extension-manager/clh;1
|-
   <td>Handles -install-toolkit-extension -install-app-extension -install-profile-extension flags
   |c-extensions  
<tr>
  |@mozilla.org/extension-manager/clh;1
   <td>m-edit <td>@mozilla.org/composer/clh;1
   |Handles -install-toolkit-extension -install-app-extension -install-profile-extension flags
   <td>Handles -edit &lt;URLOrPath&gt;
|-
<tr>
   |m-edit  
   <td>m-irc <td>@mozilla.org/chatzilla/clh;1
  |@mozilla.org/composer/clh;1
   <td>Handles -irc or -chat flags
   |Handles -edit &lt;URLOrPath&gt;
<tr>
|-
   <td>y-final <td>@mozilla.org/app-startup/clh;1
   |m-irc  
   <td>If there is a bare URL/file on the command-line, this opens a browser window with that URI/file. If there are *no* windows open at this point, it opens a default browser window.
  |@mozilla.org/chatzilla/clh;1
</table>
   |Handles -irc or -chat flags
|-
   |y-final  
  |@mozilla.org/app-startup/clh;1
   |If there is a bare URL/file on the command-line, this opens a browser window with that URI/file. If there are *no* windows open at this point, it opens a default browser window.
 
|}


The important thing is that the <strong>same command-line handlers would be used for a remote invocation (DDE or xremote) as an initial startup</strong>. This means that we can get rid of all the app-specific code in the xremote service. Firefox can keep a backwards-compatibility shim to handle the -remote flag. This would give apps equal-opportunity and silent access to argument handling. This is very similar to the way win32 DDEremoting works now, but somewhat different from the xremote model.
The important thing is that the <strong>same command-line handlers would be used for a remote invocation (DDE or xremote) as an initial startup</strong>. This means that we can get rid of all the app-specific code in the xremote service. Firefox can keep a backwards-compatibility shim to handle the -remote flag. This would give apps equal-opportunity and silent access to argument handling. This is very similar to the way win32 DDEremoting works now, but somewhat different from the xremote model.

Latest revision as of 00:51, 15 May 2007

Command-Line Handling in the xulrunner

XUL apps intended to be run by the xulrunner need to be able to flexibly handle command-line arguments. The current system does not allow arbitrary processing by command-line handlers, it is limited to opening XUL windows. Apps (especially utility apps) may wish to exectute arbitrary code and then exit, without ever opening a window.

Command-line handling is currently done through the nsICmdLineService and nsICommandLineHandler interfaces. As you can see, the nsICommandLineHandler interface is so weird that we use a C++ macro to implement it for most cases.

Instead, the command-line handling should be handled through a callback-like interface. Posit a set of interfaces:

[scriptable...]
interface nsICommandLine : nsISupports
{
  /**
   * Number of arguments in the command-line.
   */
  readonly attribute signed short length;

  /**
   * Get an argument from the array of command-line arguments.
   * 
   * @param aIndex The argument to retrieve. This index is 0-based, and does not include
   *               the application name.
   * @return       The indexth argument.
   */
  AUTF8String getArgument(in signed short aIndex);

  /**
   * Find a command-line flag. Flags can begin with a hyphen or double-hyphen,
   * and on Windows/OS2 with a forward-slash.
   * @param aFlag          The flag to locate.
   * @param aCaseSensitive If true, the case of the flag is significant.
   * @return               The position of that flag in the command-line, or -1 if not found.
   */
  signed short findFlag(in AUTF8String aFlag, in boolean aCaseSensitive);

  /**
   * Remove arguments from the command-line. This is normally done when a handler has
   * processed the arguments.
   * @param aStart Index to begin removing arguments.
   * @param aEnd   Index to stop removing arguments.
   */
  void removeArguments(in signed short aStart, in signed short aEnd);

  /**
   * The type of command-line being handled:
   * STATE_INITIAL_LAUNCH is the first launch of the app.
   * STATE_REMOTE_AUTO is a remote command-line automatically redirected to this instance.
   * STATE_REMOTE_EXPLICIT is a remote command-line explicitly requested using winDDE/xremote.
   */
  readonly attribute unsigned short state;

  const unsigned short STATE_INITIAL_LAUNCH  = 0;
  const unsigned short STATE_REMOTE_AUTO     = 1;
  const unsigned short STATE_REMOTE_EXPLICIT = 2; 

  /**
   * The "working directory" for the command-line. A remote command-line may have a different
   * working directory than the current process, so we make sure we remote that also.
   */
  readonly attribute nsIFile workingDirectory;

  /**
   * Resolve a file-path argument into an nsIFile.
   */
  nsIFile resolveFile(in AUTF8String aArgument);

  /**
   * Resolve a URI argument.
   */
  nsIURI resolveURI(in AUTF8String aArgument);
};
interface nsICommandLineHandler : nsISupports
{
  /**
   * Handle the command-line. If the handler finds arguments that it understands, it
   * should perform the appropriate actions (such as opening a window) and remove
   * those arguments from the command-line array.
   *
   * @throws NS_ERROR_ABORT to immediately cease command-line handling
   *         (if this is STATE_INITIAL_LAUNCH, quits the app);
   *         NS_SUCCESS_COMMANDLINE_RESTART_XPCOM to restart the app;
   *         All other exceptions are silently ignored.
   */
  void handle(in nsICommandLine aCommandLine);

  /**
   * When the app is launched with the -help argument, this attribute
   * is queried and displayed to the user (on stdout).
   */
  readonly attribute AUTF8String helpText;
};

Instead of treating the command-line handler as static information about which XUL window to open, it is a dynamic type that can respond to the "event" of starting the application. Arguments can start with -arg, --arg on *nix, /arg on windows; they will be normalized to -arg before handlers are called. If a unix arg is the form --arg=param, it will be split into two arguments -arg <param>. Handlers are registered with the category manager (currently, you have to register with a contractID prefix and a category, which is kinda silly) and would be run in alpha-order thusly:

category entry value (description)
command-line-handler b-jsdebug @mozilla.org/venkman/clh;1 Handles -venkman and -venkman-sync flags to debug JS, even during startup
c-extensions @mozilla.org/extension-manager/clh;1 Handles -install-toolkit-extension -install-app-extension -install-profile-extension flags
m-edit @mozilla.org/composer/clh;1 Handles -edit <URLOrPath>
m-irc @mozilla.org/chatzilla/clh;1 Handles -irc or -chat flags
y-final @mozilla.org/app-startup/clh;1 If there is a bare URL/file on the command-line, this opens a browser window with that URI/file. If there are *no* windows open at this point, it opens a default browser window.

The important thing is that the same command-line handlers would be used for a remote invocation (DDE or xremote) as an initial startup. This means that we can get rid of all the app-specific code in the xremote service. Firefox can keep a backwards-compatibility shim to handle the -remote flag. This would give apps equal-opportunity and silent access to argument handling. This is very similar to the way win32 DDEremoting works now, but somewhat different from the xremote model.

We should also document a convention for the alphabetic letter prefixes, such that important early handlers such as venkman can reliably precede handlers that might need to be debugged.

The command-line-service should not be a service, but rather a per-startup passed to the handlers. You would have a separate "command-line-service" for remote invocation of an app. The service would QI to nsIArray/MutableArray, allowing handlers to manipulate the argument array. This may cause some issues with GTK/X startup, which needs access to the original argv/argc at startup. This is already a thorn in the side of embeddors who have already initialized GTK/X and don't want our widget code trying to do it again.

A sample command-line handler might look like this (with a bit more error-checking, hopefully):

function handle(commandLine) {
  var ww = C[...].getService(I.nsIWindowWatcher);

  var found;
  while ((found = commandLine.findFlag("edit", false)) >= 0) {
    var uriarg = commandLine.getArgument(found + 1);
    var theuri = commandLine.resolveURI(uriarg);

    ww.openWindow(null, "chrome://editor/content/", null, "chrome", null, theuri);
    commandLine.removeArguments(found, found + 1);
  }

  found = commandLine.findFlag("editor", false);
  if (found >= 0) {
    ww.openWindow(null, "chrome://editor/content/", null, "chrome", null, null);
  }
}


Comments XUL:Axel Hecht

I'm not so happy about the numbers, like the

 commandLine.getArgument(found + 1);

and

 commandLine.removeArguments(found, found + 1);

I'd prefer to see just found there instead of found + 1.

bsmedberg says: Why, and how would it work? getArgument(found) returns "-arg" and getArgument(found + 1) return "param".

Could the gtk startup just be an additional command-line handler, disabled in embedding?

bsmedberg says: no. The startup sequencing does not allow for that.

Callek says: I agree with Axel on the ugliness of the look of that. Could we perhaps add a method similar to getArgumentValue(found) which would do what found + 1 does now in this case?

Neil: maybe a function void extractArguments(in string flag, in unsigned long max, out unsigned long count, [retval, array, size_is(count)] out string args); which would remove the extracted args or return null on failure.

Neil: Is there need for a case where a handler doesn't want to open a window, doesn't want to suppress other handlers but may or may not want to suppress default window opening? Currently some of the handlers have hacks that say "don't open a default window if we are remoting".