Following on from yesterday, I needed to pass arguments to the window being opened. The data I needed to pass was quite simple, so I simply serialized it to JSON and then passed them as strings in a nsICollection (actually, a @mozilla.org/supports-array;1 collection of @mozilla.org/supports-string;1) and then just eval()’d the data within the xul window’s javascript.

This is a bit of a hack, but its probably the simplest way of passing structured data. What would be nice is a bit of code that would automagically serialize a bean to JSON notation… I’m sure there are many out there (This apache struts class looks promising)

Finally worked this out…

These pages are rather helpful: Mozilla’s guide to using proxies and XulPlanet’s description of the nsIProxyObjectManager

This interface defines a proxy manager. An instance of this manager is used to create a proxy for a particular object on which you wish to invoke a method. If you are calling from any other thread that which you initialized xpcom on, you will need to do this, and as far as I am aware, there is no trivial way of doing otherwise.

Anyway, the code to open a new window would be as follows. Note, this method assumes that there is already a constructed object called windowWatcher, which is an instanceof nsIWindowWatcher, see previous post for details.

 public void openWindow(String chromeUri, String name) {
nsIEventQueueService eventQueueServive =
(nsIEventQueueService)serviceManager.getServiceByContractID(
"@mozilla.org/event-queue-service;1",
nsIEventQueueService.NS_IEVENTQUEUESERVICE_IID);

nsIEventQueue eventQueue = eventQueueServive.getSpecialEventQueue(
nsIEventQueueService.UI_THREAD_EVENT_QUEUE);

nsIProxyObjectManager proxy =
(nsIProxyObjectManager)componentManager.createInstanceByContractID(
"@mozilla.org/xpcomproxy;1",
null,
nsIProxyObjectManager.NS_IPROXYOBJECTMANAGER_IID);

nsIWindowWatcher windowProxy =
(nsIWindowWatcher)proxy.getProxyForObject(
eventQueue,
windowWatcher.NS_IWINDOWWATCHER_IID,
windowWatcher,
nsIProxyObjectManager.INVOKE_SYNC );

windowProxy.openWindow(null, chromeUri, name, "centerscreen", null);
} 

The code here uses the service manager to get an instance of the event queue manager, which is subsequently used to retreive the main UI event queue. This is then passed to the proxy manager’s getProxyForObject method, along with the IID of the nsIWindowWatcher interface, the actual instance, and flag representing the type of proxy operation (async, sync, etc, see the xulplanet page for more info).

This method returns an instance of nsIWindowWatcher, which is actually a proxy which will inject events onto the UI thread for you. Then, just use the object as normal, sweet!

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!

Development, XUL @ 10 February 2006, “No Comments”

Just a quick roundup of some interesting developments I’ve seen recently in the world of persuing elegant cross-platform solutions for media players.
Songbird is a media player in the vein of iTunes that uses XUL

http://xullicious.blogspot.com/2005/06/xul-music.html  is an example of how to make a cross platform media player using gstreamer and directx for the application, and XUL for the front end.

Zap! is a XUL based SIP (ie, VoIP) client for mozilla.  Basically a cross-platform streaming audio tool using the PortAudio audio library.

Development, DotNet, XUL @ 20 December 2005, “No Comments”

Our choice of platform for MixIndex was .NET/C# since it was, at the time, going to be tightly integrated and specific to the Windows OS. However, with the pace of development of Mono and PortableDotNet over the last couple of years, most of the app will now run on any platform. Two key components, however, do not.

First, is the streaming audio library, DirectX is used on windows, but since this is quite well abstracted, it could be rewritten on a per-platform basis, not the best solution, but it is doable.

Second, however, is the UI, System.Windows.Forms is an ugly beast, and not supported very well by either of the two open-source dotnet runtimes, so a choice needs to be made.

After looking at WX.NET, a .NET implmentation of WX widgets, and GTK#, GTK+ bindings for .NET, I was begining to get dishartened by the hackish way they were all implemented. GTK# is just ugly on all platforms, rather than looking good on one or two. WX uses the native interface, but there are no decent, free/cheap, tools to help you, the ones that do are so clunky, they make me worried as to how the rest of the system is imlpemented. All in all, very underwhelming.

Anyway, I am now looking at XUL and its damn nice, clean, standards complient, cross platform and open-source. The XUL Runner is a standalone application for running XUL apps, which would be packaged into the apps installer, and then the XUL part can communicate with the ‘real’ application over SOAP, XML RPC, or whatever, enforcing a strict seperation of UI and Application. Stuff like localization is done through entities defined in DTDs, and styling is done through css, very very cool.

This is the tutorial I followed.