Develop features for mozAddonManager

The AMO and Discovery Pane apps use the mozAddonManager web API to achieve a more seamless add-on installation user experience. However, this API is only available to a limited list of domains. If you go to https://addons.mozilla.org (production) in Firefox then mozAddonManager is available on the page. If you go to a development site then it's not.

Turning on mozAddonManager for development

To access mozAddonManager on a development site like https://addons-dev.allizom.org or https://addons.allizom.org go to about:config in Firefox and set this property to true:

extensions.webapi.testing

Refresh the page, open the JavaScript console, and you should see a global mozAddonManager object.

Install add-ons for development

To fully install add-ons from a development site like https://addons-dev.allizom.org or https://addons.allizom.org you will need to tell Firefox to honor their signing certificates. Go to about:config in Firefox and set this property to true:

xpinstall.signatures.dev-root

Restart Firefox to put it into effect. This pref allows you to fully install add-on and theme files.

Developing on Android

The presence of the mozAddonManager API on Firefox for Android will activate the add-on installation switch. Again, you will see this on https://addons.mozilla.org (production) but not on a development site.

To activate access to this API on a development site, first make sure you set the extensions.webapi.testing preference to true. If you're testing on an Android device then you can now access mozAddonManager.

If you're testing on desktop Firefox to emulate Android (for development), you need to make sure your user agent looks like Firefox for Android. You can save a custom device in Responsive Design Mode with a Firefox for Android user agent string. Make sure it is touch enabled. This will let you see the add-on installation switch on desktop.

Developing with a local server

When you're running a server locally for development, you need to grant mozAddonManager access to your localhost domain in addition to setting the extensions.webapi.testing preference to true.

You can do this by building a version of Firefox with a custom patch.

Start by setting up your machine to build Firefox. That page gives you a link to a helpful bootstrapping script. Run it like this:

python bootstrap.py

It will ask you what version of Firefox you want to build. Choose:

Firefox for Desktop

Do not choose the artifact build because unfortunately you have to compile C++ code. It will prompt you to install some dependencies. Re-run it until you have everything installed and configured.

After you have everything, the bootstrap script prompts you to check out a clone of mozilla-central.

Change into the source directory and apply this patch to allow a local server at a URL like localhost:3000.

diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -711,16 +711,11 @@ var LightWeightThemeWebInstaller = {
     let uri;
     try {
       uri = makeURI(srcURIString);
     } catch (e) {
       // makeURI fails if srcURIString is a nonsense URI
       return false;
     }

-    if (!uri.schemeIs("https")) {
-      return false;
-    }
-
-    let pm = Services.perms;
-    return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
+    return true;
   }
 };
diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -64,16 +64,17 @@ var PREF_EM_CHECK_COMPATIBILITY = MOZ_CO

 const VALID_TYPES_REGEXP = /^[\w\-]+$/;

 const WEBAPI_INSTALL_HOSTS = ["addons.mozilla.org", "testpilot.firefox.com"];
 const WEBAPI_TEST_INSTALL_HOSTS = [
   "addons.allizom.org", "addons-dev.allizom.org",
   "testpilot.stage.mozaws.net", "testpilot.dev.mozaws.net",
   "example.com",
+  "localhost",
 ];

 const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";

 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AsyncShutdown.jsm");

diff --git a/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp b/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp
--- a/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp
+++ b/toolkit/mozapps/extensions/AddonManagerWebAPI.cpp
@@ -45,16 +45,17 @@ IsValidHost(const nsACString& host) {
   // When testing allow access to the developer sites.
   if (Preferences::GetBool("extensions.webapi.testing", false)) {
     if (host.LowerCaseEqualsLiteral("addons.allizom.org") ||
         host.LowerCaseEqualsLiteral("discovery.addons.allizom.org") ||
         host.LowerCaseEqualsLiteral("addons-dev.allizom.org") ||
         host.LowerCaseEqualsLiteral("discovery.addons-dev.allizom.org") ||
         host.LowerCaseEqualsLiteral("testpilot.stage.mozaws.net") ||
         host.LowerCaseEqualsLiteral("testpilot.dev.mozaws.net") ||
+        host.LowerCaseEqualsLiteral("localhost") ||
         host.LowerCaseEqualsLiteral("example.com")) {
       return true;
     }
   }

   return false;
 }

@@ -64,19 +65,16 @@ bool
 AddonManagerWebAPI::IsValidSite(nsIURI* uri)
 {
   if (!uri) {
     return false;
   }

   bool isSecure;
   nsresult rv = uri->SchemeIs("https", &isSecure);
-  if (NS_FAILED(rv) || !isSecure) {
-    return false;
-  }

   nsAutoCString host;
   rv = uri->GetHost(host);
   if (NS_FAILED(rv)) {
     return false;
   }

   return IsValidHost(host);

This patch will: 1. allow you to use an HTTP connection 2. allow any page at localhost (on any port) to access mozAddonManager and perform some theme actions.

With this patch applied, you are ready to build Firefox. Once again, make sure you aren't configured for an artifact build since that won't work (you'll need to build C++ code). Build it!

./mach build

The first one will take a while but subsequent builds will be faster. Here's how to run it with a named profile so you don't lose your settings:

./mach run --profile mozilla-central

When Firefox starts up, go to about:config to make sure the development preference is set to true:

extensions.webapi.testing

If you go to an add-on detail page on localhost:3000 with the proper Firefox for Android user agent string (as explained above) then you should see a fancy installation switch. It worked!

If it doesn't work, you may need to add some logging and re-build Firefox. Try setting extensions.logging.enabled to true in about:config to see logging output for the AddonManager.jsm code.

When you click the switch, it will only let you install add-ons if your localhost is proxying https://addons-dev.allizom.org or https://addons.allizom.org . This is because Firefox needs signing certificates and the certs are not configurable. You can set the xpinstall.signatures.dev-root preference to true (as documented above) to activate certificates for the development sites.

Updating your Firefox source code

You will need to periodically update your Firefox source code and rebuild it with your patch.

First, set aside your patch by reverting the changes or using something like the shelve command:

hg shelve

Pull in new updates:

hg pull -u

Re-apply your patch with unshelve (or by following the steps above):

hg unshelve

Now you are ready to build Firefox again. You can check the output of hg diff to make sure the patch is still there and then kick off a new build:

./mach build