Firefox Extensions

Extensions in Firefox 67

There are a couple of major changes coming to Firefox. One is in the current Beta 67 release, while the other in the Nightly 68 release, but is covered here as an early preview for extension developers.

Respecting User Privacy

The biggest change in release 67 is Firefox now offers controls to determine which extensions run in private browsing windows. Prior to this release, all extensions ran in all windows, normal and private, which wasn’t in line with Mozilla’s commitment to user privacy. Starting with release 67, though, both developers and users have ways to specify which extensions are allowed to run in private windows.

Going Incognito

For extension developers, Firefox now fully supports the value not_allowed for the manifest `incognito` key.  As with Chrome, specifying not_allowed in the manifest will prevent the extension from running or receiving events from private windows.

The Mozilla Add-on Policies require that extensions not store browsing data or leak identity information to private windows. Depending on what features your extension provides, using not_allowed might be an easy way to guarantee that your extension adheres to the policy.

Note that Chrome’s split value for incognito is not supported in Firefox at this time.

Raising User Awareness

There are significant changes in Firefox’s behavior and user interface so that users can better see and control which extensions run in private windows.  Starting with release 67, any extension that is installed will be, by default, disallowed from running in private windows. The post-install door hanger, shown after an extension has been installed, now includes a checkbox asking the user if the extension should be allowed to run in private windows.

To avoid potentially breaking existing user workflows, extensions that are already installed when a user upgrades from a previous version of Firefox to version 67 will automatically be granted permission to run in private windows. Only newly installed extensions will be excluded from private windows by default and subject to the installation flow described above.

There are significant changes to the Add-ons Manager page (about:addons), too. First, a banner at the top of the page describes the new behavior in Firefox.

This banner will remain in Firefox for at least two releases to make sure all users have a chance to understand and get used to the new policy.

In addition, for each extension that is allowed to run in private windows, the Add-ons Manager will add a badge to the extension’s card indicating that it has this permission, as shown below.

The lack of a badge indicates that the extension is not allowed to run in private windows and will, therefore, only run in normal windows. To change the behavior and either grant or revoke permission to run in private windows, the user can click on an extension’s card to bring up its details.

On the detail page, the user can choose to either allow or disallow the extension to run in private windows.

Finally, to make sure that users of private windows are fully aware of the new extension behavior, Firefox will display a message the first time a user opens a new private window.

Proper Private Behavior

As a developer, you should take steps to ensure that, when the user has not granted your extension permission to run in private windows, it continues to work normally. If your extension depends on access to private windows, it is important to communicate this to your users, including the reasons why access is needed. You can use the extension.isAllowedIncognitoAccess API to determine whether users have granted your extension permission to run in private windows.

Note that some WebExtension API may still affect private windows, even if the user has not granted the calling extension access to private windows. The browserSettings API is the best example of this, where an extension may make changes to the general behavior of Firefox, including how private windows behave, without needing permission to access private windows.

Finally, there is a known issue where some extensions that use the proxy.settings API require private browsing permission to use that API even in normal windows (all other proxy API work as expected). Mozilla is working to address this and will be reaching out to impacted developers.

User Scripts Are Coming

This is a bit of a teaser for Firefox 68, but after many months of design, implementation and testing, a WebExtensions user scripts API is just about ready. User scripts have been around for a very long time and are often closely associated with Firefox.  With the help of a user script extension such as Greasemonkey or Tampermonkey, users can find and install scripts that modify how sites look and/or work, all without having to write an extension themselves.

Support for user scripts is available by default in the Nightly version of Firefox 68, but can be enabled in both the current Firefox release (66) and Beta release (67) versions by setting the following preference in about:config:

extensions.webextensions.userScripts.enabled = true

This is a fairly complex feature and we would love for developers to give it a try as early as possible, which is why it’s being mentioned now. Documentation on MDN is still being developed, but below is a brief description of how this feature works.

Registering A User Script

The userScripts API provides a browser.userScripts.register API very similar to the browser.contentScripts.register API. It returns a promise which is resolved to an API object that provides an unregister method to unregister the script from all child processes.

const registeredUserScript = await browser.userScripts.register(
   userScriptOptions       // object
);

...
await registeredUserScript.unregister();

userScriptOptions is an object that represents the user scripts to register. It has the same syntax as the contentScript options supported by browser.contentScripts.register that describe which web pages the scripts should be applied to, but with two differences:

    • It does not support a css property (use browser.contentScripts.register to dynamically register/unregister stylesheets).
    • It supports an optional property, scriptMetadata, a plain JSON object which contains metadata properties associated with the registered user script.

Providing User Script Functionality

To support injected user scripts, an extension must provide a special kind of content script called an APIScript. Like a regular content script, it:

The APIScript is declared in the manifest using the user_scripts.api_script property:

manifest.json
{
  ...

  "user_scripts": {
    "api_script": "apiscript.js",
  }
}


The APIScript is executed automatically on any page matched by the userScript.register API called from the same extension. It is executed before the user script is executed.

The userScript API also provides a new event, browser.userScripts.onBeforeScript, which the APIScript can listen for.  It is called right before a matched user script is executed, allowing the APIScript to export custom API methods to the user script.

browser.userScripts.onBeforeScript.addListener(listener)
browser.userScripts.onBeforeScript.removeListener(listener)
browser.userScripts.onBeforeScript.hasListener(listener)

In the above API, listener is a function called right before a user script is executed. The function will be passed a single argument, a script object that represents the user script that matched a web page. The script object provides the following properties and methods:

  • metadata – The scriptMetadata property that was set when the user script was registered via the userScripts.register API.
  • global – Provides access to the isolated sandbox for this particular user script.
  • defineGlobals – An API method that exports an object containing globally available properties and methods to the user script sandbox.  This method must be called synchronously to guarantee that the user script has not already executed.
  • export – An API method that converts a given value to a value that the user script code is allowed to access (this method can be used in API methods exported to the userScript to result or resolve non primitive values, the exported objects can also provide methods that the userScripts code is allowed to access and call).

The example below shows how a listener might work:

browser.userScripts.onBeforeScript.addListener(function (script) {

  script // This is an API object that represents the userScript
         // that is going to be executed.

  script.metadata // Access the userScript metadata (returns the
                  // value of the scriptMetadata property from
                  // the call to userScripts.register

  // Export some global properties into the userScript sandbox
  // (this method has to be called synchronously from the
  // listener, otherwise the userScript may have been already
  // be executed).
  script.defineGlobals({
    aGlobalPropertyAccessibleFromUserScriptCode: “prop value”,

    myCustomAPIMethod(param1, param2) {
      // Custom methods exported from the API script can use
      // the WebExtensions APIs available to the extension
      // content scripts
      browser.runtime.sendMessage(...);
      ...

      return 123; // primitive values can be returned directly
      ...

      // Non primitive values have to be exported explicitly
      // using the export method provided by the script API
      // object
      return script.export({{
        objKey1: {
          nestedProp: "nestedvalue",
        },
        // Explicitly exported objects can also provide methods.
        objMethod() { ... }
    },

    async myAsyncMethod(param1, param2, param2) {
    // exported methods can also be declared as async
    },
  });
});

Miscellaneous Items

It was a busy release and besides the two major features detailed above, a number of smaller features (and fixes) also made it into Firefox 67.

Thank You

Within the WebExtensions API, a total of 74 bugs were closed in Firefox 67. Volunteer contributors continue to be an integral part of the effort and a huge thank you goes out those that contributed to this release, including: Oriol Brufau, Shailja Agarwala, Edward Wu, violet.bugreport and rugk. The combined efforts of Mozilla and its amazing community members are what make Firefox the best browser in the world.

7 comments on “Extensions in Firefox 67”

  1. Gallupo wrote on

    Many good news to look forward to, but I hope that userscripts are something the user has to enable themself (just as today where extension require a informed decision). Or else i forsee a storm incoming with millions of uninformed users having something enabled by default…

  2. Jessica wrote on

    Y’all absolutely ruined the add-ons UI with Firefox 64. It’s not even properly responsive like the old one and the new one hides a lot of information that I actually use and makes many functions more annoying to use like the changelog.

    Oh and we’re still waiting for something a little closer to the add-ons we had before. This is happening so slowly. I might as well use Chrome because Firefox doesn’t have a one up anymore. In fact, Chrome does. I can set permissions for indidivual sites on Chrome. One benefit Firefox does have however, is non-ephemeral Flash permissions but Flash use will be getting *even harder* with 69, and I haven’t even touched on the terrible performance of Flash compared to Chrome.

    1. J Redhead wrote on

      Just FYI it is actually possible adjust permissions for individual sites in firefox, the UI is just hidden/clunkier:

      Click the lock for site information, then show connection details (arrow next to connection), and and “More Information” — This will open the page info popup for the page, which includes permissions.

      They really ought to expose all the information better (or at least have a keyboard shortcut to open page info!)

  3. erosman wrote on

    FireMonkey has been using the new user script API since Firefox 65.

    https://addons.mozilla.org/firefox/addon/firemonkey/

  4. Kilazur wrote on

    I just lost all my installed addons after this update. It seems Firefox simply reinstalled itself, and even re-imported bookmarks (from the last installation, I assume, since I don’t seem to have lost anything in that department).

    Any reason why?

  5. Caitlin Neiman wrote on

    Hi Kilazur, this might have been related to a new dedicated profile being installed when you upgraded to Firefox 67 (https://support.mozilla.org/en-US/kb/dedicated-profiles-firefox-installation#w_what-changes-are-we-making-to-profiles-in-firefox-67). You might want to try switching back to a previous profile and see if your extensions are still installed (instructions here: https://support.mozilla.org/en-US/kb/profile-manager-create-and-remove-firefox-profiles).

    If it wasn’t due to the profile installation, you may need to ask our support team at https://support.mozilla.org to help investigate further.

  6. basil wrote on

    “we disabled legacy addons because they were a security risk, but here, have userscripts that could also be trouble!”

    yes. i see the logic here.

    (also as a note, since i’m getting the “we don’t support this version of firefox” notifs on some of the sites i use, now i have to go to a sketchy github page to download a tweak for quantum to re-enable the legacy addons i need to work/use the internet. So that whole “doing it for your safety” thing worked out real well)