Firefox for Android: Docs for Building Add-on

I have talked about how add-ons interact with the new Firefox on Android, but we didn’t have any easily accessible documentation. Now we have some guides and code snippets:

Feel free to help make the MDN documents better!

Remember, the basics of building an add-on for Firefox on Android is no different than building an add-on for Firefox on Desktop.

I also added a basic restartless add-on skeleton for Firefox on Android on github.

Firefox Android: Add-ons in a Native World

One of the first things I yelled about when we were debating switching to a native Android UI for Firefox was add-on support. Using a XUL-based UI meant add-ons were free. The Mozilla platform has support for add-ons baked right in. Moving to a native UI would surely kill our ability to support add-ons, right? Wrong!

Add-ons are an important part of the Firefox story. Native UI builds of Firefox support add-ons. There are some things an add-on developer needs to be aware:

  • The add-ons system is the same one used in other Mozilla applications. We did not invent a new add-on system.
  • Native UI builds are considered a new application and are not add-on compatible with the XUL versions. The application ID for native UI builds is: {aa3c5121-dab2-40e2-81ca-7ea25febc110}
  • There is no visible XUL in the UI, so using overlays to try to add or change UI is useless.
  • There is a simple NativeWindow object that allows you to manipulate parts of the native Android UI.
  • Services like nsIPromptService and nsIAlertsService are implemented to use native Android UI.
  • Since overlays are useless for UI and JavaScript APIs are available for native UI, you should seriously consider just making a restartless add-on.

NativeWindow

We wanted to give add-on developers some APIs to manipulate the native Android UI, so we create a helper object called NativeWindow. The API is still young, but it gives you access to: Android Menu, Doorhanger Notifications, Context Menus (in web content) and Android popup toast alerts. The object is currently part of the main browser window, but we are considering moving it to a JS module. The basic API is here:


/*
 label: menu label
 icon: file:// or data: URI for an icon
 callback: JS function called when menu is tapped
 returns a menu ID that can be used to remove the menu
*/
menuID = NativeWindow.menu.add(label, icon, callback);
NativeWindow.menu.remove(menuID);

/*
 message: displayed text
 value: string based tag
 buttons: array of JS objects used to create buttons in the notification
 tabID: tab associated with this notification
 options: JS object that has 'persistence' and 'timeout' options
*/
NativeWindow.doorhanger.show(message, value, buttons, tabID, options);
NativeWindow.doorhanger.hide(value, tabID);

/*
 label: menu label
 selector: JS object that has a 'matches(element)' function. Used to show the menu.
 callback: JS function called when menu is tapped
 returns a menu ID that can be used to remove the menu
*/
menuID = NativeWindow.contextmenu.add(label, selector, callback);
NativeWindow.contextmenu.add(menuID);

/*
 message: displayed text
 duration: "short" or "long"; Used for alert timeout
*/
NativeWindow.toast.show(message, duration);

Some examples of what the API can do:

Doorhanger Notification


Menu Item


Context Menu Item


Toast Popup Alert


The NativeWindow API will continue to grow and mature, but I think even now it shows that add-ons can have first-class interactions with the native UI of Firefox. I am looking forward to developers trying it out and helping us push the API forward.

Firefly – Remote JS Shell for Firefox Mobile

A long time ago, in May 2008, I posted about a remote JS shell add-on I was building. I had been tinkering with a few existing projects (JSSH, SD Connect and MozRepl) but wanted to build something small, lightweight and mainly focused on helping add-on / XUL developers interact with JS running in a separate application. I tried to get the protocol closer to that used by Opera DragonFly and Crossfire, but I never had the time to get it exactly right. When I started work on Firefox Mobile, I used the add-on to interact with Firefox running on a mobile device from my desktop machine. Unfortunately, I never felt the UI and the code were good enough for a public release.

Recently, I dusted off the code, converted to restartless add-ons and made a very simple in-content UI. I say add-ons, because there are two: a probe, and a shell. You install the probe into Firefox Mobile and install the shell into Firefox Desktop (or Firefox Mobile running on a desktop – it’s up to you).

Shell

The shell is implemented as an in-content about: page [1]. After installing the shell, navigate to “about:firefly” and you’ll see the simple UI. The shell can act as a listening server or you can connect to a relay server. The listening server is simplest, so start with that.. You start the server on a specific TCP port. Once started, the shell waits for a probe to connect.

Probe

The probe is simple. After installing it, you just point it at an instance of the shell by entering the IP address and port. You can connect and disconnect from the “Remote Debugging” preferences. That’s all you need to do with the probe.

Starting a Session

Once the probe has connected to a shell, you can enter JavaScript commands into the simplistic shell UI, the code is sent to the probe and evaluated in a sandbox running in the application. Because it’s a sandbox, the probe injects some helper properties and methods:

  • window – The active chrome window. With this object, you have full access to the window’s JS and DOM.
  • firefly – Injected API with some special utility methods:
    • getWindow(type) – Returns the chrome window of a given type.
    • getWindows() – Returns an array of all open windows.
    • inspectJS(object) – Lists all properties and functions associated with a given JS object.
    • inspectDOM(selector or element) – Dumps the markup for a given DOM element. You can pass a CSS selector string or a real DOM element to the method.

Examples

Since we are talking about Firefox Mobile, you should be familiar with the internal chrome UI code before starting to poke around. The main browser.js has a Browser object that acts like a manager for the open tabs, so let’s play with it:

URL of active tab:
window.Browser.selectedBrowser.currentURI.spec

Add a new tab:
window.Browser.addTab("http://mozilla.org", true)

Inspect the active tab JS object:
firefly.inspectJS(window.Browser.selectedTab)

If you’re interested in using Firefly, you can install the add-ons from here:
Shell: firefly-shell
Probe: firefly-probe (mobile shortcut: http://bit.ly/irvpjc)

Source code:
Shell
Probe

Next Steps

  • Add access to the web content running in the child process. Firefox Mobile is multi-process, so you can’t directly access the web content from the main process.
  • Add a pretty output for the inspectXXX helpers. Instead of just dumping the simple text output into the HTML, we could make the output more dynamic – think Firebug panels.
  • Add helpers to do more profiling and data collection. Many times I want to know what is happening on the device. Things like CPU and memory usage or why the profile data is exploding.

Bug reports and feature requests welcome.

[1] Yep, an about: page in a restartless add-on. It wasn’t too hard. I am using a resource: alias for the external CSS file and the favicon. I could have just move the CSS into the XHTML file and used a data: URI for the favicon.

Cloud Printer – Print from Firefox Mobile

Recently, Google released the beginnings of a cloud-based printing system called – Google Cloud Print. The project is still in Beta, but you can install a recent version of Google Chrome on Windows and attach your local printers into the Cloud Print system (more). Google has added support for Cloud Print to few of their mobile web apps, but has not released a client application API yet. However, they did release a simple webapp demo – and where there is a demo, there are people reverse engineering it.

Cloud Printer is a restartless Firefox Mobile add-on that integrates into the Google Cloud Print system. Firefox Mobile already has code to save web pages as PDF. I took that code and send the PDF to the Google Cloud Print system.

Using the demo code and some other examples, it was fairly simple to get this to work. The current API is fairly simple and nice. I expect a few changes when the APIs become official, which should happen soon.

Note: You need to be signed into your Google Account for this to work. If you are not signed in, you’ll see a prompt and we open the Google Account login page for you.

Let me know how this works for you!

Restartless Fun

I like experimenting with restartless add-ons. This time I used a variation of the include technique to assign an image to the toolbar button and get access to a CSS file and IPC frame script. The CSS file is loaded and unloaded using nsIStyleSheetService. The IPC frame script is loaded using the global message manager. The frame script is needed because the PDF file must be generated in the content process, not the main process. You do remember that Firefox Mobile is multi-process, right?

One gotcha. Once loaded, you can’t currently unload a IPC frame script. So if you enabled/disable a restarless add-on that loads a frame script, you’ll end up with multiple instances running. Which means, when you send an IPC message, multiple listeners will respond – this is bad. I hacked around that by sending a “disable” message to my frame script when uninstalling the add-on. The “disable” message just removes the message listeners in the frame script so they won’t respond to any messages in the future. See the source code.

These restartless techniques are documented in various places, but we should start some code snippet docs on MDC too.