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:

runs in the content processes has access to the window and document globals related to the webpage attached to it can use the same subset of WebExtension APIs usually available in a content script



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.

– The property that was set when the user script was registered via the API. global – Provides access to the isolated sandbox for this particular user script.

– 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.

– 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.