Version: 1.0
Date: 2007-10-16
Author: Andreas Brodt <ihminen@garage.maemo.org>



1. About this howto

This howto describes how to develop extensions for the MircroB browser. MicroB is a web browser for Nokia Internet Tablets based on Mozilla Gecko. Like Firefox, the MicroB browser supports adding additional features through extensions. However, MicroB does not support all features provided by Mozilla extensions and extension development differs in some ways. This howto describes how to build and package MicroB extensions. As a prerequisite, you should have at least some idea about XPCOM development in JavaScript or C++. Besides, you should have heard about Debian packages.

If you have comments, feel free to write me.


2. MicroB extensions

Extensions add new features to the MicroB browser. In principle, MicroB extensions work the same way as Firefox extensions. A lot of features are supported, including XBL. However, MicroB extensions suffer from the following restrictions:

The lack of XUL practically restricts MicroB extensions to bare XPCOM components. This makes user interfaces difficult for MicroB extensions. One possibility is to generate prompts and alerts using interfaces such as nsIPromptService. Another way is to use one or more HTML pages as user interface and interact with the extension though UniversalXPConnect or make the extension expose additional JavaScript classes and/or objects. MicroB supports XPCOM components written in JavaScript and C++.

XPI packages are not supported, as all software for the maemo platform should be installed using Debian packages. This does not affect the functionality of an extension, but it does influence the build process.


3. Setting up the build environment

As MicroB does not support extensions using XUL, a MicroB extension will probably implement one or more XPCOM components. This can be done in JavaScript or in C++. This howto focuses on C++, but works with JavaScript components as well.

In order to develop C++ components, the tools, headers and platform libraries of MicroB (SDK) are required. It might be possible to obtain the SDK by installing the microb-engine-dev Debian package in the development environment (i.e., scratchbox). Please tell me, if you manage to use this approach. The way I did it is more complex and follows the Creating Custom Firefox Extensions with the Mozilla Build System howto.

In order to obtain the SDK, I compiled the microb-engine Debian package from sources in an armel target in scratchbox (as I said above, I did not test other ways of obtaining an SDK, tell me if you manage to do so). Building MicroB in scratchbox using qemu is a cumbersome and error-prone process that can easily take several hours. In case your build fails, you should first check, whether the build at least managed to process the build-tree/mozilla/extensions directory. If yes, you're fine, even though the whole build didn't succeed.

After compiling the microb-engine Debian package, you should find the extensions folder in microb-engine-<version>/build-tree/mozilla/extensions. That's where the extension development will take place. Create a subfolder here for each extension you wish to write.


4. Creating the extension

This section describes how to write and build a MicroB extension using the Mozilla build system. Except for installing the extension, this is identical to creating Firefox extensions. The next section will discuss the steps which are specific to MicroB.

4.1 Structuring

Following the aforementioned howto, it is sensible to structure the extension using a public directory for XPIDL interfaces defined by the extension (only if necessary) and a src directory for all XPCOM component code. Besides, a Makefile.in file is needed in every directory to build the extension (see the howto). The chrome.manifest and install.rdf files are required for the installed extension package. Unless your extension is extraordinarily huge and complex, I suggest the structure of your extension look like this:

+ microb-engine-<version>/build-tree/mozilla/extensions/myextension/
   + public/
      - Makefile.in
      - *.idl         // XPIDL interfaces
   + src/
      - Makefile.in
      - *.cpp         //
      - *.h           // XPCOM component implementations in JS or C++
      - *.js          //
   - Makefile.in
   - chrome.manifest
   - install.rdf

4.2 Building

Having written the XPCOM code, you probably want to build your extension. Even if all XPCOM code is written in JavaScript, building is necessary in order to process the XPIDL interfaces. First of all, proper Makefiles have to be generated from the Makefile.in files. This can be done by listing your extension in the --enable-extensions flag in the microb-engine-<version>/build-tree/mozilla/mozconfig file and rebuilding the whole MicroB browser. This has the advantage that calling make in the extensions folder will also build your extension. However, I'd rather recommend to generate the Makefile manually using the make-makefile script:

> cd microb-engine-<version>/build-tree/mozilla/extensions
> ../build/autoconf/make-makefile myextension
creating extensions/myextension/Makefile

Now you should be able to build your extension using a simple call to make in the the extension's root directory (i.e., microb-engine-<version>/build-tree/mozilla/extensions/myextension/). This will also create Makefiles for the subdirectories.

4.3 Packaging

In this step we will create an XPI package file for the extension (although MicroB does not support XPI files). For extensions that are supposed to work with MicroB as well as with other Mozilla-based software, it is a good idea to set things up like this. Besides, although MicroB cannot install XPI files, it expects extensions to have the same structure as the contents of an XPI file.

The build process will generate an XPI file automatically, if you specify the following in the top-level Makefile.in of your extension:

...
XPI_NAME             = myextension
INSTALL_EXTENSION_ID = myextension@mycompany.com
XPI_PKGNAME          = myextension
DIST_FILES           = install.rdf chrome.manifest
...

In addition to the extension's code, XPI files require two additional files: install.rdf and chrome.manifest.

The install.rdf file defines the properties of the extension. It should look somewhat like this:

<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
   <Description about="urn:mozilla:install-manifest">
     <em:id>myextension@mycompany.com</em:id>
     <em:name>My MicroB Extension</em:name>
     <em:version>1.0</em:version>
     <em:creator>Me</em:creator>
     <em:description>
       An extension for the MicroB browser doing cool stuff.
     </em:description>
     <em:targetApplication>
       <Description>
         <!-- MicroB uses Seamonkey's application ID -->
         <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id>
         <!-- Make sure these are right -->
         <em:minVersion>2.0</em:minVersion>
         <em:maxVersion>3.5</em:maxVersion>
       </Description>
     </em:targetApplication>
   </Description>
 </RDF>

The chrome.manifest file defines the location of XUL-related files. If you've read this howto carefully, you should now be wondering. No, MicroB does not support XUL. But it will refuse to load your extension if the chrome.manifest file is missing. So simply place an empty file into the root directory of your extension.

If you have these settings in place (and your code can be built without errors), then a call to make in your extension's root directory should create two things:

 + microb-engine-<version>/build-tree/mozilla/dist/xpi-stage/
   - myextension/
   - myextension.xpi

The myextension.xpi file is nothing but a ZIP file of the myextension folder.

The following example shows the structure of a simple extension. This example assumes that the extension defines a number of XPIDL interfaces (packaged as a single XPT file). Moreover, the C++ XPCOM components are distributed as a single library. XPCOM components written in JavaScript are distributed as plain .js files.

+ myextension/
   + components/
      - myextension.xpt
      - libmyextension.so
      - someJavaScriptComponent.js
      - anotherJavaScriptComponent.js
   - install.rdf
   - chrome.manifest

4.4 Installing

As MicroB cannot install the XPI file, installation must be done manually. Copying the myextension folder to /usr/lib/browser/extensions/ on your Internet Tablet and renaming it to myextension@mycompany.com will effectively install your extension for MicroB. Naturally, you can also create the /usr/lib/browser/extensions/myextension@mycompany.com/ folder manually and unzip the XPI file to this location, if you happen to prefer this.

As MicroB seems to copy all extensions from /usr/lib/browser/extensions/ to /home/user/.mozilla/microb/extensions/ and use them from there, you could install your extension directly there.

To make sure that MicroB recognizes your extension the next time you start MicroB, you have to delete some files, which keep some information about extensions:

rm -f /home/user/.mozilla/microb/compreg.dat
rm -f /home/user/.mozilla/microb/xpti.dat

5. Creating an extension Debian package

Using the steps described above, you can build, install and run a MicroB extension. However, in order to distribute your extension, what you want is a Debian package, which can be installed using the maemo Application manager.

To achieve this, we will collect the extension's files from microb-engine-<version>/build-tree/mozilla/dist/xpi-stage/myextension/ and create a Debian package that will install them to /home/user/.mozilla/microb/extensions/myextension@mycompany.com on the Internet Tablet. There probably is a better way to do this (using a debian/rules file or so), but I accomplished this job using a plain Makefile, which, after copying all files to a temporary directory, creates a Debian package using dpkg-deb. This assumes that you have dpkg-deb installed (as it is included in the package dpkg you most likely have it).

First, create a subdirectory for everything related to the Debian package. Then, write the files describing the Debian package, such as changelog and control. Besides the Makefile, a postinst and postrm script is needed. This will make the overall structure of the extension look like this:

+ microb-engine-<version>/build-tree/mozilla/extensions/myextension/
   + debian/
      - Makefile
      - changelog
      - control
      - postinst
      - postrm
   + public/
      - Makefile.in
      - *.idl
   + src/
      - Makefile.in
      - *.cpp
      - *.h
      - *.js
   - Makefile.in
   - chrome.manifest
   - install.rdf

The changelog file should be created with a tool such as dch. The control file is the Debian package descriptor. It looks like this (note the blank line at the end):

Package: myextension
Version: 1.0-1
Section: user/WebAddons
Priority: optional
Architecture: armel
Depends: microb-eal
Installed-Size: @SIZE
Maintainer: Me <me@mycompany.com>
Description: My MicroB Extension DCCI
 An extension for the MicroB browser doing cool stuff.

Most notably, the control file features the Section "user/WebAddons". In order not to have to determine the installation size manually, I put a placeholder for the Installed-Size value which is replaced by the Makefile (see below). It is sensible to have the extension package depend on MicroB. Refer to the Debian Policy Manual for further information on Debian control files.

The postinst script is run after the Debian package has been installed. You might or might not have to do own configuration work for your extension. Yet, in any case your postinstall script should contain the following code in order to have MicroB recreate the extension cache:

#!/bin/sh

# Remove XPCOM component and interface cache, so that the extension's
# new components are registered.
rm -f /home/user/.mozilla/microb/compreg.dat
rm -f /home/user/.mozilla/microb/xpti.dat

Similarly, the postrm script is run after the Debian package has been uninstalled. Again the extension cache should be updated, so that MicroB realizes that your extension is gone. So the same code as listed above should be included in the postrm script as well.

When writing your postrm and postrm scripts, keep in mind that the shell on the Internet Tablet is NOT bash!

Finally, the Makefile to create the Debian package looks as follows. It contains the debianpackage target that will do the job. The Makefile takes the files from microb-engine-<version>/build-tree/mozilla/dist/xpi-stage/myextension/, copies them to a temporary directory, adds the Debian control files, determines the installation size of the package and creates the package using dpkg-deb. The Makefile is fairly generic. You should be able to reuse it easily, just change the settings at the top of the file. When copying from this howto, make sure that the targets are indented using tab characters.

#!/usr/bin/make

# Makefile to create a Debian package for a MicroB browser
# extension.

# ===== package-specific settings ====================================

# The content directory of the XPI file, as created by the Mozilla
# build system. This value corresponds to the XPI_NAME variable in the
# extension's Makefile.in file. The value defined here will also
# appear in the file name of the Debian package.
XPI_NAME=myextension

# The version number to appear in the file name of the Debian package.
PKG_VERSION=1.0-1

# The architecture to appear in the file name of the Debian package.
PKG_ARCH=armel

# The name of the extension directory on the device. Corresponds to
# the INSTALL_EXTENSION_ID variable in the extension's Makefile.in
# file.
EXTENSION_ID=myextension@mycompany.com

# The xpi-stage folder of the Mozilla build system. This is where the
# Mozilla build system creates XPI files.
XPI_STAGE_DIR=../../../dist/xpi-stage

# Control files of the Debian package. 
PKG_CTRL_FILES=changelog postinst postrm

# ===== better don't edit anything below =============================

# Path to dpkg-deb.
DPKG_DEB=/usr/bin/dpkg-deb

# Name of temporary directory which will be created for building the
# Debian package.
TMP_DIR=tmp

# The extension folder of the MicroB browser, where all extensions are
# copied to. NO "/" AT BEGINNING!
MICROB_EXT_DIR=home/user/.mozilla/microb/extensions

# File name of the Debian package.
DEB_FILE_NAME=../$(XPI_NAME)_$(PKG_VERSION)_$(PKG_ARCH).deb

# The directory to which the actual extension files are copied during
# creation of the Debian package.
EXTENSION_BUILD_DIR=$(TMP_DIR)/$(MICROB_EXT_DIR)/$(EXTENSION_ID)

# ===== targets ======================================================

# Make sure the indenting below is done with tab characters!

debianpackage: clean
	# Create directories and copy files for the Debian package.
	mkdir -p $(EXTENSION_BUILD_DIR)
	cp -rL $(XPI_STAGE_DIR)/$(XPI_NAME)/* $(EXTENSION_BUILD_DIR)
	
	# Copy control files for the Debian package.
	mkdir -p $(TMP_DIR)/DEBIAN
	cp $(PKG_CTRL_FILES) $(TMP_DIR)/DEBIAN
	
	# Determine package install size using du
	# and replace "@SIZE" in the control file.
	cat control | \
	  sed s/@SIZE/`du -sk $(TMP_DIR) | cut -f1`/ \
	  > $(TMP_DIR)/DEBIAN/control
	
	# Create the Debian package
	$(DPKG_DEB) -b $(TMP_DIR) $(DEB_FILE_NAME)

clean:
	# Clean up possibly existing remainders of previous builds.
	rm -rf $(TMP_DIR) $(DEB_FILE_NAME)

all: debianpackage

.PHONY: all clean debianpackage

In order to build your extension and create a Debian package with a single command, it might be a good idea to create an additional target in the top-level Makefile.in of your extension. Add this at the bottom of the Makefile.in and make sure it is indented with a tab character:

...

debianpackage: export libs tools
	cd debian; make debianpackage; cd ..

Now you should be able to call make debianpackage from the extension's root directory and you will end up with a Debian package in that directory. You can install the package using the maemo Application manager.

Alternatively, you could copy the Debian package to your Internet Tablet and install the package using dpkg -i <debianpackage>. During development I found it useful to write a script which will build the package, copy the package to the Internet Tablet (using scp with ssh keys), and install the package in a single shell command.


6. Conclusion

Once you managed to set up the build environment for MicroB (and learned to write XPCOM components), it is not too hard to create extensions for MicroB. Yet, the following whishlist remains open for this howto and MicroB extensions in general:

Please write me if you have an idea concerning these points or find anything else that could be improved.