资讯动态

Selenium ADR-17670 解读:BiDi 实现边界的划定与协议中立 API 设计

发布时间:2026/9/10 9:41:28 来源:尧图企业网站定制
Selenium ADR-17670 解读BiDi 实现边界的划定与协议中立 API 设计【免费下载链接】seleniumA browser automation framework and ecosystem.项目地址: https://gitcode.com/GitHub_Trending/se/seleniumSelenium 以 WebDriver BiDi 作为底层机制来承载和扩展各语言绑定的高级 API随之而来的核心问题是用户到底该面向什么编程本 ADRdocs/decisions/17670-bidi-implementation-boundaries.md状态Accepted在项目层面一锤定音BiDi 是实现机制而非公开 API——受支持的 API 必须协议中立BiDi 协议实现保持在内部且不受弃用政策约束低层访问只能通过组合 driver而非挂接在 driver 上完成。读完本文你将掌握这一跨绑定架构决策的完整动机、三条决策的精确边界、被否决的替代方案及其理由并能据此判断 Java/Python/Ruby/.NET/JavaScript 五种绑定中哪些写法是受支持的、哪些只是过渡期产物。背景为什么必须划定实现边界Selenium 使用 WebDriver BiDi 来实现并扩展 Selenium API——网络拦截、脚本执行、日志与浏览器事件等能力如今都由 BiDi 协议模块承载。问题不在于用什么实现而在于实现细节漏到了哪里当 BiDi 的连接对象、协议类型被直接暴露在用户面前Selenium API 就与一个并不由自己掌控的活协议living spec产生了隐性耦合一旦协议演进用户程序就会被动跟随。该 ADR 记录承认当时没有任何一种绑定符合既定目标每种绑定都把 BiDi 实现细节从 driver 对象上暴露了出去。文档给出的现状如下表BindingCurrent behavior现状行为JavaDriver 暴露原始 BiDi 连接HasBiDi.getBiDi()协议类型是公开的Pythondriver.network/driver.script是正确的上层命名但返回的是低层bidi命名空间的模块而非中性封装RubyDriver 暴露 BiDi 访问器driver.bidi.NETDriver 扩展方法返回 BiDi 类型AsBiDiAsync()→IBiDiJavaScriptDriver 暴露 BiDi 访问器driver.getBidi()这些挂在 driver 身上的 BiDi 入口正是问题所在凡是能从 driver 对象触及的成员用户会天然地把它当成受支持、可依赖的 API。此外所有绑定还暴露了一个以 BiDi 命名、用于开启会话的选项enableBiDi/enable_bidi该命名同样可以改成协议中性的形式。仓库现状可以印证这一点Java 侧确实定义了 HasBiDi.java 接口default BiDi getBiDi()从 driver 上取出连接BiDiProvider.java 负责按 capabilities 判断并创建连接。.NET 侧在 WebDriver.Extensions.cs 中定义了扩展方法AsBiDiAsync(this IWebDriver webDriver, ...)返回的 IBiDi.cs 是一个继承IAsyncDisposable的公开接口。JavaScript 侧bidi/目录下散布着按模块划分的实现文件如 network.js、browser.js、logInspector.js等其中 browser.js 可见this.bidi await this._driver.getBidi()的调用模式。会话开启选项在 Python 侧体现为 options.py 中的enable_bidi _BaseOptionsDescriptor(enableBidi)描述符对应 WebDriver 能力enableBidi。这些代码都属于迁移前的形态——ADR 已接受但各绑定的收敛需要按弃用节奏逐步完成见下文后果一节。核心决策三条边界缺一不可决策总纲只有一句话BiDi 是实现机制不是公开 API。由此展开三条相互支撑的决策。决策 1受支持的 API 是协议中立的受支持的表面积绝不引用 BiDi——不出现 BiDi 类型也不向用户交付任何 BiDi 对象。用户编程时面对的是 Selenium API 与它的高级能力如network、script这类以用户语义命名的高层入口这些接口受项目的弃用政策deprecation policy约束一旦进入受支持面改动就必须遵循标准的弃用流程。这条决策的意义在于用户不需要知道某条命令背后走的是 WebDriver ClassicHTTP还是 WebDriver BiDiWebSocket协议。协议可以演进、可以被新的机制取代而用户的代码不受波及——这正是把协议中立当作受支持 API 的第一性原则的原因。决策 2BiDi 实现是内部的、不受支持的各绑定内部的 BiDi 实现层忠实实现 WebDriver BiDi 规范中的命令commands、事件events与模块modules从架构上看甚至可以由协议的 CDDL 描述直接生成仓库common/bidi/目录即存放了webdriver-bidi-1140.cddl、schema.json等协议原始素材。该实现层仍然公开可达用户可以 import 并调用但它不受弃用政策治理WebDriver BiDi 规范写来是稳定的可它不是 Selenium 自己的东西Selenium 无法对其稳定性作出承诺也就不能把它当作可承诺的、受支持的公共 API。每个绑定需要用自己的惯例来标记该层为内部。决策 2 有一个常被误读的补充说明把某表面标记为Beta并不能满足本决策。Beta在 Selenium 语境里表达的是正在走向受支持与内部实现恰恰语义相反。真正内部的东西不会被授予Beta标签因为它压根不在通往正式支持的路径上。决策 3低层访问靠组合绝不挂在 driver 上BiDi 实现如何被触达答案是组合composition把 BiDi 协议类与 driver 组合起来使用——文档给出的示意写法是BiDi::Protocol::Network.new(driver)——而永远不作为 driver 的成员暴露。这条约束的心理依据很直接凡是从 driver 对象可达的东西都会被用户隐含地解读为受支持。而通过构造器把 driver 注入进一个独立的协议类就在类型系统与阅读层面把内部实现与driver 公共面区隔开来——使用者能明确感知到自己正在进入一个协议层的、不受承诺的领域。除这三条外ADR 还明确编排orchestration、事件分发event dispatch、socket 监听、传输层transport等一律是实现细节各绑定按自身约定设为私有不属于公共讨论面。图解同一能力、三种写法、三种地位ADR 用 Ruby 给出了一个非常有教学价值的对照其中driver.network目前尚属目标态写法driver.network.add_request_handler(...) # 受支持 —— 中性命名不返回任何 BiDi 类型 BiDi::Protocol::Network.new(driver).add_intercept(...) # 内部实现 —— 组合 driver 使用不受支持 driver.bidi.network.add_intercept(...) # 不允许 —— 内部实现被暴露为 driver 成员第一行是目标形态用户面向协议中性的高层 APIdriver.network方法名表达用户意图返回值不泄漏 BiDi 类型。这属于受支持面受弃用政策治理。第二行是可用的内部通道需要直接操作协议时通过把 driver 传入BiDi::Protocol::Network构造器组合使用。它忠实但不受承诺——规范变了就得跟着改用户自担风险。第三行是被明确禁止的形态内部实现一旦变成driver.bidi.xxx这种 driver 成员链就读起来像受支持必须避免。值得注意的是 Java 侧的演化线索如今 module/Browser.java 与 browsingcontext/BrowsingContext.java 等模块类的构造均接收 driver 并通过((HasBiDi) driver).getBiDi()取得连接——从模块类持有 driver这一点看已经接近组合的模式尚未收敛的是 driver 自身仍直接暴露getBiDi()这正是本 ADR 要求逐步收口的现状缺口。备选方案的权衡为何否决另外三种设计设计记录的价值很大程度上在于被否决的选项。ADR 系统比较了四种方案逐一给出取舍理由。#候选方案结论核心理由1把整个 BiDi 协议暴露为公共 APIRejected否决该内部层忠实实现的是一个仍在演进的活规范无法承诺稳定性不能作为受支持的公共 API 交付2额外提供一个受支持的、面向内部 BiDi 实现的中层级 APIRejected否决会造成协议耦合用户会建立在 BiDi 形状的概念上重蹈 CDP 的覆辙且同时存在协议中性的高层 API与BiDi 形状的中层 API两个受支持面会让用户不知道该用哪个3把内部实现暴露为 driver 成员如driver.bidi.networkRejected否决driver 成员会被隐式解读为受支持改为类与 driver 组合能保持语义上的区隔4仅作为内部实现机制通过组合 driver 触达Accepted采纳即上述三条决策的落点方案 1 与内部层忠实实现活规范这一事实直接冲突——把不受自己掌控的规范写进受支持面等于对外承诺自己无法兑现的东西。方案 2 中提到的CDP 陷阱值得展开过去 Selenium 允许用户直接使用 Chrome DevTools Protocol开发者养成了基于 CDP 形状思考问题的习惯而 CDP 的对象模型与 Selenium API 并不对齐导致使用体验割裂、升级易碎。BiDi 若再开一个中层级受支持面等于把 CDP 犯过的错误再来一遍。同时维护两个受支持表面一个中性高层、一个 BiDi 形状中层会让用户在选择 API 时无所适从。方案 3 的表面问题是命名污染本质问题是可达性等于支持性的暗示。第 4 方案之所以胜出正是因为它用最朴素的手段——构造器注入的组合关系——把受支持面与不受支持的协议层从代码结构上分开。后果对用户、对现有 API 的影响三条后果直接决定了迁移路径与用户体验用户永远不必知道哪条命令由哪个协议提供服务。无论是 Classic 端点还是 BiDi 模块都由 Selenium 内部决定高层 API 的调用方式不随底层协议改变。这是决策 1 的必然结果也是协议中立设计的最终收益。不符合规范的既有公开面将按弃用政策逐步收敛。上表列出的HasBiDi.getBiDi()、driver.bidi、AsBiDiAsync()、driver.getBidi()以及enableBiDi/enable_bidi选项命名等现状都会在保证向后兼容的前提下走标准的弃用流程逐步收口到协议中性的形态——这也解释了为什么各绑定的符合性迁移不是一蹴而就的破坏性变更而是可预期的渐进过程。整个迁移在仓库中由每个绑定各自推进决策记录本身作为不可变档案存留于 docs/decisions/ 目录。具体受支持 API 的行为规范由其他 ADR 另行定义。本 ADR 只划定边界在哪里不规定某个 API 长什么样。例如关于 Web 扩展安装的跨绑定命名决策就记录在姊妹文档 17817-driver-extension-install.md 中——它同样遵循方法挂在 driver 实例上、不挂在 BiDi 模块上的同类原则installWebExtension/uninstallWebExtension可见协议中立、不向用户暴露 BiDi是贯穿多个 ADR 的一致思想。结语决策如何演进与复核按 docs/decisions/README.md 中记录的 ADR 流程被接受的决策是不可变的除状态行外如需改变必须新建一条取代旧记录的决策并把旧记录状态更新为Superseded by [NNNN]。因此本文所述的BiDi 是实现机制、受支持面协议中立、低层靠组合不靠挂接这三条边界在 Selenium 的决策日志中具有长期约束力——它既是维护者评审 API 形状时的标尺也是用户理解哪些代码受支持、哪些只是过渡产物的依据。相关决策docs/decisions/17670-bidi-implementation-boundaries.md【免费下载链接】seleniumA browser automation framework and ecosystem.项目地址: https://gitcode.com/GitHub_Trending/se/selenium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价