HOWTO

Async network requests

Problem

You want to make network requests or do other asynchronous work in xpcshell. However, when you start your script, it exits immediately, before the network request returns.

Solution

1. Put the following at the end of your script:

// Do async processing
// From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
print("Doing async work");
gScriptDone = false;
var gThreadManager = Cc["@mozilla.org/thread-manager;1"]
    .getService(Ci.nsIThreadManager);
var mainThread = gThreadManager.currentThread;

while (!gScriptDone)
  mainThread.processNextEvent(true);
while (mainThread.hasPendingEvents())
  mainThread.processNextEvent(true);

2. In all callbacks where your script is finished, i.e. both success and error callbacks, put:

gScriptDone = true;

If you forget some condition where your script should exit but you don't add this statement, your script will hang (busy wait).

This is of course a massive, ugly hack prone to error, but this is what the xpcshell test harness does. There should really be a better solution in xpcshell.

Using JS modules and non-UI JavaScript chrome files

Problem

You want to write a JavaScript file, and run it in xpcshell. The file uses JS modules. Maybe it even uses JavaScript files from chrome:// URLs.

E.g., you use:

Components.utils.import("resource://app/modules/gloda/log4moz.js");

However, you get (for that particular line, which is the first import):

uncaught exception: [Exception... "Component returned failure code: 0x80040111 (NS_ERROR_NOT_AVAILABLE)
[nsIXPCComponents_Utils.import]"  nsresult: "0x80040111 (NS_ERROR_NOT_AVAILABLE)"  location: "JS frame ::
file.js :: <TOP_LEVEL> :: line 12"  data: no]

Solution 1

var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Components.interfaces.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://myall/content/file.jsm");

See: http://mxr.mozilla.org/comm-central/...figUtils.js#54

Solution 2

Append the following at the top of your JS file which you want to run in xpcshell

{
  // <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
  // <https://bugzilla.mozilla.org/show_bug.cgi?id=546628>
  let Cc = Components.classes;
  let Ci = Components.interfaces;

  // Register resource://app/ URI
  let ios = Cc["@mozilla.org/network/io-service;1"]
    .getService(Ci.nsIIOService);
  let resHandler = ios.getProtocolHandler("resource")
      .QueryInterface(Ci.nsIResProtocolHandler);
  let mozDir = Cc["@mozilla.org/file/directory_service;1"]
      .getService(Ci.nsIProperties)
      .get("CurProcD", Ci.nsILocalFile);
  let mozDirURI = ios.newFileURI(mozDir);
  resHandler.setSubstitution("app", mozDirURI);

  // register chrome://* URIs
  let cr = Cc["@mozilla.org/chrome/chrome-registry;1"]
        .getService(Ci.nsIChromeRegistry);
  cr.checkForNewChrome();
}

Bug 546628 would make this unnecessary.