主题
多上下文通信机制
本文引用的源码文件
本文引用的文件
目录
引言
本扩展采用 Manifest V3 双进程架构:Chrome 扩展端位于 src/,版本为 2.3.1;独立本地 Agent 端为 Node.js 服务,入口为 agent/src/server.js。两者通过本地 HTTP/WebSocket 通信,但本文聚焦于 Chrome 扩展内部四个核心上下文的通信机制:Background Service Worker、Side Panel、Content Scripts 和 Offscreen Document。
在 MV3 中,Service Worker 生命周期受系统控制,空闲时可能被终止;Side Panel 是用户交互主界面;Content Script 注入网页执行页面级工具;Offscreen Document 则用于突破 MV3 对剪贴板等 API 的限制。这些上下文之间通过 chrome.runtime.sendMessage、chrome.runtime.connect、chrome.tabs.sendMessage 以及 Offscreen 文档的 chrome.runtime.onMessage 进行协作。
项目结构与上下文边界
扩展的上下文边界由 manifest.json 明确声明:
| 上下文 | 入口或声明位置 | 主要职责 |
|---|---|---|
| Background Service Worker | manifest.json.background.service_worker → src/background/index.js | 全局状态、消息路由、ReAct 执行循环、Agent 客户端、侧边栏控制、通知、定时任务 |
| Side Panel | manifest.json.side_panel.default_path → side_panel.html + src/side_panel/index.js | 聊天 UI、会话管理、工具面板、截图、导出、工作区、Agent 管理 |
| Content Scripts | manifest.json.content_scripts → src/content/index.js | 页面内容读取、DOM 交互、选区工具栏、区域截图、页面度量 |
| Offscreen Document | manifest.json.permissions.offscreen + src/offscreen/offscreen.html | 剪贴板读写(MV3 兼容桥) |
| Options Page | manifest.json.options_page → options.html + src/options/index.js | 配置页、Agent 配对、工具箱、MCP/Skill/RAG 设置 |
graph TB A["Side Panel<br/>src/side_panel/index.js"] --> B["Background Service Worker<br/>src/background/index.js"] C["Content Scripts<br/>src/content/index.js"] --> B D["Options Page<br/>src/options/index.js"] --> B B --> E["Offscreen Document<br/>src/offscreen/offscreen.html"] B --> F["Agent 本地服务<br/>agent/src/server.js"] B --> G["IndexedDB / chrome.storage"]
图表来源
- manifest.json#L1-L76
- src/background/index.js#L1-L20
- src/content/index.js#L1-L34
- src/offscreen/offscreen.html#L1-L12
章节来源
核心上下文职责划分
Background Service Worker
Background 是扩展的核心调度层。它承担以下职责:
- 消息路由中心:统一接收 Side Panel、Content Script、Options Page 发来的
chrome.runtime.sendMessage,按message.type分发处理。 - ReAct 执行循环:协调工具预筛选、工具执行、流式输出、完成度校验、Token 统计。
- Side Panel 生命周期管理:打开/关闭侧边栏、脱离窗口、Tab 分组、作用域模式。
- Keepalive 长连接:通过
chrome.runtime.connect建立命名端口,防止 SW 被系统回收。 - Agent 健康检查与心跳:定期检测本地 Agent 可达性,自动重连并刷新 MCP/RAG 工具。
- 通知与可见性判定:根据焦点、面板状态、发起实例身份决定是否弹出桌面通知。
- Checkpoint 恢复:SW 被回收后通过闹钟唤醒,从 IndexedDB 恢复 ReAct 任务。
章节来源
- src/background/index.js#L1-L20
- src/background/index.js#L56-L95
- src/background/index.js#L140-L218
- src/background/index.js#L882-L918
- src/background/index.js#L2005-L2307
Side Panel
Side Panel 是用户交互主界面,负责:
- 聊天与会话管理:发送消息、流式渲染、历史恢复、压缩摘要。
- 工具面板与技能选择器:展示 MCP/Skill/RAG 工具,支持 @ 引用知识库。
- 截图与图片识别:全屏截图、区域截图、图片附件。
- Agent 管理:配对、连接、重启、更新、停止。
- 配置同步:监听
chrome.storage.onChanged,实时更新记忆限制、自动压缩等开关。 - 与 Background 双向通信:通过
sendMessage发起请求,通过connect维持长连接保活。
章节来源
Content Scripts
Content Script 注入所有匹配 URL,提供页面级能力:
- 消息路由表:使用
HANDLERSMap 实现 O(1) 查找,按message.type分发到具体处理器。 - 页面读取:获取文本、HTML、选中内容、表格、元数据、链接、表单、图片。
- 页面交互:点击元素、填写表单、滚动、键盘输入、文件上传。
- 异步工具标记:通过
ASYNC_HANDLERSSet 判断是否需要保持消息通道开放。 - 顶层帧保护:通过
TOP_FRAME_ONLY_TYPES避免 iframe 响应覆盖主页面。 - 区域截图选择:在页面创建遮罩层,让用户拖拽选择截图区域。
章节来源
Offscreen Document
Offscreen Document 是 MV3 下剪贴板访问的专用上下文:
- 剪贴板读写桥:通过
navigator.clipboard.writeText/readText实现复制粘贴。 - 降级回退:当现代剪贴板 API 失败时,回退到
document.execCommand('copy/paste')。 - 国际化文案:复用
../shared/i18n.js的多语言支持。 - 最小化 HTML:仅包含一个隐藏 textarea 作为剪贴板操作容器。
章节来源
架构总览与权限模型
上下文间通信架构图
sequenceDiagram participant SP as "Side Panel" participant BG as "Background Service Worker" participant CS as "Content Script" participant OD as "Offscreen Document" participant AG as "Agent 本地服务" SP->>BG : "chrome.runtime.sendMessage(CALL_API)" BG->>BG : "工具预筛选 + ReAct 循环" BG->>CS : "chrome.tabs.sendMessage(页面工具)" CS-->>BG : "页面结果" BG->>OD : "chrome.runtime.sendMessage(COPY_TO_CLIPBOARD)" OD-->>BG : "剪贴板结果" BG->>AG : "HTTP/WebSocket 调用" AG-->>BG : "工具执行结果" BG-->>SP : "API_COMPLETE / STREAM_*"
图表来源
权限模型说明
| 权限 | 用途 | 影响范围 |
|---|---|---|
offscreen | 创建 Offscreen Document | 允许访问剪贴板等受限 API |
clipboardRead | 从剪贴板读取文本 | 通过 Offscreen Document 实现 |
clipboardWrite | 写入剪贴板文本 | 通过 Offscreen Document 实现 |
sidePanel | 打开/关闭侧边栏 | 控制 Side Panel 生命周期 |
scripting | 动态注入脚本 | 配合 Content Script 使用 |
tabs | 标签页操作 | 截图、消息转发、窗口管理 |
storage / unlimitedStorage | 配置与持久化 | Session、Checkpoint、Token 统计 |
debugger | 调试功能 | 页面调试、CDP 操作 |
webNavigation | 导航事件 | 页面加载状态监听 |
章节来源
消息路由表设计模式
Background 消息路由
Background 使用 if-chain 方式按 message.type 分发消息,注释中明确列出了所有消息类型、来源和用途:
| 消息类型 | 来源 | 用途 | 是否异步 |
|---|---|---|---|
CANCEL_REACT | Side Panel | 取消 ReAct 循环 | 否 |
TERMINATE_COMMAND | Side Panel | 终止命令(不取消 ReAct) | 否 |
RELOAD_MCP_TOOLS | Side Panel | 强制重载 MCP 工具列表 | 是 |
RAG_SEARCH | Side Panel | 知识库检索 | 是 |
GET_MCP_TOOLS | Side Panel | 获取 MCP 工具(30s 缓存) | 是 |
CAPTURE_TAB | Side Panel | 截取可见标签页 | 是 |
CALL_API | Side Panel | 主 API 调用入口 | 否(内部异步) |
SELECTION_TOOLBAR_ACTION | Content Script | 划词工具栏操作 | 否 |
FILL_SIDEPANEL_INPUT | Content Script | 填充侧边栏输入框 | 否 |
DIRECT_SEND | Content Script | 直接发送文本 | 否 |
章节来源
Content Script 消息路由
Content Script 使用 Map 实现 O(1) 查找的消息路由表:
flowchart TD
A["收到 chrome.runtime.onMessage"] --> B{"message.type 是否存在于 HANDLERS?"}
B --> |否| C["忽略消息"]
B --> |是| D["调用对应处理器"]
D --> E{"处理器返回 Promise 或属于 ASYNC_HANDLERS?"}
E --> |是| F["return true 保持通道开放"]
E --> |否| G["sendResponse 同步回复"]
图表来源
章节来源
短消息与长连接选择策略
chrome.runtime.sendMessage 的使用场景
sendMessage 适用于短消息、一次性请求-响应场景:
- Side Panel → Background:
CALL_API、CAPTURE_TAB、GET_CHAT_CONFIG - Content Script → Background:
SELECTION_TOOLBAR_ACTION、FILL_SIDEPANEL_INPUT、DIRECT_SEND - Background → Offscreen:
COPY_TO_CLIPBOARD、PASTE_FROM_CLIPBOARD - Background → Content Script:
SCREENSHOT_RESULT、STREAM_PRESELECT
特点:
- 单次请求,立即响应
- 最大消息大小 64MiB(Background 中对 executionLog 做了截断)
- 适合状态查询、工具调用、截图等
chrome.runtime.connect 的使用场景
connect 适用于长连接、双向通信、保活场景:
- Side Panel → Background Keepalive:命名端口
keepalive-{sessionId} - 身份上报:KEEPALIVE_IDENTITY 携带 windowId 和 hostTabId
- SW 重启检测:SW_RESTARTED 通知前端可恢复的 checkpoint
- 面板断开提醒:延迟复核后触发桌面通知
特点:
- 持久连接,可多次 postMessage
- 防止 SW 被系统回收
- 适合长时间运行的任务(如 ReAct 循环)
选择策略总结
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 一次性工具调用 | sendMessage | 简单、轻量、有超时 |
| 实时流式输出 | sendMessage + StreamController | 分片传输,避免大消息 |
| 保活防回收 | connect | 长连接保持 SW 活跃 |
| 双向身份同步 | connect | 端口级别的状态共享 |
| 剪贴板操作 | sendMessage to Offscreen | 隔离敏感 API |
章节来源
MV3 安全沙箱与剪贴板绕过方案
MV3 限制背景
MV3 的 Service Worker 和 Content Script 无法直接访问某些浏览器 API,特别是剪贴板。这是出于安全考虑,防止恶意扩展随意读取用户剪贴板内容。
Offscreen Document 解决方案
项目通过 Offscreen Document 绕过限制:
- 权限声明:
manifest.json中声明offscreen、clipboardRead、clipboardWrite - 按需创建:Background 通过
chrome.offscreen.createDocument按需创建 Offscreen 文档 - 单例保护:通过
creatingOffscreenDocumentPromise 避免重复创建 - 剪贴板桥接:Offscreen 文档暴露
COPY_TO_CLIPBOARD和PASTE_FROM_CLIPBOARD消息
sequenceDiagram
participant BG as "Background"
participant OD as "Offscreen Document"
participant CL as "Clipboard API"
BG->>BG : "ensureOffscreenDocument()"
BG->>OD : "chrome.runtime.sendMessage(COPY_TO_CLIPBOARD)"
OD->>CL : "navigator.clipboard.writeText()"
CL-->>OD : "成功/失败"
OD-->>BG : "{success, message/error}"
图表来源
降级策略
当现代剪贴板 API 失败时,Offscreen 文档会回退到 document.execCommand('copy/paste'):
- 创建一个隐藏的 textarea
- 设置值并 select()
- 调用 execCommand 执行复制/粘贴
- 清理临时元素
这种双重策略确保在不同浏览器版本和环境下的兼容性。
章节来源
关键通信时序
CALL_API 完整流程
sequenceDiagram participant SP as "Side Panel" participant BG as "Background" participant TS as "Tool Executor" participant RL as "React Loop" participant CS as "Content Script" participant OD as "Offscreen" participant AG as "Agent" SP->>BG : "CALL_API(messages, model, useTools)" BG->>BG : "重置 API 调用计数" BG->>TS : "getTools(agentToolIds, agentId)" TS-->>BG : "可用工具列表" BG->>BG : "工具预筛选" BG->>RL : "reactLoop(messages, tools, tabId)" loop ReAct 循环 RL->>CS : "chrome.tabs.sendMessage(页面工具)" CS-->>RL : "页面操作结果" RL->>OD : "chrome.runtime.sendMessage(COPY_TO_CLIPBOARD)" OD-->>RL : "剪贴板操作结果" RL->>AG : "HTTP/WebSocket 调用" AG-->>RL : "AI 响应" RL->>SP : "STREAM_* 流式消息" end RL-->>BG : "最终结果" BG-->>SP : "API_COMPLETE"
图表来源
Keepalive 保活流程
sequenceDiagram
participant SP as "Side Panel"
participant BG as "Background"
participant SW as "Service Worker"
SP->>BG : "chrome.runtime.connect('keepalive-{sessionId}')"
BG->>BG : "registerKeepalivePort(sessionId, port)"
SP->>BG : "port.postMessage(KEEPALIVE_IDENTITY)"
BG->>BG : "setKeepaliveIdentity(sessionId, identity)"
BG-->>SP : "HOST_TAB_RESOLVED(hostTabId)"
Note over BG : "SW 被回收前,连接断开"
BG->>BG : "onDisconnect 延迟复核"
BG->>BG : "shouldNotifyPanelDetached()"
BG-->>SP : "桌面通知如果面板确实关闭"
图表来源
章节来源
序列化、大数据与安全验证
消息序列化限制
Chrome 扩展的消息传递基于结构化克隆算法,不支持函数、循环引用、undefined 等类型。Background 中对大消息的处理:
- executionLog 截断:
truncateLogForMessage函数保留最后 1000 条日志,同时确保subtask_plan条目不被丢弃 - MAX_LOG_ENTRIES_FOR_MSG:常量定义最大日志条目数
- JSON.stringify 安全:所有跨上下文消息都经过 JSON 序列化
大数据传输优化
- 流式传输:ReAct 循环通过
STREAM_*消息分片传输 AI 响应 - 缓存机制:MCP 工具列表 30s 缓存,Skill Prompts 60s 缓存
- 按需加载:Offscreen Document 按需创建,避免启动开销
- 分页处理:Content Script 的页面交互工具支持 waitTime/timeout 参数
安全验证机制
- Source 校验:Content Script 中部分消息处理检查
sender.tab?.id - Frame 隔离:
TOP_FRAME_ONLY_TYPES防止 iframe 响应污染主页面 - 权限最小化:Offscreen Document 仅暴露剪贴板相关消息
- 错误降级:所有跨上下文调用都使用
.catch()静默失败,避免扩展崩溃
章节来源
故障排查指南
常见问题与解决方案
1. Service Worker 被回收导致消息丢失
现象:长时间运行任务后,Side Panel 无响应或显示“未连接”
原因:MV3 Service Worker 空闲时被系统回收,内存中的 activeReactLoops 清空
解决方案:
- 使用 Keepalive 长连接防止回收
- 利用 Checkpoint 机制自动恢复任务
- 监听
SW_RESTARTED消息提示用户继续执行
章节来源
2. 侧边栏无法打开或关闭
现象:快捷键无效,或点击插件图标无反应
原因:手势上下文丢失、Tab 分组状态异常、Chrome 版本兼容性问题
解决方案:
- 确保在用户手势同步栈内调用
sidePanel.open() - 检查
_openPanelsByWindow镜像状态 - 使用
bindSidePanelToTab绑定 Tab 专属面板
章节来源
3. 剪贴板操作失败
现象:复制粘贴功能不可用
原因:Offscreen Document 未创建、权限不足、浏览器 API 不支持
解决方案:
- 检查
chrome.offscreen.createDocument是否成功 - 确认 manifest 中声明了
offscreen、clipboardRead、clipboardWrite权限 - 查看 Offscreen 文档的控制台错误
章节来源
4. Content Script 消息无响应
现象:Content Script 注册的消息处理器未被调用
原因:消息类型不在 HANDLERS 中、iframe 响应冲突、扩展上下文失效
解决方案:
- 检查
message.type是否在HANDLERS中定义 - 对于顶层帧独占操作,使用
TOP_FRAME_ONLY_TYPES - 监听
Extension context invalidated错误
章节来源
结论
本项目实现了完整的 Chrome 扩展多上下文通信机制,通过精心设计的消息路由表、长连接保活策略和 Offscreen Document 权限绕过方案,解决了 MV3 架构下的核心挑战。
关键设计要点包括:
- 分层职责清晰:Background 负责调度和状态管理,Side Panel 专注 UI,Content Script 处理页面交互,Offscreen 隔离敏感 API
- O(1) 消息路由:使用 Map 实现高效的消息分发,支持异步处理器和顶层帧保护
- 保活与恢复:Keepalive 长连接防止 SW 回收,Checkpoint 机制支持任务自动恢复
- 权限最小化:Offscreen Document 仅暴露必要接口,降低安全风险
- 容错设计:所有跨上下文调用都有错误处理和降级策略
这套架构不仅满足了当前功能需求,也为未来的扩展提供了良好的基础。通过持续优化消息序列化、大数据传输和安全验证机制,可以进一步提升性能和安全性。