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
// 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" }