跳轉到主要內容
擴充功能頁面在嚴格的 content security policy 下執行:不能用 eval、不能用 new Function、不能執行動態編譯的程式。當某個功能真的需要這些能力時(樣板引擎、程式遊樂場、執行 AI 生成的片段),Manifest V3 提供了 sandbox 頁面:以失去 chrome.* API 存取為代價,換取較寬鬆 CSP 的擴充功能頁面。

宣告頁面

manifest.json
{
  "sandbox": {
    "pages": ["sandbox.html"]
  }
}
Extension.js 會把 sandbox.html 當成任何其他在 manifest 中宣告的 HTML 進入點來編譯:直接在裡面引用你的 .ts/.tsx 就會建置起來。進入點的寫法請見 HTML 指南

sandbox 頁面能做什麼、不能做什麼

能力Sandbox 頁面
evalnew Function
獨立來源(與其他頁面隔離)
chrome.* 擴充功能 API
直接存取 chrome.storage
與嵌入者用 postMessage 通訊
sandbox 頁面在獨立來源下執行,所以它無法讀取擴充功能儲存空間、呼叫擴充功能 API,也碰不到其他頁面的 DOM。所有資料都必須透過訊息傳遞流通。

嵌入與通訊

從一般的擴充功能頁面用 iframe 載入 sandbox,再透過 postMessage 交談:
newtab.html
<iframe id="sandbox" src="sandbox.html"></iframe>
newtab.ts
const frame = document.getElementById("sandbox") as HTMLIFrameElement;

frame.addEventListener("load", () => {
  frame.contentWindow?.postMessage({ template: "Hello {{name}}" }, "*");
});

window.addEventListener("message", (event) => {
  console.log("rendered:", event.data.html);
});
sandbox.ts
window.addEventListener("message", (event) => {
  const render = new Function("name", `return \`${event.data.template}\`;`);
  event.source?.postMessage({ html: render("world") }, { targetOrigin: "*" });
});
嵌入它的頁面保有 chrome.* 的存取權,並擔任中介者:它讀取儲存空間、呼叫 API,並把純資料傳進與傳出 sandbox。

別搞混:background.type

一個常見的混淆:"background": {"type": "module"} 跟沙箱化完全無關。它宣告 background service worker 為 ES module,讓 import 在註冊時就能運作。當你的 background 進入點使用 ESM 語法時,Extension.js 會自動設定它,所以你很少需要自己手寫。如果你的疑問是 service worker 裡的 import 失敗,請見 Background scripts;如果是想執行動態程式,那你來對地方了。

自訂 sandbox CSP

你可以透過 manifest 中的 content_security_policy.sandbox 進一步放寬或收緊 sandbox 的 CSP。請把 script-src 維持得跟該功能允許的一樣窄;sandbox 的存在是為了圈住動態程式,而不是用來繞過安全審查。安全檢核清單涵蓋了審查者會看哪些項目。

延伸閱讀