Windows and Dialogs
- Feature Owner
 - Daniel Matejka
 
Dialogs are alive and well in Mozilla, and this document attempts to explain their current status.
Scope
This document is not intended to be a comprehensive syntax or reference manual. With care and time, it should grow into a useful starting place for making windows and writing dialogs. The author thinks that feedback on what's missing from the document would help build a better document, so the quality of this thing is in the reader's hands. Keep that in mind while you're cursing me.
In the meantime, this document also serves as a roadmap of XPFE's dialog story. Interested parties would please review it to determine whether the package we plan to deliver will meet their needs.
Direction
Current XPFE design goals state that dialogs get no special treatment; they're just windows like any other. Windows can have parent windows, and be displayed modally, and have control callbacks, and so behave like dialogs. But our goal is that the same application code that instantiates a browser window will serve equally to instantiate a dialog window. The difference lies in the window content, and a couple of parameters controlling modality and that sort of thing.
JavaScript vs C++
Currently, all window control mechanisms flow through JavaScript. That is, any capabilities a window may have besides taking up space on the desktop are specified in the XUL describing the window, and the binding between the window content and the application is done through JavaScript in the XUL. That binding can be very short: it can fall through to application C code very quickly, but JavaScript will be part of the process. (Note that we have not yet run into any threading problems with this model, but we kind of expect to.)
JavaScript is easily added to a window: just include it in the XUL window description and follow a short but peculiar dance to hook it up. Hooking up C code involves writing a C++/JavaScript interface, and calling it from JavaScript. Currently, for historical reasons, there is an unfortunate dual standard for building these interfaces: DOM IDL and XPConnect. Though AppCores are currently built using the former, XPConnect is the preferred means for writing additional extensions.
Make a Window
There are two ways to make a window or dialog: directly through C++ and
        from JavaScript. The resulting window is an instance of nsWebShellWindow.
        This means it currently contains a hulking bunch of hackery for hooking
        up browser-window specific contents. The overhead is reduced from its
        former grandeur, but still slows window opening. We are still working
        on reducing the burden of opening a new window.
Javascript
window.open will open a XUL window if given a XUL URL to
        load. It will open the given URL as the contents of a browser window,
        unless the chrome feature is specified in window.open's
        third ("features") parameter. Mozilla supports an extended form
        of window.open which can be used to pass parameters back
        and forth to the dialog window. It's named window.openDialog,
        reasonably enough, for now. Both window.openDialog and chrome
        are described in JavaScript Extensions.
Another JavaScript interface is a Mozilla AppCore named ToolkitCore.
        It contains a handful of windowing utility functions which were more useful
        before window.open was working properly. ToolkitCore
        is deprecated: all the functionality it provides can be reached through
        standard JavaScript, and much functionality provided by standard JavaScript
        is missing from ToolkitCore.
function MakeDialog() {
   var newWindow = window.open("madedialog.xul",
                     "itsname", "chrome");
}
 or, alternatively (you'll see this in the code, but don't emulate it),
function MakeDialog() {
    var toolkitCore = XPAppCoresManager.Find("ToolkitCore");
    if (!toolkitCore) {
      toolkitCore = new ToolkitCore();
      if (toolkitCore)
        toolkitCore.Init("ToolkitCore");
    }
    if (toolkitCore)
      toolkitCore.ShowWindow("madedialog.xul",
          window);
}
      An XPAppCoresManager is pre-constructed; you can access
        it without any more preparation than shown above. ToolkitCore
        is not; it requires that small bit of preparation.
      
C++
There are two interfaces for opening new windows from C code. One is really a private interface, providing only basic window opening functionality, and in general should not be used. In fact, its mention in this document should probably be removed. The other is more general, does more interesting things, and is your friend.
nsIAppShellService::CreateTopLevelWindow
The pseudo-private C++ interface is nsIAppShellService::CreateTopLevelWindow().
        It handles the basic task of opening a window, but neglects to do a lot
        of important things like hooking the window up properly to JavaScript.
        It works like this:
  nsresult              rv;
  nsIAppShellService    *appShell;
  nsIURL                *url;
  nsIWebShellWindow     *parent, *window;
  nsIXULWindowCallbacks *callbacks;
  PRInt32               width, height;
  window = nsnull;
  rv = nsServiceManager::GetService(kAppShellServiceCID,
         kIAppShellServiceIID,
         (nsISupports**) &appShell);
  if (NS_SUCCEEDED(rv)) {
    appShell->CreateTopLevelWindow(parent, url, PR_TRUE, PR_TRUE,
                NS_CHROME_ALL_CHROME, callbacks, width, height,
                &window);
    nsServiceManager::ReleaseService(kAppShellServiceCID,
                appShell);
  }
      url is an URL describing the contents of the window. window
        is returned from the function, set to the created window. See the code
        for descriptions of the other parameters.
At this time, window descriptions must be loaded from an URL. In future releases, there may additionally be methods for loading windows from a stream. Streams would disengage the toolkit further from the source of the window description, and would be our method for creating windows whose XUL is calculated at runtime, rather than being distributed as a file. But we're uncertain whether this will truly be necessary, given current architecture.
nsIDOMWindow::OpenDialog
The primary C++ interface is nsIDOMWindow::OpenDialog. It
        has the advantage that it behaves exactly like the JavaScript window.openDialog
        function, being the same function, after all.
The close association between this function and JavaScript places constraints on its usage. Any parameters sent to this function must be ready for use in JavaScript handlers built into the new window, and so must be fitted into JavaScript wrappers. There must also be a "parent" window available to do the window opening and provide a JavaScript context.
The function to wrap a series of arguments is called JS_PushArguments,
        and is defined in jsapi.h. Its mysterious third argument
        is explained in that same header file. The sample code below will call
        OpenDialog, given an nsIDOMWindow to work with.
        OpenDialog is explained below.
        Its first three arguments are standard Open arguments. The
        remainder are parameters for the new window. That new window will have
        a property named arguments. It's an array, and arguments[0]
        is the string "I was born a C string", while arguments[1]
        is the number 298.
DoOpenDialog(nsIDOMWindow* aParent) {
  if (!aParent)
    return NS_ERROR_NULL_POINTER;
  nsresult rv = NS_ERROR_FAILURE;
  JSContext *jscx = 0;
  nsIDOMWindow *newWindow;
  // get the parent window's JS Context (this is not really a blessed thing
  // to do, but I believe it's all we have. it wants to be a real interface
  // someday.)
  nsCOMPtr<nsIScriptGlobalObject> scriptGlobalObj = do_QueryInterface(aParent);
  if (scriptGlobalObj) {
    nsCOMPtr<nsIScriptContext> scriptcx;
    scriptGlobalObj->GetContext(getter_AddRefs(scriptcx));
    if (scriptcx)
      jscx = (JSContext *) scriptcx->GetNativeContext();
  }
  // if everything went as planned...
  if (jscx) {
    // build a JavaScript "arguments" array for the OpenDialog function
    void *mark;
    jsval *argv;
    argv = JS_PushArguments(jscx, &mark, "ssssu",
      "resource://res/samples/paramdialog.xul",
      "windowName",
      "chrome",
      "I was born a C string",
      (uint32) 298);
    // if that worked, call the function.
    if (argv) {
      aParent->OpenDialog(jscx, argv, 5, &newWindow);
      JS_PopArguments(jscx, mark);
      rv = NS_OK;
    }
  }
  return rv;
}
      Load a Window
Between making and showing the window, there is a callback. The resulting
        nsWebShellWindow comes with an associated DOM content model.
        Any initial control settings or content changes which must be made before
        the window is actually shown can be done at the time of this callback.
This callback is any JavaScript specified as the value of the onload
        attribute of the <window> element. In the example below,
        the window start tag is declared
      <window ... onload="Startup()">
During the onload callback, dialog authors are free to alter
        control settings from JavaScript:
  function Startup() {
    // yellowize and check the "ow" button
    document.bgColor = "yellow";
    var checkbox = document.getElementByID("ow");
    if (checkbox)
      checkbox.checked = true;
  }
      Except that not all properties are hooked up yet. The change to background color in the example won't do anything. But the important example does work!
Dialog preprocessing can also be done in C by defining an XPIDL component,
        and loading and calling it from the Startup() method. See
        the scriptable XPIDL documentation for
        details.
Debrief a Window
After the dialog has run -- a dismissal button has been clicked, say -- dialog users will need a callback in which to query the current control settings. As always, this will be done by walking the DOM content model, and accessed through a JavaScript hook. There is no explicit debriefing hook; we've found the event handlers for the dismissal buttons sufficient so far, with one exception. In the future we will need a means to capture a pending window close resulting from the user hitting an OS-level close widget.
Data can be passed back to the calling window in more than one way. The
        dialog window and opener can agree to share information as properties
        added to the JavaScript dialog window object. Alternatively, the two windows
        could agree that the dialog window will call some function of its opener.
        A third way could be for the opener to use window.openDialog
        to pass parameters by reference to the dialog window. Any changes made
        by the dialog to these parameters would be visible in the opener window.
        See JavaScript Extensions or the examples
        for details. Two files in the source code at mozilla/xpfe/browser/samples/dexparam*.xul
        are a kind of test suite for parameter passing, and consequently spell
        out more details than will this document.
Note at time of writing, a dialog window, once closed, cannot be successfully
        accessed from JavaScript through the variable which was returned by window.open.
        This is a known bug, and wants fixing.
Intrinsic Sizing
Windows in Mozilla are also dialogs. Unlike browser windows, it is often
        best to fit a dialog window around its contents. To make this work properly,
        the XUL must be carefully designed with element sizing hints and most
        likely boxes. If a window's contents
        are properly designed, their intrinsic size can be determined, and the
        window sized to fit them. Such dialogs keep their looks after trauma like
        internationalization and content changes through DOM manipulation.
Windows opened as chrome are given intrinsic
        sizing by default. This can be overridden by specifying an explicit size;
        a width attribute on the XUL window element,
        for example. A window can also be given intrinsic sizing using nsIAppShellService::CreateTopLevelWindow
        by specifying a height and width of NS_SIZETOCONTENT. A window
        can be wrapped around its contents at any time by calling its sizeToContent
        function.
Intrinsic sizing currently does not work for HTML content. It must be laid out using XUL, which, unlike HTML, can have a notion of its own optimal size.
Javascript Extensions
Mozilla has found it necessary to support a few extensions to standard JavaScript. These are, well, nonstandard. They are therefore subject to change while the program is under development, and will not work with other browser applications. Extensions used by the windowing system are described below. Other systems' extensions are described in their respective documents.
window.open
      open(URL[, windowName [, windowFeatures]])
The standard syntax still applies. See a JavaScript reference for details.
        Mozilla, however, understands a few extensions to windowFeatures
        (and doesn't yet understand all the latest standard features). The treatment
        of these extensions is somewhat confused in the name of backward compatibility
        and because of the sometimes uneasy interplay between standards-compliant
        open and the more freewheeling openDialog.
- New Features
          
titlebar- The window can be created with or without a titlebar.close- The window can be created with or without a close widget.chrome- Normally, the URL given towindow.openis treated as a content URL. That is, Mozilla generates a browser window and loads the given URL into its content area, aswindow.openhas always behaved. However, the presence of achromeflag in thewindowFeaturesparameter will cause the given URL to be treated as the window chrome, itself. It will be treated as the top-level window contents; it will not be wrapped in a browser window. The window will also be sized to wrap its contents.dependent- The new window belongs to the calling window, on operating systems that support this behaviour. This is the kind of window that is minimized along with its parent/owner; a "popup" or "transient" window, or whatever word your OS has chosen to use.dialog- Use a dialog-style window border.modal- The window will be run modally. The call towindow.openwill not return until the user has dismissed the window. Note thatmodalimpliesdependent.
 - Misunderstood Features
          
- The following features are not implemented at time of writing:
              all z-ordering features (
alwaysLowered,alwaysRaised,z-lock),as well as copyhistory,hotkeys,screenX,screenY, andscrollbars. 
 - The following features are not implemented at time of writing:
              all z-ordering features (
 - Default Behaviour - As always, if no parameter is passed, all chrome
          is assumed turned on. Exceptions are the behaviour flags 
chrome,dependent,dialogandmodal: these are not turned on unless done explicitly. If any string at all, even a zero-length string, is given in thefeaturesparameter, any features not explicitly mentioned are assumed off.titlebarandcloseare the two exceptions: they are not considered off unless explicitly mentioned ("titlebar=no"), since anything else would break extant script. 
window.openDialog
      openDialog(URL[, windowName [, windowFeatures [, args]]])
window.openDialog is an extension to window.open.
        It behaves the same, except that it can optionally take one or more parameters
        past windowFeatures, and windowFeatures itself
        is treated a little differently.
The optional parameters, if present, will be bundled up in a JavaScript
        array and added to the newly created window as a property named arguments.
        They may be referenced in the JavaScript of the window at any time, including
        during the execution of an onload handler. These parameters
        may be used, then, to pass arguments to and from the dialog window.
A dialog summoned up
 openDialog("http://zzz.xul", "dlg", "",
        "pizza", 6.98)
could reference the "pizza" string as window.arguments[0],
        and the number as window.arguments[1].
openDialog treats the features parameter exactly
        as does open, with the following differences.
- New Features
          
all- Initially activates (or deactivates ("all=no")) all chrome (except the behaviour flagschrome,dialogandmodal). These can be overridden (so "menubar=no,all" turns on all chrome except the menubar.) This feature is explicitly ignored bywindow.open.window.openDialogfinds it useful because of its different default assumptions.
 - Default behaviour - The 
chromeanddialogfeatures are always assumed on, unless explicitly turned off ("chrome=no").openDialogtreats the absence of the features parameter as doeswindow.open, (that is, an empty string sets all features to off) exceptchromeanddialog, which default to on. If thefeaturesparameter is a zero-length string, or contains only one or more of the behaviour features (chrome,dependent,dialogandmodal) the chrome features are assumed "OS' choice." That is, window creation code is not given specific instructions, but is instead allowed to select the chrome that best fits a dialog on that operating system. 
window.sizeToContent
      sizeToContent()
This reflows the window's content, and wraps the window around those
        contents. It functions even on fixed size windows (that is, windows with
        the "resizable" flag turned off.) This function's intended use
        is with intrinsically sized windows (those which are designed to wrap
        themselves to fit their content) after changing that content, presumably
        during the execution of an onload handler. See notes on intrinsic
        sizing elsewhere.
window._content
Standard JavaScript assumes a standard browser window built on immutable contents: a menubar, a set of toolbars, and the point of this feature, a single HTML content area. Windows described by XUL can have any or none of any of these things. If the window is to include "content," that is, a self-contained rectangle capable of displaying the contents of an arbitrary URL, then it must contain one (or more) content area(s). An example of a window that includes a content area is a browser window. An example of a window that does not is a simple dialog.
Content areas are embedded in the window UI using an iframe.
        (Or a browser or editor tag; more readable synonyms for iframe.)
        The best documentation on this is, unfortunately, the XUL files in the
        Mozilla source. An iframe corresponds to a JavaScript window
        object. Note that the window UI (or chrome) is itself (XUL) content, described
        by a URL. It corresponds to a window object, as well. A content
        area is, once again, a container for URLs within URLs.
      
JavaScript code wishing to refer to the main content area of a window
        can simply use the window's _content property. A bookmark
        in another iframe or a menu item, for instance, for navigating to mozilla.org
        could be implemented using JavaScript reading
window._content.location.href = "http://www.mozilla.org/"
(Note that another iframe is another window. You need to use the
        correct window object, which, depending on the arrangement of a window's
        frames, may be a different window; perahaps parent._content.)
window._content is merely a shortcut for finding whatever
        was described in the XUL file as being the window's primary content area.
        That description is accomplished using the iframe's type
        attribute. Mozilla makes note of all iframes with a type
        of content (or content-<anything>). At this time, they just exchange insurance information
        and carry on; little is done with content frames. However, the window._content
        property returns the particular JavaScript window corresponding
        to the iframe with a type of content-primary.
        So for this shortcut to work, there must be an iframe declared
<iframe type="content-primary" .../>
somewhere in the window description. (Yes, a JavaScript window
        can be either a real window or a content area. If you find this confusing,
        a good JavaScript book may help.)
The content area can also be referenced using the target
        attribute of a link:
<a target="_content" href="http://www.mozilla.org">visit
        Mozilla</a>
window.title
This is a property of the XUL window object which is reflected
        into the window titlebar. It is functionally equivalent to the HTML document.title
        property, but is a more natural way to affect the window's titlebar than
        through the document.
close event handler
      Standard HTML defines load and unload events,
        triggered when those two things happen to documents. Mozilla additionally
        defines a close event, triggered when a close request is
        made on a window. (Say, the user clicks the "close" widget.)
        A close event handler is different from unload
        in that it intercepts the impending closure and gives the app a chance
        to head it off. unload, in contrast, is called when document
        unloading is inevitable.
The new event is intended to provide a hook for things like a "Save
        changes before closing? [OK] [Cancel]" dialog; a capability missing
        from HTML. If there is no close event handler on a Mozilla
        window, it will close as expected. If there is a close event
        handler, it should return a boolean value. It can explicitly close the
        window (window.close()), or return true. It
        must return false to stop event handling (aborting window
        closure.) In all cases, the unload event will still fire
        as the window is closing.
Examples
The following XUL describes a window containing a couple of
         buttons. One will open another window: a nonmodal dialog,
         with a bit of imagination.
         It uses the extended window.open syntax to pass
         parameters to the dialog window, and in a JS object so the
         new window can modify the parameters and return the modified
         values to this window. The other button uses a debug
         function dump to print to the console. It will
         do nothing in an optimized or production build. In that case,
         you can use the more intrusive but always functional
         alert.
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE window>
<!-- Simple sample interface for bringing up a nonmodal dialog -->
<window
  xmlns:html="http://www.w3.org/1999/xhtml"
  xmlns ="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  orient="vertical"
  title = "Dialog creation sample">
<html:script>
  <![CDATA[
  var dialogWindow = null;
  var ioSettings = new Boolean();
  // create a newdialog window, passing a couple of named parameters
  function MakeParamDialog() {
    var newWin = window.openDialog("madedialog.xul",
      "_blank", "chrome",
      {remind:true, prompt:"Give me your money and convertible bonds"});
    return newWin;
  }
  // cheesy demonstration that values are being returned
  function DumpObject(what) {
    dump("dumping object: " + what + "\n");
    for (prop in what)
      dump(" property '" + prop + "' = '" + what[prop] + "'\n");
    dump("dumped.\n");
  }
  ]]>
</html:script>
<box orient="horizontal">
  <button value="Make Dialog" onclick="dialogWindow=MakeParamDialog()"/>
  <spring style="width: 8px"/>
  <button value="Dump Window" onclick="DumpObject(dialogWindow.arguments[0])"/>
  <spring flex="1"/>
</box>
<spring flex="1"/>
</window>
      The above code will produce a live window with a functional button if
        placed in a file named, perhaps, makedialog.xul, and
        another XUL file describing the dialog to open named
        madedialog.xul in the same directory.
        It relies on the Mozilla installation to locate the
        specified stylesheet (a typical installation will load
        global.css from the selected skin).
        Launch apprunner pointing at that file to load it
        into the main window.
      
apprunner -url file:///samples/makedialog.xul
      
The following example madedialog.xul window
        contains initialization code which uses the parameters passed
        by the window.openDialog call. Note that
        other documentation covers the widgetry and contents of a window.
        The box documentation is a good place
        to begin.
 
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE window>
<!-- dialog containing a control requiring initial setup -->
<window
  xmlns:html="http://www.w3.org/1999/xhtml"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  onload="SetFromParams()"
  orient="vertical"
  title="Things to do">
<html:script>
<![CDATA[
// Initialize controls from parameters sent through openDialog
function SetFromParams() {
// look in arguments[0] for interesting properties and values
// set checkbox from that, if present
  if (window.arguments && window.arguments[0]) {
    var setting;
    var control;
    // set checkbox from the value of argment[0]'s "value" property
    if (window.arguments[0].remind) {
      setting = window.arguments[0].remind;
      control = document.getElementById("remind");
      if (control)
        if (typeof setting == "boolean")
          control.checked = setting;
        else if (typeof setting == "object")
          control.checked = setting.valueOf();
    }
    // set prompt from the value of argment[0]'s "prompt" property
    if (window.arguments[0].prompt) {
      setting = window.arguments[0].prompt;
      if (typeof setting == "string") {
        control = document.getElementById("prompt");
        if (control)
          control.setAttribute("value", setting);
      }
    }
  }
}
// OK button handler
// just close the window, since the checkbox is updating live
function DoOK() {
  window.close();
}
function copyToArgs(checkbox) {
  // if we were given an openDialog parameter, set its value
  if (window.arguments && window.arguments[0])
    window.arguments[0].remind = checkbox.checked;
}
]]>
</html:script>
<text id="prompt" value="Give me your money"/>
<checkbox id="remind" value="remind" oncommand="copyToArgs(this)"/>
<button value="OK" onclick="DoOK()"/>
</window>