This page covers the background page architecture of uBlock Origin (MV2): the central µBlock singleton object, the startup sequence, and the messaging system through which all extension components communicate. This is specific to the classic MV2 product; for the MV3 (uBOLite) runtime, see MV3 Runtime System. For the browser abstraction layer (vAPI) that the background process depends on, see Browser Abstraction Layer (vAPI).
The central shared state of the entire MV2 background process is a single object called µBlock, exported as the default from src/js/background.js and imported everywhere as µb.
background.js does not contain logic — it defines the data structure of the singleton and a handful of utility class extensions. All method implementations are attached to µb in other modules (storage.js, tab.js, ublock.js, etc.) during the module loading phase.
Diagram: µBlock Singleton Structure
Sources: src/js/background.js44-268
| Property | Type | Purpose |
|---|---|---|
userSettings | Object | Live copy of user-facing settings (merged from storage + admin overrides) |
userSettingsDefault | Object | Default values for all user settings |
hiddenSettings | Object | Advanced/power-user settings not exposed in the UI |
hiddenSettingsAdmin | Object | Admin-enforced hidden settings (take precedence over user values) |
selectedFilterLists | Array | Keys of currently enabled filter lists |
availableFilterLists | Object | Metadata for all known filter lists |
netWhitelist | Map | Per-hostname trusted-site directives |
pageStores | Map | Map of tabId → PageStore for all tracked tabs |
requestStats | Object | Global blockedCount / allowedCount counters |
readyToFilter | boolean | Set to true only after all filter lists are loaded; gates content script responses |
filteringContext | µBlock.FilteringContext | Shared instance reused for request evaluation |
systemSettings | Object | Magic numbers (compiledMagic, selfieMagic) used to detect stale cached data |
isReadyPromise | Promise | Resolved once startup is fully complete; importers can await it |
Sources: src/js/background.js147-268
background.js defines µBlock.FilteringContext as a subclass of FilteringContext from src/js/filtering-context.js It adds methods that tie a request context to the tab system:
fromTabId(tabId) — populates tabOrigin, tabHostname, tabDomain from tabContextManagerfromWebrequestDetails(details) — constructs a full context from a webRequest event detail objecttoLogger() — writes the context's filter match results to the loggerThe singleton instance µb.filteringContext is reused across requests to avoid allocation overhead.
Sources: src/js/background.js273-396
All background-process initialization is driven by the top-level IIFE in src/js/start.js It imports all background modules (which attach methods to µb as a side effect), then runs an async initialization sequence.
Diagram: Startup Sequence in start.js
Sources: src/js/start.js380-546
start.js imports the following modules, each of which attaches methods to µb as side effects:
import './vapi-common.js' // platform base
import './vapi-background.js' // browser API wrappers
import './vapi-background-ext.js' // extended browser wrappers
import './commands.js' // keyboard shortcut handlers
import './messaging.js' // IPC channel registrations
import './storage.js' // storage read/write methods on µb
import './tab.js' // tab/pagestore management on µb
import './ublock.js' // whitelist, firewall, picker methods on µb
import './utils.js' // misc utility methods on µb
Sources: src/js/start.js22-34
If userSettings.suspendUntilListsAreLoaded is true (the default on platforms where vAPI.Net.canSuspend() returns true), vAPI.net is kept suspended during startup, meaning network requests are held but not processed. Once readyToFilter is set and webRequest.start() is called, the suspension lifts and queued requests are processed.
Sources: src/js/start.js214-231 src/js/start.js477
To avoid recompiling filter lists from scratch on every startup, the filtering engines can be serialized to a "selfie" stored in cacheStorage. On startup, µb.selfieManager.load() is attempted first. If the magic numbers in the selfie match µb.systemSettings.compiledMagic and µb.systemSettings.selfieMagic, the selfie is used and loadFilterLists() is skipped.
When compiledMagic or selfieMagic are incremented in background.js, all existing selfies are invalidated on next startup.
Sources: src/js/start.js272-286 src/js/start.js445-461 src/js/background.js183-196
After the filtering engines are ready, initializeTabs() in start.js queries all currently open browser tabs via vAPI.tabs.query. For each non-discarded tab, it:
µb.tabContextManager.commit(id, url) to register the tab URLµb.bindTabToPageStore(id, 'tabCommitted', tab) to create a PageStoreSources: src/js/start.js91-135
All IPC between the background page, content scripts, and UI pages is routed through vAPI.messaging. The background side is configured entirely in src/js/messaging.js
vAPI.messaging provides two registration methods:
| Method | Purpose |
|---|---|
vAPI.messaging.setup(handler) | Registers the nameless default handler (privileged) |
vAPI.messaging.listen({ name, listener, privileged }) | Registers a named channel handler |
The privileged flag controls security: messages arriving on a privileged channel must come from extension pages (popup, dashboard, logger), not from content scripts.
Diagram: Messaging Channels in messaging.js
Sources: src/js/messaging.js259 src/js/messaging.js632-636 src/js/messaging.js831-834 src/js/messaging.js892-895 src/js/messaging.js992-996
The default handler is registered via vAPI.messaging.setup(onMessage) and handles messages from any privileged sender that don't match a named channel. Key messages:
request.what | Action |
|---|---|
getAssetContent | Fetches a filter list asset via io.get() |
reloadAllFilters | Calls µb.loadFilterLists() |
applyFilterListSelection | Calls µb.applyFilterListSelection(request) |
userSettings | Calls µb.changeUserSettings(name, value) |
createUserFilter | Calls µb.createUserFilters(request) |
getWhitelist / setWhitelist | Reads/writes µb.netWhitelist |
launchElementPicker | Calls µb.elementPickerExec(...) |
toggleHostnameSwitch | Calls µb.toggleHostnameSwitch(request) |
reloadTab | Calls vAPI.tabs.reload() or vAPI.tabs.replace() |
listsFromNetFilter | Calls staticFilteringReverseLookup.fromNetFilter() |
Sources: src/js/messaging.js94-261
popupPanel Channel (Privileged)Handles all requests from the popup UI. The most important message is getPopupData, which calls popupDataFromRequest(). This assembles a popupData object containing:
pageStore.counts)sessionFirewall state)sessionSwitches)hostnameDict and cnameMap for the firewall gridOther messages in this channel: toggleFirewallRule, toggleNetFiltering, toggleHostnameSwitch, saveFirewallRules, revertFirewallRules, hasPopupContentChanged.
Sources: src/js/messaging.js271-639
contentscript Channel (Unprivileged)Handles requests from content scripts injected into web pages. The key message is retrieveContentScriptParameters, which:
µb.readyToFilterPageStore for the tabcosmeticFilteringEngine.retrieveSpecificSelectors() to get CSS selectorscontentscript-extra.js (procedural filters)scriptletFilteringEngine.injectNow()Other messages: cosmeticFiltersInjected, getCollapsibleBlockedRequests, retrieveGenericCosmeticSelectors, messageToLogger, maybeGoodPopup.
Sources: src/js/messaging.js648-837
dashboard Channel (Privileged)Handles requests from the settings dashboard. Key operations:
request.what | Action |
|---|---|
getLists | Returns all available/selected filter list metadata |
backupUserData | Serializes all settings + filters for export |
restoreUserData | Restores from a backup and calls vAPI.app.restart() |
resetUserData | Clears cacheStorage and vAPI.storage, then restarts |
getLocalData | Returns storage usage and backup metadata |
Sources: src/js/messaging.js1008-1200 (approximate range in the full file)
Storage methods are defined in src/js/storage.js and attached to µb. They use vAPI.storage (for persistent user data) and cacheStorage (for compiled filter lists and selfies).
Diagram: Storage Method Responsibilities
Sources: src/js/storage.js51-480
loadHiddenSettings() fetches both vAPI.adminStorage (managed enterprise settings) and vAPI.storage (user settings). Admin settings take precedence — they are written into both hiddenSettingsAdmin and hiddenSettingsDefault, preventing user override.
Sources: src/js/storage.js226-290
Blocked/allowed request counts are maintained in µb.requestStats and persisted periodically (every 3.6 minutes via saveTimer, with a quick 23-second save to sessionStorage via quickSaveTimer). The incrementRequestStats(blocked, allowed) method drives these timers.
Sources: src/js/storage.js88-163
storage.js uses onBroadcast from src/js/broadcast.js to react to internal events. For example, when hiddenSettingsChanged is broadcast, it updates CNAME uncloaking options and refreshes trusted list prefixes.
Sources: src/js/storage.js305-319 src/js/storage.js445-448
Tab state is managed through two cooperating systems defined in src/js/tab.js and src/js/pagestore.js
Diagram: Tab State Objects
Sources: src/js/tab.js490-560 src/js/pagestore.js347-422
µb.tabContextManager (defined in tab.js) maintains a Map<tabId, TabContext>. A TabContext tracks the current root document URL for a tab. This is kept separate from PageStore because tab URL assignment and network request events are asynchronous and can arrive out of order.
Key methods on tabContextManager:
commit(tabId, url) — associates a URL with a tab (called on navigation events)lookup(tabId) — returns the TabContext or nullmustLookup(tabId) — returns a TabContext, creating a synthetic one for behind-the-scene tabs if neededSources: src/js/tab.js490-560
PageStore (in pagestore.js) tracks everything that happened in a tab during a page load:
frames (Map<frameId, FrameStore>) — frame URL hierarchyhostnameDetailsMap — per-hostname request counts (blocked/allowed)netFilteringCache — 15-second TTL cache of recent filter decisionsjournal — append-only log of requests used to assign requests to the correct page after asynchronous navigation eventscounts — aggregate blocked/allowed counters for the popupPageStore instances are pooled in PageStore.junkyard to reduce GC pressure.
Sources: src/js/pagestore.js347-490
Because navigation events and network request events are asynchronous and may arrive out of order, PageStore uses a journal (journalAddRequest, journalAddRootFrame, journalProcess) to defer the assignment of requests to hostname counters until navigation is committed.
Sources: src/js/pagestore.js676-750
vAPI.app.onShutdown (registered in start.js) resets all filtering engines and stops the asset updater cleanly:
staticFilteringReverseLookup.shutdown()
io.updateStop()
staticNetFilteringEngine.reset()
staticExtFilteringEngine.reset()
sessionFirewall.reset() permanentFirewall.reset()
sessionURLFiltering.reset() permanentURLFiltering.reset()
sessionSwitches.reset() permanentSwitches.reset()
Sources: src/js/start.js64-75
Diagram: How µb Coordinates Background Subsystems
Sources: src/js/start.js22-56 src/js/background.js147-264 src/js/messaging.js1-58 src/js/traffic.js1-45
Refresh this wiki
This wiki was recently refreshed. Please wait 2 days to refresh again.