The background process is the persistent engine of the Zotero Connector. Unlike injected content scripts, which live and die with the tab they are injected into, the background context outlives any individual page and maintains shared state: the translator cache, per-tab translation results, connector preferences, and the online/offline status of the Zotero desktop application. It acts as the middle-layer between the translation framework running in inject scripts and either the Zotero desktop HTTP server or the zotero.org cloud API.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/zotero/zotero-connectors/llms.txt
Use this file to discover all available pages before exploring further.
MV2 vs MV3: Event Page vs Service Worker
Manifest V2 — Event Page (Firefox)
Manifest V2 — Event Page (Firefox)
In Manifest V2 the background context is a persistent event page loaded from a list of scripts. The
backgroundIncludeBrowserExt array in gulpfile.js (plus background.js itself) defines which scripts are concatenated. The page persists for the lifetime of the browser session and has full access to DOM APIs.Manifest V3 — Service Worker (Chrome / Edge)
Manifest V3 — Service Worker (Chrome / Edge)
In Manifest V3 the background becomes a service worker, which Chrome can terminate at any time to save memory. The entry point is The
src/browserExt/background-worker.js:/*BACKGROUND SCRIPTS*/ placeholder is filled by the build pipeline with the same backgroundIncludeBrowserExt script list. keep-mv3-alive.js and background.js are appended after the shared scripts.Service workers do not have a
window object and cannot use browser APIs that require a live DOM (e.g. DOMParser). Those operations are delegated to an offscreen document instead — see the Offscreen Document section below.What the Background Process Does
Translator Cache (Zotero.Translators)
Zotero.Translators (src/common/translators.js) loads translator metadata from extension preferences on startup and maintains four in-memory caches keyed by type (import, export, web, search):
keepTranslatorsUpdated() runs a recursive timer loop that calls updateFromRemote() every 24 hours (ZOTERO_CONFIG.REPOSITORY_CHECK_INTERVAL). Metadata is first requested from the Zotero desktop client; if unavailable, it falls back to the zotero.org translator repository.
URL-Match Translator Detection
When a new page URL is reported (Zotero.Translators.getWebTranslatorsForLocation(url, rootURL)), the background iterates over the web cache and tests each translator’s target regexp against the URL. Only translators that match are returned to the injected script for the heavier detectWeb() phase.
Extension Toolbar Icon & Badge
Zotero.Connector_Browser._updateExtensionUI(tab) in src/browserExt/background.js calls browser.action.setIcon() and browser.action.setTitle() to reflect the current tab state:
| Condition | Icon shown |
|---|---|
| Translators found | Item-type icon (e.g. attachment-pdf.png, treeitem-journalArticle.png) |
| PDF in frame, no translator | images/pdf.png |
| No translator | Gray webpage icon (treeitem-webpage-gray.png) |
| Disabled URL (file://, extension page) | Zotero-Z icon, button disabled |
Connector Preferences (Zotero.Prefs)
Zotero.Prefs wraps browser.storage to provide synchronous get/set and asynchronous getAsync access to extension preferences. Preferences include translatorMetadata, firstUse, automaticSnapshots, and per-translator code caches.
Routing Translated Items
When a save is triggered,Zotero.Connector.callMethod(endpoint, data) (src/common/connector.js) POSTs to http://127.0.0.1:23119/connector/<endpoint>. If the request fails with HTTP status 0 (connection refused), isOnline is set to false and Zotero.Connector.onStateChange(false) fires, which disables Zotero.ContentTypeHandler and causes subsequent saves to fall back to the zotero.org API.
Background Script Bundle
The full background bundle (fromgulpfile.js) is:
Offscreen Document (MV3 Only)
Chrome MV3 service workers lack a DOM, so any code that needsDOMParser (used by translators) must run in an offscreen document — a hidden, tab-less HTML page that Chrome allows a service worker to create.
Zotero.OffscreenManager (src/browserExt/background/offscreenManager.js) owns the lifecycle:
offscreen/offscreen.html + offscreen/offscreen.js) hosts the full translation sandbox. When a translation is requested, the background’s OffscreenManager.sendMessage(message, payload, tab, frameId) forwards the work to the offscreen page via MessagingGeneric, receives the result, and relays it back to the inject script.
The
offscreen permission must be declared in manifest-v3.json to use browser.offscreen.createDocument(). The offscreen document is subject to Chrome’s idle-cleanup policy and may be destroyed; offscreenManager handles re-creation transparently.Keeping the MV3 Service Worker Alive
Chrome terminates idle service workers after roughly 30 seconds.src/browserExt/keep-mv3-alive.js runs a polling loop to prevent this during active operations:
Zotero.Connector_Browser.shouldKeepServiceWorkerAlive() returns truthy whenever long-running tasks are active (e.g. a Google Docs integration HTTP round-trip). The internal counter _keepServiceWorkerAlive is mutated by a reference-counted setter:
Message Listener Registration
Zotero.Messaging.init() in src/common/messaging.js registers the central browser.runtime.onMessage listener that handles all inject-to-background RPC calls:
receiveMessage() splits the message name on ".", resolves Zotero[NAMESPACE][METHOD], applies any configured background.postReceive / background.preSend hooks from MESSAGES, and returns the result to the inject side.
Background Initialization Sequence
WhenZotero.initGlobal() is called at the bottom of background.js:
Core namespaces initialized
Zotero.Prefs, Zotero.HTTP, Zotero.Connector, and other shared modules are set up.Translators initialized
Zotero.Translators.init() loads cached metadata from preferences or fetches from the remote repo.Offscreen document created (MV3/Chromium only)
Zotero.Connector_Browser.init() calls Zotero.OffscreenManager.init(), which creates the offscreen document and waits for it to signal readiness.Tab info persistence (MV3)
_tabInfo is hydrated from Zotero.Utilities.Connector.createMV3PersistentObject('tabInfo', …) so per-tab translator state survives service-worker restarts.