Someone on the internet did the thing you might have idly wondered about but never acted on: they tried to install every Firefox extension available on addons.mozilla.org. The result is exactly the kind of chaotic systems experiment that produces genuinely useful data about an ecosystem nobody has fully mapped.
The asterisk in the title matters. “Every*” is doing real work there, and understanding what qualifies that claim gets you most of the way to understanding how Firefox’s extension infrastructure actually operates.
The AMO API and What It Exposes
The Mozilla Add-ons (AMO) platform exposes a public REST API that has been the backbone of third-party extension tooling for years. You can enumerate extensions via paginated listing endpoints, filter by type (extension, theme, language pack, dictionary), sort by various criteria, and retrieve detailed metadata for each add-on including its current and historical XPI download URLs.
A typical request to the listing endpoint looks like:
GET https://addons.mozilla.org/api/v5/addons/search/?type=extension&sort=created&page=1&page_size=50
Each response includes a count field telling you the total number of add-ons matching the query. At any given time, AMO hosts somewhere in the range of 30,000 to 40,000 publicly listed extensions, with a long tail of unlisted add-ons distributed through other channels. The API returns next and previous links for pagination, making it straightforward to enumerate the entire catalogue with a simple loop.
The XPI files themselves are just ZIP archives with a .xpi extension. Unzip any of them and you find a manifest.json at the root, a directory of JavaScript and CSS resources, and sometimes a _locales directory for internationalization. There is nothing structurally exotic about them. The complexity is in what the manifests declare, not how the files are packaged.
The Signing Requirement
Here is where the asterisk starts to fill in. Firefox, unlike Chrome, requires that extensions be cryptographically signed by Mozilla before they can be installed in release builds. This policy has been in place since Firefox 48 in 2016, and it is enforced at the browser level, not just the store level.
The signing flow works like this: a developer submits an XPI to AMO, Mozilla’s automated review system validates it and checks for policy violations, and if it passes, AMO returns a signed XPI with a signature embedded in the META-INF/ directory of the ZIP. The signature covers the manifest and all content files, so a modified extension will fail the signature check at install time.
This means that any extension that was submitted but rejected, or that was listed and then removed from AMO, becomes uninstallable in standard Firefox even if you have the XPI file. Developer Edition and ESR builds allow disabling signature enforcement via xpinstall.signatures.required, but release Firefox does not expose that preference in the UI and ignores it even if set manually since Firefox 73.
For a bulk installation project, this cuts the installable set down immediately. Removed extensions, extensions that were submitted but never completed review, and any extensions distributed outside AMO without proper signing are all off the table unless you are running a non-release Firefox build.
Manifest V2 vs. V3 and What Is Currently Installable
Firefox’s extension landscape is also in the middle of a Manifest V3 migration that adds another dimension to the installability question. Firefox’s MV3 implementation differs meaningfully from Chrome’s, primarily in that Firefox preserves the blocking WebRequest API that Chrome deprecated, which is the API that makes content blockers like uBlock Origin work effectively.
Most extensions on AMO are still MV2. Some older extensions declare applications or browser_specific_settings keys in their manifests with minimum Firefox version requirements. An extension that declares it requires Firefox 90+ will refuse to install on anything older. An extension targeting a very old Firefox version may use APIs that have since been removed or reorganized.
When you try to install several thousand extensions in a single Firefox profile, you are also stacking permissions and content scripts from every one of them. Extensions that inject content scripts into <all_urls> or *://*/* are modifying every page load. An extension that declares webRequest blocking permissions and installs a blocking listener will add latency to every network request the browser makes. Multiply that by hundreds of extensions and you have a browser that is technically functional but practically unusable.
The Profile Structure and What Breaks
Firefox stores extensions in the profile directory under extensions/ as extracted directories or XPI files, depending on how they were installed. The extension state is tracked in extensions.json, a large JSON file that records installation metadata, permissions granted, and startup behavior for every installed add-on.
This file is read on every Firefox startup and parsed synchronously as part of the extension loading sequence. A profile with thousands of installed extensions will have an extensions.json in the hundreds of megabytes. The parse time for that file alone becomes a measurable fraction of startup time, and the subsequent initialization of each extension’s background scripts compounds it further.
Background pages (MV2) and background service workers (MV3) are both launched at startup for extensions that declare them. With enough extensions installed, you are spawning hundreds of JavaScript contexts before the browser even renders your first tab. Memory usage climbs accordingly, and garbage collection pauses become frequent enough to affect interactive performance.
Some extensions also store data in IndexedDB or through the storage.local API, which maps to LevelDB on disk. With enough extensions writing to storage at startup, you can hit file descriptor limits or produce lock contention on the LevelDB files that back each extension’s storage area.
What the Broken Ones Look Like
Extensions that fail to install cleanly tend to fail in a few recognizable patterns. Some throw during manifest parsing because they use a key that Firefox does not recognize and has not chosen to ignore silently. Others install successfully but crash their background script on first run because they depend on a non-standard API or call a Chrome-specific method like chrome.runtime.getPlatformInfo in a way that Firefox’s compatibility shim does not cover.
A distinct category of failure comes from extensions that were built for very old Firefox and predate the WebExtensions API entirely. Before Firefox 57, extensions could use the older XUL/XPCOM-based system (often called “legacy add-ons”). Firefox dropped support for these entirely in 57, the so-called “Firefox Quantum” release. AMO no longer serves installable versions of these to modern Firefox, but the metadata still exists in the API responses, contributing to the gap between total AMO listings and actually installable extensions.
There are also extensions that are technically valid and installable but do something immediately destructive: replacing the new tab page with a blank white screen, intercepting all keyboard events and swallowing them, or setting a proxy that routes all traffic through a server that no longer exists. Installing thousands of extensions without reviewing them first is basically a guided tour through a decade of browser extension antipatterns.
The Broader Ecosystem Picture
What this kind of experiment reveals, more than any individual finding, is the shape of the extension ecosystem over time. AMO’s catalogue is a geological record of browser development. Extensions from 2010 exist alongside extensions uploaded last month. The version skew, the API surface changes, the policy shifts around permissions and signing, and the transition from legacy to WebExtensions are all preserved in the listing data.
Chrome’s extension ecosystem has similar archaeology, but Chrome’s Web Store is less open to programmatic enumeration and the signing flow is more opaque to outside analysis. Firefox’s commitment to publishing the AMO codebase as open source and maintaining a documented API makes this kind of experiment much more tractable on the Mozilla side.
The fact that someone can spin up a script, query the AMO API, download thousands of XPIs, and install them into a Firefox profile without any special access is itself a feature of the ecosystem. It is the same openness that lets web-ext exist as a first-class developer tool, that lets security researchers audit extensions at scale, and that lets the community build tools like AMO Investigator for extension analysis.
The asterisk in “every*” is a reasonable acknowledgment that a catalogue this large, this old, and this varied is never going to be fully installable in a single pass. But the attempt produces a map of the ecosystem that you cannot get any other way. The failures are data too.