docs: 外接浏览器(Edge/Chrome)支持改动方案
- 传输层抽象:CdpTransport 接口隔离 webContents.debugger 与 WebSocket 两种通道,BrowserLike 接口与 169 个网页组件零改动 - 唯一新依赖 ws(~50KB);客户机器零安装(Edge/Chrome 原生 CDP) - 分四阶段:传输层+外部连接(2-3天) → 打开网页组件参数生效(1天) → 多标签 Target 域(2-3天) → 拾取器与高级组件适配(2-3天),合计约 1.5-2 周 - 关键坑已注明:独立 user-data-dir 必带、端口占用自增、断连报错
This commit is contained in:
parent
19eb53b5d8
commit
c592cd891f
186
docs/04-外接浏览器支持改动方案.md
Normal file
186
docs/04-外接浏览器支持改动方案.md
Normal file
@ -0,0 +1,186 @@
|
||||
# 04-外接浏览器支持改动方案(Edge / Chrome)
|
||||
|
||||
- **编写时间**:2026-10-30
|
||||
- **编写人**:刘爱家
|
||||
- **关联文档**:01-现状分析与可行性 / 02-实施方案 / 03-能力接入规划与排期
|
||||
- **目标**:让「打开网页」及全部网页自动化组件支持在客户本机的 Edge / Chrome 浏览器中执行,不再强制使用内置运行浏览器;内置浏览器保留为默认兜底。
|
||||
|
||||
---
|
||||
|
||||
## 一、现状分析(代码级)
|
||||
|
||||
### 1.1 现有架构
|
||||
|
||||
```
|
||||
网页组件(web.ts / web-batch2.ts / web-crawl.ts,169 项目录项)
|
||||
│ BrowserLike 接口(engine/types.ts,33 个方法)
|
||||
▼
|
||||
BrowserSession(browser/session.ts,1056 行,单例)
|
||||
│ this.cdp(method, params)
|
||||
▼
|
||||
win.webContents.debugger(Electron 内置窗口「RPA 运行浏览器」)
|
||||
```
|
||||
|
||||
- **会话模型**:`BrowserSession.create()` 单例复用一个 `BrowserWindow`,全局变量 `__rpaBrowserWindow` 持有;单窗口单 Tab 模型(`tab_unavailable` 错误即来自此)。
|
||||
- **CDP 使用现状**:全部经 `webContents.debugger`(协议版本 1.3),使用的域:
|
||||
- `DOM.getDocument / DOM.querySelector / DOM.setFileInputFiles`
|
||||
- `Input.dispatchMouseEvent / Input.dispatchKeyEvent / Input.insertText`
|
||||
- `Runtime.evaluate`(仅 runJs 一处)
|
||||
- `Page.enable / Page.captureScreenshot / Page.handleJavaScriptDialog`
|
||||
- `Network.enable / Network.getResponseBody`(网页监听)
|
||||
- **全部是标准 CDP 域,Edge / Chrome(同为 Chromium)原生支持,命令层零改动。**
|
||||
- **目录字段已预留**:`WebBrowser_Create / V2 / V3` 均有 `type`(浏览器类型,默认 chrome)、`isRemote`(是否远程)、`ipAddress`、`port`(默认 61619)、`arguments`(携带参数)、`openNewTab` 字段——**但实现不读取**,无论填什么一律打开内置窗口。
|
||||
|
||||
### 1.2 关键约束
|
||||
|
||||
| 约束 | 说明 |
|
||||
|---|---|
|
||||
| Electron 31 主进程 = Node 20.x | 无稳定原生 WebSocket 客户端,连接外部调试端口需引入 `ws` |
|
||||
| 用户已开着的浏览器 | 未带调试端口启动的实例无法连接(Chromium 安全限制),必须带参重启 |
|
||||
| 内置窗口独有 API | `webContents.debugger` 仅 Electron 可用;`Page.captureScreenshot` 外部浏览器同样可用(证据截图无障碍) |
|
||||
| 元素拾取器 | 现拾取链路绑定内置窗口,外部浏览器需改走 CDP DOM 快照 |
|
||||
|
||||
---
|
||||
|
||||
## 二、总体设计
|
||||
|
||||
**核心思路:传输层抽象,一次替换,上层不动。**
|
||||
|
||||
```
|
||||
BrowserLike(接口不变)
|
||||
▼
|
||||
BrowserSession(cdp() 委托给 transport,其余逻辑不变)
|
||||
▼
|
||||
CdpTransport 接口(新增)
|
||||
├── EmbeddedTransport —— 现有 webContents.debugger 封装(内置浏览器,默认)
|
||||
└── ExternalTransport —— WebSocket 直连外部浏览器调试端口(新增,依赖 ws)
|
||||
```
|
||||
|
||||
- `BrowserLike` 接口 33 个方法、上层 169 个网页组件**全部零改动**。
|
||||
- 内置浏览器保留:`type` 未填或填内置类型时走原路径,行为与现在完全一致(回归风险隔离)。
|
||||
|
||||
### 浏览器类型枚举约定
|
||||
|
||||
| type 值 | 行为 |
|
||||
|---|---|
|
||||
| `chrome` | 探测本机 Chrome 路径 → 带调试端口启动 → CDP 接管 |
|
||||
| `edge` | 探测本机 Edge 路径 → 同上 |
|
||||
| `chrome_remote` / `edge_remote`(或 isRemote=1) | 不启动进程,直接连 `ipAddress:port` 上已监听调试端口的实例 |
|
||||
| `zexplorer` / 缺省 | 内置运行浏览器(现状行为,兜底) |
|
||||
|
||||
---
|
||||
|
||||
## 三、分阶段实施
|
||||
|
||||
### 阶段一:传输层抽象 + 外部浏览器连接(核心,约 2~3 天)
|
||||
|
||||
**新增 `src/main/browser/transport.ts`**
|
||||
|
||||
```ts
|
||||
export interface CdpTransport {
|
||||
send<T>(method: string, params?: Record<string, unknown>): Promise<T>;
|
||||
on(event: string, handler: (params: unknown) => void): void; // Page.loadEventFired 等
|
||||
close(): Promise<void>;
|
||||
kind: "embedded" | "external";
|
||||
}
|
||||
```
|
||||
|
||||
- `EmbeddedTransport`:包装现有 `webContents.debugger` 的 `send/on`(`session.ts` 现有 `cdp()` 逻辑原样迁入)。
|
||||
- `ExternalTransport`:基于 `ws` 包,连接 `http://ip:port` 的调试端点:
|
||||
1. `GET /json/list` 枚举页面级 target,取 `webSocketDebuggerUrl`(或 `PUT /json/new?url=` 开新 Tab)
|
||||
2. 建立WebSocket,消息按 CDP 格式封包(`{id, method, params}` / 响应 `{id, result|error}` / 事件 `{method, params}`),promise map 按 id 配对
|
||||
3. 断线检测(ws close 事件)→ 抛 `browser_disconnected`,带「请带调试端口重启浏览器」提示
|
||||
|
||||
**新增 `src/main/browser/external.ts`(外部浏览器进程管理)**
|
||||
|
||||
- 路径探测(按优先级):
|
||||
- Chrome:注册表 `HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe` → `C:\Program Files\Google\Chrome\Application\chrome.exe` → `C:\Program Files (x86)\...`
|
||||
- Edge:`HKLM\...\App Paths\msedge.exe` → `C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`(Win10/11 必装,探测成功率 100%)
|
||||
- 启动:`child_process.spawn(browserPath, [--remote-debugging-port=<port>, --user-data-dir=<独立目录>, url, ...args])`
|
||||
- **必须带独立 `--user-data-dir`**:否则端口参数会被用户已开的实例吞掉(连不上且无报错的头号坑)
|
||||
- 端口管理:默认取 `port` 字段(61619),被占用时自增重试;启动后轮询 `GET /json/version`(超时 15s)确认就绪
|
||||
- 进程生命周期:记录 child process 引用;流程 `WebBrowser_Close` 时可选关闭(目录可加「执行后是否关闭浏览器」字段)
|
||||
|
||||
**改造 `src/main/browser/session.ts`**
|
||||
|
||||
- `BrowserSession` 构造函数改为接收 `CdpTransport`;`cdp()` 方法改为一行委托 `this.transport.send()`
|
||||
- 单例键从 `__rpaBrowserWindow` 扩展为「按 (type, user-data/port) 维度」的会话缓存,支持同流程混合使用内置 + 外部
|
||||
- `close()`:embedded 关窗口(现状逻辑);external 断开 ws,按配置决定是否杀浏览器进程
|
||||
|
||||
### 阶段二:「打开网页」组件参数生效(约 1 天)
|
||||
|
||||
**改造 `src/main/capabilities/browser.ts` 的 `WebBrowser_Create / V2 / V3 / CreateBackground`:**
|
||||
|
||||
```
|
||||
type + isRemote + ipAddress + port + arguments ──→ BrowserSessionFactory
|
||||
├─ type ∈ {chrome, edge} 且 isRemote=0 → external.ts 启动并连接
|
||||
├─ isRemote=1 → 直连 ipAddress:port
|
||||
└─ 其余 → 内置浏览器(现状)
|
||||
```
|
||||
|
||||
- `waitingload`(等待页面加载完成):external 模式下监听 `Page.loadEventFired` 实现,语义与内置一致
|
||||
- `arguments`(携带参数):透传给浏览器启动命令行
|
||||
- 日志明确输出实际使用的浏览器(如「已使用 Edge (127.0.0.1:9222) 打开 https://...」),便于客户排查
|
||||
|
||||
### 阶段三:多标签页管理(约 2~3 天)
|
||||
|
||||
现状单 Tab 模型在外部浏览器不成立,需引入 `Target` 域:
|
||||
|
||||
- `Target.getTargets` / `Target.attachToTarget`(flatten 模式)/ `Target.closeTarget`
|
||||
- 落地组件:`WebBrowser_GetTAB_V1`(按标题/URL 匹配 Tab,type 字段终于生效)、`WebBrowser_Switch`(切换当前操作的 Tab)、`CreateV3` 的 `openNewTab`
|
||||
- 实现方式:ExternalTransport 按 targetId 维护多个 ws 会话,BrowserSession 增加 `switchTab(targetId)`;内置浏览器对应到多个 `webContents`
|
||||
|
||||
### 阶段四:拾取器与高级组件适配(约 2~3 天)
|
||||
|
||||
- 元素拾取:外部浏览器场景拾取请求走 CDP `DOMSnapshot.captureSnapshot` / `DOM.getDocument` 生成候选元素树(内置窗口继续走现有拾取链路)
|
||||
- 逐组件验证(阶段一~三完成后自然可用,重点回归):`WebBrowser_GetCookieNew`、`WebBrowser_StartWeblistening / GetWeblistening`(Network 域,外部同样可用)、`WebBrowser_BatchDataCrawlNew`(browserType 字段接阶段二同一工厂)、`WebBrowser_SaveAs`(`DOM.setFileInputFiles` + `Page.handleJavaScriptDialog`)
|
||||
- `Element_*` 系列 100+ 组件无需改动,靠接口层自动生效
|
||||
|
||||
---
|
||||
|
||||
## 四、依赖与风险
|
||||
|
||||
### 新增依赖
|
||||
|
||||
| 包 | 大小 | 用途 | 风险 |
|
||||
|---|---|---|---|
|
||||
| `ws` | ~50KB 纯 JS | 连接外部浏览器调试端口(Node 20 无原生 WebSocket) | 无原生编译,供应链风险低 |
|
||||
|
||||
**客户机器零安装**:Edge/Chrome 原生支持 CDP,不装任何扩展插件。
|
||||
|
||||
### 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|---|---|
|
||||
| 用户已开浏览器实例吞掉端口参数(连不上且无提示) | 必带独立 `--user-data-dir` 启动;文档注明该限制 |
|
||||
| 调试端口与客户其他软件冲突 | 端口自增探测 + `/json/version` 就绪轮询 |
|
||||
| 外部浏览器被客户手动关闭,流程中途断连 | ws close 事件 → 明确报错「浏览器已关闭」,错误路由可接重开逻辑 |
|
||||
| 页面弹窗阻塞 CDP | `Page.handleJavaScriptDialog` 与内置行为一致,现有组件已覆盖 |
|
||||
| 混用内置/外部时上下文串流 | 会话缓存按 (type, port) 隔离;`RunContext.browser` 生命周期不变 |
|
||||
| URL 安全 | 沿用现有 `assertUrl`(仅 http/https)白名单逻辑,外部连接的 ip:port 另做私网地址校验 |
|
||||
|
||||
---
|
||||
|
||||
## 五、工作量与排期估算
|
||||
|
||||
| 阶段 | 内容 | 预估 |
|
||||
|---|---|---|
|
||||
| 一 | 传输层抽象 + 外部连接 + 进程管理 | 2~3 天 |
|
||||
| 二 | 打开网页组件参数生效 | 1 天 |
|
||||
| 三 | 多标签 Target 域 | 2~3 天 |
|
||||
| 四 | 拾取器适配 + 高级组件回归 | 2~3 天 |
|
||||
| 测试 | 与同事网页自动化测试联动,双浏览器(Edge/Chrome)× 关键链路 | 1~2 天 |
|
||||
| **合计** | | **约 1.5~2 周** |
|
||||
|
||||
阶段一+二完成后即可向客户交付「用 Edge/Chrome 打开并执行基础网页自动化」;阶段三、四为完整体验补齐,可穿插进行。
|
||||
|
||||
---
|
||||
|
||||
## 六、测试计划(验收标准)
|
||||
|
||||
1. **连接**:Edge / Chrome 各验证——全新启动、已装非默认路径(注册表探测)、isRemote 远程连接、端口占用自增
|
||||
2. **基础链路**:打开网页 → 点击 → 输入 → 取文本 → 截图 → 关闭,双浏览器全通过
|
||||
3. **多标签**:开 3 个 Tab → GetTAB 按标题匹配 → 切换 → 各 Tab 独立操作 → closeTarget
|
||||
4. **兜底**:type 缺省/填 zexplorer 时行为与现版本完全一致(内置浏览器回归)
|
||||
5. **异常**:浏览器中途被手动关、URL 非法、调试端口不通、页面弹窗
|
||||
6. **数据采集/监听**:BatchDataCrawlNew、StartWeblistening 在外部浏览器下与内置结果一致
|
||||
Loading…
Reference in New Issue
Block a user