Skip to content

Commit c526eb2

Browse files
authored
[js] Add Javascript/Typescript CDDL code generator for WebDriver BiDi (#17574)
* [js] Add Javascript/Typescript CDDL code generator for WebDriver BiDi * Fix linting * [js] Add Javascript/Typescript CDDL code generator for WebDriver BiDi * Fix linting * [js] Add Javascript/Typescript CDDL code generator for WebDriver BiDi * Fix linting * [js] Fix enhancements manifest issues found in code review - back()/forward(): accept a context id instead of hard-coding an empty string; delegate to traverseHistory({ context, delta: ±1 }) - continueWithAuth: replace username!/password! non-null assertions with an explicit undefined guard that throws a descriptive error - continueWithAuth: capture bidi.send() response and apply the same response['type'] === 'error' check as generated methods, preventing silent failures * ... * [js] Update comment for changes made to index.js * [js] Emit BiDi events via EventEmitter in bidi/index.js * Fix linting
1 parent 7ca11b9 commit c526eb2

20 files changed

Lines changed: 3422 additions & 40 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ obj/
100100
*.aps
101101
launchSettings.json
102102
Generated/
103+
# Un-ignore the hand-written tests for the generated BiDi domain API (macOS
104+
# case-insensitive filesystem would otherwise match "Generated/" above).
105+
!javascript/selenium-webdriver/test/bidi/generated/
103106
ipch/
104107
/iedriver.log
105108
/phantomjsdriver.log

javascript/selenium-webdriver/BUILD.bazel

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin")
2-
load("@aspect_rules_js//js:defs.bzl", "js_library")
2+
load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_library")
33
load("@aspect_rules_js//npm:defs.bzl", "npm_package")
44
load("@npm//:defs.bzl", "npm_link_all_packages")
55
load("@npm//javascript/selenium-webdriver:eslint/package_json.bzl", eslint_bin = "bin")
@@ -10,9 +10,37 @@ load("//common:defs.bzl", "copy_file")
1010
load("//javascript:defs.bzl", "mocha_test")
1111
load("//javascript/private:browsers.bzl", "BROWSERS")
1212
load("//javascript/private:jsdoc.bzl", "jsdoc")
13+
load("//javascript/selenium-webdriver/private:generate_bidi.bzl", "generate_bidi_library")
1314

1415
npm_link_all_packages(name = "node_modules")
1516

17+
# Generator script that reads the merged CDDL spec and produces one .ts file per BiDi domain.
18+
js_binary(
19+
name = "generate_bidi_script",
20+
data = [
21+
"generate_bidi.mjs",
22+
":node_modules/cddl",
23+
":node_modules/cddl2ts",
24+
],
25+
entry_point = "generate_bidi.mjs",
26+
)
27+
28+
# Generate WebDriver BiDi TypeScript modules from CDDL specification.
29+
# extra_cddl_files are merged with the primary BiDi spec before generation so that
30+
# adjacent specs (Permissions, Prefetch, UA Client Hints, Web Bluetooth) are included.
31+
generate_bidi_library(
32+
name = "create-bidi-src",
33+
cddl_file = "@webdriver_bidi_all_cddl//file:spec.cddl",
34+
enhancements_manifest = "//javascript/selenium-webdriver/private:bidi_enhancements_manifest",
35+
extra_cddl_files = [
36+
"@permissions_all_cddl//file:spec.cddl",
37+
"@prefetch_all_cddl//file:spec.cddl",
38+
"@ua_client_hints_all_cddl//file:spec.cddl",
39+
"@web_bluetooth_all_cddl//file:spec.cddl",
40+
],
41+
spec_version = "1.0",
42+
)
43+
1644
VERSION = "4.45.0-nightly202605121853"
1745

1846
BROWSER_VERSIONS = [
@@ -53,6 +81,7 @@ js_library(
5381
npm_package(
5482
name = "selenium-webdriver",
5583
srcs = [
84+
":create-bidi-src",
5685
":license",
5786
":manager-linux",
5887
":manager-macos",

javascript/selenium-webdriver/bidi/index.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,31 @@ class Index extends EventEmitter {
7979
}
8080
return
8181
}
82-
// Messages without a numeric id are BiDi events, not command
83-
// responses; they are routed via subscribe/EventEmitter elsewhere
84-
// and intentionally ignored by this dispatcher.
82+
// Messages without a numeric id are BiDi events, not command responses.
83+
// Re-emit them on this EventEmitter by method name (e.g.
84+
// 'browsingContext.contextCreated') so that generated domain classes can
85+
// subscribe via bidi.on(methodName, callback) instead of each attaching
86+
// a new raw ws.on('message', ...) listener. The existing hand-written
87+
// modules (logInspector, network, etc.) continue to use their own
88+
// ws.on('message', ...) listeners unchanged — this emission is purely
89+
// additive and does not affect those code paths.
8590
if (payload == null || typeof payload.id !== 'number') {
91+
if (payload != null && typeof payload.method === 'string') {
92+
// 'error' is a reserved EventEmitter event — emitting it without a
93+
// listener throws and crashes the process. Route any peer-supplied
94+
// method named 'error' through the same guarded path used for JSON
95+
// parse failures rather than forwarding it directly.
96+
if (payload.method === 'error') {
97+
const err = new Error(`BiDi protocol error event: ${JSON.stringify(payload.params)}`)
98+
if (this.listenerCount('error') > 0) {
99+
this.emit('error', err)
100+
} else {
101+
process.emitWarning(err.message, 'BiDiProtocolWarning')
102+
}
103+
} else {
104+
this.emit(payload.method, payload.params)
105+
}
106+
}
86107
return
87108
}
88109
const entry = this._pending.get(payload.id)

0 commit comments

Comments
 (0)