Code snippets

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

Starting from Firefox 53, no new legacy add-ons will be accepted on addons.mozilla.org (AMO) for desktop Firefox and Firefox for Android.

Starting from Firefox 57, WebExtensions will be the only supported extension type. Desktop Firefox and Firefox for Android will not load other extension types.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more information.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

This article includes code snippets to help you write an add-on for Firefox for Android. For additional code samples, check out the Firefox for Android Add-ons Github repo.

Obtain NativeWindow and BrowserApp object with add-on SDK

The NativeWindow and BrowserApp object are only available to privileged code running on Firefox for Android, and is intended for use by Firefox for Android add-ons.
// Obtain component object : Chrome Authority
// https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Chrome_Authority
var { Cu } = require("chrome");
// Obtain commonly used services : Services.jsm
// https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/Services.jsm
Cu.import("resource://gre/modules/Services.jsm");
function getNativeWindow() {
	let window = Services.wm.getMostRecentWindow("navigator:browser");
	return window.NativeWindow;
}
function getBrowserApp() {
	let window = Services.wm.getMostRecentWindow("navigator:browser");
	return window.BrowserApp;
}

Tabs and Browsers

Most tab and browser management happens through the BrowserApp object. Because Firefox on Android uses both a native UI and a JavaScript Gecko layer, tabs need to be synchronized. Every tab has an ID which is used to identify it when interacting with the native UI.

Waiting for UIReady

Before trying to access any methods or properties of the BrowserApp object, you must wait for the code to be properly initialized. That typically happens in the "load" handler for the chrome browser window. This can lead to races if an add-on also uses the "load" event to do it's initialization. An easy way around this is to use the BrowserApp "UIReady" event.

let addPageLoadListener = function() {
  BrowserApp.deck.addEventListener("load", onPageLoad, false);
};
if(BrowserApp.deck) {
  // BrowserApp.deck has been initialized.
  addPageLoadListener();
} else {
  // Use the global chrome window to wait for BrowserApp to initialize.
  window.addEventListener("UIReady", function onUIReady(){
    window.removeEventListener("UIReady", onUIReady, false);
    addPageLoadListener();
  }, false);
}

Selected Tab and Browser

// gets the array of open tabs
BrowserApp.tabs;
// gets the selected tab
BrowserApp.selectedTab;
// gets the selected browser
BrowserApp.selectedTab.browser;
// gets the selected browser (a shortcut)
BrowserApp.selectedBrowser;

Looking up Tabs and Browsers

Sometimes you have a reference to an object and want to lookup the Tab or Browser associated with that object.

// Lookup tab using an ID
let tab = BrowserApp.getTabForId(aID);
// Lookup tab using a browser
let tab = BrowserApp.getTabForBrowser(aBrowser);
// Lookup tab using a DOM window
let tab = BrowserApp.getTabForWindow(aWindow);
// Lookup browser using a tab
let browser = tab.browser;
// Lookup browser using a DOM window
let browser = BrowserApp.getBrowserForWindow(aWindow);
// Lookup browser using a DOM document
let browser = BrowserApp.getBrowserForDocument(aDocument);

Tab Management

// Adding a tab
let tab = BrowserApp.addTab();
// Close a tab
BrowserApp.closeTab(aTab);
// Select a tab
BrowserApp.selectTab(aTab);
// Listening for tab events
function watchTab(aEvent) {
  // the target is a XUL browser element
  let browser = aEvent.target;
}
BrowserApp.deck.addEventListener("TabOpen", watchTab, false);
BrowserApp.deck.addEventListener("TabClose", watchTab, false);
BrowserApp.deck.addEventListener("TabSelect", watchTab, false);

Detecting Page Loads

function onPageLoad(aEvent) {
  // the target is an HTMLDocument
  let doc = aEvent.originalTarget;
  let browser = BrowserApp.getBrowserForDocument(doc);
  let tab = BrowserApp.getTabForBrowser(browser);
}
BrowserApp.deck.addEventListener("load", onPageLoad, true);

Detecting Private Browsing Mode

Private browsing mode in Firefox for Android is per-tab, not per-window

function isPrivateTab(aTab) {
  return aTab.browser.docShell.QueryInterface(Components.interfaces.nsILoadContext).usePrivateBrowsing;
}
BrowserApp.addTab("http://developer.mozilla.org/", {isPrivate: isPrivateTab(BrowserApp.selectedTab)});

Supporting both desktop and mobile

The same add-on can support both desktop and mobile versions of Firefox. Some of the capabilities between the platforms are different, and may require some pieces of separate logic.

Detecting XUL support

Mobile add-ons do not support using XUL for the UI. Mobile UI can be implemented in HTML. To detect if the platform supports XUL:

function isXULAvailable() {
  return Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULRuntime)
    .widgetToolkit.toLowerCase() != "android"
}

Document Tags and Contributors

 Last updated by: rebloor,