Browser Extension

September 12, 2026 ยท View on GitHub

The extension is MV3 in both browsers with one shared manifest base: Chromium runs the background as a service worker and Firefox as an event page. The toolbar click starts one explicit-action job on exactly the clicked tab. The background opens the dedicated job tab and performs finite source operations in the clicked tab; it does not reload the source page or install a persistent source collector. There is no fallback extension page or unrelated tab monitoring.

Discovery

Scanning begins only after an explicit toolbar action. The icon is grey while idle and blue with a dot while the job is active. The source binding is invalidated on navigation, tab close, cancellation, or worker restart. The background never polls, enumerates tabs, or rearms a source operation.

Candidates come from the monitored tab's own performance timeline. The background deliberately observes no traffic: a webRequest listener without host permissions is deaf, and activeTab does not enable observation. No permanent host permissions are declared.

crates/dezoomify-core runs through WASM inside the dedicated extension job tab. It recognizes formats from fetched bytes, not URL text. The first candidate whose bytes produce an image confirms detection in the job tab.

apps/extension/src/background/source-operations.ts contains the two self-contained functions passed to scripting.executeScript(). The first takes a bounded snapshot of the document URL and retained resource-timing entries when the job tab is ready. The second performs a tab-origin fetch and returns bounded structured-cloneable chunks. Neither operation registers a listener or observes resources after it returns. A later snapshot is bounded and deduplicated if discovery requests more candidates. If an operation cannot start or its binding is stale, the toolbar shows an error badge; the extension never silently opens another page.

Fetching

The extension fetches with tab-origin direct fetch under the narrowest grant: activeTab for the clicked tab, or an explicitly granted optional host permission for another origin or redirect target. It uses the current browser session and validates each operation's URL, method, declared headers, result shape, and byte cap.

Readable metadata, processed tiles, and clean saves use tab-origin bytes. The extension never uses the metadata CORS proxy. If readable fetching is unavailable for an ordinary unprocessed tile, the job may use an ordinary <img> display fallback; the result stays visible but tainted and cannot be read or programmatically saved.

Job and save

The job tab uses the WASM core to discover an image, select a level, plan tiles, apply processing, and assemble the result on a canvas. A clean canvas is saved through a Blob URL and anchor click, which needs no downloads permission. A tainted canvas finishes as display-only and never receives pixel reads or serialization calls afterward.

User-visible job failures stay in the job tab. Background failures keep an error badge and action title until the user clicks again or the source tab leaves the bound page.

Native handoff

The extension may offer native handoff for huge outputs or local destinations. It reaches native only through allowlisted Native Messaging. A fresh challenge and one-use nonce bind messages to one consent session and prevent replay; they do not establish identity. Cookies pass only after a prompt names the destination origins and scope, and are not intentionally persisted.

Packaging

WXT generates both MV3 manifests from apps/extension/wxt.config.ts. Chromium uses the bundled background.js service worker. Firefox uses the same classic IIFE artifact through background.scripts; packaging parses it with node --check. The store package ships only the background finite-operation coordinator, the React dedicated job tab (bundling the workspace @dezoomify/shared-ui and @dezoomify/browser-runtime packages), icons, and WASM. No source content script or fallback extension-page entry is packaged or tested.

Extension build, development, test, and release entry points regenerate the WASM glue from the current Rust source before WXT builds. The extension test gate does so before its unit suite, whose worker contract runs a real generated WASM session through the first discovery round trip; an absent, stale, or incompatible binding is a blocking failure before browser E2E starts. WXT packaging requires the root workspace dependencies installed by cargo xtask setup or pnpm install --frozen-lockfile; it never invokes a second package manager.

User-facing job behavior comes from the same protocol and scenarios as web and desktop. See Testing and Releases. For user-facing use, see browser extension.

Diagnostics

The background logs structured console lines ([dezoomify:background] <level> <code> <detail>). Lifecycle milestones are logged at info, recoverable states at warn, and terminal failures at error. Logged URLs are redacted. User-visible failures travel through the dedicated job tab rather than disappearing with the toolbar state.