# 03 — Chrome extension 細節 extension 是 MV3 (Manifest v3),由 4 個 JS context 組成。這份解釋每個 context 的責任、它們之間怎麼傳訊息、有哪些**非顯而易見的 invariants**(特別是跨 tab 隔離——v2.3.4 那個 bug 就在這層)。 ## 1. Manifest 在說什麼 [`manifest.json`](../../chrome-extension/manifest.json) 三組關鍵宣告: ```json "permissions": [ "storage", // chrome.storage.sync (settings) + .local (av task history) "contextMenus", // 右鍵選單「Send to NAS」 "notifications", // download 完成通知 "webRequest", // 攔截 m3u8/mp4 URL(不需 webRequestBlocking — 只 observe) "webNavigation", // 主 frame 換頁時清掉該 tab 的 detected URLs "sidePanel", // 側邊欄 UI "cookies" // worker 抓 cookies 給 NAS 用 ], "host_permissions": [""], // 全網才能攔到所有 video URL "background": { "service_worker": "background.js" }, "content_scripts": [ { "js":["inject.js"], "world":"MAIN", "run_at":"document_start", "all_frames":true }, { "js":["content.js"], "run_at":"document_idle", "all_frames":true } ], "side_panel": { "default_path":"sidepanel.html" }, "options_page": "options/options.html" ``` `` host permission 是**必要的** — 沒它 webRequest 看不到任何網路請求。Chrome Web Store policy 對它很嚴,要解釋為什麼需要(用來在使用者瀏覽的任何網站偵測影片)。 ## 2. 四個 JS context 的責任分工 ``` ┌────────────────────────────────────────────────────────────────────────┐ │ Browser process │ │ │ │ ┌──────────────────────┐ │ │ │ background.js (SW) │ ← persistent-ish (idle 30s 才 unload) │ │ │ - webRequest 攔截 │ │ │ │ - 訊息路由 hub │ │ │ │ - capturedHeaders │ │ │ │ - currentTabUrls │ │ │ │ - AV-task pipeline │ │ │ └─────┬────────────────┘ │ │ │ chrome.runtime.sendMessage │ │ │ │ │ ┌─────┴────────────────┐ ┌────────────────────────────────────────┐ │ │ │ sidepanel.js + .html│ │ Per-tab context: │ │ │ │ - UI render │ │ ┌─────────────────────────────────┐ │ │ │ │ - send/cancel/sort │ │ │ inject.js (MAIN world) │ │ │ │ │ - polls /api/jobs │ │ │ - patch fetch / XHR │ │ │ │ │ │ │ │ - 偵測 manifest by content │ │ │ │ └──────────────────────┘ │ └────────────┬────────────────────┘ │ │ │ │ │ window.postMessage │ │ │ │ ┌────────────▼────────────────────┐ │ │ │ │ │ content.js (ISOLATED world) │ │ │ │ │ │ - 收 inject 訊息 │ │ │ │ │ │ - 抓 og:image / video.poster │ │ │ │ │ │ - forward to background │ │ │ │ │ └─────────────────────────────────┘ │ │ │ └────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────────────────┘ ``` ### 2.1 background.js — service worker 最重要、最複雜的一塊。**唯一**能監聽 `chrome.webRequest` 的 context。 主要職責: 1. **webRequest 攔截**(攔到符合 .m3u8/.mpd/.mp4/.mov 的 URL 就 register 進 `currentTabUrls[tabId]`) 2. **headers capture**(`onSendHeaders` 抓 actual cookies/Referer 存進 `capturedHeaders[url]`) 3. **訊息路由**(sidepanel / content script 來的 `getDetectedUrls`、`sendToNAS`、`manifestDetected` 等等) 4. **AV-task pipeline**(hidden mode 的 code-based 自動化下載) 5. **在 sidepanel 開的時候推 `detectedUrlsUpdated` 給它** key in-memory state(line 4-23 of [background.js](../../chrome-extension/background.js)): ```js let currentTabUrls = {}; // { [tabId]: [{url, timestamp, hitCount, ...}] } let currentTabUrlKeys = {}; // { [tabId]: Set } — for dedup let orphanUrlInfos = []; // service-worker / no-tabId 的 fallback let capturedHeaders = {}; // { [url]: { headers, timestamp, initiator, tabId } } let userClickedVideoByTab = {}; // 「user 真的點過播放」訊號 let pageThumbnailsByTab = {}; // og:image /