Firefox Extension Development Expert
Build, debug, publish, and maintain Firefox WebExtensions. Covers Manifest V2 and V3, all 51 browser.* APIs, cross-browser compatibility, AMO publishing, and Firefox-specific features. Actively writes code, scaffolds projects, and fetches live documentation from MDN.
When to Use
Trigger on: Firefox extension, WebExtension, browser add-on, manifest.json for extensions, content scripts, background scripts, browser.tabs, browser.storage, AMO publishing, web-ext CLI, Manifest V3 migration, cross-browser extension, sidebar extension, native messaging extension.
Documentation Lookup Strategy
Knowledge sourced from MDN WebExtensions docs + live search for details not covered in reference files.
Lookup priority
- Check reference files first -
references/browser-api-reference.md,references/manifest-schema.md,references/amo-publishing.md,references/mdn-api-urls.md - WebFetch MDN page - if the reference files lack detail on a specific method, event, or parameter, fetch the MDN page directly. URL pattern:
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/<APIName>/<method>. Seereferences/mdn-api-urls.mdfor the full URL index. - WebSearch as fallback - when the exact URL is unknown or for newer/experimental APIs, search with:
site:developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions <query> - Extension Workshop - for publishing, review policies, migration guides:
site:extensionworkshop.com <query>
When to search
- User asks about a specific API method/event not detailed in reference files
- User asks about browser compatibility for a specific feature
- User asks about experimental or recently added APIs
- User references an error message or unexpected behavior
- User needs examples beyond what reference files provide
Key resources
- MDN docs source: mdn/content
- Official examples: mdn/webextensions-examples
- Browser API polyfill: mozilla/webextension-polyfill
- web-ext CLI: mozilla/web-ext
- Extension Workshop: extensionworkshop.com
Operational Modes
Scaffolding a New Extension
When the user wants to create a new extension:
- Ask: MV2 or MV3? (default MV3)
- Ask: what does the extension do? (content script, popup, sidebar, DevTools, background-only)
- Generate project structure based on answers:
Minimal MV3 scaffold:
my-extension/
manifest.json # MV3, gecko ID, permissions
background.js # event page with onInstalled listener
icons/
icon-48.png # placeholder note
icon-96.png
With popup:
+ popup/
popup.html
popup.js
popup.css
With content script:
+ content-scripts/
content.js
styles.css
With sidebar (Firefox-only):
+ sidebar/
sidebar.html
sidebar.js
With options page:
+ options/
options.html
options.js
With i18n:
+ _locales/
en/messages.json
- Generate manifest.json with correct keys for chosen components
- Generate starter code with common patterns (message passing, storage, event listeners)
- Include a
.web-extension-idfile reminder for AMO
Writing Extension Code
When implementing features:
- Always use
browser.*namespace (notchrome.*) - Firefox-first - Always use Promises/async-await (not callbacks)
- Register all event listeners at top level in background scripts (MV3 requirement)
- Use
storage.local/storage.sessionfor state (not globals, not localStorage) - Use
browser.alarmsfor periodic tasks (not setTimeout/setInterval in MV3) - Sanitize any user/page input before DOM insertion
- Use
activeTabpermission when possible instead of broad host permissions - Include error handling for all API calls (
try/catchor.catch())
Debugging Assistance
When the user reports a bug or unexpected behavior:
- Check manifest.json for permission/key issues
- Check content script match patterns
- Check MV3 event page rules (top-level listener registration, state persistence)
- Suggest
web-ext lintfor common issues - Point to relevant debugging tools (
about:debugging, Browser Console, Extension Debugger) - If the issue involves a specific API, fetch the MDN page for current behavior/compatibility
Extension Anatomy
Every extension is a collection of files packaged as a .zip. The only mandatory file is manifest.json.
File Structure
my-extension/
manifest.json # REQUIRED - metadata + pointers
background.js # event handling, extension lifecycle
content-scripts/
content.js # injected into web pages
styles.css # injected styles
popup/
popup.html # toolbar button popup
popup.js
popup.css
sidebar/
sidebar.html # sidebar panel (Firefox-only)
sidebar.js
options/
options.html # settings page
options.js
icons/
icon-48.png
icon-96.png
_locales/ # i18n strings
en/messages.json
web-accessible-resources/ # files accessible to page scripts
Component Types
| Component | Purpose | API Access |
|---|---|---|
| Background scripts | Browser event handling, extension lifecycle | Full (all permitted APIs) |
| Content scripts | DOM manipulation in web pages | Limited subset + messaging |
| Popup | Toolbar button UI (HTML) | Full (same as background) |
| Sidebar | Side panel UI (Firefox-only) | Full (same as background) |
| Options page | User preferences UI | Full (same as background) |
| Extension pages | Custom HTML loaded via tabs.create/windows.create |
Full (same as background) |
| DevTools page | Developer tools panel | Full + devtools APIs |
Content Scripts
Loading Methods
1. Declarative (manifest.json):
{
"content_scripts": [{
"matches": ["*://*.example.com/*"],
"exclude_matches": ["*://admin.example.com/*"],
"js": ["jquery.js", "content.js"],
"css": ["styles.css"],
"run_at": "document_idle",
"all_frames": false,
"match_about_blank": false
}]
}
run_at values:
document_start- before DOM constructiondocument_end- DOM ready, before subresourcesdocument_idle- default, after page load
2. Programmatic (MV3 scripting API):
await browser.scripting.executeScript({ target: { tabId }, files: ["content.js"] });
await browser.scripting.insertCSS({ target: { tabId }, files: ["styles.css"] });
await browser.scripting.registerContentScripts([{
id: "my-script", matches: ["*://*.example.com/*"],
js: ["content.js"], persistAcrossSessions: true
}]);
Content Script Environment
- DOM access: Full access to page DOM via standard Web APIs
- JS isolation: Cannot see page-defined JS variables (Xray vision in Firefox)
- API access: Limited subset -
runtime,i18n,storage,extension,menus.getTargetElement
Background Scripts
MV3 Event Page (Firefox)
Firefox MV3 uses event pages (non-persistent background pages with DOM access), NOT service workers like Chrome.
Critical rules for event pages:
- Register listeners at top level synchronously - deferred registration breaks wake-up
- Persist state via
storage.session/storage.local, not globals - Use
browser.alarmsinstead ofsetTimeout/setInterval runtime.onSuspendfires before unload - use for cleanup
Message Passing
One-Off Messages
// Content script -> Background
browser.runtime.sendMessage({ type: "getData", key: "user" }).then(response => console.log(response));
// Background -> Content script
await browser.tabs.sendMessage(tabId, { type: "update", data: newData });
Port Connections (Long-Lived)
const port = browser.runtime.connect({ name: "data-stream" });
port.postMessage({ greeting: "hello" });
port.onMessage.addListener((msg) => { /* handle */ });
Content Script to Page Script (window.postMessage)
Security warning: Never eval() data received from page scripts. Extensions are privileged code - hostile pages can exploit this.
Match Patterns
Syntax: <scheme>://<host><path>
| Pattern | Matches |
|---|---|
<all_urls> |
All URLs with supported schemes |
*://*/* |
All HTTP/HTTPS/WS URLs |
*://*.mozilla.org/* |
mozilla.org + subdomains |
https://mozilla.org/ |
Exact host, HTTPS only |
Permissions
Install-Time vs Optional
permissions+host_permissions(MV3) granted at installoptional_permissions+optional_host_permissionsrequested at runtime from user action handler- Users can revoke optional permissions in Add-ons Manager (since Firefox 84)
Native Messaging
Enables communication between extension and native applications.
- Permission:
nativeMessaging - Native app manifest: JSON with
name,path,type: "stdio",allowed_extensions - Wire protocol: JSON UTF-8, 32-bit unsigned length prefix
- Limits: Max 1 MB from app, max 4 GB to app
- Use
runtime.connectNative()for connection-based orruntime.sendNativeMessage()for one-off
Firefox-Specific Features
| Feature | API | Notes |
|---|---|---|
| Container Tabs | contextualIdentities |
Isolated cookie stores, requires contextualIdentities + cookies |
| Sidebar | sidebarAction |
Firefox-exclusive sidebar panel |
| Protocol Handlers | manifest protocol_handlers |
Register as URI protocol handler |
| userScripts (MV3) | userScripts |
Isolated execution worlds, must be in optional_permissions |
| webRequest Blocking in MV3 | webRequest |
Firefox retains blocking capability alongside DNR |
| Other exclusive | dns, pkcs11, find, captivePortal, tabs.saveAsPDF(), tabs.print() |
Manifest V3 Migration
Firefox MV3 vs Chrome MV3
| Feature | Firefox MV3 | Chrome MV3 |
|---|---|---|
| Background | Event pages (DOM access) | Service workers (no DOM) |
| webRequest blocking | Fully supported + DNR | Removed; DNR only |
| host_permissions | Treated as optional (user-grantable) | Granted at install |
| MV2 support | Supported on Firefox ESR through 2025; deprecation timeline announced by Mozilla | Removed (stable rollout completed through 2025) |
| Data cloning | Structured clone algorithm | JSON serialization |
Migration Checklist (MV2 to MV3)
- Set
"manifest_version": 3 - Move host permissions from
permissionstohost_permissions/optional_host_permissions - Rename
browser_actiontoaction,_execute_browser_actionto_execute_action - Remove
browser_stylefrom all manifest keys - Replace
tabs.executeScript/insertCSS/removeCSSwithbrowser.scriptingAPI - Convert background to non-persistent
- Move all event listeners to top-level synchronous registration
- Replace global state variables with
storage.session/storage.local - Replace
setTimeout/setIntervalwithbrowser.alarms - Update CSP format:
"content_security_policy": { "extension_pages": "..." } - Update
web_accessible_resourcesto object array format - Remove all
eval()usage (forbidden in MV3)
Cross-Browser Compatibility
Namespace
- Firefox:
browser.*(Promises) +chrome.*(callbacks) - Chrome:
chrome.*(MV2: callbacks, MV3: Promises from Chrome 121+) - Safari:
browser.*(Promises)
Strategy
- Code for Firefox first using
browser.*+ Promises - Apply webextension-polyfill for Chrome/Edge/Opera
- Use runtime feature detection for platform-specific APIs
- Create separate
manifest.jsonper browser
Development Workflow
web-ext CLI
web-ext run --firefox nightly --browser-console --start-url https://example.com
web-ext lint
web-ext build --overwrite-dest
web-ext sign --channel unlisted --api-key $WEB_EXT_API_KEY --api-secret $WEB_EXT_API_SECRET
Loading Temporarily
- Open Firefox -
about:debugging#/runtime/this-firefox - Click "Load Temporary Add-on..."
- Select
manifest.jsonfrom extension directory
Debugging
- Browser Console:
Ctrl+Shift+J- background script logs - Extension Debugger:
about:debugging- Inspect on extension - Content Script Console: Regular page DevTools (select extension context)
Common Patterns
- Toolbar button with popup:
actionmanifest key + popup HTML/JS - Context menu:
browser.menus.create()+menus.onClicked - Page modification: Content script with DOM manipulation
- Options page:
options_uimanifest key + storage sync - Web request interception:
webRequest.onBeforeRequestwith blocking - Badge text:
browser.action.setBadgeText()+setBadgeBackgroundColor() - Periodic tasks (MV3):
browser.alarms.create()+alarms.onAlarm
Debugging Checklist
- Check
about:debugging- is the extension loaded? Any errors? - Browser Console (
Ctrl+Shift+J) - background script errors - Page DevTools Console - content script errors (select extension context)
- Permissions - are all needed permissions declared?
- Match patterns - do content script patterns match the target URLs?
- CSP violations - check for Content Security Policy blocks
- MV3 event page - are listeners registered synchronously at top level?
- Storage - using
browser.storageinstead ofwindow.localStorage? - Run
web-ext lintto catch common issues
References
references/browser-api-reference.md- Complete list of all 51 browser.* APIs with methods, events, and permissionsreferences/manifest-schema.md- Full manifest.json key reference with MV2/MV3 examplesreferences/amo-publishing.md- AMO publishing checklist, review policies, CSP, security best practices, i18nreferences/mdn-api-urls.md- Direct MDN URL index for all browser.* APIs, manifest keys, and Extension Workshop resourcesreferences/best-practices.md- Best practices, pitfalls, and anti-patterns: JS patterns, Workers, sessions, startup, security, performance, cross-browser