After poking around for some hours (then days) trying to find some useful information on how to use JavaXPCOM (which is a bridge between XPCOM and Java, formally known as Javaconnect) I stumbled across a few things as well as hacking together some understanding myself and through C++ examples of XPCOM usage.

We decided a while back to adopt an XML based protocol wherever possible, and moreover, RDF where appropriate. When communicating with a central server, this would use HTTP, so it seemed logical to use the same on the localhost. So, XUL uses AJAX, basically, to do asynchronous calls over HTTP to localhost, and retreives XML which is then passed into objects representing the returned resources. This way, we dont need to heavily integrate Java and XUL using XPCOM, and we allow other applications to be able to place nicely with the API over a well-known protocol and format. Anyway, this means that the only work JavaXPCOM needs to do is:

  • Start XPCOM using a XULRunner installation
  • Start an XUL window and pass control over to it
  • Be able to call javascript/xul events from java (to open windows, etc)

So to start with, we need to find an XULRunner install. As it stands, we will distribute a version with the installer, but JavaXPCOM contains code to easily find installed versions as well (this is actually documented on the JavaXPCOM site).

  1. Set the path the XULRunner directory, here we are doing it explicitly: File grePath = new File(new File(".").getAbsolutePath(), "platform/win32/xulrunner");
  2. Next create an instance of the LocationProvider LocationProvider locProvider = new LocationProvider(grePath);
  3. Initialize JavaXPCOM embedding: Mozilla.getInstance().initEmbedding(grePath, grePath, locProvider);
  4. Now we need to start an XUL application, so we get an instance of the XPCOM service manager nsIServiceManager serviceManager = moz.getServiceManager();
  5. Now we need to get the @mozilla.org/toolkit/app-startup;1 service: nsIAppStartup appStartup = (nsIAppStartup)serviceManager.getServiceByContractID("@mozilla.org/toolkit/app-startup;1", nsIAppStartup.NS_IAPPSTARTUP_IID);
  6. Get the nsIWindowWatcher interface to the above nsIWindowCreator windowCreator = (nsIWindowCreator)appStartup.queryInterface(nsIWindowCreator.NS_IWINDOWCREATOR_IID);
  7. Get the window watcher service nsIWindowWatcher windowWatcher = (nsIWindowWatcher)serviceManager.getServiceByContractID("@mozilla.org/embedcomp/window-watcher;1", nsIWindowWatcher.NS_IWINDOWWATCHER_IID);
  8. Set the window creator (from step 6) windowWatcher.setWindowCreator(windowCreator);
  9. Create the root XUL window: nsIDOMWindow win = windowWatcher.openWindow(null, "chrome://your-app/content/window.xul", "mywindow", "chrome,resizable,centerscreen", null);
  10. Set this as the active window windowWatcher.setActiveWindow(win);
  11. Hand over the application to xpcom/xul, this will block: appStartup.run();

Now, the LocationProvider is responsible for providing path(s) and file(s) that XPCOM needs. I have implemented a version that when the ‘ChromeML’ location is asked for returns an array of File’s, the first of which points to my own applications chrome folder, and the second of which is the xulrunner’s own chrome folder. This seems to work, but there are several issues:

  1. I do not seem to be able to open XUL windows (or in anyway interact with xpcom/xul) from another thread. Since this run() method on nsIAppStartup blocks, this is a bit annoying. I presume there is a way to run this loop manually, or hook into it so we can queue up requests from other threads to be invoked on the xpcom thread.

Ok, thats only one major issue for now, but I’m sure I will think of others. If anyone has any ideas how these problems can be solved, or how one may circumvent them, I’m all ears. Also, I know that this area is so bleeding edge it hurts, so it would be nice to talk to some other people doing the same things!