mcpField
I
integrated-browser

integrated-browser

github.com/microsoft开发194.4k67 次安装今天更新
代码仓库

关于 integrated-browser

面向 VS Code 集成浏览器贡献者的架构技能,介绍 Chromium 页面、进程边界、状态管理与安全设计,并说明如何扩展浏览器行为。

主要功能

  1. 01通过 Electron WebContentsView 嵌入 Chromium
  2. 02说明主进程、共享进程和渲染进程职责
  3. 03基于 IPC 管理页面状态与操作
  4. 04区分会话、组和 CDP 隔离
  5. 05通过编辑器贡献模型扩展功能

适用场景

  1. 01修改代码前理解浏览器架构
  2. 02添加页面状态或操作
  3. 03检查页面共享与信任边界

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/microsoft/vscode --skill integrated-browser

安装后在 Claude Code 中输入/integrated-browser确认已加载。

详细介绍

集成浏览器架构

集成浏览器(“browserView”)通过 Electron WebContentsView 在 VS Code 中嵌入了一个真正的 Chromium 浏览器。它可渲染实时页面,将每个页面显示为一个编辑器标签页,并让 Agent 通过工具操控这些页面。它为产品内的浏览器标签页和 Agent“浏览器”工具提供支持。它不是旧版 extensions/simple-browser(一个嵌入在 webview 中的 iframe);现在在桌面端,它会将请求委托给这里的实现。

这是一个重量级、对安全性要求很高的多进程基础组件,几乎所有设计决策都由此而来。 本文件介绍那些很少变化的关键理念。这里有意不罗列当前的功能、工具、命令和设置——这些内容变化频繁;应以 features/ 和 tools/ 目录中的最新内容为准。先在这里建立心智模型,再去查看你要修改的具体代码。

一切设计所遵循的核心理念

页面是一个原生 WebContentsView,只能由主进程创建、拥有并定位。其他任何对象都不能直接操作它。 页面由主进程拥有,由操作系统合成器绘制在工作台 DOM 上方,而不是绘制在其中。其他一切都围绕这一点展开:

  • 渲染进程(编辑器 UI 和 Agent 工具)无法持有页面;它持有模型/代理,并通过 IPC 与主进程通信。
  • Playwright 依赖重且生命周期长,因此运行在共享进程中,也通过 IPC 访问页面。
  • 页面会绘制在 DOM 上方,因此工作台必须手动协调对齐、z 顺序、焦点和屏幕截图。

三个进程,以及这样划分的原因

进程位置此处包含的内容
主进程platform/browserView/electron-mainWebContentsView、会话、信任、权限、历史记录、CDP、屏幕截图——权威的页面状态。只有主进程能创建原生视图。
共享进程platform/browserView/nodePlaywright + 远程/分组自动化服务。将重量级依赖从主进程中移出(以保证稳定性),也从渲染进程中移出(以避免影响生命周期)。
渲染进程workbench/contrib/browserView + platform/browserView/electron-browser编辑器窗格、UI 功能扩展、Agent 工具、页面预加载脚本。只持有轻量代理。

分层规则: platform 不得导入 workbench;Agent 宿主不得导入 workbench;共享类型应放在 platform/common 中。渲染进程通过 ProxyChannel IPC 访问主进程/共享进程,绝不通过导入实现来访问。浏览器事件共用一个按宿主窗口作用域的即时订阅通道,用于接收所有事件;工作台会先应用初始快照,再将实时变更路由为模型本地事件。缓冲区作为元数据旁的元组/数组元素传递,而不是嵌套在 JSON 对象中。通道在 electron-main/app.ts 和 electron-utility/sharedProcess/sharedProcessMain.ts 中注册。划分如下:页面操作/状态 → 主进程;Agent 自动化 → 共享进程;CDP 连接管理使用独立通道,渲染进程和共享进程都通过它访问主进程。

渲染进程持有的是一面镜子,而非真相

渲染进程模型是主进程视图所拥有状态的只读副本。数据流是单向的:

renderer feature ──command──▶ model ──IPC──▶ main BrowserView ──▶ Chromium
       ▲                                              │
       └──────────── event (state changed) ◀──────────┘

要执行操作,请调用方法(往返主进程);要响应状态变化,请监听模型事件。切勿在渲染进程中计算页面状态——它无法访问网页内容。

  • 新状态(url/title/loading/zoom/…):在主进程的 BrowserView 中将其设为权威状态,发出变更事件,然后在渲染进程模型中镜像该字段和事件,并将其添加到 BrowserViewMainService 的类型化转发映射中。必须在导航前安装转发;不要添加按浏览器分别订阅的 IPC。每个窗口订阅都会以当前快照作为起始状态,这定义了工作台就绪状态,无需单独的初始化 RPC。工作台拥有每个浏览器的事件发送器映射,将其传入模型,并按浏览器 ID 和事件名称直接分发;模型只负责接收事件。
  • 新操作(navigate/reload/focus/…):在主进程中定义,并在渲染进程模型上公开一个精简的代理方法,通过通道往返调用。

如果你想让渲染进程从页面上“直接读取”某些内容,这就说明你需要添加新的镜像状态和来自主进程的事件。

原生视图浮在 DOM 上方(覆盖层问题)

这是最容易出错的领域,也是几乎所有“无法移动/错位/菜单透出/无法聚焦”问题的根源。页面由主进程以屏幕坐标绘制在渲染进程之上,因此工作台会模拟一个普通 DOM 元素。请将页面的矩形区域、可见性、焦点和图像视为工作台需要协调的内容,而非 DOM 的事实:

  • 对齐。 编辑器会渲染一个空的 DOM 占位元素,测量其屏幕矩形区域,并将边界发送给主进程。CSS 缩放与屏幕像素并不一致,因此边界会对齐到像素。任何会移动页面的新布局,都必须参与这项边界计算,而不能只移动 DOM 盒子。
  • 层级顺序。 原生视图绘制在所有工作台 UI 之上,因此本应显示在页面上方的菜单/弹出窗口/悬停提示/对话框都会被遮挡。工作台会检测重叠并隐藏原生视图,改为显示占位内容。任何覆盖在页面上的新浮动 UI 都必须能被这套机制检测到——较高的 CSS z-index 无济于事。
  • 闪烁遮罩。 隐藏或重新定位期间,会用定时截图代替页面。截图也是页面图像传递到渲染进程/Agent 的唯一合理方式——没有人会读取原生像素。
  • 焦点与键盘。 焦点通过显式桥接。一个预加载脚本(注入到每个页面的隔离世界中)负责决定页面保留哪些按键,以及将哪些按键转发给 VS Code 键绑定。请将其视为信任边界:假设页面具有恶意,保持脚本精简且无副作用。

浏览器标签页是真正的编辑器,并由扩展贡献功能

页面是一个普通编辑器——在标准注册表中,一个只读、可序列化的 EditorInput + EditorPane,并会惰性地解析为视图模型(无需关闭标签页即可卸载)。因此,它自然具备标签页、拆分、持久化、焦点和键绑定作用域等功能。

编辑器窗格本身很精简。行为通过本地贡献模型(与工作台贡献分离)添加:这些小型类会附加到编辑器、获取 DI,并接入一组固定的生命周期钩子(模型附加/分离、布局覆盖、调整大小/可见性、焦点、UI 插入)。每个贡献的生命周期都限定于附加的模型,因此每页的可释放对象会在导航/关闭时清理。即使是原生视图渲染,也只是一个贡献。

要添加行为,请参照同类贡献编写一个新贡献——不要扩展编辑器或主视图。 影响页面矩形区域的贡献通过带优先级的布局覆盖组合(优先级较低的先运行;例如,仿真功能先设置视口大小,像素对齐最后运行),因此顺序很重要——绝不要硬编码像素值。

身份与隔离:会话、组和 CDP

请明确区分以下三者:

  • 会话 = 存储身份。 每个 Electron 会话都与一个 BrowserSession 一对一映射(Cookie、缓存、存储),其 ID 同时用作 CDP 浏览器上下文 ID。作用范围可以是全局、按工作区或临时;多个标签页可以共用一个会话。安全控制在此实施:file://、证书信任和权限都由会话管控(例如,访问本地文件需要工作区信任)。扩展页面访问范围的新能力应归属于会话,而非某项功能。
  • 组 = 自动化可见范围。 一个组会集成一组动态视图,并将其作为一个逻辑上的 CDP“浏览器”公开。组会引用视图,但不拥有视图;一个视图可以属于多个组。借此,不同客户端(聊天会话、DevTools、扩展)可以各自只看到所需的子集。
  • CDP 通过代理转发,绝不直接暴露。 一个了解协议的代理负责实现浏览器级/目标级域(发现、自动附加、扁平化会话、上下文),并将其余请求按目标转发。这样,一个逻辑浏览器就能由随时加入或离开的视图组成,Playwright 也能在不直接接触 Chromium 的情况下连接。

Cookie/登录/存储 → 会话。“此客户端能看到哪些页面” → 组。协议本身 → 代理。

Agent 在隐私保护下与用户共用页面

  • 共用同一页面,协同操作。 Playwright 通过 CDP 操作用户看到的同一个 WebContentsView,每个聊天会话使用一个连接。用户和 Agent 的操作可能发生冲突;冲突时以用户为准(用户触发的提示框/对话框可以中断自动化)。设备模拟由工作台(而非 Playwright)负责,因此除 Agent 执行操作期间外,Playwright 的自动模拟会被抑制。请假设用户可能同时与同一页面交互。
  • 分享前,页面内容保持私密。 默认情况下,Agent 无法看到页面内容。页面的共享状态会控制内容访问;另有一个可用性门控(聊天是否启用、Agent 模式、设置)决定是否注册完整工具集——未满足时,仅提供一种有限能力:打开 URL,但无法访问内容。URL 会经过网络筛选器检查,被阻止时会进行屏蔽。应将页面内容视为不可信的模型输入(提示词注入)。任何新增的 Agent 界面都必须遵守这些门控。(工具名称位于 platform 中,这样不依赖 workbench 的 Agent 主机也能引用这些名称。)

远程页面

浏览器上下文(及其存储)会绑定到一个网络:本地网络,或按权威来源确定的远程网络——窗口的远程网络(远程工作区窗口)或远程 Agent 主机(Agents 窗口会话)。标签页会保存其请求的会话(存储范围、亲和性和网络);创建时会应用当前信任和代理限制,但不会覆盖该请求。弹出窗口和自动化上下文会继承父级的网络。远程流量通过窗口本地扩展主机中的 HTTPS 代理传输;若代理启动失败,视图创建也会失败,而不会回退到本地网络。若已知某主机不支持代理,新标签页将使用本地网络,并显示警告。

网络属于浏览器上下文,而非其所有者、受众或策略。全局存储始终位于本地。工作区存储使用窗口的网络;其他网络则需要通过存储亲和性(Agent 主机加文件夹根目录)选择工作区存储文件夹中的一个分区,否则会使用不与其他视图共享的内存存储。清除工作区存储时也会清除这些分区。

测试策略

让每一层测试都精简并聚焦场景。 保护主要功能,避免其故障破坏核心浏览器场景,并采用仍能覆盖实际风险的最低成本测试层。不要在不同层重复测试同一行为,也不要将偶然的实现细节固化为测试。

  • 单元测试覆盖重要的独立逻辑,例如状态转换、协议转换、持久化规则、安全门控和故障处理。使用有代表性的用例,不必穷举琐碎分支或私有结构。
  • 组件测试覆盖主要的渲染器交互、命令、由上下文键控制的可见性,以及无需原生页面的核心渲染行为。除非结构本身就是契约,否则避免断言像素级细节或 DOM 结构。
  • 扩展 API 测试位于 extensions/vscode-api-tests/src/singlefolder-tests/browser*.test.ts,是浏览器 API、CDP 行为、浏览器工具、扩展宿主连接,以及向扩展开放的跨进程契约的首选集成测试层。
  • 其他端到端集成测试覆盖需要真实服务、但不需要完整工作台流程的重要进程边界或运行时集成。
  • 冒烟测试仅覆盖那些有意义的故障模式需要实际 Electron 工作台、渲染器/主进程/共享进程连接,或原生 WebContentsView 的主要用户流程。这包括预加载脚本键盘路由、原生焦点/可见性/生命周期、Electron 权限、弹出式编辑器、原生视图上的工作台 UI,以及实时页面到聊天的附件等核心风险。每项测试都应只覆盖主流程,将相关断言归为一个连贯的流程,并将变体、边缘情况和视觉细节留给较低层测试。

好的断言应体现用户契约,并在不破坏行为的更改和重构中保持稳定。测试应保护那些故障会实质性破坏主要浏览器场景的行为,并放在仍能覆盖该故障模式的最低成本层。只要保持连贯,冒烟测试覆盖应扩展现有流程;否则,主要流程最多使用少量场景。

实践指南

  • 仅限桌面端。 Web 端不会运行任何内容;将代码添加到 electron-*/node,并让 browser/ 存根抛出 "not available in web"。
  • 遵循现有模式。 新增能力 → 新的功能贡献和/或工具,参照同类实现。跨进程状态 → 使用主进程中的模型方法 + 事件,而不是渲染器本地状态。布局更改 → 使用优先级覆盖,而不是像素值。
  • 注意信任边界: 预加载脚本(恶意页面)、会话(存储/权限/文件访问)、Agent 门控(共享 + 可用性 + 网络过滤器)以及聊天附件(提示词注入)。

评论

支持 Markdown