# 05 — Firefox extension specification Owner: lane **EXT**. TypeScript, MV3, `browser.*` promise APIs, built with esbuild, packaged with `web-ext`. ## 1. Why Firefox MV3 is actually good news here Chrome's MV3 removed blocking `webRequest`, which is why IDM-style "grab the download before the browser starts it" is hard there. **Firefox kept blocking `webRequest` in MV3.** That means we can inspect response headers and cancel the browser's own download, which is exactly the interception model IDM uses. Build for Firefox first and do not compromise the design to stay Chrome-portable. ## 2. Capture pipeline ``` onBeforeSendHeaders ──► stash request headers by requestId (ring buffer, 5 min TTL) │ onHeadersReceived ───► shouldCapture(details, headers, settings)? │ │ │ yes│ │ ▼ │ cookies.getAll(url) ──► transport.send("capture.offer", {...}) │ │ │ daemon replies {action:"take", taskId} │ ▼ └──────────────► return {cancel: true} ← browser never starts the download ``` `shouldCapture` returns true when **any** of: - `Content-Disposition: attachment` present, or - the file extension is in the user's monitored list (Options → File Types, mirrored from the daemon so the two never disagree), or - `Content-Type` is in the monitored MIME list and not `text/html`, or - `Content-Length` > `minSizeBytes` (default 1 MiB) **and** the type is not renderable. And **none** of: - the tab is a `blob:`/`data:` URL we generated, - the host is on the user's exclusion list, - the response is a navigation to an HTML page, - the user held the bypass modifier (default: Alt) on the click. **Fail-open, always.** If the daemon is unreachable or the RPC times out (750 ms budget), `return {}` and let Firefox download it normally. A download manager that eats downloads when its daemon is down is worse than no download manager. This rule is non-negotiable and has a dedicated conformance test. Belt-and-braces second path: `browser.downloads.onCreated` → if it slipped past the header hook, `downloads.cancel(id)` + `downloads.erase(id)` and offer to the daemon. Some downloads (form POSTs, service-worker-generated blobs) only surface here. ## 3. Other surfaces | Surface | Behaviour | |---|---| | Context menu (link) | "Download with Velox" | | Context menu (image/video/audio) | "Download with Velox" | | Context menu (page/selection) | "Download all links with Velox…" → opens the batch dialog with the harvested list | | Toolbar popup | Active downloads with live progress (via `event.task.progress` relayed over the transport), pause/resume, "Add URL", speed indicator, daemon status dot | | Media panel | Floating in-page button on a tab where a video/HLS/DASH stream was detected — "Download this video ▾" listing quality variants | | Options page | Transport & pairing · monitored types · min size · exclusion list · default category/folder · bypass modifier · enable/disable capture | | Keyboard | Configurable command to grab the current tab's URL | **Media detection:** `webRequest` sniffing for `.m3u8`, `.mpd`, `Content-Type: application/vnd.apple.mpegurl` / `dash+xml`, plus a content script observing `MediaSource.addSourceBuffer` and `