Skip to main content
Manifest V3 的 service worker 沒有 DOM。當背景邏輯需要解析 DOM、播放音訊、存取剪貼簿,或其他只有 window 才有的 API 時,Chrome 的答案就是 offscreen document:一個隱形的擴充功能頁面,由你透過 chrome.offscreen 按需建立。

設定

manifest.json 中請求權限:
manifest.json
Offscreen 頁面並不是 manifest 的進入點,因此透過 pages/ 特殊資料夾宣告,Extension.js 會像處理其他 HTML 進入點一樣編譯它:
pages/offscreen.html

建立、重複使用、關閉

Chrome 每個擴充功能只允許存在一個 offscreen document,當已經有一個時再呼叫 createDocument 會丟出例外。可靠的做法是先用一個 ensure 函式檢查是否已有現存的文件:
background.ts
creating 這把鎖很重要:兩個事件可能會在文件尚未存在前競相進入 ensureOffscreen,而第二次的 createDocument 呼叫就會丟出例外。 工作完成後關閉它以釋放記憶體:

選擇 reason

reasons 陣列會告訴 Chrome 這份文件為何存在。常見的值有: 有一個行為值得留意:使用 AUDIO_PLAYBACK 時,Chrome 會在音訊停止播放後約 30 秒自動關閉該文件。其他 reason 下,文件會一直存在,直到你關閉它或擴充功能卸載為止。

從背景存取剪貼簿

寫入剪貼簿是需要 offscreen document 最常見的理由。navigator.clipboard 位於 window 上, 而 Manifest V3 的 service worker 沒有 window,因此寫入必須發生在一個真正的頁面上。 請求權限並給出對應的 reason:
manifest.json
建立 offscreen 頁面時,加上一個給 copy 指令使用的 textarea:
pages/offscreen.html
pages/offscreen.ts
接著從 service worker 把文字送出去:
background.ts
這種情境下,請把 chrome.offscreen.Reason.CLIPBOARD 傳給 createDocument document.execCommand("copy") 才是這裡可行的呼叫。較新的 navigator.clipboard.writeText 需要一個取得焦點的文件,而 offscreen document 永遠不會取得焦點。 讀取剪貼簿需要 clipboardRead 權限,以及同一份文件。 內容腳本,或使用者自己開啟的擴充功能頁面,都不需要這一套。兩者都有真正的 window,因此在一次使用者手勢之後,navigator.clipboard.writeText 就能運作。

Extension.js 會幫你打包這個頁面

chrome.offscreen.createDocument 接收的是一個 URL 字串,而不是 import。Extension.js 會在建置期間讀取這個字串,並把該 HTML 頁面以及頁面載入的一切都加進輸出。就算只有 service worker 提到過這個頁面,它一樣會進到 dist/ 這個字串必須是純字面值才行。在執行階段組出來的 URL 對建置是不可見的,頁面就會被漏掉。

與文件通訊

Offscreen documents 使用標準的 runtime messaging。請為你的訊息加上範圍標記,讓其他介面可以忽略它們:
pages/offscreen.ts

Firefox

Firefox 並未實作 chrome.offscreen。它的 Manifest V3 背景以 event page 的形式執行,原本就具備 DOM 存取能力,因此同樣的 DOM 工作可以直接在背景腳本中執行。請使用瀏覽器專屬的 manifest 欄位搭配能力檢查(typeof chrome.offscreen !== "undefined")來進行分支處理。

延伸閱讀