> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# 浏览器插件 chrome.* API 全量速查大全
- URL: https://blog.vercanti.com/liu-lan-qi-cha-jian-chrome-api-quan-liang-su-cha-da-quan/
- Published: 2026-08-28T14:35:29.000Z
- Updated: 2026-08-28T14:58:54.000Z
- Description: 本手册按命名空间组织。每个命名空间一个 ## 主节，每个常用方法一个 ### 子节，配参数表与最小可运行示例。代码若运行在 Service Worker（背景脚本，下文简称 SW）、内容脚本（content script）、弹窗页（popup）或选项页（options），均在示例中标注上下文。 下表用于快速判断某 API 需要在 manifest.json 声明哪些权限，以及可在哪些上下文调用。「SW」=Service Worker；「CS」=内容脚本；「页面」=popup/options/sidePanel 等扩展页。 全上下文可用，无需任何权限，是消
- Author: yellowdog
- Tags: 前端开发, 浏览器插件开发

> 官方文档：<https://developer.chrome.com/docs/extensions/reference/api>  
> 适用版本：Manifest V3 / Chrome 138+  
> 核实日期：2026-06-06  
> 本篇为系列「API 参考篇」。所有 `chrome.*` 命名空间方法在 MV3 下默认返回 Promise（同时仍兼容回调风格），本文示例一律采用 Promise 风格。

本手册按命名空间组织。每个命名空间一个 `##` 主节，每个常用方法一个 `###` 子节，配参数表与最小可运行示例。代码若运行在 Service Worker（背景脚本，下文简称 SW）、内容脚本（content script）、弹窗页（popup）或选项页（options），均在示例中标注上下文。

## API 总览：权限与可用上下文

下表用于快速判断某 API 需要在 `manifest.json` 声明哪些权限，以及可在哪些上下文调用。「SW」=Service Worker；「CS」=内容脚本；「页面」=popup/options/sidePanel 等扩展页。

| API 命名空间                     | 所需 permission                                               | 可用上下文        | 备注                |
| ---------------------------- | ----------------------------------------------------------- | ------------ | ----------------- |
| chrome.runtime               | 无                                                           | SW / CS / 页面 | 全上下文可用，无需声明       |
| chrome.tabs                  | 无（读 url/title 需 tabs 或 host 权限）                             | SW / 页面      | CS 不可直接用          |
| chrome.windows               | 无                                                           | SW / 页面      | 同 tabs            |
| chrome.storage               | storage                                                     | SW / CS / 页面 | 四区共享 API          |
| chrome.scripting             | scripting \+ host 权限                                        | SW / 页面      | 注入目标需 host 权限     |
| chrome.action                | 无（manifest 需 action 键）                                      | SW / 页面      | 工具栏图标             |
| chrome.contextMenus          | contextMenus                                                | SW           | 右键菜单              |
| chrome.alarms                | alarms                                                      | SW           | 定时任务              |
| chrome.commands              | 无（manifest 需 commands 键）                                    | SW           | 快捷键               |
| chrome.notifications         | notifications                                               | SW / 页面      | 系统通知              |
| chrome.sidePanel             | sidePanel                                                   | SW / 页面      | 侧边栏，Chrome 114+   |
| chrome.permissions           | 无                                                           | SW / 页面      | 运行时申请 optional 权限 |
| chrome.cookies               | cookies \+ host 权限                                          | SW / 页面      | 读写 Cookie         |
| chrome.webNavigation         | webNavigation                                               | SW           | 导航事件监听            |
| chrome.declarativeNetRequest | declarativeNetRequest 或 declarativeNetRequestWithHostAccess | SW           | 声明式拦截             |
| chrome.identity              | identity                                                    | SW / 页面      | OAuth             |
| chrome.offscreen             | offscreen                                                   | SW           | 在 SW 中用 DOM       |
| chrome.i18n                  | 无（需 \_locales/）                                             | SW / CS / 页面 | 国际化               |
| chrome.downloads             | downloads                                                   | SW / 页面      | 下载管理              |
| chrome.bookmarks             | bookmarks                                                   | SW / 页面      | 书签                |
| chrome.history               | history                                                     | SW / 页面      | 历史记录              |

---

## chrome.runtime

全上下文可用，无需任何权限，是消息通信与生命周期管理的核心。

### chrome.runtime.id

只读属性，返回当前扩展的 ID（32 位小写字母字符串）。可在 CS 中用于拼接 `chrome-extension://` URL。

```js
// 上下文：任意
console.log(chrome.runtime.id); // "abcdefghijklmnopabcdefghijklmnop"

```

### chrome.runtime.getURL(path)

把扩展内的相对路径转换为完整的 `chrome-extension://<id>/...` 绝对 URL。常用于在 CS 中加载 `web_accessible_resources` 里声明的资源。

| 参数   | 类型     | 默认值 | 说明                 |
| ---- | ------ | --- | ------------------ |
| path | string | 必填  | 相对扩展根目录的路径，前导 / 可选 |

```js
// 上下文：内容脚本
const imgUrl = chrome.runtime.getURL("assets/logo.png");
const img = document.createElement("img");
img.src = imgUrl;
document.body.append(img);

// 错误：直接拼字符串，扩展 ID 改变后失效
// const bad = "chrome-extension://abcd.../assets/logo.png"; // 错误：硬编码 ID

```

### chrome.runtime.getManifest()

同步返回解析后的 `manifest.json` 对象，无参数。用于读取版本号等。

```js
// 上下文：任意
const { version, name } = chrome.runtime.getManifest();
console.log(`${name} v${version}`);

```

### chrome.runtime.sendMessage(extensionId?, message, options?)

向扩展内其它上下文（SW、其它页面）发送一次性消息，返回 Promise 解析为接收方的响应。单条消息上限 **64 MiB**。

| 参数          | 类型     | 默认值       | 说明                       |
| ----------- | ------ | --------- | ------------------------ |
| extensionId | string | 当前扩展      | 目标扩展 ID，发给自己可省略          |
| message     | any    | 必填        | 任意 JSON 可序列化值            |
| options     | object | undefined | includeTlsChannelId 等，少用 |

```js
// 上下文：popup
const res = await chrome.runtime.sendMessage({ type: "getCount" });
console.log(res.count);

// 错误：消息含函数/DOM 节点，无法 JSON 序列化
// await chrome.runtime.sendMessage({ el: document.body }); // 错误：不可序列化

```

### chrome.runtime.onMessage

监听 `sendMessage` 发来的消息。回调签名 `(message, sender, sendResponse)`。**异步回复必须 `return true`** 以保持消息通道开放；或在 Chrome 148+ 直接返回 Promise。

| 回调参数         | 类型            | 说明                    |
| ------------ | ------------- | --------------------- |
| message      | any           | 发送方传入的消息体             |
| sender       | MessageSender | 含 tab、id、url、origin 等 |
| sendResponse | function      | 调用以回送响应，可异步调用         |

```js
// 上下文：Service Worker
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  if (msg.type === "getCount") {
    chrome.storage.local.get("count").then(({ count }) => {
      sendResponse({ count: count ?? 0 });
    });
    return true; // 关键：异步回复需返回 true 保持通道
  }
  // 错误：async 监听器整体返回 Promise 在 148 前不被识别，回复丢失
});

```

### chrome.runtime.connect(extensionId?, connectInfo?)

建立长连接，返回 `Port` 对象，适合高频或流式通信。

| 参数          | 类型     | 默认值       | 说明                     |
| ----------- | ------ | --------- | ---------------------- |
| extensionId | string | 当前扩展      | 目标扩展 ID                |
| connectInfo | object | undefined | { name } 命名通道，便于区分连接类型 |

```js
// 上下文：popup
const port = chrome.runtime.connect({ name: "live-feed" });
port.postMessage({ subscribe: true });
port.onMessage.addListener((m) => console.log("推送：", m));
port.onDisconnect.addListener(() => console.log("通道关闭"));

```

### chrome.runtime.onConnect

监听 `connect` 建立的连接，回调收到 `Port`。

```js
// 上下文：Service Worker
chrome.runtime.onConnect.addListener((port) => {
  if (port.name !== "live-feed") return;
  port.onMessage.addListener((m) => {
    if (m.subscribe) port.postMessage({ tick: Date.now() });
  });
});

```

### chrome.runtime.onInstalled

扩展首次安装、更新或 Chrome 升级时触发。回调参数 `details.reason` 取值：`"install"`（首装）、`"update"`（更新）、`"chrome_update"`（浏览器升级）、`"shared_module_update"`（依赖模块更新）。

```js
// 上下文：Service Worker
chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason === "install") {
    chrome.storage.local.set({ count: 0 }); // 初始化默认配置
  } else if (details.reason === "update") {
    console.log("从", details.previousVersion, "升级");
  }
});

```

### chrome.runtime.onStartup

用户启动 Chrome、加载该扩展的 profile 时触发一次。无参数。注意：扩展更新或安装时**不会**触发。

```js
// 上下文：Service Worker
chrome.runtime.onStartup.addListener(() => {
  console.log("浏览器启动，SW 唤醒");
});

```

### chrome.runtime.lastError

只读属性，仅在回调风格 API 出错时于回调内有值。Promise 风格用 `try/catch` 即可，一般不再需要它。

```js
// 上下文：Service Worker（回调风格 API 仍存在的场景）
chrome.tabs.sendMessage(tabId, { ping: 1 }, () => {
  if (chrome.runtime.lastError) {
    console.warn("目标无监听器：", chrome.runtime.lastError.message);
  }
});

```

### chrome.runtime.getContexts(filter)

Chrome 116+。查询当前扩展存活的所有上下文（SW、offscreen、popup、tab 等），返回 `ExtensionContext[]`。

| 参数     | 类型     | 默认值 | 说明                                       |
| ------ | ------ | --- | ---------------------------------------- |
| filter | object | 必填  | 含 contextTypes、documentUrls、tabIds 等过滤字段 |

`contextTypes` 取值：`"TAB"`、`"POPUP"`、`"BACKGROUND"`、`"OFFSCREEN_DOCUMENT"`、`"SIDE_PANEL"`、`"DEVELOPER_TOOLS"`。

```js
// 上下文：Service Worker
const ctxs = await chrome.runtime.getContexts({
  contextTypes: ["OFFSCREEN_DOCUMENT"]
});
const hasOffscreen = ctxs.length > 0; // 判断 offscreen 是否已存在

```

---

## chrome.tabs

无需权限即可调用，但读取 `url`、`title`、`favIconUrl`、`pendingUrl` 需要 `tabs` 权限或匹配该标签的 host 权限。不可在 CS 中直接使用。

### chrome.tabs.query(queryInfo)

按条件查询标签页，返回 `Tab[]`。

| 参数                      | 类型                   | 默认值 | 说明                         |
| ----------------------- | -------------------- | --- | -------------------------- |
| queryInfo.active        | boolean              | 不限  | 是否为窗口内激活标签                 |
| queryInfo.currentWindow | boolean              | 不限  | 是否在当前窗口                    |
| queryInfo.url           | string \| string\[\] | 不限  | match pattern 过滤，需 host 权限 |
| queryInfo.status        | string               | 不限  | "loading" 或 "complete"     |
| queryInfo.pinned        | boolean              | 不限  | 是否固定                       |

```js
// 上下文：Service Worker
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
console.log("当前标签：", tab.id, tab.url);

// 错误：不传任何条件会返回所有窗口所有标签，浪费
// const all = await chrome.tabs.query({}); // 错误：全表扫描

```

### chrome.tabs.get(tabId)

按 ID 获取单个标签的最新信息。

| 参数    | 类型     | 默认值 | 说明    |
| ----- | ------ | --- | ----- |
| tabId | number | 必填  | 标签 ID |

```js
// 上下文：Service Worker
const tab = await chrome.tabs.get(123);

```

### chrome.tabs.create(createProperties)

新建标签页，返回新建的 `Tab`。

| 参数       | 类型      | 默认值   | 说明                                |
| -------- | ------- | ----- | --------------------------------- |
| url      | string  | 新标签页  | 目标 URL，扩展页用 chrome.runtime.getURL |
| active   | boolean | true  | 是否前台激活                            |
| index    | number  | 末尾    | 在窗口中的位置                           |
| windowId | number  | 当前窗口  | 目标窗口 ID                           |
| pinned   | boolean | false | 是否固定                              |

```js
// 上下文：Service Worker
await chrome.tabs.create({ url: chrome.runtime.getURL("options.html") });

```

### chrome.tabs.update(tabId?, updateProperties)

修改标签属性（导航、激活、静音等）。

| 参数                      | 类型      | 默认值    | 说明         |
| ----------------------- | ------- | ------ | ---------- |
| tabId                   | number  | 当前激活标签 | 省略则作用于激活标签 |
| updateProperties.url    | string  | 不变     | 导航到新 URL   |
| updateProperties.active | boolean | 不变     | 激活该标签      |
| updateProperties.muted  | boolean | 不变     | 静音/取消静音    |
| updateProperties.pinned | boolean | 不变     | 固定/取消固定    |

```js
// 上下文：Service Worker
await chrome.tabs.update(tabId, { url: "https://example.com", active: true });

```

### chrome.tabs.remove(tabIds)

关闭一个或多个标签。

| 参数     | 类型                   | 默认值 | 说明    |
| ------ | -------------------- | --- | ----- |
| tabIds | number \| number\[\] | 必填  | 单个或数组 |

```js
// 上下文：Service Worker
await chrome.tabs.remove([101, 102]); // 批量关闭

```

### chrome.tabs.sendMessage(tabId, message, options?)

向指定标签内的内容脚本发送消息，返回 Promise 解析为 CS 的响应。

| 参数              | 类型     | 默认值      | 说明         |
| --------------- | ------ | -------- | ---------- |
| tabId           | number | 必填       | 目标标签       |
| message         | any    | 必填       | JSON 可序列化值 |
| options.frameId | number | 所有 frame | 仅发给指定子框架   |

```js
// 上下文：Service Worker
try {
  const res = await chrome.tabs.sendMessage(tabId, { type: "highlight" });
} catch (e) {
  // 该标签无内容脚本监听时抛错，需捕获
  console.warn("无 CS 接收：", e.message);
}

```

### chrome.tabs.captureVisibleTab(windowId?, options?)

截取指定窗口当前可见标签的画面，返回 data URL。需要 `activeTab` 或 `<all_urls>` host 权限。

| 参数              | 类型     | 默认值    | 说明              |
| --------------- | ------ | ------ | --------------- |
| windowId        | number | 当前窗口   | 目标窗口 ID         |
| options.format  | string | "jpeg" | "jpeg" 或 "png"  |
| options.quality | number | 由格式定   | 0-100，仅 jpeg 有效 |

```js
// 上下文：Service Worker（需 activeTab 权限）
const dataUrl = await chrome.tabs.captureVisibleTab(undefined, {
  format: "png"
});

```

### chrome.tabs.group(options)

将标签加入标签组，返回 `groupId`。

| 参数                                | 类型                   | 默认值  | 说明        |
| --------------------------------- | -------------------- | ---- | --------- |
| options.tabIds                    | number \| number\[\] | 必填   | 要分组的标签    |
| options.groupId                   | number               | 新建组  | 加入已有组的 ID |
| options.createProperties.windowId | number               | 当前窗口 | 新组所在窗口    |

```js
// 上下文：Service Worker
const groupId = await chrome.tabs.group({ tabIds: [101, 102] });
await chrome.tabGroups.update(groupId, { title: "工作", color: "blue" });

```

### chrome.tabs.onUpdated

标签更新（URL、加载状态、标题等变化）时触发。回调 `(tabId, changeInfo, tab)`。

```js
// 上下文：Service Worker
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === "complete" && tab.url?.startsWith("https://")) {
    console.log("页面加载完成：", tab.url);
  }
});

```

### chrome.tabs.onActivated

用户切换激活标签时触发。回调 `activeInfo` 含 `tabId`、`windowId`。

```js
// 上下文：Service Worker
chrome.tabs.onActivated.addListener(({ tabId }) => {
  console.log("切换到标签", tabId);
});

```

### chrome.tabs.onRemoved

标签关闭时触发。回调 `(tabId, removeInfo)`，`removeInfo.isWindowClosing` 表示是否因窗口关闭。

```js
// 上下文：Service Worker
chrome.tabs.onRemoved.addListener((tabId, info) => {
  console.log("关闭标签", tabId, "窗口关闭：", info.isWindowClosing);
});

```

---

## chrome.windows

操作浏览器窗口，无需权限。

### chrome.windows.create(createData)

新建窗口，返回 `Window`。

| 参数             | 类型                   | 默认值      | 说明                                                  |
| -------------- | -------------------- | -------- | --------------------------------------------------- |
| url            | string \| string\[\] | 新标签页     | 初始打开的 URL                                           |
| type           | string               | "normal" | "normal" / "popup" / "panel"                        |
| state          | string               | "normal" | "normal" / "minimized" / "maximized" / "fullscreen" |
| focused        | boolean              | true     | 是否获取焦点                                              |
| width / height | number               | 自动       | 像素尺寸                                                |
| left / top     | number               | 自动       | 屏幕位置                                                |
| incognito      | boolean              | false    | 是否隐身窗口                                              |

```js
// 上下文：Service Worker
await chrome.windows.create({
  url: chrome.runtime.getURL("panel.html"),
  type: "popup",
  width: 400,
  height: 600
});

```

### chrome.windows.get(windowId, queryOptions?)

获取指定窗口。`queryOptions.populate` 为 `true` 时附带 `tabs` 数组。

| 参数                    | 类型      | 默认值   | 说明       |
| --------------------- | ------- | ----- | -------- |
| windowId              | number  | 必填    | 窗口 ID    |
| queryOptions.populate | boolean | false | 是否包含标签列表 |

```js
// 上下文：Service Worker
const win = await chrome.windows.get(winId, { populate: true });
console.log("窗口含标签数：", win.tabs.length);

```

### chrome.windows.getAll(queryOptions?)

返回所有窗口数组。

```js
// 上下文：Service Worker
const wins = await chrome.windows.getAll({ populate: false });

```

### chrome.windows.update(windowId, updateInfo)

修改窗口状态、位置、尺寸。

| 参数                       | 类型      | 默认值 | 说明               |
| ------------------------ | ------- | --- | ---------------- |
| windowId                 | number  | 必填  | 窗口 ID            |
| updateInfo.focused       | boolean | 不变  | 置顶聚焦             |
| updateInfo.state         | string  | 不变  | 同 create 的 state |
| updateInfo.drawAttention | boolean | 不变  | 任务栏闪烁提醒          |

```js
// 上下文：Service Worker
await chrome.windows.update(winId, { focused: true, state: "maximized" });

```

### chrome.windows.remove(windowId)

关闭指定窗口。

```js
// 上下文：Service Worker
await chrome.windows.remove(winId);

```

---

## chrome.storage

需 `storage` 权限。四个存储区共享同一套方法 API。所有值必须 JSON 可序列化。

### 四区配额对比

| 区       | 总配额                | 单项上限           | 最大项数 | 写频率限制              | 同步         | 上下文        |
| ------- | ------------------ | -------------- | ---- | ------------------ | ---------- | ---------- |
| local   | 10 MB              | 无              | 无    | 无                  | 否          | SW/CS/页面   |
| sync    | 100 KB（102,400 字节） | 8 KB（8,192 字节） | 512  | 120 次/分、1,800 次/小时 | 跨设备        | SW/CS/页面   |
| session | 10 MB              | 无              | 无    | 无                  | 否（内存，重启清空） | 默认仅 SW，可放开 |
| managed | —                  | —              | —    | 只读                 | 由策略下发      | SW/CS/页面   |

`local` 在 Chrome 113 及更早为 5 MB；`session` 在 Chrome 111 及更早为 1 MB。`managed` 由企业策略 JSON 提供，扩展只读。

### get(keys?)

读取一个或多个键，返回对象。`keys` 为 `null` 或省略时读取整个区。

| 参数   | 类型                   | 默认值    | 说明   |      |               |
| ---- | -------------------- | ------ | ---- | ---- | ------------- |
| keys | string \| string\[\] | object | null | null | 传对象时其值作为缺省返回值 |

```js
// 上下文：任意
const { theme = "light" } = await chrome.storage.local.get({ theme: "light" });
const multi = await chrome.storage.sync.get(["a", "b"]);

```

### set(items)

写入键值对，整体覆盖同名键。

| 参数    | 类型     | 默认值 | 说明           |
| ----- | ------ | --- | ------------ |
| items | object | 必填  | 一次写多个键，减少写次数 |

```js
// 上下文：任意
await chrome.storage.local.set({ count: 5, theme: "dark" });

// 错误：循环里逐条 set，撞 sync 写频率上限
// for (const k in data) await chrome.storage.sync.set({ [k]: data[k] }); // 错误：高频写

```

### remove(keys)

删除指定键。

| 参数   | 类型                   | 默认值 | 说明    |
| ---- | -------------------- | --- | ----- |
| keys | string \| string\[\] | 必填  | 要删除的键 |

```js
// 上下文：任意
await chrome.storage.local.remove(["count", "theme"]);

```

### clear()

清空整个存储区，无参数。

```js
// 上下文：任意
await chrome.storage.session.clear();

```

### getBytesInUse(keys?)

返回指定键（或整区）占用的字节数。`sync` 区按此判断是否接近配额。

| 参数   | 类型                   | 默认值  | 说明   |        |
| ---- | -------------------- | ---- | ---- | ------ |
| keys | string \| string\[\] | null | null | 省略统计整区 |

```js
// 上下文：任意
const used = await chrome.storage.sync.getBytesInUse(null);
console.log(`已用 ${used}/102400 字节`);

```

### setAccessLevel(accessOptions)

控制存储区能否被不可信上下文（如 MAIN world 注入脚本）访问。主要用于 `session` 区。

| 参数                        | 类型     | 默认值                 | 说明     |
| ------------------------- | ------ | ------------------- | ------ |
| accessOptions.accessLevel | string | "TRUSTED\_CONTEXTS" | 访问级别枚举 |

`accessLevel` 取值：`"TRUSTED_CONTEXTS"`（仅扩展自身上下文，默认）、`"TRUSTED_AND_UNTRUSTED_CONTEXTS"`（含内容脚本等外部上下文）。

```js
// 上下文：Service Worker
// 允许内容脚本读写 session 区
await chrome.storage.session.setAccessLevel({
  accessLevel: "TRUSTED_AND_UNTRUSTED_CONTEXTS"
});

```

### onChanged

任意键变化时触发。`storage.onChanged` 监听所有区；各区也有自身 `onChanged`。回调 `(changes, areaName)`，`changes` 为 `{ key: { oldValue, newValue } }`。

```js
// 上下文：任意
chrome.storage.onChanged.addListener((changes, area) => {
  if (area === "local" && changes.theme) {
    console.log("主题从", changes.theme.oldValue, "变为", changes.theme.newValue);
  }
});

```

---

## chrome.scripting

需 `scripting` 权限，注入目标还需对应 host 权限（或 `activeTab`）。仅可在 SW 与扩展页调用。

### executeScript(injection)

向目标注入 JS 函数或文件，返回 `InjectionResult[]`。

| 参数                          | 类型         | 默认值        | 说明                               |
| --------------------------- | ---------- | ---------- | -------------------------------- |
| injection.target            | object     | 必填         | { tabId, frameIds?, allFrames? } |
| injection.func              | function   | —          | 要执行的函数（与 files 二选一）              |
| injection.args              | any\[\]    | \[\]       | 传给 func 的参数，须可序列化                |
| injection.files             | string\[\] | —          | 要注入的脚本文件路径                       |
| injection.world             | string     | "ISOLATED" | 执行环境，见下                          |
| injection.injectImmediately | boolean    | false      | 尽早注入而不等导航稳定                      |

`world` 取值：`"ISOLATED"`（扩展专属隔离环境，与页面共享 DOM 但 JS 变量隔离，默认）、`"MAIN"`（页面主世界，与网页脚本共享 `window`，可访问页面变量但无 chrome.\* 高权 API）。

```js
// 上下文：Service Worker
const [result] = await chrome.scripting.executeScript({
  target: { tabId },
  func: (prefix) => prefix + document.title, // 函数体在目标页运行
  args: ["标题："]
});
console.log(result.result);

// MAIN world 读取页面全局变量
await chrome.scripting.executeScript({
  target: { tabId },
  world: "MAIN",
  func: () => window.__APP_STATE__
});

// 错误：func 引用了 SW 作用域的外部变量（闭包不跨进程）
// const x = 1; executeScript({ target, func: () => x }); // 错误：func 内 x 未定义

```

### insertCSS(injection)

注入样式表，返回 `Promise<void>`。

| 参数               | 类型         | 默认值      | 说明                               |
| ---------------- | ---------- | -------- | -------------------------------- |
| injection.target | object     | 必填       | { tabId, frameIds?, allFrames? } |
| injection.css    | string     | —        | CSS 字符串（与 files 二选一）             |
| injection.files  | string\[\] | —        | CSS 文件路径                         |
| injection.origin | string     | "AUTHOR" | "AUTHOR" 或 "USER"（优先级不同）         |

```js
// 上下文：Service Worker
await chrome.scripting.insertCSS({
  target: { tabId },
  css: "body { filter: invert(1); }"
});

```

### removeCSS(injection)

移除此前用 `insertCSS` 注入的样式，参数须与注入时一致。

```js
// 上下文：Service Worker
await chrome.scripting.removeCSS({
  target: { tabId },
  css: "body { filter: invert(1); }" // 须与 insert 时完全相同
});

```

### registerContentScripts(scripts)

动态注册内容脚本（无需写在 manifest），返回 `Promise<void>`。

| 参数（每项）                | 类型         | 默认值              | 说明                                                     |
| --------------------- | ---------- | ---------------- | ------------------------------------------------------ |
| id                    | string     | 必填               | 唯一标识，不能以 \_ 开头                                         |
| matches               | string\[\] | 必填               | match pattern                                          |
| js / css              | string\[\] | —                | 脚本/样式文件                                                |
| runAt                 | string     | "document\_idle" | "document\_start" / "document\_end" / "document\_idle" |
| world                 | string     | "ISOLATED"       | 同 executeScript                                        |
| persistAcrossSessions | boolean    | true             | 浏览器重启后是否保留                                             |
| allFrames             | boolean    | false            | 是否注入所有子框架                                              |

```js
// 上下文：Service Worker
await chrome.scripting.registerContentScripts([{
  id: "auto-cs",
  matches: ["https://example.com/*"],
  js: ["injected.js"],
  runAt: "document_idle"
}]);

```

### updateContentScripts(scripts)

按 `id` 更新已注册脚本的字段，返回 `Promise<void>`。

```js
// 上下文：Service Worker
await chrome.scripting.updateContentScripts([{
  id: "auto-cs",
  matches: ["https://example.com/*", "https://example.org/*"]
}]);

```

### unregisterContentScripts(filter?)

注销动态脚本。省略 `filter` 注销全部。

| 参数         | 类型         | 默认值 | 说明        |
| ---------- | ---------- | --- | --------- |
| filter.ids | string\[\] | 全部  | 要注销的脚本 ID |

```js
// 上下文：Service Worker
await chrome.scripting.unregisterContentScripts({ ids: ["auto-cs"] });

```

### getRegisteredContentScripts(filter?)

返回已注册的动态脚本列表。

```js
// 上下文：Service Worker
const scripts = await chrome.scripting.getRegisteredContentScripts();
console.log("已注册数量：", scripts.length);

```

---

## chrome.action

manifest 需声明 `action` 键。无需 permission。控制工具栏图标。

### setBadgeText(details)

设置图标角标文字（最多约 4 字符可见）。

| 参数            | 类型     | 默认值 | 说明        |
| ------------- | ------ | --- | --------- |
| details.text  | string | ""  | 角标文本，空串清除 |
| details.tabId | number | 全局  | 仅对指定标签生效  |

```js
// 上下文：Service Worker
await chrome.action.setBadgeText({ text: "5", tabId });

```

### setBadgeBackgroundColor(details)

设置角标背景色。

| 参数            | 类型                   | 默认值 | 说明                          |
| ------------- | -------------------- | --- | --------------------------- |
| details.color | string \| number\[\] | —   | "#FF0000" 或 \[255,0,0,255\] |
| details.tabId | number               | 全局  | 限定标签                        |

```js
// 上下文：Service Worker
await chrome.action.setBadgeBackgroundColor({ color: "#D32F2F" });

```

### setIcon(details)

动态切换图标，返回 `Promise<void>`。

| 参数                | 类型                  | 默认值 | 说明                               |
| ----------------- | ------------------- | --- | -------------------------------- |
| details.path      | string \| object    | —   | { "16": "...", "32": "..." } 多尺寸 |
| details.imageData | ImageData \| object | —   | 由 OffscreenCanvas 生成的位图          |
| details.tabId     | number              | 全局  | 限定标签                             |

```js
// 上下文：Service Worker
await chrome.action.setIcon({ path: { 16: "on16.png", 32: "on32.png" } });

```

### setPopup(details)

设置点击图标时弹出的 HTML。设为 `""` 则恢复触发 `onClicked`。

| 参数            | 类型     | 默认值 | 说明                   |
| ------------- | ------ | --- | -------------------- |
| details.popup | string | —   | popup 页路径，空串禁用 popup |
| details.tabId | number | 全局  | 限定标签                 |

```js
// 上下文：Service Worker
await chrome.action.setPopup({ popup: "popup.html" });

```

### setTitle(details)

设置图标悬停提示。

```js
// 上下文：Service Worker
await chrome.action.setTitle({ title: "未读 5 条" });

```

### enable(tabId?) / disable(tabId?)

启用/禁用图标点击。禁用后图标变灰。

```js
// 上下文：Service Worker
await chrome.action.disable(tabId);
await chrome.action.enable(tabId);

```

### onClicked

仅当**未设置 popup** 时，点击图标触发。回调收到当前 `Tab`。

```js
// 上下文：Service Worker
chrome.action.onClicked.addListener((tab) => {
  console.log("点击图标，当前页：", tab.url);
});

```

---

## chrome.contextMenus

需 `contextMenus` 权限。仅在 SW 中创建。

### create(createProperties, callback?)

创建右键菜单项，同步返回菜单 ID。**应在 `onInstalled` 中创建**，避免重复。

| 参数                  | 类型         | 默认值        | 说明                                                      |
| ------------------- | ---------- | ---------- | ------------------------------------------------------- |
| id                  | string     | 自动         | 菜单项唯一 ID                                                |
| title               | string     | 必填（除分隔符外）  | 显示文字，%s 表示选中文本                                          |
| contexts            | string\[\] | \["page"\] | "page"/"selection"/"link"/"image"/"editable"/"action" 等 |
| type                | string     | "normal"   | "normal"/"checkbox"/"radio"/"separator"                 |
| parentId            | string     | 无          | 父菜单 ID，构建子菜单                                            |
| documentUrlPatterns | string\[\] | 不限         | 仅在匹配页面显示                                                |

```js
// 上下文：Service Worker
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: "search-sel",
    title: '搜索 "%s"',
    contexts: ["selection"]
  });
});

```

### update(id, updateProperties)

修改已有菜单项。

```js
// 上下文：Service Worker
await chrome.contextMenus.update("search-sel", { title: "已禁用", enabled: false });

```

### remove(menuItemId) / removeAll()

删除单个或全部菜单项。

```js
// 上下文：Service Worker
await chrome.contextMenus.remove("search-sel");
await chrome.contextMenus.removeAll();

```

### onClicked

菜单项被点击时触发。回调 `(info, tab)`，`info` 含 `menuItemId`、`selectionText`、`linkUrl`、`checked` 等。

```js
// 上下文：Service Worker
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "search-sel") {
    chrome.tabs.create({
      url: "https://www.google.com/search?q=" + encodeURIComponent(info.selectionText)
    });
  }
});

```

---

## chrome.alarms

需 `alarms` 权限。MV3 中替代 `setTimeout/setInterval`（SW 随时可能休眠）。

最小周期：Chrome 120 起从 1 分钟降为 **30 秒**（`periodInMinutes: 0.5`）。已打包扩展中小于 0.5 的值不被采纳并产生警告；未打包扩展无此限制。Chrome 117 起最多 **500** 个活动 alarm。

### create(name?, alarmInfo)

创建定时器，返回 `Promise<void>`。同名 alarm 会被覆盖。

| 参数                        | 类型     | 默认值 | 说明                           |
| ------------------------- | ------ | --- | ---------------------------- |
| name                      | string | ""  | alarm 名称                     |
| alarmInfo.when            | number | —   | 绝对触发时间（epoch ms），与 delay 二选一 |
| alarmInfo.delayInMinutes  | number | —   | 首次触发延迟（分钟）                   |
| alarmInfo.periodInMinutes | number | —   | 重复周期（分钟），最小 0.5              |

```js
// 上下文：Service Worker
await chrome.alarms.create("sync", {
  delayInMinutes: 1,
  periodInMinutes: 30
});

// 错误：MV3 的 SW 会休眠，setInterval 不可靠
// setInterval(sync, 1800000); // 错误：SW 休眠后定时丢失

```

### get(name?)

获取指定 alarm，返回 `Alarm | undefined`。

```js
// 上下文：Service Worker
const a = await chrome.alarms.get("sync");
console.log(a?.scheduledTime);

```

### getAll()

返回所有 alarm 数组。

```js
// 上下文：Service Worker
const all = await chrome.alarms.getAll();

```

### clear(name?) / clearAll()

清除指定或全部 alarm，返回 `Promise<boolean>`。

```js
// 上下文：Service Worker
await chrome.alarms.clear("sync");
await chrome.alarms.clearAll();

```

### onAlarm

alarm 到期触发，回调收到 `Alarm` 对象（含 `name`、`scheduledTime`、`periodInMinutes`）。

```js
// 上下文：Service Worker
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === "sync") doSync();
});

```

---

## chrome.commands

manifest 需声明 `commands` 键。无需 permission。绑定键盘快捷键。

### getAll()

返回已注册命令及其当前快捷键。

```js
// 上下文：Service Worker
const cmds = await chrome.commands.getAll();
cmds.forEach((c) => console.log(c.name, c.shortcut));

```

### onCommand

自定义命令触发时回调 `(command, tab)`。保留命令 `_execute_action`（打开 popup）与 `_execute_browser_action` 由浏览器直接处理，**不触发** `onCommand`。

```js
// 上下文：Service Worker
chrome.commands.onCommand.addListener((command, tab) => {
  if (command === "toggle-feature") {
    chrome.tabs.sendMessage(tab.id, { type: "toggle" });
  }
});

```

manifest 配置示例：

```json
{
  "commands": {
    "toggle-feature": {
      "suggested_key": { "default": "Ctrl+Shift+Y" },
      "description": "切换功能"
    },
    "_execute_action": {
      "suggested_key": { "default": "Ctrl+Shift+U" }
    }
  }
}

```

---

## chrome.notifications

需 `notifications` 权限。

### create(notificationId?, options)

创建系统通知，返回最终 `notificationId`。

| 参数               | 类型         | 默认值  | 说明                                |
| ---------------- | ---------- | ---- | --------------------------------- |
| notificationId   | string     | 自动生成 | 省略则随机生成                           |
| options.type     | string     | 必填   | "basic"/"image"/"list"/"progress" |
| options.iconUrl  | string     | 必填   | 图标 URL（扩展内资源）                     |
| options.title    | string     | 必填   | 标题                                |
| options.message  | string     | 必填   | 正文                                |
| options.buttons  | object\[\] | 无    | 最多 2 个，{ title }                  |
| options.priority | number     | 0    | \-2 到 2                           |

```js
// 上下文：Service Worker
await chrome.notifications.create("done", {
  type: "basic",
  iconUrl: chrome.runtime.getURL("icon128.png"),
  title: "任务完成",
  message: "已同步 5 项",
  buttons: [{ title: "查看" }]
});

```

### update(notificationId, options)

更新已有通知，返回 `Promise<boolean>`（是否存在）。

```js
// 上下文：Service Worker
await chrome.notifications.update("done", { message: "已同步 6 项" });

```

### clear(notificationId)

清除通知，返回 `Promise<boolean>`。

```js
// 上下文：Service Worker
await chrome.notifications.clear("done");

```

### onClicked / onButtonClicked

通知本体或按钮被点击时触发。

```js
// 上下文：Service Worker
chrome.notifications.onClicked.addListener((id) => {
  console.log("点击通知", id);
});
chrome.notifications.onButtonClicked.addListener((id, btnIndex) => {
  if (id === "done" && btnIndex === 0) chrome.tabs.create({ url: "result.html" });
});

```

---

## chrome.sidePanel

需 `sidePanel` 权限，Chrome 114+。manifest 可配 `side_panel.default_path`。

### setPanelBehavior(behavior)

控制点击工具栏图标是否打开侧边栏。

| 参数                              | 类型      | 默认值   | 说明       |
| ------------------------------- | ------- | ----- | -------- |
| behavior.openPanelOnActionClick | boolean | false | 点图标即开侧边栏 |

```js
// 上下文：Service Worker
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true });

```

### setOptions(options)

为全局或指定标签设置侧边栏页面与启用状态。

| 参数              | 类型      | 默认值  | 说明          |
| --------------- | ------- | ---- | ----------- |
| options.tabId   | number  | 全局   | 限定标签        |
| options.path    | string  | —    | 侧边栏 HTML 路径 |
| options.enabled | boolean | true | 是否在该标签启用    |

```js
// 上下文：Service Worker
await chrome.sidePanel.setOptions({
  tabId,
  path: "panel.html",
  enabled: true
});

```

### open(options)

以编程方式打开侧边栏。**必须在用户手势回调内调用**（如 onClicked、onCommand）。

| 参数               | 类型     | 默认值 | 说明                     |
| ---------------- | ------ | --- | ---------------------- |
| options.tabId    | number | —   | 在该标签打开（与 windowId 二选一） |
| options.windowId | number | —   | 在该窗口打开                 |

```js
// 上下文：Service Worker（用户手势内）
chrome.action.onClicked.addListener(async (tab) => {
  await chrome.sidePanel.open({ tabId: tab.id });
});

// 错误：在非手势上下文（如 alarm）调用会被拒绝
// chrome.alarms.onAlarm.addListener(() => chrome.sidePanel.open({...})); // 错误：无手势

```

### getOptions(options)

查询当前侧边栏配置。

```js
// 上下文：Service Worker
const opts = await chrome.sidePanel.getOptions({ tabId });

```

---

## chrome.permissions

无需 permission。运行时申请 manifest `optional_permissions` / `optional_host_permissions` 中声明的可选权限。

### contains(permissions)

检查是否已拥有某权限，返回 `Promise<boolean>`。

| 参数                      | 类型         | 默认值  | 说明                    |
| ----------------------- | ---------- | ---- | --------------------- |
| permissions.permissions | string\[\] | \[\] | API 权限名               |
| permissions.origins     | string\[\] | \[\] | host 权限 match pattern |

```js
// 上下文：页面或 Service Worker
const has = await chrome.permissions.contains({ permissions: ["downloads"] });

```

### request(permissions)

弹窗请求权限，返回 `Promise<boolean>`。**必须在用户手势内调用**。

```js
// 上下文：popup（按钮点击回调内）
button.addEventListener("click", async () => {
  const granted = await chrome.permissions.request({
    permissions: ["downloads"],
    origins: ["https://*.example.com/*"]
  });
  if (granted) startDownload();
});

// 错误：页面加载时直接请求，无手势会失败
// window.onload = () => chrome.permissions.request({...}); // 错误：无用户手势

```

### remove(permissions)

撤销已授予的可选权限，返回 `Promise<boolean>`。

```js
// 上下文：页面或 Service Worker
await chrome.permissions.remove({ permissions: ["downloads"] });

```

### getAll()

返回当前所有有效权限（含 manifest 静态权限与已授予的可选权限）。

```js
// 上下文：页面或 Service Worker
const all = await chrome.permissions.getAll();
console.log(all.permissions, all.origins);

```

---

## chrome.cookies

需 `cookies` 权限 + 目标域的 host 权限。

### get(details)

读取单个 Cookie，返回 `Cookie | null`。

| 参数              | 类型     | 默认值 | 说明              |
| --------------- | ------ | --- | --------------- |
| details.url     | string | 必填  | Cookie 所属 URL   |
| details.name    | string | 必填  | Cookie 名        |
| details.storeId | string | 当前  | Cookie store ID |

```js
// 上下文：Service Worker（需 host 权限）
const c = await chrome.cookies.get({ url: "https://example.com", name: "sid" });
console.log(c?.value);

```

### getAll(details)

按条件批量获取 Cookie，返回 `Cookie[]`。

| 参数             | 类型      | 默认值 | 说明         |
| -------------- | ------- | --- | ---------- |
| details.domain | string  | 不限  | 域过滤        |
| details.name   | string  | 不限  | 名称过滤       |
| details.url    | string  | 不限  | URL 过滤     |
| details.secure | boolean | 不限  | 仅安全 Cookie |

```js
// 上下文：Service Worker
const cookies = await chrome.cookies.getAll({ domain: "example.com" });

```

### set(details)

写入或更新 Cookie，返回写入的 `Cookie`。

| 参数                     | 类型      | 默认值           | 说明                               |
| ---------------------- | ------- | ------------- | -------------------------------- |
| details.url            | string  | 必填            | 关联 URL                           |
| details.name           | string  | ""            | Cookie 名                         |
| details.value          | string  | ""            | Cookie 值                         |
| details.expirationDate | number  | 会话级           | epoch 秒，省略为会话 Cookie             |
| details.httpOnly       | boolean | false         | 是否 HttpOnly                      |
| details.sameSite       | string  | "unspecified" | "no\_restriction"/"lax"/"strict" |

```js
// 上下文：Service Worker
await chrome.cookies.set({
  url: "https://example.com",
  name: "theme",
  value: "dark",
  expirationDate: Math.floor(Date.now() / 1000) + 86400
});

```

### remove(details)

删除 Cookie。

```js
// 上下文：Service Worker
await chrome.cookies.remove({ url: "https://example.com", name: "theme" });

```

### onChanged

Cookie 增删改时触发。回调 `changeInfo` 含 `cookie`、`removed`、`cause`。

```js
// 上下文：Service Worker
chrome.cookies.onChanged.addListener((info) => {
  console.log(info.cause, info.removed, info.cookie.name);
});

```

---

## chrome.webNavigation

需 `webNavigation` 权限。监听页面导航各阶段。所有事件支持 `addListener(cb, filter?)`，`filter.url` 可按 URL 条件过滤。回调含 `tabId`、`frameId`、`url`、`timeStamp`。

### onCommitted

导航已确定并开始加载文档时触发（此时 URL 与导航类型已定）。

```js
// 上下文：Service Worker
chrome.webNavigation.onCommitted.addListener((d) => {
  if (d.frameId === 0) console.log("主框架导航至", d.url, d.transitionType);
}, { url: [{ hostSuffix: "example.com" }] });

```

### onCompleted

文档及子资源加载完成时触发。

```js
// 上下文：Service Worker
chrome.webNavigation.onCompleted.addListener((d) => {
  if (d.frameId === 0) console.log("加载完成", d.url);
});

```

### onHistoryStateUpdated

SPA 通过 `history.pushState/replaceState` 改变 URL 而无整页刷新时触发，是监听单页应用路由变化的关键。

```js
// 上下文：Service Worker
chrome.webNavigation.onHistoryStateUpdated.addListener((d) => {
  console.log("SPA 路由变化", d.url);
});

```

其它常用事件：`onBeforeNavigate`（导航即将发生）、`onDOMContentLoaded`（DOM 就绪）、`onErrorOccurred`（导航失败）、`onReferenceFragmentUpdated`（仅 `#` 锚点变化）、`onCreatedNavigationTarget`（新标签/窗口被创建为导航目标）。

---

## chrome.declarativeNetRequest

需 `declarativeNetRequest`（可阻塞）或 `declarativeNetRequestWithHostAccess`（仅在有 host 权限的请求上生效）。声明式拦截/改写网络请求，无需读取请求内容，性能与隐私优于已废弃的阻塞式 `webRequest`。

规则数量上限：静态规则跨启用 ruleset 保底 **30,000** 条（`GUARANTEED_MINIMUM_STATIC_RULES`）；动态规则上限 **30,000**（`MAX_NUMBER_OF_DYNAMIC_RULES`）；会话规则上限 **5,000**（`MAX_NUMBER_OF_SESSION_RULES`）；静态 ruleset 总数 **100**，同时启用至多 **50**。规则 `action.type` 取值：`"block"`（拦截）、`"redirect"`（重定向）、`"allow"`（放行，优先于 block）、`"upgradeScheme"`（HTTP 升级 HTTPS）、`"modifyHeaders"`（改写请求/响应头）。

### updateDynamicRules(options)

增删动态规则（跨会话持久），返回 `Promise<void>`。

| 参数                    | 类型         | 默认值  | 说明           |
| --------------------- | ---------- | ---- | ------------ |
| options.addRules      | Rule\[\]   | \[\] | 新增规则         |
| options.removeRuleIds | number\[\] | \[\] | 按 ID 删除，先删后加 |

```js
// 上下文：Service Worker
await chrome.declarativeNetRequest.updateDynamicRules({
  removeRuleIds: [1],
  addRules: [{
    id: 1,
    priority: 1,
    action: { type: "block" },
    condition: {
      urlFilter: "||ads.example.com",
      resourceTypes: ["script", "image"]
    }
  }]
});

```

### getDynamicRules(filter?)

返回当前动态规则。

```js
// 上下文：Service Worker
const rules = await chrome.declarativeNetRequest.getDynamicRules();

```

### updateSessionRules(options)

增删会话规则（浏览器关闭即清除，不写磁盘），参数同 `updateDynamicRules`。

```js
// 上下文：Service Worker
await chrome.declarativeNetRequest.updateSessionRules({
  addRules: [{
    id: 100,
    priority: 1,
    action: {
      type: "modifyHeaders",
      requestHeaders: [{ header: "x-tag", operation: "set", value: "ext" }]
    },
    condition: { urlFilter: "||api.example.com", resourceTypes: ["xmlhttprequest"] }
  }]
});

```

### getSessionRules()

返回当前会话规则。

```js
// 上下文：Service Worker
const session = await chrome.declarativeNetRequest.getSessionRules();

```

静态规则在 manifest 的 `declarative_net_request.rule_resources` 中以 JSON 文件声明，随扩展打包，适合稳定的大规模过滤列表：

```json
{
  "declarative_net_request": {
    "rule_resources": [{
      "id": "ruleset_1",
      "enabled": true,
      "path": "rules.json"
    }]
  }
}

```

---

## chrome.identity

需 `identity` 权限。OAuth2 与第三方登录。

### getAuthToken(details?)

获取 Google OAuth2 令牌（仅限 Google 账户场景），返回 `{ token, grantedScopes }`。需在 manifest 配 `oauth2.client_id` 与 `scopes`。

| 参数                  | 类型         | 默认值         | 说明         |
| ------------------- | ---------- | ----------- | ---------- |
| details.interactive | boolean    | false       | 是否允许弹出登录界面 |
| details.scopes      | string\[\] | manifest 配置 | 覆盖默认 scope |

```js
// 上下文：Service Worker 或页面
const { token } = await chrome.identity.getAuthToken({ interactive: true });
const res = await fetch("https://www.googleapis.com/oauth2/v1/userinfo", {
  headers: { Authorization: `Bearer ${token}` }
});

```

### launchWebAuthFlow(details)

通用 OAuth2 流程（适配任意第三方 IdP），打开授权页并在重定向回 `getRedirectURL()` 时返回最终 URL。

| 参数                  | 类型      | 默认值   | 说明         |
| ------------------- | ------- | ----- | ---------- |
| details.url         | string  | 必填    | 授权端点完整 URL |
| details.interactive | boolean | false | 是否显示授权界面   |

```js
// 上下文：页面（用户手势内）
const redirectUrl = chrome.identity.getRedirectURL();
const authUrl = `https://github.com/login/oauth/authorize?client_id=ID&redirect_uri=${encodeURIComponent(redirectUrl)}`;
const resultUrl = await chrome.identity.launchWebAuthFlow({
  url: authUrl,
  interactive: true
});
const code = new URL(resultUrl).searchParams.get("code");

```

### getRedirectURL(path?)

同步返回 `https://<extension-id>.chromiumapp.org/<path>` 形式的回调 URL，用于在 IdP 注册 redirect\_uri。

```js
// 上下文：任意
console.log(chrome.identity.getRedirectURL("cb"));
// https://abcdefg.chromiumapp.org/cb

```

---

## chrome.offscreen

需 `offscreen` 权限。MV3 的 SW 无 DOM、无 `window`，本 API 创建隐藏文档以执行需要 DOM 的操作（解析 HTML、读剪贴板、播放音频等）。**同一时刻只能有一个** offscreen 文档（split 模式下普通与隐身各一个）。

### createDocument(parameters)

创建 offscreen 文档，加载完成后 resolve。

| 参数                       | 类型         | 默认值 | 说明            |
| ------------------------ | ---------- | --- | ------------- |
| parameters.url           | string     | 必填  | 扩展内 HTML 相对路径 |
| parameters.reasons       | string\[\] | 必填  | 用途枚举，见下       |
| parameters.justification | string     | 必填  | 给审核与用户看的理由说明  |

`reasons` 取值：`"AUDIO_PLAYBACK"`（播放音频）、`"CLIPBOARD"`（读写剪贴板）、`"DOM_PARSER"`（用 DOMParser）、`"DOM_SCRAPING"`（解析抓取 DOM）、`"BLOBS"`（创建对象 URL）、`"IFRAME_SCRIPTING"`、`"USER_MEDIA"`、`"DISPLAY_MEDIA"`、`"WEB_RTC"`、`"LOCAL_STORAGE"`、`"WORKERS"`、`"BATTERY_STATUS"`、`"MATCH_MEDIA"`、`"GEOLOCATION"`、`"TESTING"`。

```js
// 上下文：Service Worker
async function ensureOffscreen() {
  const existing = await chrome.runtime.getContexts({
    contextTypes: ["OFFSCREEN_DOCUMENT"]
  });
  if (existing.length) return; // 已存在则跳过，避免重复创建报错
  await chrome.offscreen.createDocument({
    url: "offscreen.html",
    reasons: ["DOM_PARSER"],
    justification: "解析远程返回的 HTML 片段"
  });
}

// 错误：未判重，第二次创建直接抛 "Only a single offscreen document may be created"
// await chrome.offscreen.createDocument({...}); // 错误：重复创建

```

### closeDocument()

关闭当前 offscreen 文档，无参数，返回 `Promise<void>`。

```js
// 上下文：Service Worker
await chrome.offscreen.closeDocument();

```

### hasDocument()

返回是否存在 offscreen 文档。注意：此为旧式判断，新代码推荐 `chrome.runtime.getContexts`。

```js
// 上下文：Service Worker
if (!(await chrome.offscreen.hasDocument())) {
  await chrome.offscreen.createDocument({ /* ... */ });
}

```

---

## chrome.i18n

无需 permission，但需建立 `_locales/<lang>/messages.json` 并在 manifest 设 `default_locale`。

### getMessage(messageName, substitutions?)

同步返回当前 UI 语言对应的本地化字符串。

| 参数            | 类型                   | 默认值 | 说明                   |
| ------------- | -------------------- | --- | -------------------- |
| messageName   | string               | 必填  | messages.json 中的键名   |
| substitutions | string \| string\[\] | 无   | 替换 $1\-$9 占位符，最多 9 个 |

```js
// 上下文：任意
const title = chrome.i18n.getMessage("appTitle");
const greet = chrome.i18n.getMessage("greet", ["小明"]); // "你好，小明"

```

`_locales/zh_CN/messages.json`：

```json
{
  "appTitle": { "message": "我的扩展" },
  "greet": { "message": "你好，$1", "placeholders": {} }
}

```

### getUILanguage()

同步返回浏览器 UI 语言（如 `"zh-CN"`），无参数。

```js
// 上下文：任意
console.log(chrome.i18n.getUILanguage());

```

### detectLanguage(text)

检测文本语言，返回 `{ isReliable, languages: [{ language, percentage }] }`。

| 参数   | 类型     | 默认值 | 说明    |
| ---- | ------ | --- | ----- |
| text | string | 必填  | 待检测文本 |

```js
// 上下文：任意
const r = await chrome.i18n.detectLanguage("这是一段中文");
console.log(r.languages[0].language); // "zh"

```

---

## chrome.downloads

需 `downloads` 权限。

### download(options)

发起下载，返回 `downloadId`。

| 参数                     | 类型      | 默认值        | 说明                              |
| ---------------------- | ------- | ---------- | ------------------------------- |
| options.url            | string  | 必填         | 下载地址                            |
| options.filename       | string  | 服务器推断      | 相对默认下载目录的路径                     |
| options.saveAs         | boolean | false      | 是否弹「另存为」                        |
| options.conflictAction | string  | "uniquify" | "uniquify"/"overwrite"/"prompt" |

```js
// 上下文：Service Worker 或页面
const id = await chrome.downloads.download({
  url: "https://example.com/report.pdf",
  filename: "reports/report.pdf"
});

```

其它常用：`search(query)` 查询下载项、`cancel(id)` 取消、`pause(id)` / `resume(id)` 暂停恢复、`onChanged` 监听状态变化。

---

## chrome.bookmarks

需 `bookmarks` 权限。

### create(bookmark)

创建书签或文件夹（省略 `url` 即文件夹），返回 `BookmarkTreeNode`。

| 参数                | 类型     | 默认值     | 说明       |
| ----------------- | ------ | ------- | -------- |
| bookmark.parentId | string | 「其他书签」  | 父节点 ID   |
| bookmark.title    | string | ""      | 标题       |
| bookmark.url      | string | 无（=文件夹） | 链接地址     |
| bookmark.index    | number | 末尾      | 在父节点中的位置 |

```js
// 上下文：Service Worker 或页面
const node = await chrome.bookmarks.create({
  title: "Chrome 扩展文档",
  url: "https://developer.chrome.com/docs/extensions"
});

```

其它常用：`search(query)` 搜索、`getTree()` 取完整树、`remove(id)` / `removeTree(id)` 删除、`update(id, changes)` 改标题或 URL、`move(id, dest)` 移动。

---

## chrome.history

需 `history` 权限。

### search(query)

搜索浏览历史，返回 `HistoryItem[]`。

| 参数               | 类型     | 默认值    | 说明            |
| ---------------- | ------ | ------ | ------------- |
| query.text       | string | ""（全部） | 模糊匹配 URL/标题   |
| query.startTime  | number | 24 小时前 | 起始时间 epoch ms |
| query.endTime    | number | 现在     | 结束时间          |
| query.maxResults | number | 100    | 返回上限          |

```js
// 上下文：Service Worker 或页面
const items = await chrome.history.search({
  text: "github",
  maxResults: 20
});

```

其它常用：`addUrl({ url })` 添加访问记录、`deleteUrl({ url })` 删除某 URL、`deleteRange({ startTime, endTime })` 按时间段删、`getVisits({ url })` 取访问明细、`onVisited` / `onVisitRemoved` 事件。

---

## 最佳实践

### 1\. 批量读写 storage，避免逐条操作

`set`/`get` 接受多键对象，一次调用比循环多次调用快得多，且能规避 `sync` 区每分钟 120 次的写频率上限。

```js
// 推荐：单次写多键
await chrome.storage.local.set({ a: 1, b: 2, c: 3 });

// 错误：循环逐条，慢且易触限流
// for (const [k, v] of entries) await chrome.storage.sync.set({ [k]: v }); // 错误：高频写

```

### 2\. tabs.query 必须带条件，杜绝全表扫描

无条件 `query({})` 返回所有窗口所有标签，浪费且需更宽权限。绝大多数场景只需当前激活标签。

```js
// 推荐：精确锁定
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });

```

### 3\. 用 alarms 替代 setTimeout/setInterval

MV3 的 SW 空闲约 30 秒后被回收，定时器随之失效。持久定时任务一律用 `chrome.alarms`，并记得最小周期 0.5 分钟。

```js
// 推荐
chrome.runtime.onInstalled.addListener(() => {
  chrome.alarms.create("poll", { periodInMinutes: 5 });
});
chrome.alarms.onAlarm.addListener((a) => a.name === "poll" && poll());

```

### 4\. 异步消息回复显式 return true，或返回 Promise

`onMessage` 监听器若在异步操作后调用 `sendResponse`，未 `return true` 会导致通道提前关闭、响应丢失。Chrome 148+ 可直接返回 Promise 替代。

```js
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  fetchData().then(sendResponse);
  return true; // 推荐：保持通道
});

```

### 5\. offscreen / 资源创建前先判重

offscreen 文档全局仅一个，重复 `createDocument` 直接抛错。创建前用 `getContexts` 检查，contextMenus 则在 `onInstalled` 中创建以避免 SW 多次唤醒重复添加。

```js
const has = (await chrome.runtime.getContexts({
  contextTypes: ["OFFSCREEN_DOCUMENT"]
})).length > 0;
if (!has) await chrome.offscreen.createDocument({ /* ... */ });

```

### 6\. 优先 declarativeNetRequest 而非阻塞式 webRequest

MV3 已移除阻塞式 `webRequest`。请求拦截/改写改用声明式规则，性能更高且无需读取请求体。频繁变动的规则用 `updateDynamicRules`，临时规则用会话规则。

```js
await chrome.declarativeNetRequest.updateDynamicRules({
  addRules: [{ id: 1, priority: 1, action: { type: "block" },
    condition: { urlFilter: "||tracker.com", resourceTypes: ["script"] } }]
});

```

### 7\. 运行时权限按需申请，缩小默认权限面

把非核心权限放进 `optional_permissions`，在用户触发对应功能时用 `permissions.request`（须在手势内）申请，既过审更顺利，也减少安装时的权限警告。

```js
btn.onclick = async () => {
  if (await chrome.permissions.request({ permissions: ["cookies"] })) {
    readCookies();
  }
};

```

---

## 常见陷阱

### 陷阱 1：executeScript 的 func 无法访问外部闭包变量

- 现象：`func: () => useExternal` 在目标页执行时报 `useExternal is not defined`。
- 原因：`func` 会被序列化为字符串发送到目标页进程执行，与 SW 作用域完全隔离，闭包不跨进程。
- 解决：所有外部数据通过 `args` 数组传入，且必须 JSON 可序列化。

```js
const value = 42;
await chrome.scripting.executeScript({
  target: { tabId },
  func: (v) => { document.title = v; }, // 通过参数接收
  args: [value]
});

```

### 陷阱 2：tabs.sendMessage 目标无内容脚本时抛错

- 现象：调用 `chrome.tabs.sendMessage` 抛 `Could not establish connection. Receiving end does not exist.`
- 原因：目标标签尚未注入内容脚本（如 `chrome://` 页、刚打开未匹配的页、CS 注入失败）。
- 解决：用 `try/catch` 包裹；或先 `executeScript` 确保 CS 存在，再发消息。

```js
try {
  await chrome.tabs.sendMessage(tabId, { ping: 1 });
} catch {
  await chrome.scripting.executeScript({ target: { tabId }, files: ["cs.js"] });
  await chrome.tabs.sendMessage(tabId, { ping: 1 });
}

```

### 陷阱 3：storage.sync 静默超配额导致写入失败

- 现象：写入大对象后读回为空或报错，但小数据正常。
- 原因：`sync` 单项上限 8 KB、总量 100 KB、最多 512 项，超限的 `set` 会通过 `runtime.lastError` 报错而非抛异常，容易被忽略。
- 解决：大数据放 `local`（10 MB）；`sync` 只存小配置；写前用 `getBytesInUse` 估算，Promise 风格用 `try/catch` 捕获超配额错误。

```js
try {
  await chrome.storage.sync.set({ bigBlob: hugeString }); // 可能超 8KB/项
} catch (e) {
  console.warn("sync 超配额，降级到 local：", e.message);
  await chrome.storage.local.set({ bigBlob: hugeString });
}

```

### 陷阱 4：sidePanel.open / permissions.request 在非手势上下文被拒

- 现象：在 `alarm`、`onMessage`、页面加载等非用户手势回调中调用，方法静默失败或报错。
- 原因：这两个 API 出于安全要求必须由用户手势（点击、快捷键）直接触发。
- 解决：只在 `action.onClicked`、`commands.onCommand`、DOM `click` 等手势回调内调用，且避免在手势回调里先 `await` 其它异步操作再调用（部分浏览器会判定手势已失效）。

---

## 参见

- [浏览器插件开发完全指南](https://blog.vercanti.com/liu-lan-qi-cha-jian-kai-fa-wan-quan-zhi-nan-vite-vue-manifest-v3/)
- [浏览器插件-基础概念与架构模型](https://blog.vercanti.com/liu-lan-qi-cha-jian-ji-chu-gai-nian-yu-jia-gou-mo-xing/)
- [浏览器插件-中级开发指南](https://blog.vercanti.com/liu-lan-qi-cha-jian-zhong-ji-kai-fa-zhi-nan-manifest-v3-vite-vue/)
- [浏览器插件-高级开发指南](https://blog.vercanti.com/liu-lan-qi-cha-jian-gao-ji-kai-fa-zhi-nan-manifest-v3/)
- [浏览器插件-调试与排错手册](https://blog.vercanti.com/liu-lan-qi-cha-jian-diao-shi-yu-pai-cuo-shou-ce/)