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 guide walks you through developing a Firefox Hub add-on for Firefox for Android. The Firefox Hub APIs allow add-ons to add new content to the Firefox for Android home page. These APIs are new in Firefox 30, and increased capabilities are planned for future releases. The two main APIs that this guide covers are the Home.panels
API and the HomeProvider
API. For some example code to get started, you can copy the hub boilerplate add-on from github.
This guide assumes you understand the basics of building a restartless add-on for Firefox for Android. Please see the basic Firefox for Android add-on Walkthrough guide for more information about getting started.
Overview
There are two main parts to building a Firefox Hub add-on: creating a home panel, and storing data to show in that panel. Home panels consist of different views, each of which displays data from a given dataset.
Creating a home panel
To create a home panel, first use the Home.panels
API to register a panel. The register
API takes a panel id and an options callback function as parameters. This options callback is called to dynamically generate an options object whenever a panel is installed or updated, which allows for dynamic locale changes.
Cu.import("resource://gre/modules/Home.jsm"); const PANEL_ID = "test.panel@mydomain.com"; const DATASET_ID = "test.dataset@mydomain.com"; function panelOptionsCallback() { return { title: "My Panel", views: [{ type: Home.panels.View.LIST, dataset: DATASET_ID }] }; } Home.panels.register(PANEL_ID, panelOptionsCallback);
You must always register your panel on startup, but when your add-on is installed (or whenever you want to actually add the panel to the user's home page), you must also install it.
Home.panels.install(PANEL_ID);
See the Home.panels
API documentation for details about how to add more features to your panels, such as authentication and empty views. You can also register to listen for user actions, such as installing or refreshing a panel.
Storing data
Use the HomeProvider
API to store data to display in your home panel. The HomeProvider
API gives you access to HomeStorage
objects, which allow you to asynchronously save and delete data for a given dataset. The save
and deleteAll
APIs return promises that are resolved when the transaction is complete. You can use Task.jsm
to execute these transactions within a task.
Cu.import("resource://gre/modules/HomeProvider.jsm"); Cu.import("resource://gre/modules/Task.jsm"); let items = [ { url: "http://example.com/1", title: "Example 1" }, { url: "http://example.com/2", title: "Example 2" } ]; let storage = HomeProvider.getStorage(DATASET_ID); Task.spawn(function() { // Delete any existing items. yield storage.deleteAll(); // Save the new items. yield storage.save(items); // New way to replace items in Firefox 31+ // yield storage.save(items, { replace: true }); }).then(null, Cu.reportError);
In practice, if you are going to use a network request to fetch your data, you should use the requestSync
API to allow HomeProvider
to decide if it's a good time to sync.
function syncCallback() { // Fetch new data and use HomeProvider APIs to store data. } HomeProvider.requestSync(DATASET_ID, syncCallback);
You can also use the addPeriodicSync
API to request data syncing at a regular interval.
// Calls syncData callback once every 3600 seconds (1 hour) HomeProvider.addPeriodicSync(DATASET_ID, 3600, syncCallback);
View types
Each panel you create with the Hub APIs can have a number of views. For each view, you can choose one of three different layouts: list, grid, or speed dial.
List
The list view displays items in a list, from top to bottom:
To create a list view, specify the view's type
as LIST
:
Cu.import("resource://gre/modules/Home.jsm"); function optionsCallback() { return { title: "My list view", views: [{ dataset: DATASET_ID, type: Home.panels.View.LIST }] }; } Home.panels.register(PANEL_ID, optionsCallback);
List dataset
For each item, the list view will display the following attributes from the view's dataset, if they are present:
title
description
- the image referenced by
image_url
In the dataset, you must supply at least one of these attributes for each item.
Grid
A grid view displays items in a grid, as images:
To create a grid view, specify the view's type
as GRID
:
Cu.import("resource://gre/modules/Home.jsm"); function optionsCallback() { return { title: "My grid view", views: [{ dataset: DATASET_ID, type: Home.panels.View.GRID }] }; } Home.panels.register(PANEL_ID, optionsCallback);
Grid dataset
For each item, the grid view will display the following attributes from the view's dataset, if they are present:
title
description
- the image referenced by
image_url
The grid view is primarily designed to display the image referenced by image_url
.
In the dataset, you must supply at least one of these attributes for each item.
Speed dial
Starting in Firefox 41, there's a special type of grid view called a speed dial. In this view each item is presented as a background image or a background color, and optionally a title or an icon:
To create a speed dial view, specify GRID
as the view's type
and ICON
as the view's itemType
:
Cu.import("resource://gre/modules/Home.jsm");
function optionsCallback() {
return {
title: "My speed dial view",
views: [{
dataset: DATASET_ID,
type: Home.panels.View.GRID,
itemType: Home.panels.Item.ICON
}]
};
}
Home.panels.register(PANEL_ID, optionsCallback);
Speed dial dataset
For each item, the speed dial view will display the following attributes from the view's dataset, if they are present:
background_color
- the image referenced by
background_url
, as a background image - the image referenced by
image_url
, as an icon title
In the dataset, you must supply at least one of title
, description
, or image_url
for each item.
Headers
From Firefox 42 onwards, you can specify a header image for a grid view using the header
view option:
Cu.import("resource://gre/modules/Home.jsm"); function optionsCallback() { return { title: "My speed dial view", views: [{ dataset: DATASET_ID, type: Home.panels.View.GRID, itemType: Home.panels.Item.ICON, header: { image_url: "http://path/to/pumpkin.png", url: "http://www.mozilla.org" } }] }; } Home.panels.register(PANEL_ID, optionsCallback);
Header images are placed at the top of the page and extend right across the page:
Best practices
Here are some tips for developing hub add-ons:
- Use unique ids for panels and datasets.
- Register panels in your add-on's
startup()
function. - Unregister panels in your add-on's
shutdown()
function. - Delete panel data when a panel is uninstalled.
Example add-ons
Here are some open source add-ons that use these hub APIs: