chrome-extensions

chrome-extensions

熱門

使用 Manifest V3 最佳實務建置並發布 Chrome 擴充功能。每當使用者要求建立、修改、除錯或理解 Chrome 瀏覽器擴充功能、附加元件,或任何涉及 Chrome 擴充功能 API 的事項時,請使用此技能。觸發關鍵字包括:'Chrome 擴充功能'、'瀏覽器擴充功能'、'manifest.json'、'內容腳本'、'service worker'(瀏覽器情境)、'彈出視窗'(瀏覽器擴充功能情境)、'側邊面板'、'chrome.* API'、'declarativeNetRequest'、'omnibox'、'內容選單'(擴充功能情境)、'userScripts'、'使用者腳本'、'腳本管理員',或任何要求建置與 Chrome 瀏覽器 UI 整合的功能。也適用於發布到 Chrome 線上應用程式商店:'發布擴充功能'、準備擴充功能以供發布、回應審查拒絕、撰寫權限說明,或草擬隱私權政策。

1628星標
64分支
更新於 2026/7/24
SKILL.md
唯讀
名稱
chrome-extensions
描述

使用 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" 欄位:

  1. 封裝擴充功能一次(chrome://extensions → 封裝擴充功能)
  2. 從 .crx 中擷取公開金鑰
  3. 在 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.setBadgeTextchrome.action.setIconchrome.action.onClicked 需要在 manifest.json 中有 "action" 鍵 — 即使是空的。沒有它,chrome.actionundefined

// ❌ 損壞 — 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" 情境)
  • 來自 commands API 的鍵盤快捷鍵
  • 接受 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.downloadschrome.tabschrome.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.onMessage
  • chrome.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.tabCapturechrome.desktopCapturechrome.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 條目必須恰好有 codefile 其中一個。id 限制: 不能以 _ 開頭。

參見 references/extensions/user-scripts.md

19. chrome.windows 沒有 .query() 方法 — 使用 getAllgetLastFocusedgetCurrent

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 方法: getAllgetLastFocusedgetCurrentget(windowId)createupdateremove。參見 references/extensions/tab-management.md

一律使用 Manifest V3

絕不產生 Manifest V2 程式碼。

  • background.service_worker 而不是 background.scripts
  • chrome.action 而不是 chrome.browserAction
  • chrome.scripting.executeScript 而不是 chrome.tabs.executeScript
  • host_permissionspermissions 分開
  • 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 拒絕,更新檔案並在版本歷史中加入註記

如何填寫

對於每個部分,從實際專案檔案中提取資訊:

  1. 讀取 manifest.json 以提取名稱、版本、描述、permissions、host_permissions
  2. 掃描程式碼庫以了解資料收集(storage、fetch 呼叫、分析)
  3. 檢查圖示檔案及其尺寸
  4. 查看擴充功能的 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/.envCHROMEWEBSTORE.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.urltab.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.downloadschrome.action
  • [ ] chrome.notificationschrome.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 條目每個恰好有 codefile 其中一個(不能兩者都有,也不能都沒有)
  • [ ] 使用者腳本 id 值不以底線開頭
  • [ ] sidePanel.setPanelBehavior 使用 openPanelOnActionClick — 不是 openPanelOnActionIconClick
  • [ ] 所有非同步操作都有錯誤處理
  • [ ] host_permissions 限定特定網域(除非需要,否則不是 <all_urls>
  • [ ] 具有非同步回應的 onMessage 監聽器中有 return true
  • [ ] 在 chrome.contextMenuscontexts 中使用 "tab" 需要 Chrome M150+