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.
From Firefox 53 onwards, no new legacy add-ons will be accepted on addons.mozilla.org (AMO).
From Firefox 57 onwards, WebExtensions will be the only supported extension type, and Firefox will not load other 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.
A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.
The Add-on Repository JavaScript code module makes it easy for your extension to interface with the AMO repository. You an use the code module to get lists of add-ons and even install new add-ons. This article provides some sample code that queries the recommended add-ons list on AMO and lets the user click a button to install an add-on from the list.
Importing the repository code module
Before you can use the Add-on Repository API, you need to import the code module:
Components.utils.import("resource://gre/modules/AddonRepository.jsm");
Having done this, you can then access the API through the resulting AddonRepository
object.
Enabling the recommendation feature
In current builds of Firefox 4, the recommendation API doesn't work because the preference for the URL to query to get recommended add-ons is not included by default; see bug 628785. To make the service work for the time being, you can use code like this when your extension starts up:
var prefsService = Components.classes["@mozilla.org/preferences-service;1"] .getService(Components.interfaces.nsIPrefService); var prefBranch = prefsService.getBranch("extensions."); var recUrl = ""; try { recUrl = prefBranch.getCharPref("getAddons.recommended.url"); } catch(e) { recurl = ""; } if (recUrl == "") { prefBranch.setCharPref("getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/list/recommended/all/%MAX_RESULTS%/%OS%/%VERSION%?src=firefox"); prefsService.savePrefFile(null); }
This fetches the value of the extensions.getAddons.recommended.url
preference, and, if the preference doesn't exist or has no value, sets the value of the preference to the correct one for the AMO site.
Starting a request
To start a search of the repository, you can use either of the following methods:
searchAddons()
- Queries the add-on repository for add-ons matching given search criteria.
retrieveRecommendedAddons()
- Retrieves a list of recommended add-ons, as determined by the AMO site administrators.
This example will use the latter, in order to randomly select a recommended add-on and offer to install it. However, the code to perform a search term based query would be very similar.
When the user clicks a toolbar button to initiate the query, the following code gets run to start the request:
AddonRepository.retrieveRecommendedAddons(10, this);
This asks the repository to fetch up to 10 add-ons, using the object this
as the target for callbacks. The callback object needs to implement the SearchCallback
interface, providing the methods that get called when a search either fails or completes successfully.
Handling failed requests
The callback object must have a searchFailed()
method; this gets called when a repository search fails to execute. The most common cause of failure (other than the search URL preference being incorrect) is if there is already a pending request, since only one request can be in progress at a time. For example:
searchFailed: function() { this.showNotification("I have no recommendations for you right now!", "Oh noes!", null); },
Here, we call a showNotification()
method with some parameters that we'll look at shortly when we get to our showNotification()
method below. The important thing to note is that this will handle the failure case.
Handling successful requests
The callback object's searchSucceeded() method gets called when a search completes successfully. It receives a list of the matching addons, the number of add-ons returned, and the total number of add-ons that matched the query (in case the returned number is smaller than the requested number, for example).
For example:
searchSucceeded: function(addons, addonCount, totalResults) { var num = Math.floor(Math.random() * addonCount); this.showNotification("Would you like to try the " + addons[num].name + " addon?", "Install", addons[num].install); },
This routine randomly selects one of the returned add-ons, then calls the previously mentioned showNotification()
routine, passing in as parameters a prompt including the name of the returned add-on, a label for the button to show in the notification ("Install"), and the AddonInstall
object that can be used with the Add-on Manager API to install the add-on.
Installing the add-on
The showNotification()
routine displays a notification box offering to install the recommended add-on, if one was found, or reports an error if the search failed:
showNotification: function(prompt, button, installObj) { this.install = installObj; var box = PopupNotifications.show(gBrowser.selectedBrowser, "sample-popup", prompt, null, /* anchor ID */ { label: button, accessKey: "I", callback: function() { if (popupnotifications.install) { popupnotifications.install.install(); } else { PopupNotifications.remove(box); } } }, null /* secondary action */ ); }
The code here starts by stashing the passed-in AddonInstall
object for later use, then creates and displays the pop-up notification box with the text and button label passed into the method.
The pop-up callback function that gets called when the user clicks the button looks to see if there's a non-null AddonInstall
object reference; if it's null
, then the pop-up is displaying an error notification, so clicking the button simply dismisses the pop-up. Otherwise, the AddonInstall
object's install()
method is called to install the add-on.
This doesn't display any UI showing that the install is taking place; however, if you go to the Add-on Manager panel, you'll see the pending install listed among your add-ons.