firefox-extension · diff
v2.0.0 to v2.0.0
68 added, 351 removed. Audit A to A.
---
name: firefox-extension
description: Use when developing Firefox WebExtensions - Manifest V2/V3 configuration, WebExtension APIs, content security policy, web-ext CLI, cross-browser support with the polyfill, AMO submission, or debugging extensions
metadata:
author: mte90
version: 2.0.0
tags:
- firefox
- webextension
- browser-extension
- mozilla
- amo
- manifest-v3
---
- # Firefox WebExtension Development
-
- Complete reference for building, testing, and publishing browser extensions for Mozilla Firefox.
-
- ## Overview
-
- Firefox extensions use the WebExtensions API with the `browser.*` namespace (Promise-based natively). Firefox supports both Manifest V2 and V3, with MV2 NOT deprecated (unlike Chrome).
-
- **Key Characteristics:**
- - Global namespace: `browser` (Promise-based)
- - Both MV2 and MV3 supported
- - Firefox-specific APIs: `sidebarAction`, `userScripts`, `contextualIdentities`, `protocol_handlers`
- - Submission via AMO (addons.mozilla.org)
-
- ## Manifest Structure
-
- ### Manifest V3 (Recommended, Firefox 109+)
-
- ```json
- {
- "manifest_version": 3,
- "name": "Extension Name",
- "version": "1.0.0",
- "description": "Brief description",
-
- "browser_specific_settings": {
- "gecko": {
- "id": "extension@example.com",
- "strict_min_version": "109.0"
- },
- "gecko_android": {
- "strict_min_version": "109.0"
- }
- },
-
- "background": {
- "service_worker": "background.js",
- "type": "module"
- },
-
- "action": {
- "default_popup": "popup.html",
- "default_icon": {
- "16": "icons/icon-16.png",
- "48": "icons/icon-48.png",
- "96": "icons/icon-96.png"
- },
- "default_title": "Extension Title"
- },
-
- "content_scripts": [
- {
- "matches": ["https://*/*"],
- "js": ["content.js"],
- "css": ["styles.css"],
- "run_at": "document_idle"
- }
- ],
-
- "permissions": ["storage", "activeTab"],
- "host_permissions": ["https://api.example.com/*"],
-
- "content_security_policy": {
- "extension_pages": "script-src 'self'; object-src 'self';"
- }
- }
-
- > See `references/security-crossbrowser.md` for full CSP reference.
-
- ### Manifest V2 (Still Supported)
-
- ```json
- {
- "manifest_version": 2,
- "name": "Extension Name",
- "version": "1.0.0",
-
- "browser_specific_settings": {
- "gecko": {
- "id": "extension@example.com",
- "strict_min_version": "78.0"
- }
- },
-
- "background": {
- "scripts": ["background.js"],
- "persistent": false
- },
-
- "browser_action": {
- "default_popup": "popup.html",
- "default_icon": "icons/icon-48.png"
- },
-
- "permissions": ["storage", "activeTab", "https://*/*"]
- }
- ```
-
- ### MV2 vs MV3 Key Differences
-
- | Feature | MV2 | MV3 | Firefox Version Notes |
- |---------|-----|-----|----------------------|
- | Toolbar button | `browser_action` | `action` | `action` available since Firefox 109+ |
- | Background | `background.scripts` / `page` | `background.service_worker` | Service workers since Firefox 109+; MV2 background pages still supported |
- | Host permissions | In `permissions` | Separate `host_permissions` | MV3 separation enforced from Firefox 109+ |
- | Default CSP | `script-src 'self'; object-src 'self';` | `script-src 'self'; upgrade-insecure-requests;` | CSP tightening in MV3 blocks inline scripts |
- | Request blocking | `webRequest.onBeforeRequest` | `declarativeNetRequest` | MV3: `webRequest` is non-blocking only; blocking requires `declarativeNetRequest` (rule limits apply) |
- | **MV3 availability** | N/A | Supported | Firefox 109+ (ESR 115+) — verify against MDN for ESR boundaries |
- | **MV2 deprecation** | Active | N/A | Firefox has NOT deprecated MV2 (unlike Chrome); verify current status on MDN |
-
- **MV3 forces these practical changes:**
- - No remote code: all logic must be bundled; no `eval()`, no dynamic `import()` from network
- - `background.scripts` → `background.service_worker` (event-driven, ephemeral)
- - CSP tightening: inline scripts (`onclick="..."`) blocked; move to event listeners
- - `webRequest` blocking → `declarativeNetRequest` (max 30,000 rules, 5,000 dynamic rules)
-
- ### All Manifest Keys Reference
-
- **Metadata:**
- - `name` (required) - Extension name
- - `version` (required) - Version string (e.g., "1.0.0")
- - `description` - Short description
- - `author` - Author name
- - `homepage_url` - Extension homepage
- - `icons` - Extension icons object
-
- **Firefox-Specific:**
- - `browser_specific_settings.gecko.id` - **Required for AMO**
- - `browser_specific_settings.gecko.strict_min_version` - Minimum Firefox version
- - `browser_specific_settings.gecko.strict_max_version` - Maximum Firefox version
- - `browser_specific_settings.gecko_android` - Android-specific settings
-
- **Background & Scripts:**
- - `background` - Service worker (MV3) or scripts/page (MV2)
- - `content_scripts` - Scripts injected into pages
- - `userScripts` - User script registration (Firefox-only)
- - `declarative_net_request` - Rule-based request modification
-
- **UI Components:**
- - `action` (MV3) / `browser_action` (MV2) - Toolbar button
- - `page_action` - Address bar button
- - `sidebar_action` - Sidebar panel (Firefox-only)
- - `options_ui` - Options page configuration
- - `devtools_page` - DevTools extension page
-
- **Permissions:**
- - `permissions` - API permissions
- - `host_permissions` (MV3) - Host access
- - `optional_permissions` - Optional API permissions
- - `optional_host_permissions` - Optional host access
-
- ### Permissions Justification Discipline
-
- High-risk permissions trigger AMO reviewer scrutiny. Document the narrowest alternative:
-
- | Permission | What it grants | Narrower alternative |
- |------------|----------------|---------------------|
- | `<all_urls>` | Access to every website | Specific host patterns (`https://*.example.com/*`) |
- | `webRequest` | Inspect/modify all network traffic | `declarativeNetRequest` for static rules; limit scope with host_permissions |
- | `cookies` | Read/write all cookies | `host_permissions` for specific domains; justify in AMO submission |
- | `debugger` | Attach debugger to any tab | Rarely justified; consider `devtools` API instead |
- | `tabs` | Read all tab metadata | `activeTab` for on-demand access; `scripting` API for content injection |
- | `bookmarks` | Read/modify all bookmarks | None — justify necessity in AMO notes |
- | `history` | Read browsing history | None — AMO requires strong justification |
-
- **AMO review trigger:** Permission justification must appear in the extension listing description or AMO notes. Vague justifications lead to rejection.
-
- **Other:**
- - `commands` - Keyboard shortcuts
- - `omnibox` - Address bar integration
- - `web_accessible_resources` - Resources accessible from pages
- - `protocol_handlers` - Custom protocol handlers
- - `chrome_settings_overrides` - Override homepage/search
- - `chrome_url_overrides` - Override new tab/bookmarks
-
- ## web-ext CLI Reference
-
- ### Installation
-
- ```bash
- npm install -g web-ext
- ```
-
- ### Commands
-
- **Run extension (development):**
- ```bash
- web-ext run # Default Firefox
- web-ext run --firefox-path /path/to/firefox # Specific Firefox
- web-ext run --target firefox-android # Android
- web-ext run --profile-create-new # Clean profile
- web-ext run --start-url https://example.com # Start URL
- web-ext run --verbose # Verbose output
- web-ext run --no-reload # Disable auto-reload
- ```
-
- **Lint extension:**
- ```bash
- web-ext lint # Validate manifest and code
- web-ext lint --warnings-as-errors
- web-ext lint --self-hosted # Skip AMO-specific checks
- ```
-
- **Build extension:**
- ```bash
- web-ext build # Create .zip in web-ext-artifacts/
- web-ext build --source-dir ./src
- web-ext build --artifacts-dir ./dist
- ```
-
- **Sign extension:**
- ```bash
- web-ext sign --api-key $KEY --api-secret $SECRET
- web-ext sign --channel listed # Public on AMO
- web-ext sign --channel unlisted # Direct download
- ```
-
- **Other commands:**
- ```bash
- web-ext docs # Open documentation
- web-ext dump-config # Show configuration
- ```
-
- ### Configuration File (.web-ext-config.js)
-
- ```javascript
- module.exports = {
- sourceDir: './src',
- artifactsDir: './dist',
+ > **Objective:** Use this skill to reduce CSP-violation rejections, manifest-schema breakages, AMO-review failures, and cross-browser compat bugs by enforcing manifest-version discipline, CSP-safe patterns, and web-ext-verification rules below.
- run: {
- firefox: '/Applications/Firefox.app/Contents/MacOS/firefox',
- startUrl: 'https://example.com',
- pref: ['extensions.webextensions.debug=true']
- },
+ # Firefox WebExtension Development
- build: {
- overwriteDest: true
- },
+ ## Core Rules (do X because Y)
- sign: {
- apiKey: process.env.AMO_API_KEY,
- apiSecret: process.env.AMO_API_SECRET,
- channel: 'listed'
- }
- };
- ```
+ - **Use `browser` namespace because** it is Promise-based natively in Firefox, avoiding callback boilerplate.
+ - **Pin `manifest_version: 3` for new extensions because** MV2 is sunsetting and AMO warns on new MV2 submissions.
+ - **Always include `browser_specific_settings.gecko.id` because** AMO rejects extensions without a unique ID.
+ - **Set `strict_min_version: "109.0"` for MV3 because** service workers and the `action` API require Firefox 109+.
+ - **Use `web-ext run --profile-create-new` for testing because** stale profile state causes false positives.
- ### Environment Variables
+ See `references/apis.md` for the complete WebExtension API namespace list (51 APIs) and usage patterns.
- ```bash
- WEB_EXT_API_KEY=your_key
- WEB_EXT_API_SECRET=your_secret
- WEB_EXT_SOURCE_DIR=./src
- WEB_EXT_ARTIFACTS_DIR=./dist
- WEB_EXT_FIREFOX=/path/to/firefox
- ```
+ ## Manifest V3 Rules
- ## Best Practices
+ See `references/manifest-v3.md` for the full MV3 manifest schema reference.
- ### Background Service Worker (MV3)
+ ### MV2 vs MV3 Migration Rules
- ```javascript
- // Keep service worker alive briefly
- let keepAlive;
+ - **Use `action` instead of `browser_action` because** MV3 requires the unified `action` key (Firefox 109+).
+ - **Use `background.service_worker` instead of `background.scripts` because** MV3 enforces event-driven, ephemeral background logic.
+ - **Move `permissions` with host URLs to `host_permissions` because** MV3 separates API permissions from host access.
+ - **Replace `webRequest` blocking with `declarativeNetRequest` because** MV3 makes `webRequest` non-blocking only (max 30,000 static rules, 5,000 dynamic).
+ - **Remove all `eval()`, `new Function()`, and dynamic imports because** MV3 CSP forbids remote code and AMO rejects it immediately.
+ - **Use `addEventListener` instead of inline `onclick=""` because** MV3 CSP blocks inline scripts.
- browser.runtime.onMessage.addListener((msg) => {
- clearTimeout(keepAlive);
- keepAlive = setTimeout(() => {}, 25000);
- return handleMessage(msg);
- });
- ```
+ See `references/manifest-keys.md` for the complete manifest key enumeration with Firefox-specific keys.
- ### Error Handling
+ ## Permissions Discipline
- ```javascript
- async function safeAsync(fn) {
- try {
- return await fn();
- } catch (error) {
- console.error('Error:', error);
- return { error: error.message };
- }
- }
- ```
+ See `references/permissions.md` for the full permissions reference with AMO review triggers.
- ### Performance
+ ### High-Risk Permissions (AMO Scrutiny)
- - Use `browser.tabs.query()` with specific filters
- - Debounce frequent operations
- - Cache storage API results
- - Use `alarms` API instead of `setInterval` in background
+ - **Avoid `<all_urls>` because** it triggers automatic reviewer scrutiny; use specific host patterns instead.
+ - **Justify `cookies`, `history`, `bookmarks` in AMO notes because** reviewers reject vague justifications.
+ - **Prefer `activeTab` over `tabs` because** it requests access only when the user interacts with your extension.
+ - **Use `declarativeNetRequest` instead of `webRequest` for static rules because** it is MV3-compliant and AMO-favored.
- ## File Structure Template
+ ## web-ext CLI Rules
- ```
- my-extension/
- ├── manifest.json
- ├── background.js
- ├── content.js
- ├── popup.html
- ├── popup.js
- ├── options.html
- ├── options.js
- ├── browser-polyfill.js
- ├── styles/
- │ ├── popup.css
- │ └── content.css
- ├── icons/
- │ ├── icon-16.png
- │ ├── icon-32.png
- │ ├── icon-48.png
- │ └── icon-96.png
- ├── _locales/
- │ ├── en/
- │ │ └── messages.json
- │ └── it/
- │ └── messages.json
- ├── .web-ext-config.js
- ├── package.json
- └── README.md
- ```
+ ### Development Workflow Rules
- ## AMO Submission & Review
+ - **Run `web-ext lint` before every build because** it catches manifest errors and AMO policy violations early.
+ - **Use `web-ext run --verbose` for debugging because** it shows injection failures and CSP violations in real-time.
+ - **Test with `--profile-create-new` because** stale cookies/cache mask real-world failures.
+ - **Run `web-ext build` to create the submission artifact because** it validates the zip structure AMO expects.
+ - **Sign with `--channel listed` for AMO submission because** unlisted bypasses human review.
- **Official references:**
- - [AMO Review Guidelines](https://reviewers.addons.mozilla.org/)
- - [Distribution Policy](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/AMO/Policy)
- - [Rejection Guidelines](https://extensionworkshop.com/documentation/publish/add-on-policies/)
+ See `references/web-ext.md` for the complete web-ext command reference with all flags.
- ### Automatic Rejection Triggers
+ ## CSP & Security Rules
- These issues cause immediate automated or quick human rejection:
+ See `references/security-crossbrowser.md` for the full CSP reference and cross-browser polyfill patterns.
- | Issue | Fix |
- |-------|-----||
- | Missing `browser_specific_settings.gecko.id` | Add unique UUID or email-based ID |
- | Data collection disclosure missing | Add `data_collection` field in manifest or privacy policy URL |
- | Homepage/source URL 404s | Verify URLs before submission; use permanent links |
- | Icon fails size rules | Provide 48x48 and 96x96 PNGs; no transparency issues |
- | Description reads as keyword spam | Write natural description; avoid stuffed keywords |
- | Missing permission justification | Document permission usage in AMO notes |
+ ### CSP-Safe Patterns (do X because Y)
- ### Human Reviewer Flags
+ - **Never use `eval()`, `new Function()`, or `Function("...")` because** AMO rejects remote code execution immediately.
+ - **Escape all dynamic content into DOM nodes instead of `innerHTML` because** CSP blocks inline scripts and XSS.
+ - **Use `'wasm-unsafe-eval'` only for WebAssembly because** the broader `'unsafe-eval'` triggers AMO rejection.
+ - **Remove `http://localhost:<port>` from CSP before submission because** it allows remote scripts in production.
+ - **Bundle all dependencies locally instead of CDN scripts because** CSP forbids remote script sources.
- Reviewers manually check for these red flags:
+ ### Cross-Browser Polyfill Rules
- - **Remote code execution:** Any `eval()`, `new Function()`, dynamic script loading from network
- - **Overbroad host permissions:** `<all_urls>` without clear justification
- - **Data collection beyond declared purpose:** Privacy policy must match actual behavior
- - **Hidden functionality:** Code paths not disclosed in listing
- - **Obfuscated code:** Minification ok; obfuscation triggers source code request
+ - **Use `webextension-polyfill` because** Firefox's `browser` API is Promise-based while Chrome uses callbacks.
+ - **Feature-detect Firefox-only APIs (`sidebarAction`, `userScripts`) because** they crash in Chrome without guards.
+ - **Use `browser.action || browser.browserAction` fallback because** MV2/MV3 have different key names.
- ### Review Timeline & Response
+ ## AMO Submission Rules
- - **Automated validation:** 5-15 minutes
- - **Human review (listed):** 1-7 days
- - **Human review (unlisted):** Faster, often same-day
+ ### Pre-Submission Checklist (do X because Y)
- **When rejected:**
- 1. Read reviewer notes carefully (found in Developer Hub)
- 2. Fix the specific issue; don't resubmit without changes
- 3. Add clarifying notes if the rejection was a misunderstanding
- 4. Resubmit within 30 days to keep the review queue position
+ - **Verify `web-ext lint` passes with no errors because** AMO's automated gate rejects manifest/schema errors.
+ - **Ensure `browser_specific_settings.gecko.id` is set because** AMO rejects extensions without a unique ID.
+ - **Provide 48x48 and 96x96 PNG icons because** AMO's size rules cause automatic rejection.
+ - **Add a privacy policy URL if collecting data because** AMO requires data collection disclosure.
+ - **Write a natural description (no keyword stuffing) because** reviewers flag spammy listings.
+ - **Document permission usage in AMO notes because** vague justifications lead to rejection.
- ### Data Collection Disclosure
+ ### Rejection Response Rules
- If your extension collects/transmits user data, you MUST:
- - Add a privacy policy URL in the manifest (`homepage_url` or dedicated field)
- - Disclose what data is collected, how, and why
- - Implement user consent before collection
- - Declare data retention policy
+ - **Read reviewer notes in Developer Hub before resubmitting because** fixes must address the specific issue.
+ - **Resubmit within 30 days because** the review queue position expires.
+ - **Add clarifying notes for misunderstandings because** reviewers may approve with explanation.
- See `references/testing-amo.md` for the full AMO checklist.
+ See `references/testing-amo.md` for the full AMO checklist, debugging commands, and troubleshooting table.
## Deep Dives
Load these reference files on demand for detailed information:
- - **WebExtension APIs** (51 namespaces, core patterns, Firefox-specific): `references/apis.md`
- - **Security & Cross-Browser** (CSP, webextension-polyfill): `references/security-crossbrowser.md`
- - **Testing & AMO** (debugging, troubleshooting, submission): `references/testing-amo.md`
+ - **WebExtension APIs** (51 namespaces, core patterns, Firefox-specific APIs): `references/apis.md`
+ - **Manifest V3 Schema** (complete MV3 manifest reference): `references/manifest-v3.md`
+ - **Manifest Keys** (all keys with Firefox-specific extensions): `references/manifest-keys.md`
+ - **Permissions** (AMO review triggers, narrower alternatives): `references/permissions.md`
+ - **web-ext CLI** (complete command reference): `references/web-ext.md`
+ - **Security & Cross-Browser** (CSP rules, polyfill patterns): `references/security-crossbrowser.md`
+ - **Testing & AMO** (debugging, troubleshooting, submission checklist): `references/testing-amo.md`
- ## References
+ ## External References
- [MDN WebExtensions](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
- [Extension Workshop](https://extensionworkshop.com/)
- - [Manifest V3 Migration](https://extensionworkshop.com/documentation/develop/manifest-v3-migration-guide/)
+ - [Manifest V3 Migration Guide](https://extensionworkshop.com/documentation/develop/manifest-v3-migration-guide/)
- [web-ext CLI Reference](https://extensionworkshop.com/documentation/develop/web-ext-command-reference/)
- [AMO Developer Hub](https://addons.mozilla.org/developers/)
- [webextension-polyfill](https://github.com/mozilla/webextension-polyfill)
- - [Browser Compatibility](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Browser_support_for_JavaScript_APIs)