BrowserSkill OOPIF 占用式滚动条几何回归:occupied-scrollbar 视口换算与快照坐标校验实战 BrowserSkill OOPIF 占用式滚动条几何回归occupied-scrollbar 视口换算与快照坐标校验实战【免费下载链接】BrowserSkillLet AI agents use your real, logged-in browser without interrupting your work. CLI extension for browser automation across any shell-capable AI agent.项目地址: https://gitcode.com/GitHub_Trending/br/BrowserSkill导读本文围绕开源仓库 BrowserSkill 中专门用于守护 OOPIFOut-of-Process iframe跨进程 iframe几何坐标正确性的回归用例oopif-scrollbars展开深入剖析iframe 内容四边距content quad包含子视口滚动条空间、而Page.getLayoutMetrics().cssLayoutViewport却排除该空间这一坐标换算陷阱并完整讲解其双嵌套 OOPIF 夹具设计、scrollbars查询参数、浏览器级数值回归测试与 CLI 冒烟测试的实战用法。读完本文你将掌握如何在 BrowserSkill 中复现并回归 OOPIF 滚动条几何问题、理解快照坐标与实时几何的校验方法以及如何验证跨进程裁剪与真实点击落点。背景为什么 OOPIF 的滚动条空间会让坐标失真在浏览器自动化中定位元素坐标的核心输入是 CDPChrome DevTools Protocol提供的两类几何数据iframe 的内容四边距content quad包含该 iframe 子视口滚动条所占的空间Page.getLayoutMetrics()返回的cssLayoutViewport是 CSS 像素下的布局视口尺寸明确排除滚动条空间。这两者的口径不一致是 frame-geometry.ts 等坐标解析链路必须处理的经典问题。若直接把cssLayoutViewport的尺寸映射到整个 content quad 上所有经过该 OOPIF 边界的元素位置和尺寸都会被拉伸——误差量与滚动条宽度成正比且会随嵌套层级逐级放大。正确的换算原则可以概括为两句话完整的目标局部视口full target-local viewport决定缩放比例scale坐标从子帧映射到父帧时必须基于包含滚动条空间的完整视口计算缩放可见视口visible viewport负责在每个 OOPIF 边界处裁剪内容任何被边界裁掉的部分都不能进入最终的坐标结果同时它还要约束目标局部动作点target-local action point的合法范围。这正是 resolveNodeGeometry 的实现逻辑先通过projectRegionToViewport将局部区域投影到顶层视口再用clipPolygon在每一层视口矩形内做裁剪最后取最大可见区域计算actionPoint顶层标签页视口内的动作点与targetActionPointOOPIF 局部视口内的动作点用于 OOPIF 本地输入事件。回归夹具设计双嵌套 OOPIF × 可控滚动条回归用例所在的目录结构遵循 regression 用例规范——每个用户上报的坏例badcase拥有独立目录内含清单manifest、合成夹具fixture和 README。本用例包含三个文件oopif-scrollbars.case.json用例清单声明断言与冒烟步骤oopif-scrollbars.fixture.mjs合成页面夹具README.md症状、复现方式与回归说明。拓扑跨环回主机名的两层嵌套夹具通过localhost与127.0.0.1两个环回主机名交替渲染父子帧强制每个 iframe 成为跨进程的 OOPIF根页面/oopif-scrollbars内嵌外层 iframe#cross主机名切到另一环回地址外层帧页面/oopif-scrollbars/frame内再嵌内层 iframe#nested主机名再切回。两个 iframe 的 URL 由夹具脚本动态拼接并携带run与scrollbars参数fixtures.mjs 的 withRun同时渲染三层页面共享同一个路由处理器靠pathname区分层级。几何要素滚动、边框、内边距与缩放夹具刻意叠加了大量干扰几何的要素oopif-scrollbars.fixture.mjs要素具体取值根页面滚动body宽 2000px、高 2400px加载后滚动到(80, 240)外层 iframe420×320left:340px; top:400pxborder:6pxpadding:8pxtransform: scale(1.2)内层 iframe200×120border-width:4pxpadding:6pxtransform: scale(0.8)自定义滚动条::-webkit-scrollbar宽 17px、高 11px部分裁剪按钮#edgeposition:fixed; right:-20px; bottom:-15px80×60只有部分可见完全裁剪按钮#outsideposition:fixed; left:calc(100% 2px)位于视口之外探针按钮#probe60×20三层页面位置各不相同缩放后的 iframe ownerscale(1.2)与scale(0.8)让坐标换算必须经过非 1 的缩放系数而两个被裁剪按钮则用于验证完全不可见即拒绝与部分可见需裁剪两条分支。scrollbars查询参数夹具通过 URL 查询参数精确控制子页面的滚动条占用形态both默认垂直与水平滚动条均占用vertical仅垂直滚动条占用水平方向隐藏horizontal仅水平滚动条占用none两个方向都隐藏。实现上子页面根据mode控制html的overflow-x/overflow-y并通过geometry.scrollbars事件上报实际占用情况innerWidth document.documentElement.clientWidth表示垂直滚动条占用innerHeight document.documentElement.clientHeight表示水平滚动条占用。浏览器级数值回归真实 Chrome 中的坐标契约运行命令与前置条件数值回归由 Vitest 驱动要求 Node 22 以及本机可执行的 Chrome通过BSK_GEOMETRY_CHROME环境变量指定BSK_GEOMETRY_CHROME/path/to/chrome pnpm --filter browser-skill/extension exec vitest run \ src/tools/__tests__/snapshot-coordinates.browser.test.ts要点说明该测试文件snapshot-coordinates.browser.test.ts与 snapshot-unit 回归共享同一个浏览器运行器一次运行同时覆盖snapshot-coordinates与oopif-scrollbars两个夹具运行器chrome.mjs通过mkdtemp创建隔离的浏览器配置文件测试结束后递归删除绝不附加到用户日常使用的 Chrome也不使用任何个人凭据或私人标签页用例是**显式开启opt-in**的describe.skipIf(!process.env.BSK_GEOMETRY_CHROME)保证在未设置该变量时普通单元测试运行会自动跳过不引入额外的浏览器下载或新包依赖。测试矩阵13 组几何组合测试用it.each展开 13 组参数化组合每组 30 秒超时5 组 device-scale / browser-zoom 组合(1, 1)、(0.8, 1)、(1, 1.25)、(2, 1)、(2, 0.8)分别搭配snapshot-coordinates scrollbarsnone与oopif-scrollbars scrollbarsboth共 10 组3 组单轴/无滚动条形态vertical、horizontal、nonedevice-scale 与 zoom 均为 1。浏览器缩放通过写入配置文件的default_zoom_levelMath.log(zoom) / Math.log(1.2)实现设备缩放通过--force-device-scale-factor强制并显式断言cssVisualViewport.zoom与期望值一致toBeCloseTo确保测试的就是你以为的配置——不支持的环境会直接失败而不是静默测错。五大校验点原文档将测试断言归纳为五类逐条对应源码实现验证真实的 OOPIF 拓扑与占用式滚动条尺寸测试对每个 CDP target 执行Page.getFrameTree组装帧图再逐帧调用DOM.getFrameOwner建立父子关系对oopif-scrollbars期望 3 个会话、3 个帧根 两层 OOPIF并通过innerWidth - clientWidth断言滚动条占用恰好符合配置的形态快照几何 vs 实时几何对照独立 DOM 矩形在每层帧的独立世界isolated world中运行oracleExpression用getBoundingClientRect()、clientWidth/clientHeight构建独立预言机再将生产链路捕获的localRect/顶层rect与预言机逐层投影、裁剪后的结果比对容差为2 个 CSS 像素以内的布局取整误差派发真实点击并验证落点归属对每个可见探针测试通过Input.dispatchMouseEventmousePressedmouseReleased在顶层会话派发真实点击再轮询geometry.clicked事件断言命中探针 id 且事件path指向正确的帧路径/oopif-scrollbars、/oopif-scrollbars/frame或/oopif-scrollbars/nested拒绝完全裁剪的控件并校验快照测量复用#outside被完全裁剪时快照侧要求node.rect为null且resolveNodeGeometry必须返回code: permission_denied对应 frame-geometry.ts 的 element not visible 分支同时测试统计 CDP 调用要求每个 target 的Page.getLayoutMetrics恰好读取一次calls.filter(...).toHaveLength(sessions.length)视口尺寸读取window.innerWidth类表达式也不允许重复——这正是 GeometryContext 的按 target 缓存机制 在测试中的印证覆盖 5 种缩放组合与单轴/无滚动条形态即上文测试矩阵。CLI 冒烟测试端到端语义验证除数值回归外用例还提供 CLI 冒烟smoke测试通过真实 CLI 二进制驱动整个打开页面 → 观察 → 关闭会话流程BSK_AUTO_UPDATEoff pnpm eval:browser smoke --case oopif-scrollbars --bsk ./target/debug/bsk说明eval:browser是 package.json 中定义的脚本node evals/browser/cli.mjssmoke子命令的完整用法见 cli.mjs--bsk ./target/debug/bsk指向已构建的 Rust CLI 调试二进制需先cargo buildBSK_AUTO_UPDATEoff关闭自动更新避免冒烟期间触发版本检查。冒烟工作流定义在 case.json 的 smoke.stepsnavigate→wait-site-event等待根页面geometry.ready→observe执行器bsk-runner.mjs随后session start、session stop并记录sessionStopped证据。冒烟断言的职责边界在 case.json 的 assertions 中写得很清楚geometry.readyroot至少 1 次——嵌套帧夹具已就绪geometry.scrollbarsvertical与horizontal同时为真至少 2 次——两层嵌套帧都发生了滚动条占用观测响应文本包含OOPIF-SCROLLBARS标记会话已关闭sessionStopped。重要边界冒烟测试只验证夹具可观测、标记可发现、会话可关闭等语义不负责数值几何与真实点击的正确性——CLI 观测不暴露原始坐标盒raw boxes这类断言必须交给上文介绍的浏览器级 Vitest 回归。与快照坐标回归的关系oopif-scrollbars与snapshot-coordinates是同一坐标问题域下的两个互补夹具snapshot-coordinates 的 README 有专门说明snapshot-coordinates聚焦Blink 布局单位layout units与 CSS 像素的换算DOMSnapshot 的 bounds 与文档滚动偏移保留布局单位在设备缩放/浏览器缩放下会同时污染位置与尺寸甚至早于 iframe 投影与视口裁剪就开始出错其夹具默认保留非占用式OOPIF 滚动条oopif-scrollbars聚焦占用式滚动条覆盖两层 OOPIF 边界、裁剪与真实点击可视为前者的纵深补充两个夹具共用同一个 Vitest 浏览器测试文件运行一次命令即可全部覆盖snapshot-coordinates夹具的 URL 追加classic-scrollbars可启用其常规非占用OOPIF 滚动条形态用于对比两种滚动条占用语义。两个用例的回归入口都可以用 regression README 的脚手架命令快速生成新用例的起点pnpm eval:browser scaffold case-id --title ... --source issue-or-pr如何将这套回归应用到日常开发开发期本地装有 Chrome 的开发者设置BSK_GEOMETRY_CHROME后直接运行上述 Vitest 命令即可在 CI 前拦截 OOPIF 几何回归不设置该变量时普通vitest run自动跳过不影响日常开发速度发布前结合 CLI 冒烟用例跑通端到端语义BSK_AUTO_UPDATEoff pnpm eval:browser smoke ...确认会话、导航、观察、关闭全链路正常扩展新场景修改夹具的scrollbars形态、探针位置或 iframe 缩放系数即可低成本覆盖新的滚动条/嵌套组合所有合成夹具均为纯代码生成不携带任何真实站点数据。结语oopif-scrollbars回归用例是 BrowserSkill 在 OOPIF 几何问题上的一次定点爆破它用最小的合成页面精确复现了content quad 含滚动条空间、cssLayoutViewport不含的口径差把坐标换算、逐层裁剪、真实点击与测量复用全部固化为可重复的自动化断言。对任何需要处理跨进程 iframe 坐标的浏览器自动化实现而言这套数值回归 CLI 冒烟的双层防护与 13 组参数化矩阵都是一份可以直接借鉴的工程范本。【免费下载链接】BrowserSkillLet AI agents use your real, logged-in browser without interrupting your work. CLI extension for browser automation across any shell-capable AI agent.项目地址: https://gitcode.com/GitHub_Trending/br/BrowserSkill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考