Exploring XUL

Starting to learn a new platform can be daunting. Especially, when it is as big as the Mozilla platform. The XUL tutorial and reference were exactly what I needed to start learning. However, I quickly found myself wanting to experiment with little XUL snippets just to see what they looked like at runtime. Or, I’d want to try applying some CSS to the elements. I decided to build a XULRunner application that provided a simple way to experiment with XUL. It also gave me a good hands-on project I could use to really start learning the platform.

The result is XUL Explorer. It’s a simple editor that can preview XUL on a tab or in a separate popup window. It has a list of code snippets (small fragments of XUL or JavaScript) that can be quickly inserted into the editor. The XUL can be load and saved to files. The JavaScript Error Console is available to help debug problems. The help menu is hooked to XUL information on MDC. There is even a simple “keyword” help lookup for XUL elements.

xulexplorer.png

I made an install (Windows only) that includes XULRunner 1.8.0.4, so you have everything you need to start using it. I am working on Mac and Linux installs too, but for now you can download the source. I did not package the source into JAR files. I wanted the code to be easy to access, in case anyone wants to see how it works or show me better ways of doing things.

Install: xulexplorer-setup.exe 5.6MB
Source: xulexplorer-src.zip

NOTE: If you want to use something online, check out the XUL editor on Ted Mielczarek’s website.

Code Snippets – Get Your Code Snippets

If you’re new to Firefox extension or XULRunner programming, one of the first things you may want to do is find a good tutorial to help get a “hello world” project bootstrapped. Mozilla Developer Center (MDC) has more than a few to help you get started: Firefox Sidebar, Firefox Statusbar, XULRunner Application. We are working to get more on the way too.

The next thing you may need is small code snippets to help show you how to do common things. Things you probably already know how to do in another language or framework. You just need to know how to do it with XUL, JavaScript and XPCOM. You should definitely check out the Code Snippets section on the MDC. There is also an Example Code section on Mozillazine.

If there is something you want to know how to do, but it’s not on the MDC yet, let me know. Also, for any of you experienced Mozilla developers out there, go check out the code snippets and please add some of your own. Just keep the code snippet short and sweet, we’re not writing a full sample extension or application. If you do want to write a large sample extension or application, create a full blown tutorial.

Site Specific Browers

Recent posts from Zach Lipton and Robert O’Callahan share a common theme: Give web applications better integration into the desktop.

I have a strong interest in this area myself. A while ago I came across a desktop application for the Mac called Pryo. It’s an application with an embedded browser designed to work exclusively with a single web application: Campfire (a web-based IM service from 37Signals). Pryo allows for many things that are either difficult or impossible for Campfire to do on its own. The guys behind Pryo call it a Site Specific Browser. There is even a Windows clone in the works.

Zach provides a simple proposal for “appifying” Firefox for specific sites. I am wondering if a Firefox extension couldn’t be used to test out some of these features. Instead of a tag, I was thinking of a to some sort of manifest file. The manifest could specify how to modify the Firefox UI to be more site specific as well as setup desktop shortcuts. Of course, the user should be in full control of allowing any of this to happen. Preferences would control a site’s ability to “appify” itself and the user should be able to revert back to the “non-appified” version of the site.

Site specific browsers could be the Next Big Thing with Ajax applications appearing faster than rabbits. Seems like TechCrunch has 2 or 3 new web applications popping up everyday. Better desktop integration would definitely enhance the user experience.

Mozilla Platform – XPCOM in C++

In the spirit of “you can never have too many”, I thought it would be a good idea to document my process of creating an XPCOM object in C++. XPCOM is Mozilla’s cross platform component object model, similar to Microsoft’s COM technology. XPCOM components can be implemented in C, C++, and JavaScript, and can be used from C, C++, and JavaScript. That means you can call JavaScript methods from C++ and vice versa.

Applications built on the Mozilla Platform use XUL and JavaScript (and XBL) for the UI. Much of the core technology is implemented in C/C++ and exposed to JavaScript as XPCOM components. I have been experimenting with XULRunner and thought it would be good experience to build some XPCOM components.

Step 1: Development Setup

The simplest way to get an XPCOM component built is to use the Gecko SDK. On Windows, the SDK is built using a Microsoft compiler, so you need to use one too. I am using Microsoft’s free Visual C++ Express. You could try to use a different compiler, but you are going to need to build XULRunner from source code. Not the simplest thing to do and it would be incompatible with production releases of XULRunner from Mozilla.

I am using XULRunner 1.8.0.4 which has a pre-built SDK at gecko-sdk-win32-msvc-1.8.0.4.zip. I extracted the SDK to a folder called xulrunner-1.8.0.4, but you can call yours whatever you want.

You also need to a couple pre-built libraries (glib-1.2.dll & libIDL-0.6.dll) from the wintools.zip archive. I copied glib-1.2.dll & libIDL-0.6.dll into the bin subfolder of gecko-sdk.

Note: wintools.zip seems old and lots of newer MDC documentation refers to moztools.zip archive, but the version of xpidl.exe that comes with the gecko-sdk crashes with the DLL’s from moztools.

Recap:

  • Use the right Gecko SDK for your XULRunner release
  • Use a Microsoft compiler
  • Use pre-built glib-1.2.dll & libIDL-0.6.dll libraries from wintools.zip

Here is what my folder structure looks like:

xpcom-folders.png

Step 2: Create a VC++ Project

I wish Visual Studio project and file templates (or wizards) existed for creating XPCOM modules and components, but they don’t. I am considering building some, but for now, I offer a XPCOM project that contains a simple XPCOM component. You can use the project as a starting point and modify the component files to add your own functionality.

I started with a standard multi-thread DLL project. Then I made the following tweaks:

  • Added “..\gecko-sdk\include” to Additional Include Directories
  • Added “..\gecko-sdk\lib” to Additional Library Directories
  • Added “nspr4.lib xpcom.lib xpcomglue_s.lib” to Additional Dependencies
  • Added “XP_WIN;XP_WIN32” to Preprocessor Definitions
  • Turned off precompiled headers (just to keep it simple)
  • Used a custom build step for the XPCOM IDL file (spawned xpidl-build.bat to process the IDL with Mozilla toolset, not MIDL)

VC++ Express Project: xpcom-test.zip

Note: I am using xpcom_glue as described in this MDC article. I am using frozen linkage (dependent on XPCOM). I am not defining XPCOM_GLUE and I am linking against xpcomglue_s.lib

Step 3: Create an XPCOM Component

A full tutorial of XPCOM is beyond the scope of this posting. Check out these resources for more information on the world of XPCOM:

Ok then, on with the basic, oversimplified example. Your XPCOM component is made up of 3 parts:

  • Component interface described using IDL. The interface defines the methods, including arguments and return types, of the component.
  • Component implementation using C++. The implementation is where the methods actually do the work.
  • Component factory module, also in C++. The factory is in charge of creating instances of the implementations.

Let’s specify a simple interface:


#include "nsISupports.idl"

[scriptable, uuid(263ed1ba-5cc1-11db-9673-00e08161165f)]
interface ISpecialThing : nsISupports
{
  attribute AString name;

  long add(in long a, in long b);
};

Remember to generate your own GUID. The next step is to compile the IDL into a type-library (*.XPT) and a C++ header file (*.H), which we can use to define our implementation object. The blank VC++ project has a BAT file that will create the XPT and the H files. The command executes XPIDL.EXE twice, like this:


{path_to_geckosdk}\bin\xpidl.exe -m header -I..\gecko-sdk\idl {your_idl_file}
{path_to_geckosdk}\bin\xpidl.exe -m typelib -I..\gecko-sdk\idl {your_idl_file}

The generated H file actually has a skeleton implemenation (commented out). You can take the code and create implementation H and CPP files. They could look like this:


H file:

#ifndef __SPECIALTHING_IMPL_H__
#define __SPECIALTHING_IMPL_H__

#include "comp.h"
#include "nsStringAPI.h"

#define SPECIALTHING_CONTRACTID "@starkravingfinkle.org/specialthing;1"
#define SPECIALTHING_CLASSNAME "SpecialThing"
#define SPECIALTHING_CID { 0x245626, 0x5cc1, 0x11db, { 0x96, 0x73, 0x0, 0xe0, 0x81, 0x61, 0x16, 0x5f } }

class CSpecialThing : public ISpecialThing
{
public:
	NS_DECL_ISUPPORTS
	NS_DECL_ISPECIALTHING

	CSpecialThing();

private:
	~CSpecialThing();

protected:
	/* additional members */
	nsString mName;
};

#endif

CPP file:

#include "comp-impl.h"

NS_IMPL_ISUPPORTS1(CSpecialThing, ISpecialThing)

CSpecialThing::CSpecialThing()
{
	/* member initializers and constructor code */
	mName.Assign(L"Default Name");
}

CSpecialThing::~CSpecialThing()
{
	/* destructor code */
}

/* attribute AString name; */
NS_IMETHODIMP CSpecialThing::GetName(nsAString & aName)
{
	aName.Assign(mName);
	return NS_OK;
}
NS_IMETHODIMP CSpecialThing::SetName(const nsAString & aName)
{
	mName.Assign(aName);
	return NS_OK;
}

/* long add (in long a, in long b); */
NS_IMETHODIMP CSpecialThing::Add(PRInt32 a, PRInt32 b, PRInt32 *_retval)
{
	*_retval = a + b;
	return NS_OK;
}

Lastly, we need to create the module implementation. I put this together from some samples I found on the MDC site:


#include "nsIGenericFactory.h"
#include "comp-impl.h"

NS_GENERIC_FACTORY_CONSTRUCTOR(CSpecialThing)

static nsModuleComponentInfo components[] =
{
    {
       SPECIALTHING_CLASSNAME, 
       SPECIALTHING_CID,
       SPECIALTHING_CONTRACTID,
       CSpecialThingConstructor,
    }
};

NS_IMPL_NSGETMODULE("SpecialThingsModule", components) 

Assuming you have the right SDK and setup the include and LIB folders correctly, the project should build your XPCOM component.

Step 4: Test Component in a XULRunner Application

In order to test your component in a XULRunner application, you need to “install” the component, “clear” the component registry, and use the component from JavaScript.

  1. Install Component: Copy your XPT and DLL files to the {app}/components folder. You should not put your component in the xulrunner/components folder.
  2. Clear Registry: Increment the BuildID in your {app}/application.ini.
  3. Use in JavaScript:

function doXPCOM() {
  try {
    const cid = "@starkravingfinkle.org/specialthing;1";
    var obj = Components.classes[cid].createInstance();
    obj = obj.QueryInterface(Components.interfaces.ISpecialThing);
  }
  catch (err) {
    alert(err);
    return;
  }

  var res = obj.add(3, 4);
  alert('3+4 = ' + res);

  var name = obj.name;
  alert('Name = ' + name);

  obj.name = 'New Name';
  name = obj.name;
  alert('Name = ' + name);
}

As I find errors and new information, I’ll be sure to update this post.

Update: Updated doXPCOM per Christian’s comments.

Mozilla Platform – UI Basics [Part 2]

Last time I covered some simple XUL for creating windows, menus and toolbars. This time I’ll look at dialogs, your own and common OS dialogs too. Dialogs are pretty fundamental to a desktop application. Certain types of dialogs are used over and over. So much so that the OS can provide a default implementation. File open and save are good examples. Whenever possible, it is a good idea to reuse these ‘native’ dialogs so users get a consistent experience across applications.

Custom Dialogs

Building dialogs in XUL is very similar to creating windows. Each dialog seems to be in it’s own XUL file. Also like windows, XUL provides a

element to act as the container for the dialog. As their window counterparts, dialog XUL files can have DTD, CSS, JS links too. Here is an example XUL dialog:







  

  
  
    
    
      
      
      
    
  


XUL window elements have a special method to openDialogs. Here is the code needed to open a dialog:


function openDialog() {
  window.openDialog("chrome://basicapp/content/dialog.xul", "newdlg", "modal");
}

mydialog.png
(on Win2K)

The first thing that caught my eye about

is the button and related attributes. In an effort to make things easier for developers and more consistent for users, XUL has a mechanism to auto create and position the core dialog buttons (‘ok’, ‘cancel’ and help’ for example). The developer just declares the need for the button, the caption and access key for the button and the JS function to call if the button is pressed. XUL handles placing and styling the buttons on the dialog. This is also nice for cross-platform applications too. Each OS seems to have its own convention for where buttons should be placed on a dialog. Here is a short list of the button attributes (see the above link for a complete reference):

  • buttons – comma separated list of buttons to show on dialog (‘accept’, ‘cancel’, ‘help’, ‘extra1’ and ‘extra2’)
  • buttonlabelaccept – label for accept button (same for other buttons)
  • buttonaccesskeyaccept – access key for accept button (same for other buttons)
  • ondialogaccept – Javascript to execute if accept is pressed (same for other buttons)

XUL has a wide range of input controls you can use on a dialog. In the future, I will try to go into more detail on some of the existing and planned XUL input controls. Not too sure if I’ll use the , but if I did, the single element would be a big time saver over building the header from scratch.

Common Dialogs

Some of the most frequently used common dialogs are for opening and saving files. For instance, Windows has supported builtin file open and file save dialogs for many years. It makes creating an application easier for developers. The consisent UI also makes using an application easier too. XUL supports native implementations of filepickers (Mozilla for File Open and Save dialogs). Newer releases will allow using preferences to switch to a XUL emulation filepicker, if you want to. The XUL filepickers are XPCOM components and must be instantiated before using, like this:


function doFileOpen() {
  /* See: http://developer.mozilla.org/en/docs/XUL_Tutorial:Open_and_Save_Dialogs */
  
  var nsIFilePicker = Components.interfaces.nsIFilePicker;
  var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);

  fp.init(window, "Open File", nsIFilePicker.modeOpen);
  fp.appendFilters(nsIFilePicker.filterText | nsIFilePicker.filterAll);
  
  var res = fp.show();
  if (res == nsIFilePicker.returnOK) {
    var thefile = fp.file;
    alert(thefile.leafName);
    // --- do something with the file here ---
  }  
}

XUL does not appear to currently support any other common dialogs. That could change with Firefox 2 and 3 releases coming up. Firefox and Thunderbird both support nearly native Page Setup and Print dialogs. However, XUL does support elements to make creating Wizards a simple task as well.

I still want to look at Input Controls, Printing, Clipboard and XPCOM.

Mozilla Platform – UI Basics [Part 1]

My quest to build a basic desktop application using XUL continues. Last time I installed XULRunner and built a very simple, bare-bones test application. This time I want to add some of the things common to a desktop application UI:

  • Windows and Dialogs
  • Menus and Toolbars
  • OS Common Dialogs
  • Controls or Widgets

Windows
Each window or dialog should be created in it’s own XUL file. The XUL file may also contain other top-level declarations for CSS and DTD, which I will discuss in a moment. is the basic windowing element in XUL. It has attributes to set the title/caption as well as control width and height. Although I have not seen it mentioned yet, I am assuming that you can only have one element per XUL file. Here is an example: