diff --git a/docs/04-外接浏览器支持改动方案.md b/docs/04-外接浏览器支持改动方案.md new file mode 100644 index 0000000..12b09e1 --- /dev/null +++ b/docs/04-外接浏览器支持改动方案.md @@ -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(method: string, params?: Record): Promise; + on(event: string, handler: (params: unknown) => void): void; // Page.loadEventFired 等 + close(): Promise; + 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=, --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 在外部浏览器下与内置结果一致