web-extension-starter/docs/FIREFOX_MV2.md
2026-01-03 21:40:04 +05:30

5.4 KiB

Using Manifest V2 for Firefox

By default, this starter uses Manifest V3 for all browsers. However, if you need to support older Firefox versions (< 109) or prefer MV2 for Firefox, follow this guide.

Why Use MV2 for Firefox?

  • Older Firefox support: Firefox 109+ is required for MV3
  • Extended support: Mozilla has not announced a deprecation date for MV2
  • API differences: Some APIs work differently between MV2 and MV3

Required Changes

1. Update source/manifest.json

Replace the unified manifest with browser-specific versions:

{
  "__chrome__manifest_version": 3,
  "__firefox__manifest_version": 2,
  "name": "Sample WebExtension",
  "version": "0.0.0",

  "icons": {
    "16": "assets/icons/favicon-16.png",
    "32": "assets/icons/favicon-32.png",
    "48": "assets/icons/favicon-48.png",
    "128": "assets/icons/favicon-128.png"
  },
  "description": "Sample description",
  "homepage_url": "https://github.com/abhijithvijayan/web-extension-starter",
  "short_name": "Sample Name",

  "__chrome__permissions": [
    "activeTab",
    "storage"
  ],

  "__chrome__optional_permissions": [],

  "__chrome__host_permissions": [],

  "__chrome__optional_host_permissions": [
    "http://*/*",
    "https://*/*"
  ],

  "__firefox__permissions": [
    "activeTab",
    "storage",
    "http://*/*",
    "https://*/*"
  ],

  "__chrome__content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self';"
  },
  "__firefox__content_security_policy": "script-src 'self'; object-src 'self'",

  "__chrome|firefox__author": "abhijithvijayan",

  "__firefox__applications": {
    "gecko": {
      "id": "{754FB1AD-CC3B-4856-B6A0-7786F8CA9D17}"
    }
  },

  "__chrome__minimum_chrome_version": "88",

  "__chrome__action": {
    "default_popup": "Popup/popup.html",
    "default_icon": {
      "16": "assets/icons/favicon-16.png",
      "32": "assets/icons/favicon-32.png",
      "48": "assets/icons/favicon-48.png",
      "128": "assets/icons/favicon-128.png"
    },
    "default_title": "tiny title"
  },

  "__firefox__browser_action": {
    "default_popup": "Popup/popup.html",
    "default_icon": {
      "16": "assets/icons/favicon-16.png",
      "32": "assets/icons/favicon-32.png",
      "48": "assets/icons/favicon-48.png",
      "128": "assets/icons/favicon-128.png"
    },
    "default_title": "tiny title",
    "browser_style": false
  },

  "__chrome__options_page": "Options/options.html",
  "options_ui": {
    "page": "Options/options.html",
    "open_in_tab": true
  },

  "background": {
    "__chrome__service_worker": "assets/js/background.bundle.js",
    "__chrome__type": "module",
    "__firefox__scripts": [
      "assets/js/background.bundle.js"
    ]
  },

  "content_scripts": [
    {
      "run_at": "document_start",
      "matches": [
        "http://*/*",
        "https://*/*"
      ],
      "css": [],
      "js": [
        "assets/js/contentScript.bundle.js"
      ]
    }
  ],

  "__firefox__web_accessible_resources": [
    "assets/*"
  ],

  "__chrome__web_accessible_resources": [
    {
      "resources": ["assets/*"],
      "matches": ["http://*/*", "https://*/*"]
    }
  ]
}

2. Update vite.config.ts

Firefox MV2 background scripts don't support ES modules. Update the buildIIFEScripts plugin to also build the background script as IIFE for Firefox:

// Build scripts as IIFE (no ES module imports)
// Content scripts can't use ES modules when injected via manifest
buildIIFEScripts({
  scripts: [
    {
      name: 'contentScript',
      entry: path.resolve(sourcePath, 'ContentScript/index.ts'),
    },
    // Firefox MV2 background scripts don't support ES modules
    ...(targetBrowser === 'firefox'
      ? [
          {
            name: 'background',
            entry: path.resolve(sourcePath, 'Background/index.ts'),
          },
        ]
      : []),
  ],
  outDir: getOutDir(),
  isDevelopment,
}),

Also update the rollup input to exclude background for Firefox (since it's built as IIFE):

rollupOptions: {
  input: {
    popup: path.resolve(sourcePath, 'Popup/popup.html'),
    options: path.resolve(sourcePath, 'Options/options.html'),
    // Background script: Chrome MV3 uses ES modules (service worker)
    // Firefox MV2 is built separately as IIFE via buildIIFEScripts plugin
    ...(targetBrowser !== 'firefox'
      ? { background: path.resolve(sourcePath, 'Background/index.ts') }
      : {}),
  },
  // ... rest of config
}

Key Differences: MV2 vs MV3

Feature MV2 (Firefox) MV3 (Chrome/Firefox)
Background background.scripts background.service_worker (Chrome) / background.scripts with type: module (Firefox)
Action browser_action action
Host permissions In permissions array Separate host_permissions array
Web accessible resources String array Object array with resources and matches
CSP String Object with extension_pages
ES modules in background Not supported Supported

Browser Support with MV2

When using MV2 for Firefox:

Browser Version Manifest
Chrome 88+ MV3
Firefox 48+ MV2
Edge 88+ MV3
Opera 74+ MV3

Notes

  • The webextension-polyfill library handles API differences between browsers
  • Content scripts work the same way in both MV2 and MV3 (IIFE format)
  • Test thoroughly on both browsers when using mixed manifest versions