mirror of
https://github.com/Tencent/BrowserSkill.git
synced 2026-09-10 13:01:23 +08:00
236 lines
14 KiB
Markdown
236 lines
14 KiB
Markdown
# Visual observation:PR 0 验证结果与架构决策
|
||
|
||
日期:2026-09-08。生产基线:main `3c5f838`。分支:`codex/visual-observation-design`。
|
||
|
||
本次只新增设计、合成 fixture 和诊断工具。没有生产算法变更,没有 commit/push。
|
||
总体设计及后续 PR 依赖见 [架构与实施计划](visual-observation.md)。
|
||
本文是历史证据;新会话请按 [验收清单](visual-observation-acceptance.md) 执行相关检查并说明结果,不继承本文的通过结论。
|
||
|
||
## 1. 环境与证据等级
|
||
|
||
| 环境 | 实际版本/说明 |
|
||
|---|---|
|
||
| PATH 中的 bsk | 0.1.7,未用于有效页面基线 |
|
||
| 仓库已有 target/debug/bsk | 0.2.0;`update --check` 报告发布最新版也是 0.2.0,没有安装更新 |
|
||
| daemon / connected extension | 0.2.0,protocol 1.1 |
|
||
| 页面基线 Chrome | 151.0.0.0,由 bsk browsers 报告 |
|
||
| 生产扩展构建 | `pnpm ext:build`,用户 reload 当前 main 基线构建后采集 |
|
||
| 独立协议实验 Chrome | 152.0.7977.76,临时无登录 profile;与用户浏览器不是同一个运行实例 |
|
||
| 基线测试 | VOM 47 tests / 2 files;相关扩展 217 tests / 14 files;全部通过 |
|
||
|
||
页面结果是实际 bsk + reload 扩展的结果。协议实验是独立 Chrome 的真实 CDP 响应。
|
||
临时 replay 是将合成响应送入 main adapter 的诊断,不代表另一条真实浏览器完整运行。
|
||
不同证据不混用,版本号本身不证明构建 commit。
|
||
|
||
所有内部页面原文、地址、iframe 标识、截图和用户信息仅保存在本机临时目录,不进入仓库。
|
||
本文仅保存匿名化事实,仓库 fixture 完全独立构造。
|
||
|
||
## 2. 原始问题已确认:表格加载完成后仍有观察缺口
|
||
|
||
有效就绪条件:前台可见页面已经绘制出网格、列标题和六行表格。不能使用以下替代条件:
|
||
|
||
- `document.readyState === complete`。
|
||
- AX 已有嵌入应用的工具栏。
|
||
- 页面出现 iframe。
|
||
- 背景截图仍为站点加载 logo。
|
||
|
||
本次初始后台窗口恰好出现“AX 已有工具栏,图像仍为加载占位”的情况。这些样本已排除。
|
||
在前台绘制完成后重新取得匹配的截图和 observe,结果为:
|
||
|
||
| 事实 | 结果 |
|
||
|---|---|
|
||
| 实际截图 | 可见列标题、网格与六行表格 |
|
||
| main observe | 63 个 ref,含外围和嵌入应用工具栏 |
|
||
| truncated | false |
|
||
| 表格列内容 | 没有相应文本表示 |
|
||
| visual ref | 没有 |
|
||
| 直接打开已观察到的嵌入应用 | 发现一个无 aria-label 的 Canvas,CSS 尺寸 1450×835、backing store 2900×1670,DPR 2 |
|
||
|
||
直接打开嵌入应用用于验证其渲染介质;其宽度变化是独立 tab 布局,不能拿这组坐标当父页面中的 crop。
|
||
父页面的截图/VOM 对照与直接嵌入页的 DOM 证据分别记录。
|
||
|
||
结论:主要能力缺口是视觉内容没有观察入口;不属于这次有效样本的文本预算耗尽。
|
||
新架构不能承诺从 Canvas 自动恢复单元格语义;正确结果是保留工具栏,并提供可截图的区域入口。
|
||
|
||
## 3. 合成最小复现
|
||
|
||
`table.html` → 跨站点 `grid.html`:一个 iframe、DOM 工具栏、一个无名 Canvas。
|
||
Canvas 绘制三列、六行,所有文字与原页面无关。
|
||
|
||
main 实际输出的关键部分:
|
||
|
||
```text
|
||
@view 1440x923
|
||
@e1 button "Page action"
|
||
Iframe "Table application"
|
||
navigation
|
||
@e2 button "Insert record"
|
||
@e3 button "Filter"
|
||
```
|
||
|
||
`ref_count=3`,`truncated=false`。截图已人工确认包含 `Canvas column A/B/C` 和六行。
|
||
这些像素文字既没有文本表示,也没有 visual ref。该最小复现已覆盖原场景的关键结构,
|
||
无需站点权限或生产选择器。
|
||
|
||
## 4. CSS 与语义/交互分离
|
||
|
||
真实浏览器实验确认:
|
||
|
||
- aria-hidden 祖先、inert 祖先、pointer-events:none 不会使 Canvas 自动视觉消失。
|
||
- `visibility:hidden` 祖先下的 `visibility:visible` 子 Canvas 可以恢复可见。
|
||
- `hidden` 元素被 `display:block` 覆盖时仍可绘制。
|
||
- opacity:0 祖先会使子树透明;不能仅检查 Canvas 自己的 opacity。
|
||
- display:none 无 layout bounds;visibility:hidden 可以仍有 bounds。
|
||
- AX fallback 文本不包含绘制的 `PIXEL TABLE 42`,不能用 fallback 存在作为发现去重条件。
|
||
|
||
决定:事实层分别保存 computed visibility、透明继承、语义暴露和交互属性。
|
||
视觉发现不读取 semantic keep/drop 结果;祖先索引只能传播符合属性语义的状态。
|
||
|
||
`checkVisibility()` 的实验作用仅限 CSS suppression,不用它证明 overflow、viewport 或遮挡结果。
|
||
常规 overflow 由独立几何规则负责;任意遮挡不进入首版精确能力范围。
|
||
|
||
## 5. 单位、frame 投影与截图
|
||
|
||
### 5.1 DPR 与截图像素是不同契约
|
||
|
||
独立 Chrome 上对相同 120×40 CSS clip 的结果:
|
||
|
||
| Emulated DPR | DOMSnapshot Canvas w×h | Screenshot w×h | layoutViewport/cssLayoutViewport 宽度比 |
|
||
|---|---|---|---|
|
||
| 1 | 120×40 | 120×40 | 1 |
|
||
| 1.5 | 120×40 | 180×60 | 1 |
|
||
| 2 | 120×40 | 240×80 | 1 |
|
||
|
||
因此该比值不能用作通用截图 DPR。原始 DOMSnapshot 单位与截图 raster scale 必须分别适配,
|
||
输出 PNG 尺寸是最终预算事实。不能把本组 DPR emulation 结果泛化为所有浏览器缩放组合。
|
||
|
||
### 5.2 三种缩放分别记账
|
||
|
||
- CSS `zoom:1.25`:120px 宽 Canvas 的布局 bounds 为 150px,运行时几何同步变化。
|
||
- Emulation.setPageScaleFactor(1.5):记录为 page-scale/pinch 类证据,不充当 UI zoom。
|
||
- 用户 Chrome 原生 UI 缩放到 90%:界面明确显示 90%,DPR 从 2 变为约 1.8,innerWidth 从 1440 变为 1600,visualViewport.scale 仍为 1;Canvas CSS 宽约 120.0000076,截图仍是 2880px 宽。main VOM 视口为 1600×1025。已恢复 100%。
|
||
|
||
现有 bsk 的 Meta+- CDP 输入未改变浏览器缩放,所以该无效果尝试不算缩放验证;
|
||
有效样本来自原生浏览器 UI 操作。真实 UI zoom 仅验证了上述点,不能宣称完整 zoom 矩阵已通过。
|
||
|
||
决定:使用 CSS 视口空间作为发现/截图区域公共契约;精确单位 adapter 的实现还必须在 PR 1
|
||
跑计划内组合回归,不用一个比例同时解决所有协议字段。
|
||
|
||
### 5.3 Frame 空间与 main 已知偏移
|
||
|
||
fixture local Canvas 原点为 (17,23)。
|
||
|
||
| 场景 | 实测 owner content 原点(target root) | 正确 top Canvas 原点 | main parser replay |
|
||
|---|---|---|---|
|
||
| 同 target frame,10px border | (50,610) | (67,633) | (57,623) |
|
||
| 上述 frame 内 nested,另有 7px border | (87,707) | (104,730) | (87,713) |
|
||
| OOPIF,10px border | (410,610) | (427,633) | 本表不混入非完整 replay 的 OOPIF 结果 |
|
||
|
||
根 target 的 DOMSnapshot 含三份 document(top/same/nested);OOPIF 在单独 target 中只返回自己的 document。
|
||
probe 通过实际 `TargetInfo.type=iframe` 验证 OOPIF,而非仅根据 origin 推断进程隔离。
|
||
|
||
子 frame scrollY=10 后,runtime local y 从 23 变为 13,snapshot document y 仍为 23。
|
||
owner scale(1.25) 后 content quad 宽度变为 375,child innerWidth 仍为 300。
|
||
|
||
决定:frame-local 原始矩形必须先减该 document 的滚动,再经过一次权威投影;同 target 的
|
||
owner quad 已是 target-root 坐标,不能重复逐层加偏移。禁止 border-box 替代 content origin。
|
||
|
||
Box 种类也必须进入契约:带 5px border 的 120px Canvas,其 snapshot/border quad 宽 130,
|
||
content quad 宽 120。视觉入口采用 border-box,实时验证同种 box;iframe 投影则采用 content quad。
|
||
带 border/padding 的 overflow owner,snapshot.clientRects 对应 `[clientLeft,clientTop,clientWidth,clientHeight]`,
|
||
本 fixture 为 `[5,5,68,38]`。裁剪边界应从 client box 派生,不能直接拿 ancestor border bounds。
|
||
单轴 `overflow-x:clip;overflow-y:visible` 的 computed style 保持分轴,必须分轴传播。
|
||
|
||
CSS zoom 1.25 后,上述 owner 的 bounds 变为 `[745,455.625,97,59.5]`,clientRects 仍为
|
||
`[5,5,68,38]`,offsetRects 为 `[596,365,78,48]`。直接按 border-width 比例推导 client box
|
||
会混入不同空间与整数取整。首版将有 CSS zoom/DOM transform 的 overflow 祖先组合明确降级,
|
||
不承诺精确 crop;普通未变换 overflow 及 iframe content-quad 投影保留支持。
|
||
|
||
## 6. Document 与 ref 时效
|
||
|
||
真实 CDP 实验:
|
||
|
||
| 操作 | frameId | loaderId | document backend | anchor |
|
||
|---|---|---|---|---|
|
||
| history.replaceState | 不变 | 不变 | 不变 | 仍属于当前树 |
|
||
| 跨 document 导航 | 不变 | 变化 | 变化 | 旧目标不可继续视为当前内容 |
|
||
| document.open/write/close | 不变 | 不变 | 本次实验不变 | 旧 Canvas 失去连接,documentElement 被替换 |
|
||
|
||
决定:身份取 target attachment epoch + frameId + loaderId + documentElement backend identity;
|
||
工具执行时仍检查 anchor.isConnected 及其所属 documentElement。不能仅凭 loaderId 判断有效。
|
||
DOMSnapshot 可提供根元素 backend identity,frame graph 需要保留 loaderId;当前 main graph
|
||
没有该字段,这是后续数据契约的明确修改点。
|
||
|
||
12 次静态几何采样完全相同;0.5px 移动可被检测;真实 90% 缩放引入约 1e-5 CSS px 的
|
||
数值误差。首版采用最大边误差 0.25 CSS px 作为保守工程阈值,超出要求重新 observe。
|
||
这不是大规模统计得到的通用稳定性保证;改变阈值需更新其测试及行为文档。
|
||
|
||
既有 eN 被下一次观察重用后,单靠旧字符串不能检测它来自上一代。内部 generation 不消除
|
||
该协议限制。本次不改变协议,只验证当前存储目标的身份/几何。Canvas 可以在身份与几何
|
||
不变时重绘;截图始终表达执行时图像,不承诺观察时像素快照。
|
||
|
||
## 7. 既有路径回归基线:用于防止负优化
|
||
|
||
诊断复制当前 renderer 到临时目录,在三个明确循环点加计数;生产文件未改变。
|
||
复现命令见 [fixture README](../../apps/extension/test-fixtures/visual-observation/README.md)。
|
||
|
||
| 输入 | Render stack visits | DOM context candidates | Same-container sibling visits |
|
||
|---|---:|---:|---:|
|
||
| 100 headings + 100 View buttons | 201 | 10,000 | 15,050 |
|
||
| 200 headings + 200 View buttons | 401 | 40,000 | 60,100 |
|
||
| 400 headings + 400 View buttons | 801 | 160,000 | 240,200 |
|
||
| 2,000 transparent wrappers + control | 2,001 | 0 | 0 |
|
||
| 4,000 transparent wrappers + control | 4,001 | 0 | 0 |
|
||
|
||
显式栈访问线性并通过深树输入;本组宽树上下文计数呈二次增长。这里记录的是现有路径的
|
||
特征,供后续同输入比较是否发生回退,不是本次要求修复的问题,也不是 Canvas 新算法的模板。
|
||
计数不是完整 CPU profile,更不能替代 Canvas 发现、裁剪、选择和截图各自的性能测试。
|
||
|
||
处理边界:
|
||
|
||
1. 既有语义、DOM 操作和截图路径对照 main 不发生性能回退;不把历史算法优化列为本次前置依赖。
|
||
2. Canvas 新路径按自己的规模和访问模式论证算法;发现、裁剪、去重、选择和截图分别验收。
|
||
3. 精确 crop key 去重的选择来自首版的覆盖等价契约和新增成本分析,与 #165 采用什么算法无关。
|
||
4. PR 7/8 分别报告既有路径回归、无 Canvas 页面的额外成本、有 Canvas 页面的新增成本。
|
||
新功能有合理的额外成本不等于旧算法负优化;重复执行旧工作则属于需要消除的回退。
|
||
|
||
## 8. 锁定首版策略与退化
|
||
|
||
| 决策 | 首版契约 | 理由/验收 |
|
||
|---|---|---|
|
||
| 去重 | 同 document + 同直接 parent + 四条 crop 边完全相等 | 无模糊聚类;误差导致重复比误合并更可控 |
|
||
| 索引容量 | 50,000 keys;耗尽后其余候选继续未去重选择 | 内存有界,不因早期小图耗尽而停止后续发现 |
|
||
| K | min(预算推导容量, 512) | 所有行仍逐条核算可变成本 |
|
||
| 默认视觉预算 | 未给总 maxTokens 时 2048 estimated tokens | 有显式 maxTokens 时全部纳入同一预算 |
|
||
| 摘要预留 | 固定提示行按实际估算器成本预留 | 不用不可验证的固定 token 常量表示某段文本 |
|
||
| 几何时效 | 最大边差 > 0.25 CSS px 即重新观察 | 身份和裁剪策略变化独立拒绝 |
|
||
| 图像 | max edge 2048、max pixels 4,000,000、最多两次 capture | 成功必须以实际尺寸满足后置条件 |
|
||
| 外部几何并发 | 4 | 单操作缓存;操作结束释放 |
|
||
| 新增纯计算取消 | 每 256 项形成工作块并让出事件循环 | 不仅是轮询无法及时更新的 signal |
|
||
|
||
这些数值是可审查的首版资源政策,不是声称已证明用户体验最优的测量结果。
|
||
参数可以在对应模块 PR 中经新的证据调整,但必须一并更新契约及测试。
|
||
|
||
进一步收敛:首版精确区域支持轴向矩形、普通 overflow 链、正向轴对齐平移/缩放与 frame。
|
||
旋转、透视、clip-path、mask、活动 pinch-zoom 进入明确的 geometry-unsupported 降级,
|
||
不给看似精确的 region ref,提示现有视口截图途径。不会把包围矩形偷换成精确区域。
|
||
|
||
## 9. 已执行的检查与下一阶段门槛
|
||
|
||
已执行:
|
||
|
||
- main VOM tests:47 passed。
|
||
- main 相关 extension tests:217 passed,含 frame/element geometry、observation、interaction、ref-store、VOM。
|
||
- `pnpm ext:build` 成功,用户 reload 后采集有效页面基线。
|
||
- 独立 Chrome browser contract probe:39 个断言通过;可复现命令见 fixture README。
|
||
- main 合成 capture replay、显式栈/上下文操作计数。
|
||
- 实际 Chrome UI 90% zoom 样本,恢复 100%。
|
||
- 原始表格与合成嵌入式表格均人工查看截图,并与未截断 observe 对照。
|
||
|
||
PR 0 交付的是已明确边界的设计和基线,不是未来功能已经通过:新的 discovery、typed ref、
|
||
freshness resolver、pixel planner 和 renderer 接线尚未实现。后续每个 PR 必须验证自己的
|
||
生产实现,不能把本次浏览器事实验证替代实现验收。
|
||
|
||
人工证据不设置默认 CI 对内网站点的访问依赖。合成 probe 可按需执行;页面原文不进仓库。
|
||
后续 PR 的职责和验收保持在主设计中,本文负责记录证据、决策及没有作出的保证。
|