
chrome-extensions
熱門使用 Manifest V3 最佳實務建置並發布 Chrome 擴充功能。每當使用者要求建立、修改、除錯或理解 Chrome 瀏覽器擴充功能、附加元件,或任何涉及 Chrome 擴充功能 API 的事項時,請使用此技能。觸發關鍵字包括:'Chrome 擴充功能'、'瀏覽器擴充功能'、'manifest.json'、'內容腳本'、'service worker'(瀏覽器情境)、'彈出視窗'(瀏覽器擴充功能情境)、'側邊面板'、'chrome.* API'、'declarativeNetRequest'、'omnibox'、'內容選單'(擴充功能情境)、'userScripts'、'使用者腳本'、'腳本管理員',或任何要求建置與 Chrome 瀏覽器 UI 整合的功能。也適用於發布到 Chrome 線上應用程式商店:'發布擴充功能'、準備擴充功能以供發布、回應審查拒絕、撰寫權限說明,或草擬隱私權政策。
使用 Manifest V3 最佳實務建置並發布 Chrome 擴充功能。每當使用者要求建立、修改、除錯或理解 Chrome 瀏覽器擴充功能、附加元件,或任何涉及 Chrome 擴充功能 API 的事項時,請使用此技能。觸發關鍵字包括:'Chrome 擴充功能'、'瀏覽器擴充功能'、'manifest.json'、'內容腳本'、'service worker'(瀏覽器情境)、'彈出視窗'(瀏覽器擴充功能情境)、'側邊面板'、'chrome.* API'、'declarativeNetRequest'、'omnibox'、'內容選單'(擴充功能情境)、'userScripts'、'使用者腳本'、'腳本管理員',或任何要求建置與 Chrome 瀏覽器 UI 整合的功能。也適用於發布到 Chrome 線上應用程式商店:'發布擴充功能'、準備擴充功能以供發布、回應審查拒絕、撰寫權限說明,或草擬隱私權政策。
Chrome 擴充功能
使用 Manifest V3 建置生產級 Chrome 擴充功能,並發布至 Chrome 線上應用程式商店。
第一部分 — 建置擴充功能
強制規則
這些規則處理擴充功能最常見的損壞原因。違反任何一項都會導致無法運作的建置。
1. 圖示:只能參考你建立的檔案 — 或完全省略圖示
❌ 損壞 — 參考不存在的檔案,或對所有尺寸重複使用同一檔案:
"icons": { "16": "icon.png", "48": "icon.png", "128": "icon.png" }
✅ 正確 — 每個尺寸都是獨立檔案,且像素尺寸正確:
"icons": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" }
(其中 icon-16.png 為 16×16px,icon-48.png 為 48×48px,icon-128.png 為 128×128px)
✅ 也正確 — 若無法產生真實 PNG 檔案,則從 manifest 省略圖示:
(直接移除 "icons" 和 "default_icon" 欄位 — Chrome 會使用預設圖示)
如果你包含圖示參考,你必須建立實際的影像檔案。 用腳本產生(參見 references/extensions/icons.md)或省略。絕不要參考不存在的檔案。
2. 側邊面板:你必須提供開啟方式
定義 "side_panel": {"default_path": "..."} 並不會讓它可開啟。加入觸發器:
// 在 service-worker.js — 點擊擴充功能圖示時開啟側邊面板
// 重要:chrome.action.onClicked 只有在沒有 default_popup 時才會觸發
chrome.action.onClicked.addListener(async (tab) => {
await chrome.sidePanel.open({ windowId: tab.windowId });
});
如果擴充功能同時有彈出視窗和側邊面板,在彈出視窗中加入按鈕呼叫 chrome.sidePanel.open()。或者,使用 chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }) — 但屬性是 openPanelOnActionClick,不是 openPanelOnActionIconClick;"Icon" 變體會造成同步 TypeError,靜默中止 service worker。使用 setPanelBehavior 時,也不要同時定義 default_popup。參見 references/extensions/side-panel.md。
3. 程式碼執行:僅限沙盒 iframe
擴充功能 CSP 會封鎖所有擴充功能頁面中的 eval()、new Function()、內嵌 <script>。
// ❌ 損壞 — 直接 iframe DOM 存取會拋出 SecurityError
iframe.contentDocument.write(html);
// ❌ 損壞 — 在擴充功能頁面中使用 eval
eval(userCode); // CSP 封鎖此操作
// ✅ 選項 A:在 manifest 中設定沙盒 + postMessage
// manifest.json: { "sandbox": { "pages": ["sandbox.html"] } }
iframe.contentWindow.postMessage({ html, css, js }, '*');
// sandbox.html 接收並執行:
window.addEventListener('message', (e) => { eval(e.data.js); /* 在沙盒中允許 */ });
// ✅ 選項 B:Blob URL(建立獨立來源,繞過擴充功能 CSP)
iframe.src = URL.createObjectURL(new Blob([doc], { type: 'text/html' }));
// ✅ 選項 C:srcdoc
iframe.srcdoc = `<style>${css}</style>${html}<script>${js}<\/script>`;
完整細節參見 references/extensions/csp-sandbox.md。
4. tab.url 需要 tabs 權限
沒有它,tab.url 會靜默回傳 undefined — 不會拋出錯誤。
// manifest.json — 如果你在任何地方讀取 tab.url 或 tab.title,則為必要:
{ "permissions": ["tabs"] }
參見 references/extensions/tab-management.md。
5. 一律使用 async/await — 絕不使用 .then() 鏈
// ❌ 不好
chrome.tabs.query({active: true, currentWindow: true}).then(tabs => {
chrome.scripting.executeScript({target: {tabId: tabs[0].id}, files: ['content.js']}).then(() => {});
});
// ✅ 好
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['content.js'] });
對於執行非同步工作的 runtime.onMessage 監聽器:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
(async () => {
const data = await chrome.storage.local.get('key');
sendResponse({ data });
})();
return true; // 保持通道開啟
});
6. 內容腳本:不要阻塞主執行緒
當修改大量 DOM 元素時,使用 requestAnimationFrame 批次處理,並在批次之間讓出:
async function highlightAll(elements) {
const BATCH = 20;
for (let i = 0; i < elements.length; i += BATCH) {
await new Promise(r => requestAnimationFrame(() => {
elements.slice(i, i + BATCH).forEach(el => el.style.backgroundColor = 'yellow');
r();
}));
if (globalThis.scheduler?.yield) await scheduler.yield();
}
}
參見 references/extensions/content-scripts.md。
7. Service worker 是短暫的 — 絕不要將狀態儲存在變數中
// ❌ 損壞 — 當 SW 終止時狀態遺失(約 30 秒無活動)
let count = 0;
chrome.tabs.onUpdated.addListener(() => { count++; });
// ✅ 正確 — 持久化在 chrome.storage,每個事件讀取
chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
if (changeInfo.status !== 'complete') return;
const { count = 0 } = await chrome.storage.local.get('count');
await chrome.storage.local.set({ count: count + 1 });
await chrome.action.setBadgeText({ text: String(count + 1) });
});
使用 chrome.alarms 代替 setTimeout/setInterval。參見 references/extensions/service-worker.md。
8. chrome.identity:開發與正式環境的擴充功能 ID 不同
使用 Google 登入時,OAuth client_id 綁定特定擴充功能 ID。ID 在未封裝的開發環境和 Chrome 線上應用程式商店之間會改變。
為了在開發期間穩定 ID,在 manifest.json 中加入 "key" 欄位:
- 封裝擴充功能一次(chrome://extensions → 封裝擴充功能)
- 從 .crx 中擷取公開金鑰
- 在 manifest.json 中加入
"key": "MIIBIjANBgkqh..."
務必記錄:「發布到 Chrome 線上應用程式商店後,請使用商店指派的擴充功能 ID 更新 OAuth client。」參見 references/extensions/auth-identity.md。
9. 內容選單:動作後顯示使用者回饋
當內容選單項目執行動作(儲存、複製等)時,向使用者確認。使用通知、徽章閃爍或注入的 toast — 不要讓動作靜默發生。完整的 toast 實作參見 references/extensions/context-menus.md。
10. Prompt API:可在 service worker、彈出視窗和側邊面板中使用
LanguageModel API 可在所有擴充功能情境中運作 — service worker、彈出視窗和側邊面板 — 無需額外的 manifest 權限。擴充功能也獲得 LanguageModel.params(),這在網頁上不可用:
const params = await LanguageModel.params();
// { defaultTopK: 3, maxTopK: 128, defaultTemperature: 1, maxTemperature: 2 }
對於一般 Prompt API 模式(可用性檢查、建立 session、串流),使用 modern-web-guidance 技能。擴充功能特定的接線範例參見 references/extensions/prompt-api.md。
11. chrome.action API 需要在 manifest 中有 action
使用 chrome.action.setBadgeText、chrome.action.setIcon 或 chrome.action.onClicked 需要在 manifest.json 中有 "action" 鍵 — 即使是空的。沒有它,chrome.action 是 undefined。
// ❌ 損壞 — manifest 沒有 "action" 鍵
await chrome.action.setBadgeText({ text: '5' });
// TypeError: Cannot read properties of undefined (reading 'setBadgeText')
// ✅ 修正 — 在 manifest.json 中加入 "action"(至少是空物件)
{ "action": {} }
// 或帶有彈出視窗:
{ "action": { "default_popup": "popup/popup.html" } }
12. activeTab 僅在直接使用者手勢時運作 — 不是從側邊面板
activeTab 僅在以下情況授予目前分頁的暫時存取權:
- 點擊擴充功能動作圖示
- 內容選單項目(包括
"tab"情境) - 來自
commandsAPI 的鍵盤快捷鍵 - 接受 omnibox 建議
它不授予在側邊面板中點擊按鈕、稍後開啟的彈出視窗按鈕,或任何程式化觸發的存取權。
// ❌ 損壞 — activeTab 無法從側邊面板按鈕點擊運作
document.getElementById('summarize').addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: () => document.body.innerText });
});
// ✅ 修正 — 改用 "tabs" 權限 + 特定 host_permissions
// manifest.json: { "permissions": ["tabs", "scripting"], "host_permissions": ["<all_urls>"] }
參見 references/extensions/side-panel.md。
13. DevTools 面板 URL 相對於擴充功能根目錄
建立 DevTools 面板時,面板 HTML 路徑相對於擴充功能根目錄,不是相對於呼叫 chrome.devtools.panels.create() 的 devtools 頁面。
// ❌ 損壞 — 路徑相對於 devtools/ 目錄
chrome.devtools.panels.create("My Panel", "", "panel/panel.html");
// ✅ 正確 — 從擴充功能根目錄的完整路徑
chrome.devtools.panels.create("My Panel", "", "devtools/panel/panel.html");
參見 references/extensions/devtools.md。
14. Offscreen 文件無法存取大多數 chrome.* API
Offscreen 文件(chrome.offscreen)受到嚴格限制。大多數 chrome.* API 不可用,包括 chrome.downloads、chrome.tabs、chrome.action 等。
// ❌ 損壞 — chrome.downloads 在 offscreen 文件中是 undefined
chrome.downloads.download({ url, filename: 'recording.webm' }); // TypeError
// ❌ 損壞 — chrome.action 在 offscreen 文件中是 undefined
chrome.action.setBadgeText({ text: 'REC' }); // TypeError
Offscreen 文件中唯一可用的 API 是:
chrome.runtime.sendMessage/chrome.runtime.onMessagechrome.runtime.getURL- 標準 Web API(DOM、fetch、MediaRecorder、Canvas、Web Audio 等)
經驗法則: Offscreen 文件執行 Web API 工作(錄製、解析、音訊)。Service worker 執行所有 chrome.* API 工作(下載、徽章更新、通知)。使用 chrome.runtime.sendMessage 在兩者之間橋接。參見 references/extensions/message-passing.md。
15. 通知和徽章圖示必須參考真實影像檔案
chrome.notifications.create() 需要有效的 iconUrl 指向實際影像檔案。如果檔案不存在或路徑錯誤,呼叫會失敗並出現 "Unable to download all specified images."
// ❌ 損壞 — 圖示檔案不存在
chrome.notifications.create('reminder', {
type: 'basic',
iconUrl: 'icons/icon-128.png', // 檔案不在擴充功能中!
title: 'Reminder',
message: 'Time is up!'
});
// ✅ 在執行時期透過 OffscreenCanvas 產生 data URL — 不需要檔案。
// 可重複使用的實作參見 `references/extensions/icons.md`。
const iconUrl = await getIconDataUrl();
chrome.notifications.create('reminder', { type: 'basic', iconUrl, title: 'Reminder', message: 'Time is up!' });
這適用於 chrome.* API 中的所有影像參考 — 通知、chrome.action.setIcon、內容選單圖示等。如果你參考檔案,它必須存在。
16. 分頁擷取:使用狀態鎖定防止雙重啟動
chrome.tabCapture.getMediaStreamId() 如果在前一個擷取仍活動時呼叫,會失敗並出現 "Cannot capture a tab with an active stream"。快速雙擊擴充功能圖示很容易觸發此問題。使用明確的狀態鎖定:
// ❌ 損壞 — 沒有防止快速點擊的保護
let isRecording = false;
chrome.action.onClicked.addListener(async (tab) => {
if (isRecording) { stopRecording(); isRecording = false; }
else { isRecording = true; startRecording(tab); } // 第二次點擊 = "active stream" 錯誤
});
// ✅ 正確 — 使用過渡狀態鎖定並發操作
// 狀態機:'idle' → 'starting' → 'recording' → 'stopping' → 'idle'
// 將狀態儲存在 chrome.storage.session(SW 重新啟動後仍存在,瀏覽器關閉時清除)
chrome.action.onClicked.addListener(async (tab) => {
const { recordingState = 'idle' } = await chrome.storage.session.get('recordingState');
if (recordingState === 'starting' || recordingState === 'stopping') return;
if (recordingState === 'idle') {
await chrome.storage.session.set({ recordingState: 'starting' });
try {
await startRecording(tab);
await chrome.storage.session.set({ recordingState: 'recording' });
await chrome.action.setBadgeText({ text: 'REC' });
await chrome.action.setBadgeBackgroundColor({ color: '#FF0000' });
} catch (err) {
console.error('Failed to start recording:', err);
await chrome.storage.session.set({ recordingState: 'idle' });
}
} else if (recordingState === 'recording') {
await chrome.storage.session.set({ recordingState: 'stopping' });
try { await stopRecording(); }
finally {
await chrome.storage.session.set({ recordingState: 'idle' });
await chrome.action.setBadgeText({ text: '' });
}
}
});
此模式適用於任何管理獨佔資源的 chrome API:chrome.tabCapture、chrome.desktopCapture、chrome.offscreen.createDocument(一次只允許一個 offscreen 文件)。參見 references/extensions/media-capture.md。
17. chrome.desktopCapture 需要具有 URL 存取權的目標分頁
從 service worker 呼叫 chrome.desktopCapture.chooseDesktopMedia() 時,你必須傳入活動分頁作為 targetTab 參數。分頁物件必須有 url 欄位,這需要 "tabs" 權限。
// ❌ 損壞 — 從 service worker 呼叫時沒有 targetTab
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], (streamId) => { ... });
// 錯誤:"A target tab is required when called from a service worker context."
// ❌ 損壞 — 分頁沒有 url 欄位(缺少 "tabs" 權限)
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], tab, (streamId) => { ... });
// 錯誤:"targetTab doesn't have URL field set."
// ✅ 正確 — manifest 中有 "tabs" 權限 + 傳入分頁物件
// manifest.json: { "permissions": ["tabs", "desktopCapture"] }
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], tab, (streamId) => {
if (!streamId) return; // 使用者取消
});
注意: 僅錄製分頁時,偏好使用 chrome.tabCapture.getMediaStreamId()。僅當使用者應選擇要擷取的螢幕或視窗時,才使用 chrome.desktopCapture。參見 references/extensions/media-capture.md。
18. 使用者腳本:四個不明顯的陷阱
chrome.userScripts 在執行時期執行使用者提供的程式碼。用於腳本管理員和使用者自動化 — 不是用於擴充功能隨附的腳本。
- 屬性存取時若未啟用會拋出例外。 Chrome 138+ 要求使用者在擴充功能詳細資料頁面切換「允許使用者腳本」;Chrome < 138 要求開發者模式。在任何
chrome.userScripts.*呼叫前,務必呼叫isUserScriptsAvailable(),並在回傳 false 時顯示錯誤 UI。 - 註冊的腳本在擴充功能更新時會被清除。 將設定持久化在
chrome.storage;在runtime.onInstalled的"update"原因中重新註冊。 - 訊息傳遞需要明確選擇加入。 先呼叫
configureWorld({ messaging: true });監聽runtime.onUserScriptMessage,而不是runtime.onMessage。 ScriptSource限制: 每個js條目必須恰好有code或file其中一個。id限制: 不能以_開頭。
參見 references/extensions/user-scripts.md。
19. chrome.windows 沒有 .query() 方法 — 使用 getAll、getLastFocused 或 getCurrent
與 chrome.tabs.query() 不同,chrome.windows API 沒有 .query() 方法。
// ❌ 損壞 — chrome.windows.query 不存在
const windows = await chrome.windows.query({ focused: true });
// TypeError: chrome.windows.query is not a function
// ✅ 正確 — 根據需求使用正確的方法
const focused = await chrome.windows.getLastFocused({ populate: true });
const current = await chrome.windows.getCurrent({ populate: true });
const all = await chrome.windows.getAll({ populate: true });
chrome.windows 方法: getAll、getLastFocused、getCurrent、get(windowId)、create、update、remove。參見 references/extensions/tab-management.md。
一律使用 Manifest V3
絕不產生 Manifest V2 程式碼。
background.service_worker而不是background.scriptschrome.action而不是chrome.browserActionchrome.scripting.executeScript而不是chrome.tabs.executeScripthost_permissions與permissions分開- HTML 中沒有內嵌腳本 — 使用
<script src="file.js"> - 沒有內嵌事件處理器 — 使用
addEventListener
第二部分 — 發布到 Chrome 線上應用程式商店
管理 CHROMEWEBSTORE.md — 所有 Chrome 線上應用程式商店清單中繼資料、權限說明、隱私揭露、版本歷史和發布就緒狀態的單一事實來源。
核心工作流程
每次你以影響商店存在的方式修改 Chrome 擴充功能專案時,更新(或建立)專案根目錄中的 CHROMEWEBSTORE.md。該檔案追蹤開發者在 Chrome 開發人員儀表板中需要填寫的所有內容,讓他們可以從單一文件複製貼上,而不是在發布時手忙腳亂。
何時建立 CHROMEWEBSTORE.md
在以下任何情況發生時立即建立:
- 使用者說要發布擴充功能
- 使用者要求「為商店做準備」或「準備發布」
- 你正在建置一個最終會上架商店的新擴充功能
- 使用者詢問商店清單要求
使用 references/webstore/chromewebstore-template.md 中的範本作為起點。在產生檔案前先閱讀它。
何時更新 CHROMEWEBSTORE.md
在以下情況更新:
- 使用者可見變更:更新「最後更新」日期,更新描述中的功能清單,並在版本歷史中加入條目
- manifest.json 變更:如果 permissions、host_permissions 或 content_scripts 變更,更新權限說明部分 — 每個權限都需要審查團隊能理解的平實英語理由
- 新版本:在版本歷史中加入條目,包含版本號碼、日期和摘要
- 隱私相關變更:如果資料收集、儲存或傳輸變更,更新隱私與資料使用部分和隱私權政策
- 資產變更:如果圖示或 UI 變更,註記哪些截圖需要更新
- 拒絕回應:如果使用者回報 CWS 拒絕,更新檔案並在版本歷史中加入註記
如何填寫
對於每個部分,從實際專案檔案中提取資訊:
- 讀取
manifest.json以提取名稱、版本、描述、permissions、host_permissions - 掃描程式碼庫以了解資料收集(storage、fetch 呼叫、分析)
- 檢查圖示檔案及其尺寸
- 查看擴充功能的 UI 以了解功能,用於描述
以具體、誠實且以利益為導向的語氣撰寫商店面向的文案。Chrome 線上應用程式商店審查團隊會拒絕模糊的描述。「讓你的生活更輕鬆」會被拒絕。「在任何網頁上突顯搜尋結果,並讓你將突顯內容儲存到本機清單」會通過。
絕不要提及實作細節。 使用者關心擴充功能為他們做什麼,而不是如何建置。移除任何 API、函式庫、框架或程式碼模式的提及:
| ❌ 實作細節(刪除) | ✅ 使用者利益(保留) |
|---|---|
| "使用 MutationObserver 偵測頁面變更" | "在你瀏覽時自動偵測新內容" |
| "使用自訂元素和 Shadow DOM 建置" | "無縫運作,不影響頁面樣式" |
| "由 service worker 提供背景處理" | "在背景安靜執行,不減慢瀏覽器" |
| "利用 chrome.storage.sync API" | "你的設定在所有裝置間同步" |
| "實作 declarativeNetRequest 進行過濾" | "封鎖廣告和追蹤器,不讀取你的頁面內容" |
CHROMEWEBSTORE.md 部分
在產生檔案前閱讀 references/webstore/chromewebstore-template.md — 它定義每個部分涵蓋的內容以及如何填寫。風險最高的部分是權限說明:為每個權限和每個 host_permission 撰寫具體的平實英語理由。「擴充功能運作所需」會被拒絕。閱讀 references/webstore/privacy-policy.md 以取得產生隱私權政策的指引。
發布前檢查清單
提交前,執行 references/webstore/review-checklist.md。最常見的首次提交失敗:
- 每個權限和 host_permission 必須有具體說明(不是「運作所需」)
- 隱私權政策 URL 必須有效且與資料使用揭露表單相符
- 至少 1 張 1280×800 或 640×400 的截圖
- ZIP 必須排除
.git/、node_modules/、.env、CHROMEWEBSTORE.md
商店清單文案指南
有關文案指南和常見拒絕原因,參見 references/webstore/store-listing.md。關鍵規則:以功能開頭("在任何網頁上突顯搜尋詞"),而不是感受("再次享受搜尋")。
參考檔案
有關詳細的 API 模式和發布指引,請在撰寫程式碼或內容前閱讀相關檔案:
| 主題 | 參考 |
|---|---|
| 側邊面板 | references/extensions/side-panel.md |
| 內容腳本與 DOM | references/extensions/content-scripts.md |
| 彈出視窗 | references/extensions/popup-ui.md |
| Service worker 生命週期 | references/extensions/service-worker.md |
| 程式碼執行與 CSP | references/extensions/csp-sandbox.md |
| API 呼叫 | references/extensions/api-calling.md |
| Declarative Net Request | references/extensions/declarative-net-request.md |
| Chrome Prompt API | references/extensions/prompt-api.md |
| DevTools 面板 | references/extensions/devtools.md |
| 驗證 | references/extensions/auth-identity.md |
| 內容選單 | references/extensions/context-menus.md |
| Omnibox | references/extensions/omnibox.md |
| 儲存 | references/extensions/storage.md |
| 分頁與視窗管理 | references/extensions/tab-management.md |
| 分頁/桌面擷取 | references/extensions/media-capture.md |
| 使用者腳本 | references/extensions/user-scripts.md |
| 訊息傳遞 | references/extensions/message-passing.md |
| 圖示 | references/extensions/icons.md |
| CHROMEWEBSTORE.md 範本 | references/webstore/chromewebstore-template.md |
| 隱私權政策指引 | references/webstore/privacy-policy.md |
| 發布前審查檢查清單 | references/webstore/review-checklist.md |
| 商店清單技巧與拒絕 | references/webstore/store-listing.md |
輸出檢查清單
交付前驗證每個項目:
- [ ]
manifest_version: 3— 任何地方都沒有 V2 API - [ ] manifest 中參考的所有圖示檔案都是具有正確尺寸的真實檔案 — 或省略圖示
- [ ] 側邊面板有明確的開啟觸發器(不只是 manifest 宣告)
- [ ] 程式碼執行使用 sandbox/blob/srcdoc — 擴充功能頁面中沒有
eval() - [ ] 如果存取
tab.url或tab.title,則宣告tabs權限 - [ ] 所有程式碼使用
async/await— 沒有.then()鏈 - [ ] 內容腳本使用
requestAnimationFrame批次處理 DOM 更新 - [ ] Service worker 不在全域變數中儲存狀態 — 使用
chrome.storage - [ ] HTML 中沒有內嵌腳本或事件處理器
- [ ] 內容選單動作顯示使用者確認
- [ ] 如果使用
chrome.action.*API,manifest 中有"action": {}(或更多) - [ ] 如果從側邊面板讀取/執行分頁:使用
tabs+host_permissions(不是activeTab) - [ ]
chrome.devtools.panels.create()中的 DevTools 面板路徑相對於擴充功能根目錄 - [ ] Offscreen 文件僅使用
chrome.runtime訊息傳遞 — 沒有chrome.downloads、chrome.action等 - [ ]
chrome.notifications、chrome.action.setIcon等中的所有影像參考指向真實檔案(或使用 data URL) - [ ] 分頁/桌面擷取使用狀態鎖定以防止雙重啟動錯誤
- [ ]
chrome.desktopCapture.chooseDesktopMedia傳入具有tabs權限的targetTab - [ ]
chrome.windows呼叫使用getAll/getLastFocused/getCurrent— 不是.query()(它不存在) - [ ] 使用前檢查
chrome.userScripts可用性(如果使用者未啟用,API 會拋出例外) - [ ] 使用者腳本設定持久化在
chrome.storage,並在runtime.onInstalled的"update"原因中還原 - [ ] 在使用者腳本傳送訊息前呼叫
configureWorld({ messaging: true });監聽onUserScriptMessage而不是onMessage - [ ]
ScriptSource條目每個恰好有code或file其中一個(不能兩者都有,也不能都沒有) - [ ] 使用者腳本
id值不以底線開頭 - [ ]
sidePanel.setPanelBehavior使用openPanelOnActionClick— 不是openPanelOnActionIconClick - [ ] 所有非同步操作都有錯誤處理
- [ ]
host_permissions限定特定網域(除非需要,否則不是<all_urls>) - [ ] 具有非同步回應的
onMessage監聽器中有return true - [ ] 在
chrome.contextMenus的contexts中使用"tab"需要 Chrome M150+





