Skip to main content
december garnet-smith

how to port an mv3 chrome extension to firefox

A tile with the Firefox logo on an orange background

Photo by Rubaitul Azad on Unsplash

Last year, I created my first Chrome extension (No Linkedin News). I always intended to port my extensions to Firefox but found that Firefox didn't offer much support for Manifest V3 (MV3) at the time. Since Google Chrome is moving forward with plans to deprecate Manifest V2 extensions this year, I wanted to take the time to port all of my Chrome extensions to Firefox, starting with Better YouTube Search, Hide Amazon Cart, and Reddit to Old Reddit.

While making these Chrome extensions cross-browser compatible, I noticed that some of the documentation that I found on Chrome and Firefox seemed to contradict one another. I ran into several issues on both sides so thought I would write a post detailing the steps that I took in hopes that it's helpful to others. You can check out this pull request for Reddit to Old Reddit on Github to view these changes line-by-line. In this tutorial I'll use this extension as an example; it's a small browser extension that redirects users from reddit.com to old.reddit.com. The tech stack is vanilla JS, CSS, and HTML.

To port my Chrome extensions to Firefox, I went through the following steps:

Step 1: Adding a gecko id

Firefox has some different requirements for their manifest.json than Chrome. For this extension, the main differences were:

Firefox expects the gecko id to be either a GUID or a string formatted similar to an email address. (Using a real email address can result in privacy issues due to increased spam.)

Here's an example of what that code might look like:

 "browser_specific_settings": {
    "gecko": { 
        "id": "{1f39e6db-82c0-4af2-a25e-3f16809b31f6}"
    } 
  }

Step 2: Removing any extension_ids

Chrome and Firefox expect any web_accessible_resources extension_ids to be in differing formats. Chrome expects a string of lowercase letters while Firefox expects a GUID with brackets, alphanumeric characters, and four hyphens. To fix this issue, I deleted the extension id. However, Chrome requires each object in the web_accessible_resources array to have at least two properties, so I added the match key with the same URLs as host_permissions.

For Reddit to Old Reddit, here's what that looks like:

 "web_accessible_resources": [
    {
      "resources": ["css/*.css"],
      "matches": ["*://www.reddit.com/*"]
    }
  ],

Step 3: Adding the Background Script

Firefox does not currently support service workers. Instead, they use non-persistent background scripts, also called event pages. However, Chrome's Manifest V3 architecture relies heavily on service workers. To reconcile these differences, Firefox allows you to add your original service workers as background scripts. To do this, I used the script property in addition to the service_worker property in the background key. Here's an example:

  "background": {
    "service_worker": "scripts/background.js",
    "scripts": ["scripts/background.js"]
  },

Step 4: Testing the Extension on Firefox

I tested the extension using the [web-ext](https://github.com/mozilla/web-ext) command line tools, which offer helpful commands to build Firefox add-ons. These are a few of the commands:

After updating the manifest.json successfully, I ran the web-ext run command in the terminal, which opened up a new window. I then went to Reddit, where I noticed a notification asking me to enable the browser extension on reddit.com, which I did. After refreshing the page, I was able to confirm that the code was working and redirecting to old.reddit.com.

Step 5: Signing the Firefox Add-On

Now that my extension was working, I thought I could load the extension into Firefox and that would be it, similar to how extensions work in Chrome. However, Firefox add-ons must be signed in order for them to be loaded in the browser by default. As part of this process, each add-on is also added to Mozilla's directory, even if they're unlisted. This differs considerably from Google Chrome, but does make it more difficult for someone to create and distribute a malicious extension because first they would need to create a developer account.

I created a Mozilla developer account here and then generated an API key and secret, both of which are needed to sign the extension. I then ran the command web-ext sign with the following flags:
--channel, which can be listed or unlisted depending on whether or not the extension should be listed publicly or available for self-distribution only, --api-key, and --api-secret.

Once it was signed successfully, Firefox generated a web-ext-artifacts folder, in which there was a new file with an xpi extension.

Step 6: Installing the Firefox Add-On

Now that I had the xpi file, I was ready to install the extension. I typed about:addons in the URL to get to the add-ons manager page. On the top right was a gear icon, which showed an additional menu once clicked. One of the menu options allowed me to install an add-on from a file, which I used to upload the xpi file.

Step 7: Enabling Permissions

Even if URLs are listed in the host_permissions key in the manifest.json file, they're not enabled by default in Firefox. Instead, users need to navigate to those URLs, click on the extension, and enable the permissions for those pages directly.

For example, for the Reddit to Old Reddit extension, a user would go to reddit.com, click the extension icon in the top right, and allow the extension to either run when they click the extension or on all pages on reddit.com. When a user refreshes the page, the extension will run as normal.

Main Takeaways:

Now that I've ported a few Chrome extensions to Firefox, I think there are a few issues to keep in mind.

All in all, once I had the steps down, porting each extension to Firefox was pretty straightforward - but figuring this out took a long time because the docs for both browsers seemed to be a bit unclear in this area. I hope that as we move closer to June 2024 Chrome will offer more support for cross-browser extensions and APIs so porting Chrome extensions to Firefox is easier for developers in the future.


Want more articles on browser extension development delivered straight to your inbox? Subscribe below!