puppeteer-bidi 0.0.1.beta10 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENTS.md +44 -0
- data/API_COVERAGE.md +331 -0
- data/CLAUDE/expose_function_implementation.md +271 -0
- data/CLAUDE/porting_puppeteer.md +20 -0
- data/CLAUDE.md +3 -1
- data/DEVELOPMENT.md +14 -0
- data/README.md +47 -415
- data/development/generate_api_coverage.rb +412 -0
- data/development/puppeteer_revision.txt +1 -0
- data/lib/puppeteer/bidi/async_utils.rb +25 -9
- data/lib/puppeteer/bidi/browser.rb +120 -23
- data/lib/puppeteer/bidi/browser_context.rb +185 -2
- data/lib/puppeteer/bidi/connection.rb +16 -5
- data/lib/puppeteer/bidi/cookie_utils.rb +192 -0
- data/lib/puppeteer/bidi/core/browser.rb +3 -0
- data/lib/puppeteer/bidi/core/browsing_context.rb +83 -40
- data/lib/puppeteer/bidi/core/realm.rb +6 -0
- data/lib/puppeteer/bidi/core/request.rb +79 -35
- data/lib/puppeteer/bidi/core/session.rb +1 -0
- data/lib/puppeteer/bidi/core/user_context.rb +5 -3
- data/lib/puppeteer/bidi/element_handle.rb +200 -8
- data/lib/puppeteer/bidi/errors.rb +4 -0
- data/lib/puppeteer/bidi/exposed_function.rb +390 -0
- data/lib/puppeteer/bidi/frame.rb +197 -20
- data/lib/puppeteer/bidi/http_request.rb +577 -0
- data/lib/puppeteer/bidi/http_response.rb +161 -10
- data/lib/puppeteer/bidi/locator.rb +792 -0
- data/lib/puppeteer/bidi/page.rb +783 -22
- data/lib/puppeteer/bidi/query_handler.rb +1 -1
- data/lib/puppeteer/bidi/timeout_settings.rb +23 -6
- data/lib/puppeteer/bidi/version.rb +1 -1
- data/lib/puppeteer/bidi.rb +40 -6
- data/sig/puppeteer/bidi/browser.rbs +53 -6
- data/sig/puppeteer/bidi/browser_context.rbs +36 -0
- data/sig/puppeteer/bidi/cookie_utils.rbs +64 -0
- data/sig/puppeteer/bidi/core/browsing_context.rbs +16 -6
- data/sig/puppeteer/bidi/core/request.rbs +14 -11
- data/sig/puppeteer/bidi/core/user_context.rbs +2 -2
- data/sig/puppeteer/bidi/element_handle.rbs +28 -0
- data/sig/puppeteer/bidi/errors.rbs +4 -0
- data/sig/puppeteer/bidi/exposed_function.rbs +127 -0
- data/sig/puppeteer/bidi/frame.rbs +38 -4
- data/sig/puppeteer/bidi/http_request.rbs +162 -0
- data/sig/puppeteer/bidi/http_response.rbs +67 -8
- data/sig/puppeteer/bidi/locator.rbs +267 -0
- data/sig/puppeteer/bidi/page.rbs +209 -8
- data/sig/puppeteer/bidi/timeout_settings.rbs +16 -4
- data/sig/puppeteer/bidi.rbs +15 -3
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7242d594b90af9ed2739ef56e234d3d26805cf19d688212e9b0b68ad823a96c2
|
|
4
|
+
data.tar.gz: 6bca1fe1f82165851376466367cee72ec59f10fabb6b2970a70bcffde5ef42b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c7f73fa6f5351d1e1e12a87388e56c05faa11286bf724e400ec97e19092a81a6e22c6718970c028305717afdaf560ddd16dbfa6f499b5dbc28313b8b55d3347
|
|
7
|
+
data.tar.gz: ad2cc2f58a6da1d6bff87bde5eab283536f46aa160bbc3ac197c6574f9fd5f35950ca47331c9363feea4a93eaa8af76322f38b123e31d9212206d01e01d2d9d4
|
data/AGENTS.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Start Here (Project-Specific Guidance)
|
|
4
|
+
|
|
5
|
+
- Read `CLAUDE.md` and `CLAUDE/` first; they define the core async architecture, porting workflow, and testing strategy.
|
|
6
|
+
- Ruby: requires `>= 3.2` (CI covers 3.2–3.4). Prefer a version manager (`rbenv`, `asdf`, etc.) over system Ruby.
|
|
7
|
+
|
|
8
|
+
## Project Structure & Module Organization
|
|
9
|
+
|
|
10
|
+
- `lib/puppeteer/bidi/`: user-facing API (sync, calls `.wait`).
|
|
11
|
+
- `lib/puppeteer/bidi/core/`: low-level BiDi core (async, returns `Async::Task`).
|
|
12
|
+
- `spec/integration/`: browser-driven specs; fixtures in `spec/assets/`.
|
|
13
|
+
|
|
14
|
+
## Build, Test, and Development Commands
|
|
15
|
+
|
|
16
|
+
- See `DEVELOPMENT.md` for the full command list and environment variables.
|
|
17
|
+
- Run RSpec via `rbenv exec bundle exec rspec ...` when using rbenv.
|
|
18
|
+
|
|
19
|
+
## Coding Style & Naming Conventions
|
|
20
|
+
|
|
21
|
+
- Ruby: 2-space indent, double-quoted strings, ~120 char lines; follow RuboCop (`.rubocop.yml`).
|
|
22
|
+
- BiDi-only: do not introduce CDP-related ports.
|
|
23
|
+
- Async: core returns `Async::Task`; upper layer must call `.wait` on every core call (see `CLAUDE/two_layer_architecture.md`).
|
|
24
|
+
- Reviews to watch: WS messages can be handled out-of-order; “wait for event” code must not hang (register listeners before commands, handle “already happened”, cancel on errors).
|
|
25
|
+
|
|
26
|
+
## Testing Guidelines
|
|
27
|
+
|
|
28
|
+
- Prefer `spec/integration/` and the shared-browser `with_test_state` pattern (see `CLAUDE/testing_strategy.md`).
|
|
29
|
+
- Use `pending` for browser limitations vs `skip` for unimplemented features.
|
|
30
|
+
|
|
31
|
+
## Commit & Pull Request Guidelines
|
|
32
|
+
|
|
33
|
+
- See `DEVELOPMENT.md` for commit, PR, and release conventions.
|
|
34
|
+
|
|
35
|
+
## Agent Notes (Porting/Review)
|
|
36
|
+
|
|
37
|
+
- When porting from upstream TS, mirror optional vs default fields: defaults are for validation, and optional keys should be omitted from payloads unless explicitly provided.
|
|
38
|
+
- Match upstream error messages as closely as possible (including interpolated values) so tests align with Puppeteer.
|
|
39
|
+
- In core layer option checks, use key presence (`options.key?`) when upstream uses `'in'` to distinguish "missing" from `nil`.
|
|
40
|
+
- During reviews, compare both implementation (`packages/puppeteer-core/src/bidi/*.ts`) and tests (`test/src/page.spec.ts`) to catch behavior parity gaps.
|
|
41
|
+
|
|
42
|
+
## Security & Configuration Tips
|
|
43
|
+
|
|
44
|
+
- Do not commit credentials; review network-fetched changes (e.g., `scripts/update_injected_source.rb` → `lib/puppeteer/bidi/injected.js`) carefully.
|
data/API_COVERAGE.md
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# API Coverage
|
|
2
|
+
|
|
3
|
+
- Puppeteer commit: `7d750c25cb29764f2fb31cb90b750a8eec350199`
|
|
4
|
+
- Generated by: `development/generate_api_coverage.rb`
|
|
5
|
+
- Coverage: `182/265` (`68.68%`)
|
|
6
|
+
|
|
7
|
+
## Browser (Puppeteer::Bidi::Browser)
|
|
8
|
+
|
|
9
|
+
| Node.js | Ruby | Supported |
|
|
10
|
+
| --- | --- | :---: |
|
|
11
|
+
| `Browser.addScreen` | `Puppeteer::Bidi::Browser#add_screen` | ❌ |
|
|
12
|
+
| `Browser.browserContexts` | `Puppeteer::Bidi::Browser#browser_contexts` | ❌ |
|
|
13
|
+
| `Browser.close` | `Puppeteer::Bidi::Browser#close` | ✅ |
|
|
14
|
+
| `Browser.cookies` | `Puppeteer::Bidi::Browser#cookies` | ✅ |
|
|
15
|
+
| `Browser.createBrowserContext` | `Puppeteer::Bidi::Browser#create_browser_context` | ✅ |
|
|
16
|
+
| `Browser.defaultBrowserContext` | `Puppeteer::Bidi::Browser#default_browser_context` | ✅ |
|
|
17
|
+
| `Browser.deleteCookie` | `Puppeteer::Bidi::Browser#delete_cookie` | ✅ |
|
|
18
|
+
| `Browser.deleteMatchingCookies` | `Puppeteer::Bidi::Browser#delete_matching_cookies` | ✅ |
|
|
19
|
+
| `Browser.disconnect` | `Puppeteer::Bidi::Browser#disconnect` | ✅ |
|
|
20
|
+
| `Browser.getWindowBounds` | `Puppeteer::Bidi::Browser#get_window_bounds` | ❌ |
|
|
21
|
+
| `Browser.installExtension` | `Puppeteer::Bidi::Browser#install_extension` | ❌ |
|
|
22
|
+
| `Browser.isConnected` | `Puppeteer::Bidi::Browser#is_connected` | ❌ |
|
|
23
|
+
| `Browser.newPage` | `Puppeteer::Bidi::Browser#new_page` | ✅ |
|
|
24
|
+
| `Browser.pages` | `Puppeteer::Bidi::Browser#pages` | ✅ |
|
|
25
|
+
| `Browser.process` | `Puppeteer::Bidi::Browser#process` | ✅ |
|
|
26
|
+
| `Browser.removeScreen` | `Puppeteer::Bidi::Browser#remove_screen` | ❌ |
|
|
27
|
+
| `Browser.screens` | `Puppeteer::Bidi::Browser#screens` | ❌ |
|
|
28
|
+
| `Browser.setCookie` | `Puppeteer::Bidi::Browser#set_cookie` | ✅ |
|
|
29
|
+
| `Browser.setWindowBounds` | `Puppeteer::Bidi::Browser#set_window_bounds` | ❌ |
|
|
30
|
+
| `Browser.target` | `Puppeteer::Bidi::Browser#target` | ❌ |
|
|
31
|
+
| `Browser.targets` | `Puppeteer::Bidi::Browser#targets` | ❌ |
|
|
32
|
+
| `Browser.uninstallExtension` | `Puppeteer::Bidi::Browser#uninstall_extension` | ❌ |
|
|
33
|
+
| `Browser.userAgent` | `Puppeteer::Bidi::Browser#user_agent` | ✅ |
|
|
34
|
+
| `Browser.version` | `Puppeteer::Bidi::Browser#version` | ❌ |
|
|
35
|
+
| `Browser.waitForTarget` | `Puppeteer::Bidi::Browser#wait_for_target` | ✅ |
|
|
36
|
+
| `Browser.wsEndpoint` | `Puppeteer::Bidi::Browser#ws_endpoint` | ✅ |
|
|
37
|
+
|
|
38
|
+
## BrowserContext (Puppeteer::Bidi::BrowserContext)
|
|
39
|
+
|
|
40
|
+
| Node.js | Ruby | Supported |
|
|
41
|
+
| --- | --- | :---: |
|
|
42
|
+
| `BrowserContext.browser` | `Puppeteer::Bidi::BrowserContext#browser` | ✅ |
|
|
43
|
+
| `BrowserContext.clearPermissionOverrides` | `Puppeteer::Bidi::BrowserContext#clear_permission_overrides` | ❌ |
|
|
44
|
+
| `BrowserContext.close` | `Puppeteer::Bidi::BrowserContext#close` | ✅ |
|
|
45
|
+
| `BrowserContext.cookies` | `Puppeteer::Bidi::BrowserContext#cookies` | ✅ |
|
|
46
|
+
| `BrowserContext.deleteCookie` | `Puppeteer::Bidi::BrowserContext#delete_cookie` | ✅ |
|
|
47
|
+
| `BrowserContext.deleteMatchingCookies` | `Puppeteer::Bidi::BrowserContext#delete_matching_cookies` | ✅ |
|
|
48
|
+
| `BrowserContext.newPage` | `Puppeteer::Bidi::BrowserContext#new_page` | ✅ |
|
|
49
|
+
| `BrowserContext.overridePermissions` | `Puppeteer::Bidi::BrowserContext#override_permissions` | ✅ |
|
|
50
|
+
| `BrowserContext.pages` | `Puppeteer::Bidi::BrowserContext#pages` | ✅ |
|
|
51
|
+
| `BrowserContext.setCookie` | `Puppeteer::Bidi::BrowserContext#set_cookie` | ✅ |
|
|
52
|
+
| `BrowserContext.targets` | `Puppeteer::Bidi::BrowserContext#targets` | ❌ |
|
|
53
|
+
| `BrowserContext.waitForTarget` | `Puppeteer::Bidi::BrowserContext#wait_for_target` | ❌ |
|
|
54
|
+
|
|
55
|
+
## ElementHandle (Puppeteer::Bidi::ElementHandle)
|
|
56
|
+
|
|
57
|
+
| Node.js | Ruby | Supported |
|
|
58
|
+
| --- | --- | :---: |
|
|
59
|
+
| `ElementHandle.$` | `Puppeteer::Bidi::ElementHandle#query_selector` | ✅ |
|
|
60
|
+
| `ElementHandle.$$` | `Puppeteer::Bidi::ElementHandle#query_selector_all` | ✅ |
|
|
61
|
+
| `ElementHandle.$$eval` | `Puppeteer::Bidi::ElementHandle#eval_on_selector_all` | ✅ |
|
|
62
|
+
| `ElementHandle.$eval` | `Puppeteer::Bidi::ElementHandle#eval_on_selector` | ✅ |
|
|
63
|
+
| `ElementHandle.asLocator` | `Puppeteer::Bidi::ElementHandle#as_locator` | ✅ |
|
|
64
|
+
| `ElementHandle.autofill` | `Puppeteer::Bidi::ElementHandle#autofill` | ❌ |
|
|
65
|
+
| `ElementHandle.backendNodeId` | `Puppeteer::Bidi::ElementHandle#backend_node_id` | ❌ |
|
|
66
|
+
| `ElementHandle.boundingBox` | `Puppeteer::Bidi::ElementHandle#bounding_box` | ✅ |
|
|
67
|
+
| `ElementHandle.boxModel` | `Puppeteer::Bidi::ElementHandle#box_model` | ✅ |
|
|
68
|
+
| `ElementHandle.click` | `Puppeteer::Bidi::ElementHandle#click` | ✅ |
|
|
69
|
+
| `ElementHandle.clickablePoint` | `Puppeteer::Bidi::ElementHandle#clickable_point` | ✅ |
|
|
70
|
+
| `ElementHandle.contentFrame` | `Puppeteer::Bidi::ElementHandle#content_frame` | ✅ |
|
|
71
|
+
| `ElementHandle.drag` | `Puppeteer::Bidi::ElementHandle#drag` | ❌ |
|
|
72
|
+
| `ElementHandle.dragAndDrop` | `Puppeteer::Bidi::ElementHandle#drag_and_drop` | ❌ |
|
|
73
|
+
| `ElementHandle.dragEnter` | `Puppeteer::Bidi::ElementHandle#drag_enter` | ❌ |
|
|
74
|
+
| `ElementHandle.dragOver` | `Puppeteer::Bidi::ElementHandle#drag_over` | ❌ |
|
|
75
|
+
| `ElementHandle.drop` | `Puppeteer::Bidi::ElementHandle#drop` | ❌ |
|
|
76
|
+
| `ElementHandle.focus` | `Puppeteer::Bidi::ElementHandle#focus` | ✅ |
|
|
77
|
+
| `ElementHandle.hover` | `Puppeteer::Bidi::ElementHandle#hover` | ✅ |
|
|
78
|
+
| `ElementHandle.isHidden` | `Puppeteer::Bidi::ElementHandle#hidden?` | ✅ |
|
|
79
|
+
| `ElementHandle.isIntersectingViewport` | `Puppeteer::Bidi::ElementHandle#intersecting_viewport?` | ✅ |
|
|
80
|
+
| `ElementHandle.isVisible` | `Puppeteer::Bidi::ElementHandle#visible?` | ✅ |
|
|
81
|
+
| `ElementHandle.press` | `Puppeteer::Bidi::ElementHandle#press` | ✅ |
|
|
82
|
+
| `ElementHandle.screenshot` | `Puppeteer::Bidi::ElementHandle#screenshot` | ✅ |
|
|
83
|
+
| `ElementHandle.scrollIntoView` | `Puppeteer::Bidi::ElementHandle#scroll_into_view` | ✅ |
|
|
84
|
+
| `ElementHandle.select` | `Puppeteer::Bidi::ElementHandle#select` | ✅ |
|
|
85
|
+
| `ElementHandle.tap` | `Puppeteer::Bidi::ElementHandle#tap` | ❌ |
|
|
86
|
+
| `ElementHandle.toElement` | `Puppeteer::Bidi::ElementHandle#to_element` | ✅ |
|
|
87
|
+
| `ElementHandle.touchEnd` | `Puppeteer::Bidi::ElementHandle#touch_end` | ❌ |
|
|
88
|
+
| `ElementHandle.touchMove` | `Puppeteer::Bidi::ElementHandle#touch_move` | ❌ |
|
|
89
|
+
| `ElementHandle.touchStart` | `Puppeteer::Bidi::ElementHandle#touch_start` | ❌ |
|
|
90
|
+
| `ElementHandle.type` | `Puppeteer::Bidi::ElementHandle#type` | ✅ |
|
|
91
|
+
| `ElementHandle.uploadFile` | `Puppeteer::Bidi::ElementHandle#upload_file` | ✅ |
|
|
92
|
+
| `ElementHandle.waitForSelector` | `Puppeteer::Bidi::ElementHandle#wait_for_selector` | ✅ |
|
|
93
|
+
|
|
94
|
+
## FileChooser (Puppeteer::Bidi::FileChooser)
|
|
95
|
+
|
|
96
|
+
| Node.js | Ruby | Supported |
|
|
97
|
+
| --- | --- | :---: |
|
|
98
|
+
| `FileChooser.accept` | `Puppeteer::Bidi::FileChooser#accept` | ✅ |
|
|
99
|
+
| `FileChooser.cancel` | `Puppeteer::Bidi::FileChooser#cancel` | ✅ |
|
|
100
|
+
| `FileChooser.isMultiple` | `Puppeteer::Bidi::FileChooser#multiple?` | ✅ |
|
|
101
|
+
|
|
102
|
+
## Frame (Puppeteer::Bidi::Frame)
|
|
103
|
+
|
|
104
|
+
| Node.js | Ruby | Supported |
|
|
105
|
+
| --- | --- | :---: |
|
|
106
|
+
| `Frame.$` | `Puppeteer::Bidi::Frame#query_selector` | ✅ |
|
|
107
|
+
| `Frame.$$` | `Puppeteer::Bidi::Frame#query_selector_all` | ✅ |
|
|
108
|
+
| `Frame.$$eval` | `Puppeteer::Bidi::Frame#eval_on_selector_all` | ✅ |
|
|
109
|
+
| `Frame.$eval` | `Puppeteer::Bidi::Frame#eval_on_selector` | ✅ |
|
|
110
|
+
| `Frame.addScriptTag` | `Puppeteer::Bidi::Frame#add_script_tag` | ❌ |
|
|
111
|
+
| `Frame.addStyleTag` | `Puppeteer::Bidi::Frame#add_style_tag` | ❌ |
|
|
112
|
+
| `Frame.childFrames` | `Puppeteer::Bidi::Frame#child_frames` | ✅ |
|
|
113
|
+
| `Frame.click` | `Puppeteer::Bidi::Frame#click` | ✅ |
|
|
114
|
+
| `Frame.content` | `Puppeteer::Bidi::Frame#content` | ✅ |
|
|
115
|
+
| `Frame.evaluate` | `Puppeteer::Bidi::Frame#evaluate` | ✅ |
|
|
116
|
+
| `Frame.evaluateHandle` | `Puppeteer::Bidi::Frame#evaluate_handle` | ✅ |
|
|
117
|
+
| `Frame.focus` | `Puppeteer::Bidi::Frame#focus` | ❌ |
|
|
118
|
+
| `Frame.frameElement` | `Puppeteer::Bidi::Frame#frame_element` | ✅ |
|
|
119
|
+
| `Frame.goto` | `Puppeteer::Bidi::Frame#goto` | ✅ |
|
|
120
|
+
| `Frame.hover` | `Puppeteer::Bidi::Frame#hover` | ✅ |
|
|
121
|
+
| `Frame.isDetached` | `Puppeteer::Bidi::Frame#detached?` | ✅ |
|
|
122
|
+
| `Frame.locator` | `Puppeteer::Bidi::Frame#locator` | ✅ |
|
|
123
|
+
| `Frame.name` | `Puppeteer::Bidi::Frame#name` | ✅ |
|
|
124
|
+
| `Frame.page` | `Puppeteer::Bidi::Frame#page` | ✅ |
|
|
125
|
+
| `Frame.parentFrame` | `Puppeteer::Bidi::Frame#parent_frame` | ✅ |
|
|
126
|
+
| `Frame.select` | `Puppeteer::Bidi::Frame#select` | ✅ |
|
|
127
|
+
| `Frame.setContent` | `Puppeteer::Bidi::Frame#set_content` | ✅ |
|
|
128
|
+
| `Frame.tap` | `Puppeteer::Bidi::Frame#tap` | ❌ |
|
|
129
|
+
| `Frame.title` | `Puppeteer::Bidi::Frame#title` | ❌ |
|
|
130
|
+
| `Frame.type` | `Puppeteer::Bidi::Frame#type` | ✅ |
|
|
131
|
+
| `Frame.url` | `Puppeteer::Bidi::Frame#url` | ✅ |
|
|
132
|
+
| `Frame.waitForFunction` | `Puppeteer::Bidi::Frame#wait_for_function` | ✅ |
|
|
133
|
+
| `Frame.waitForNavigation` | `Puppeteer::Bidi::Frame#wait_for_navigation` | ✅ |
|
|
134
|
+
| `Frame.waitForSelector` | `Puppeteer::Bidi::Frame#wait_for_selector` | ✅ |
|
|
135
|
+
|
|
136
|
+
## HTTPRequest (Puppeteer::Bidi::HTTPRequest)
|
|
137
|
+
|
|
138
|
+
| Node.js | Ruby | Supported |
|
|
139
|
+
| --- | --- | :---: |
|
|
140
|
+
| `HTTPRequest.abort` | `Puppeteer::Bidi::HTTPRequest#abort` | ✅ |
|
|
141
|
+
| `HTTPRequest.abortErrorReason` | `Puppeteer::Bidi::HTTPRequest#abort_error_reason` | ✅ |
|
|
142
|
+
| `HTTPRequest.continue` | `Puppeteer::Bidi::HTTPRequest#continue` | ✅ |
|
|
143
|
+
| `HTTPRequest.continueRequestOverrides` | `Puppeteer::Bidi::HTTPRequest#continue_request_overrides` | ✅ |
|
|
144
|
+
| `HTTPRequest.enqueueInterceptAction` | `Puppeteer::Bidi::HTTPRequest#enqueue_intercept_action` | ✅ |
|
|
145
|
+
| `HTTPRequest.failure` | `Puppeteer::Bidi::HTTPRequest#failure` | ✅ |
|
|
146
|
+
| `HTTPRequest.fetchPostData` | `Puppeteer::Bidi::HTTPRequest#fetch_post_data` | ✅ |
|
|
147
|
+
| `HTTPRequest.finalizeInterceptions` | `Puppeteer::Bidi::HTTPRequest#finalize_interceptions` | ✅ |
|
|
148
|
+
| `HTTPRequest.frame` | `Puppeteer::Bidi::HTTPRequest#frame` | ✅ |
|
|
149
|
+
| `HTTPRequest.hasPostData` | `Puppeteer::Bidi::HTTPRequest#has_post_data` | ❌ |
|
|
150
|
+
| `HTTPRequest.headers` | `Puppeteer::Bidi::HTTPRequest#headers` | ✅ |
|
|
151
|
+
| `HTTPRequest.initiator` | `Puppeteer::Bidi::HTTPRequest#initiator` | ✅ |
|
|
152
|
+
| `HTTPRequest.interceptResolutionState` | `Puppeteer::Bidi::HTTPRequest#intercept_resolution_state` | ✅ |
|
|
153
|
+
| `HTTPRequest.isInterceptResolutionHandled` | `Puppeteer::Bidi::HTTPRequest#intercept_resolution_handled?` | ✅ |
|
|
154
|
+
| `HTTPRequest.isNavigationRequest` | `Puppeteer::Bidi::HTTPRequest#navigation_request?` | ✅ |
|
|
155
|
+
| `HTTPRequest.method` | `Puppeteer::Bidi::HTTPRequest#method` | ✅ |
|
|
156
|
+
| `HTTPRequest.postData` | `Puppeteer::Bidi::HTTPRequest#post_data` | ✅ |
|
|
157
|
+
| `HTTPRequest.redirectChain` | `Puppeteer::Bidi::HTTPRequest#redirect_chain` | ✅ |
|
|
158
|
+
| `HTTPRequest.resourceType` | `Puppeteer::Bidi::HTTPRequest#resource_type` | ✅ |
|
|
159
|
+
| `HTTPRequest.respond` | `Puppeteer::Bidi::HTTPRequest#respond` | ✅ |
|
|
160
|
+
| `HTTPRequest.response` | `Puppeteer::Bidi::HTTPRequest#response` | ✅ |
|
|
161
|
+
| `HTTPRequest.responseForRequest` | `Puppeteer::Bidi::HTTPRequest#response_for_request` | ✅ |
|
|
162
|
+
| `HTTPRequest.url` | `Puppeteer::Bidi::HTTPRequest#url` | ✅ |
|
|
163
|
+
|
|
164
|
+
## HTTPResponse (Puppeteer::Bidi::HTTPResponse)
|
|
165
|
+
|
|
166
|
+
| Node.js | Ruby | Supported |
|
|
167
|
+
| --- | --- | :---: |
|
|
168
|
+
| `HTTPResponse.buffer` | `Puppeteer::Bidi::HTTPResponse#buffer` | ✅ |
|
|
169
|
+
| `HTTPResponse.content` | `Puppeteer::Bidi::HTTPResponse#content` | ✅ |
|
|
170
|
+
| `HTTPResponse.frame` | `Puppeteer::Bidi::HTTPResponse#frame` | ✅ |
|
|
171
|
+
| `HTTPResponse.fromCache` | `Puppeteer::Bidi::HTTPResponse#from_cache` | ❌ |
|
|
172
|
+
| `HTTPResponse.fromServiceWorker` | `Puppeteer::Bidi::HTTPResponse#from_service_worker` | ❌ |
|
|
173
|
+
| `HTTPResponse.headers` | `Puppeteer::Bidi::HTTPResponse#headers` | ✅ |
|
|
174
|
+
| `HTTPResponse.json` | `Puppeteer::Bidi::HTTPResponse#json` | ✅ |
|
|
175
|
+
| `HTTPResponse.ok` | `Puppeteer::Bidi::HTTPResponse#ok` | ❌ |
|
|
176
|
+
| `HTTPResponse.remoteAddress` | `Puppeteer::Bidi::HTTPResponse#remote_address` | ✅ |
|
|
177
|
+
| `HTTPResponse.request` | `Puppeteer::Bidi::HTTPResponse#request` | ✅ |
|
|
178
|
+
| `HTTPResponse.securityDetails` | `Puppeteer::Bidi::HTTPResponse#security_details` | ✅ |
|
|
179
|
+
| `HTTPResponse.status` | `Puppeteer::Bidi::HTTPResponse#status` | ✅ |
|
|
180
|
+
| `HTTPResponse.statusText` | `Puppeteer::Bidi::HTTPResponse#status_text` | ✅ |
|
|
181
|
+
| `HTTPResponse.text` | `Puppeteer::Bidi::HTTPResponse#text` | ✅ |
|
|
182
|
+
| `HTTPResponse.timing` | `Puppeteer::Bidi::HTTPResponse#timing` | ✅ |
|
|
183
|
+
| `HTTPResponse.url` | `Puppeteer::Bidi::HTTPResponse#url` | ✅ |
|
|
184
|
+
|
|
185
|
+
## JSHandle (Puppeteer::Bidi::JSHandle)
|
|
186
|
+
|
|
187
|
+
| Node.js | Ruby | Supported |
|
|
188
|
+
| --- | --- | :---: |
|
|
189
|
+
| `JSHandle.asElement` | `Puppeteer::Bidi::JSHandle#as_element` | ✅ |
|
|
190
|
+
| `JSHandle.dispose` | `Puppeteer::Bidi::JSHandle#dispose` | ✅ |
|
|
191
|
+
| `JSHandle.evaluate` | `Puppeteer::Bidi::JSHandle#evaluate` | ✅ |
|
|
192
|
+
| `JSHandle.evaluateHandle` | `Puppeteer::Bidi::JSHandle#evaluate_handle` | ✅ |
|
|
193
|
+
| `JSHandle.getProperties` | `Puppeteer::Bidi::JSHandle#get_properties` | ✅ |
|
|
194
|
+
| `JSHandle.getProperty` | `Puppeteer::Bidi::JSHandle#get_property` | ✅ |
|
|
195
|
+
| `JSHandle.jsonValue` | `Puppeteer::Bidi::JSHandle#json_value` | ✅ |
|
|
196
|
+
| `JSHandle.remoteObject` | `Puppeteer::Bidi::JSHandle#remote_object` | ✅ |
|
|
197
|
+
| `JSHandle.toString` | `Puppeteer::Bidi::JSHandle#to_string` | ❌ |
|
|
198
|
+
|
|
199
|
+
## Keyboard (Puppeteer::Bidi::Keyboard)
|
|
200
|
+
|
|
201
|
+
| Node.js | Ruby | Supported |
|
|
202
|
+
| --- | --- | :---: |
|
|
203
|
+
| `Keyboard.down` | `Puppeteer::Bidi::Keyboard#down` | ✅ |
|
|
204
|
+
| `Keyboard.press` | `Puppeteer::Bidi::Keyboard#press` | ✅ |
|
|
205
|
+
| `Keyboard.sendCharacter` | `Puppeteer::Bidi::Keyboard#send_character` | ✅ |
|
|
206
|
+
| `Keyboard.type` | `Puppeteer::Bidi::Keyboard#type` | ✅ |
|
|
207
|
+
| `Keyboard.up` | `Puppeteer::Bidi::Keyboard#up` | ✅ |
|
|
208
|
+
|
|
209
|
+
## Mouse (Puppeteer::Bidi::Mouse)
|
|
210
|
+
|
|
211
|
+
| Node.js | Ruby | Supported |
|
|
212
|
+
| --- | --- | :---: |
|
|
213
|
+
| `Mouse.click` | `Puppeteer::Bidi::Mouse#click` | ✅ |
|
|
214
|
+
| `Mouse.down` | `Puppeteer::Bidi::Mouse#down` | ✅ |
|
|
215
|
+
| `Mouse.drag` | `Puppeteer::Bidi::Mouse#drag` | ❌ |
|
|
216
|
+
| `Mouse.dragAndDrop` | `Puppeteer::Bidi::Mouse#drag_and_drop` | ❌ |
|
|
217
|
+
| `Mouse.dragEnter` | `Puppeteer::Bidi::Mouse#drag_enter` | ❌ |
|
|
218
|
+
| `Mouse.dragOver` | `Puppeteer::Bidi::Mouse#drag_over` | ❌ |
|
|
219
|
+
| `Mouse.drop` | `Puppeteer::Bidi::Mouse#drop` | ❌ |
|
|
220
|
+
| `Mouse.move` | `Puppeteer::Bidi::Mouse#move` | ✅ |
|
|
221
|
+
| `Mouse.reset` | `Puppeteer::Bidi::Mouse#reset` | ✅ |
|
|
222
|
+
| `Mouse.up` | `Puppeteer::Bidi::Mouse#up` | ✅ |
|
|
223
|
+
| `Mouse.wheel` | `Puppeteer::Bidi::Mouse#wheel` | ✅ |
|
|
224
|
+
|
|
225
|
+
## Page (Puppeteer::Bidi::Page)
|
|
226
|
+
|
|
227
|
+
| Node.js | Ruby | Supported |
|
|
228
|
+
| --- | --- | :---: |
|
|
229
|
+
| `Page.$` | `Puppeteer::Bidi::Page#query_selector` | ✅ |
|
|
230
|
+
| `Page.$$` | `Puppeteer::Bidi::Page#query_selector_all` | ✅ |
|
|
231
|
+
| `Page.$$eval` | `Puppeteer::Bidi::Page#eval_on_selector_all` | ✅ |
|
|
232
|
+
| `Page.$eval` | `Puppeteer::Bidi::Page#eval_on_selector` | ✅ |
|
|
233
|
+
| `Page.addScriptTag` | `Puppeteer::Bidi::Page#add_script_tag` | ❌ |
|
|
234
|
+
| `Page.addStyleTag` | `Puppeteer::Bidi::Page#add_style_tag` | ❌ |
|
|
235
|
+
| `Page.authenticate` | `Puppeteer::Bidi::Page#authenticate` | ✅ |
|
|
236
|
+
| `Page.bringToFront` | `Puppeteer::Bidi::Page#bring_to_front` | ❌ |
|
|
237
|
+
| `Page.browser` | `Puppeteer::Bidi::Page#browser` | ❌ |
|
|
238
|
+
| `Page.browserContext` | `Puppeteer::Bidi::Page#browser_context` | ✅ |
|
|
239
|
+
| `Page.click` | `Puppeteer::Bidi::Page#click` | ✅ |
|
|
240
|
+
| `Page.close` | `Puppeteer::Bidi::Page#close` | ✅ |
|
|
241
|
+
| `Page.content` | `Puppeteer::Bidi::Page#content` | ✅ |
|
|
242
|
+
| `Page.cookies` | `Puppeteer::Bidi::Page#cookies` | ✅ |
|
|
243
|
+
| `Page.createCDPSession` | `Puppeteer::Bidi::Page#create_cdp_session` | ❌ |
|
|
244
|
+
| `Page.createPDFStream` | `Puppeteer::Bidi::Page#create_pdf_stream` | ❌ |
|
|
245
|
+
| `Page.deleteCookie` | `Puppeteer::Bidi::Page#delete_cookie` | ✅ |
|
|
246
|
+
| `Page.emulate` | `Puppeteer::Bidi::Page#emulate` | ❌ |
|
|
247
|
+
| `Page.emulateCPUThrottling` | `Puppeteer::Bidi::Page#emulate_cpu_throttling` | ❌ |
|
|
248
|
+
| `Page.emulateFocusedPage` | `Puppeteer::Bidi::Page#emulate_focused_page` | ❌ |
|
|
249
|
+
| `Page.emulateIdleState` | `Puppeteer::Bidi::Page#emulate_idle_state` | ❌ |
|
|
250
|
+
| `Page.emulateMediaFeatures` | `Puppeteer::Bidi::Page#emulate_media_features` | ❌ |
|
|
251
|
+
| `Page.emulateMediaType` | `Puppeteer::Bidi::Page#emulate_media_type` | ❌ |
|
|
252
|
+
| `Page.emulateNetworkConditions` | `Puppeteer::Bidi::Page#emulate_network_conditions` | ❌ |
|
|
253
|
+
| `Page.emulateTimezone` | `Puppeteer::Bidi::Page#emulate_timezone` | ❌ |
|
|
254
|
+
| `Page.emulateVisionDeficiency` | `Puppeteer::Bidi::Page#emulate_vision_deficiency` | ❌ |
|
|
255
|
+
| `Page.evaluate` | `Puppeteer::Bidi::Page#evaluate` | ✅ |
|
|
256
|
+
| `Page.evaluateHandle` | `Puppeteer::Bidi::Page#evaluate_handle` | ✅ |
|
|
257
|
+
| `Page.evaluateOnNewDocument` | `Puppeteer::Bidi::Page#evaluate_on_new_document` | ✅ |
|
|
258
|
+
| `Page.exposeFunction` | `Puppeteer::Bidi::Page#expose_function` | ✅ |
|
|
259
|
+
| `Page.focus` | `Puppeteer::Bidi::Page#focus` | ✅ |
|
|
260
|
+
| `Page.frames` | `Puppeteer::Bidi::Page#frames` | ✅ |
|
|
261
|
+
| `Page.getDefaultNavigationTimeout` | `Puppeteer::Bidi::Page#get_default_navigation_timeout` | ✅ |
|
|
262
|
+
| `Page.getDefaultTimeout` | `Puppeteer::Bidi::Page#get_default_timeout` | ✅ |
|
|
263
|
+
| `Page.goBack` | `Puppeteer::Bidi::Page#go_back` | ✅ |
|
|
264
|
+
| `Page.goForward` | `Puppeteer::Bidi::Page#go_forward` | ✅ |
|
|
265
|
+
| `Page.goto` | `Puppeteer::Bidi::Page#goto` | ✅ |
|
|
266
|
+
| `Page.hover` | `Puppeteer::Bidi::Page#hover` | ✅ |
|
|
267
|
+
| `Page.isClosed` | `Puppeteer::Bidi::Page#closed?` | ✅ |
|
|
268
|
+
| `Page.isDragInterceptionEnabled` | `Puppeteer::Bidi::Page#is_drag_interception_enabled` | ❌ |
|
|
269
|
+
| `Page.isJavaScriptEnabled` | `Puppeteer::Bidi::Page#is_java_script_enabled` | ❌ |
|
|
270
|
+
| `Page.isServiceWorkerBypassed` | `Puppeteer::Bidi::Page#is_service_worker_bypassed` | ❌ |
|
|
271
|
+
| `Page.locator` | `Puppeteer::Bidi::Page#locator` | ✅ |
|
|
272
|
+
| `Page.mainFrame` | `Puppeteer::Bidi::Page#main_frame` | ✅ |
|
|
273
|
+
| `Page.metrics` | `Puppeteer::Bidi::Page#metrics` | ❌ |
|
|
274
|
+
| `Page.openDevTools` | `Puppeteer::Bidi::Page#open_dev_tools` | ❌ |
|
|
275
|
+
| `Page.pdf` | `Puppeteer::Bidi::Page#pdf` | ❌ |
|
|
276
|
+
| `Page.queryObjects` | `Puppeteer::Bidi::Page#query_objects` | ❌ |
|
|
277
|
+
| `Page.reload` | `Puppeteer::Bidi::Page#reload` | ✅ |
|
|
278
|
+
| `Page.removeExposedFunction` | `Puppeteer::Bidi::Page#remove_exposed_function` | ✅ |
|
|
279
|
+
| `Page.removeScriptToEvaluateOnNewDocument` | `Puppeteer::Bidi::Page#remove_script_to_evaluate_on_new_document` | ✅ |
|
|
280
|
+
| `Page.resize` | `Puppeteer::Bidi::Page#resize` | ❌ |
|
|
281
|
+
| `Page.screencast` | `Puppeteer::Bidi::Page#screencast` | ❌ |
|
|
282
|
+
| `Page.screenshot` | `Puppeteer::Bidi::Page#screenshot` | ✅ |
|
|
283
|
+
| `Page.select` | `Puppeteer::Bidi::Page#select` | ✅ |
|
|
284
|
+
| `Page.setBypassCSP` | `Puppeteer::Bidi::Page#set_bypass_csp` | ❌ |
|
|
285
|
+
| `Page.setBypassServiceWorker` | `Puppeteer::Bidi::Page#set_bypass_service_worker` | ❌ |
|
|
286
|
+
| `Page.setCacheEnabled` | `Puppeteer::Bidi::Page#set_cache_enabled` | ✅ |
|
|
287
|
+
| `Page.setContent` | `Puppeteer::Bidi::Page#set_content` | ✅ |
|
|
288
|
+
| `Page.setCookie` | `Puppeteer::Bidi::Page#set_cookie` | ✅ |
|
|
289
|
+
| `Page.setDefaultNavigationTimeout` | `Puppeteer::Bidi::Page#set_default_navigation_timeout` | ✅ |
|
|
290
|
+
| `Page.setDefaultTimeout` | `Puppeteer::Bidi::Page#set_default_timeout` | ✅ |
|
|
291
|
+
| `Page.setDragInterception` | `Puppeteer::Bidi::Page#set_drag_interception` | ❌ |
|
|
292
|
+
| `Page.setExtraHTTPHeaders` | `Puppeteer::Bidi::Page#set_extra_http_headers` | ✅ |
|
|
293
|
+
| `Page.setGeolocation` | `Puppeteer::Bidi::Page#set_geolocation` | ✅ |
|
|
294
|
+
| `Page.setJavaScriptEnabled` | `Puppeteer::Bidi::Page#set_java_script_enabled` | ❌ |
|
|
295
|
+
| `Page.setOfflineMode` | `Puppeteer::Bidi::Page#set_offline_mode` | ❌ |
|
|
296
|
+
| `Page.setRequestInterception` | `Puppeteer::Bidi::Page#set_request_interception` | ✅ |
|
|
297
|
+
| `Page.setUserAgent` | `Puppeteer::Bidi::Page#set_user_agent` | ✅ |
|
|
298
|
+
| `Page.setViewport` | `Puppeteer::Bidi::Page#set_viewport` | ✅ |
|
|
299
|
+
| `Page.tap` | `Puppeteer::Bidi::Page#tap` | ❌ |
|
|
300
|
+
| `Page.target` | `Puppeteer::Bidi::Page#target` | ❌ |
|
|
301
|
+
| `Page.title` | `Puppeteer::Bidi::Page#title` | ✅ |
|
|
302
|
+
| `Page.type` | `Puppeteer::Bidi::Page#type` | ✅ |
|
|
303
|
+
| `Page.url` | `Puppeteer::Bidi::Page#url` | ✅ |
|
|
304
|
+
| `Page.viewport` | `Puppeteer::Bidi::Page#viewport` | ✅ |
|
|
305
|
+
| `Page.waitForDevicePrompt` | `Puppeteer::Bidi::Page#wait_for_device_prompt` | ❌ |
|
|
306
|
+
| `Page.waitForFileChooser` | `Puppeteer::Bidi::Page#wait_for_file_chooser` | ✅ |
|
|
307
|
+
| `Page.waitForFrame` | `Puppeteer::Bidi::Page#wait_for_frame` | ❌ |
|
|
308
|
+
| `Page.waitForFunction` | `Puppeteer::Bidi::Page#wait_for_function` | ✅ |
|
|
309
|
+
| `Page.waitForNavigation` | `Puppeteer::Bidi::Page#wait_for_navigation` | ✅ |
|
|
310
|
+
| `Page.waitForNetworkIdle` | `Puppeteer::Bidi::Page#wait_for_network_idle` | ✅ |
|
|
311
|
+
| `Page.waitForRequest` | `Puppeteer::Bidi::Page#wait_for_request` | ✅ |
|
|
312
|
+
| `Page.waitForResponse` | `Puppeteer::Bidi::Page#wait_for_response` | ✅ |
|
|
313
|
+
| `Page.waitForSelector` | `Puppeteer::Bidi::Page#wait_for_selector` | ✅ |
|
|
314
|
+
| `Page.windowId` | `Puppeteer::Bidi::Page#window_id` | ❌ |
|
|
315
|
+
| `Page.workers` | `Puppeteer::Bidi::Page#workers` | ❌ |
|
|
316
|
+
|
|
317
|
+
## Puppeteer (Puppeteer::Bidi)
|
|
318
|
+
|
|
319
|
+
| Node.js | Ruby | Supported |
|
|
320
|
+
| --- | --- | :---: |
|
|
321
|
+
| `Puppeteer.clearCustomQueryHandlers` | `Puppeteer::Bidi.clear_custom_query_handlers` | ❌ |
|
|
322
|
+
| `Puppeteer.connect` | `Puppeteer::Bidi.connect` | ✅ |
|
|
323
|
+
| `PuppeteerNode.connect` | `Puppeteer::Bidi.connect` | ✅ |
|
|
324
|
+
| `Puppeteer.customQueryHandlerNames` | `Puppeteer::Bidi.custom_query_handler_names` | ❌ |
|
|
325
|
+
| `PuppeteerNode.defaultArgs` | `Puppeteer::Bidi.default_args` | ❌ |
|
|
326
|
+
| `PuppeteerNode.executablePath` | `Puppeteer::Bidi.executable_path` | ❌ |
|
|
327
|
+
| `PuppeteerNode.launch` | `Puppeteer::Bidi.launch` | ✅ |
|
|
328
|
+
| `Puppeteer.registerCustomQueryHandler` | `Puppeteer::Bidi.register_custom_query_handler` | ❌ |
|
|
329
|
+
| `PuppeteerNode.trimCache` | `Puppeteer::Bidi.trim_cache` | ❌ |
|
|
330
|
+
| `Puppeteer.unregisterCustomQueryHandler` | `Puppeteer::Bidi.unregister_custom_query_handler` | ❌ |
|
|
331
|
+
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# ExposeFunction and EvaluateOnNewDocument Implementation
|
|
2
|
+
|
|
3
|
+
This document details the implementation of `Page.evaluateOnNewDocument` and `Page.exposeFunction`, which use BiDi preload scripts and `script.message` channel for communication.
|
|
4
|
+
|
|
5
|
+
## Page.evaluateOnNewDocument
|
|
6
|
+
|
|
7
|
+
Injects JavaScript to be evaluated before any page scripts run.
|
|
8
|
+
|
|
9
|
+
### BiDi Implementation
|
|
10
|
+
|
|
11
|
+
Uses `script.addPreloadScript` command:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
def evaluate_on_new_document(page_function, *args)
|
|
15
|
+
expression = build_evaluation_expression(page_function, *args)
|
|
16
|
+
script_id = @browsing_context.add_preload_script(expression).wait
|
|
17
|
+
NewDocumentScriptEvaluation.new(script_id)
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Key Points
|
|
22
|
+
|
|
23
|
+
1. **Preload Scripts Persist**: Scripts added via `addPreloadScript` run on every navigation
|
|
24
|
+
2. **Argument Serialization**: Arguments are serialized into the script as JSON literals
|
|
25
|
+
3. **Return Value**: Returns a `NewDocumentScriptEvaluation` with the script ID for later removal
|
|
26
|
+
|
|
27
|
+
### Example
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# Inject code that runs before any page scripts
|
|
31
|
+
script = page.evaluate_on_new_document("window.injected = 123")
|
|
32
|
+
page.goto(server.empty_page)
|
|
33
|
+
result = page.evaluate("window.injected") # => 123
|
|
34
|
+
|
|
35
|
+
# Remove when done
|
|
36
|
+
page.remove_script_to_evaluate_on_new_document(script.identifier)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Page.exposeFunction
|
|
40
|
+
|
|
41
|
+
Exposes a Ruby callable as a JavaScript function on the page.
|
|
42
|
+
|
|
43
|
+
### BiDi Implementation
|
|
44
|
+
|
|
45
|
+
Uses `script.message` channel for bidirectional communication:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Page (JS) Ruby (ExposedFunction)
|
|
49
|
+
| |
|
|
50
|
+
|-- callback([resolve,reject,args]) -->|
|
|
51
|
+
| |
|
|
52
|
+
|<-- resolve(result) or reject(error) -|
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Key Components
|
|
56
|
+
|
|
57
|
+
#### 1. Channel Argument Pattern
|
|
58
|
+
|
|
59
|
+
BiDi uses a special `channel` argument type:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
def channel_argument
|
|
63
|
+
{
|
|
64
|
+
type: "channel",
|
|
65
|
+
value: {
|
|
66
|
+
channel: @channel, # Unique channel ID
|
|
67
|
+
ownership: "root" # Keep handles alive
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### 2. Function Declaration
|
|
74
|
+
|
|
75
|
+
The exposed function creates a Promise that waits for the Ruby callback:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
(callback) => {
|
|
79
|
+
Object.assign(globalThis, {
|
|
80
|
+
[name]: function (...args) {
|
|
81
|
+
return new Promise((resolve, reject) => {
|
|
82
|
+
callback([resolve, reject, args]);
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### 3. Message Handling
|
|
90
|
+
|
|
91
|
+
Ruby listens for `script.message` events and processes calls:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
def handle_message(params)
|
|
95
|
+
return unless params["channel"] == @channel
|
|
96
|
+
|
|
97
|
+
# Extract data handle with [resolve, reject, args]
|
|
98
|
+
data_handle = JSHandle.from(params["data"], realm.core_realm)
|
|
99
|
+
|
|
100
|
+
# Call Ruby function and send result back
|
|
101
|
+
result = @apply.call(*args)
|
|
102
|
+
send_result(data_handle, result)
|
|
103
|
+
end
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Session Event Subscription
|
|
107
|
+
|
|
108
|
+
**Important**: `script.message` must be subscribed in the session:
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
# In Core::Session
|
|
112
|
+
def subscribe_to_events
|
|
113
|
+
subscribe([
|
|
114
|
+
"browsingContext.load",
|
|
115
|
+
"browsingContext.domContentLoaded",
|
|
116
|
+
# ... other events
|
|
117
|
+
"script.message", # Required for exposeFunction
|
|
118
|
+
]).wait
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Frame Handling
|
|
123
|
+
|
|
124
|
+
ExposedFunction handles dynamic frames by:
|
|
125
|
+
|
|
126
|
+
1. **Listening to frameattached**: Injects into new frames
|
|
127
|
+
2. **Using preload scripts**: For top-level browsing contexts (not iframes)
|
|
128
|
+
3. **Using callFunction**: For immediate injection into current context
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
def inject_into_frame(frame)
|
|
132
|
+
# Add preload script for top-level contexts only
|
|
133
|
+
if frame.browsing_context.parent.nil?
|
|
134
|
+
script_id = frame.browsing_context.add_preload_script(
|
|
135
|
+
function_declaration,
|
|
136
|
+
arguments: [channel]
|
|
137
|
+
).wait
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Always call function for immediate availability
|
|
141
|
+
realm.core_realm.call_function(
|
|
142
|
+
function_declaration,
|
|
143
|
+
false,
|
|
144
|
+
arguments: [channel]
|
|
145
|
+
).wait
|
|
146
|
+
end
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Error Handling
|
|
150
|
+
|
|
151
|
+
#### Standard Errors
|
|
152
|
+
|
|
153
|
+
Errors are serialized with name, message, and stack trace:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
def send_error(data_handle, error)
|
|
157
|
+
name = error.class.name
|
|
158
|
+
message = error.message
|
|
159
|
+
stack = error.backtrace&.join("\n")
|
|
160
|
+
|
|
161
|
+
data_handle.evaluate(<<~JS, name, message, stack)
|
|
162
|
+
([, reject], name, message, stack) => {
|
|
163
|
+
const error = new Error(message);
|
|
164
|
+
error.name = name;
|
|
165
|
+
if (stack) { error.stack = stack; }
|
|
166
|
+
reject(error);
|
|
167
|
+
}
|
|
168
|
+
JS
|
|
169
|
+
end
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### Non-Error Values (ThrownValue)
|
|
173
|
+
|
|
174
|
+
Ruby doesn't support `throw "string"` syntax. Use `ThrownValue`:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
class ThrownValue < StandardError
|
|
178
|
+
attr_reader :value
|
|
179
|
+
|
|
180
|
+
def initialize(value)
|
|
181
|
+
@value = value
|
|
182
|
+
super("Thrown value")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Usage
|
|
187
|
+
page.expose_function("throwValue") do |value|
|
|
188
|
+
raise ExposedFunction::ThrownValue.new(value)
|
|
189
|
+
end
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Cleanup
|
|
193
|
+
|
|
194
|
+
Disposal removes the function from all frames and cleans up resources:
|
|
195
|
+
|
|
196
|
+
```ruby
|
|
197
|
+
def dispose
|
|
198
|
+
session.off("script.message", &@listener)
|
|
199
|
+
page.off(:frameattached, &@frame_listener)
|
|
200
|
+
|
|
201
|
+
# Remove from globalThis
|
|
202
|
+
remove_binding_from_frame(@frame)
|
|
203
|
+
|
|
204
|
+
# Remove preload scripts
|
|
205
|
+
@scripts.each do |frame, script_id|
|
|
206
|
+
frame.browsing_context.remove_preload_script(script_id).wait
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Testing Considerations
|
|
212
|
+
|
|
213
|
+
### CSP Headers
|
|
214
|
+
|
|
215
|
+
Some tests require Content-Security-Policy headers. Use `TestServer#set_csp`:
|
|
216
|
+
|
|
217
|
+
```ruby
|
|
218
|
+
server.set_csp("/empty.html", "script-src 'self'")
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Test Asset
|
|
222
|
+
|
|
223
|
+
`spec/assets/tamperable.html` captures `window.injected` before page scripts run:
|
|
224
|
+
|
|
225
|
+
```html
|
|
226
|
+
<script>
|
|
227
|
+
window.result = window.injected;
|
|
228
|
+
</script>
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Common Pitfalls
|
|
232
|
+
|
|
233
|
+
### 1. Missing script.message Subscription
|
|
234
|
+
|
|
235
|
+
**Problem**: `exposeFunction` doesn't receive callbacks
|
|
236
|
+
|
|
237
|
+
**Solution**: Ensure `script.message` is in session event subscriptions
|
|
238
|
+
|
|
239
|
+
### 2. Ownership: "root" Required
|
|
240
|
+
|
|
241
|
+
**Problem**: JSHandle becomes invalid before processing
|
|
242
|
+
|
|
243
|
+
**Solution**: Use `ownership: "root"` in channel argument to keep handles alive
|
|
244
|
+
|
|
245
|
+
### 3. Preload Scripts for Iframes
|
|
246
|
+
|
|
247
|
+
**Problem**: `addPreloadScript` not supported for iframe contexts
|
|
248
|
+
|
|
249
|
+
**Solution**: Only use preload scripts for top-level contexts; use `callFunction` for iframes
|
|
250
|
+
|
|
251
|
+
### 4. TypeError on raise nil
|
|
252
|
+
|
|
253
|
+
**Problem**: Ruby's `raise nil` throws `TypeError: exception class/object expected`
|
|
254
|
+
|
|
255
|
+
**Solution**: Catch and convert to `send_thrown_value`:
|
|
256
|
+
|
|
257
|
+
```ruby
|
|
258
|
+
rescue TypeError => e
|
|
259
|
+
if e.message.include?("exception class/object expected")
|
|
260
|
+
send_thrown_value(data_handle, nil)
|
|
261
|
+
else
|
|
262
|
+
send_error(data_handle, e)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## References
|
|
268
|
+
|
|
269
|
+
- [WebDriver BiDi script.message](https://w3c.github.io/webdriver-bidi/#event-script-message)
|
|
270
|
+
- [WebDriver BiDi addPreloadScript](https://w3c.github.io/webdriver-bidi/#command-script-addPreloadScript)
|
|
271
|
+
- [Puppeteer ExposedFunction.ts](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/bidi/ExposedFunction.ts)
|
data/CLAUDE/porting_puppeteer.md
CHANGED
|
@@ -212,3 +212,23 @@ curl -sL https://raw.githubusercontent.com/puppeteer/puppeteer/main/test/assets/
|
|
|
212
212
|
```
|
|
213
213
|
|
|
214
214
|
**Why this matters**: Test assets are designed to test specific edge cases (rotated elements, complex layouts, etc.). Using simplified versions defeats the purpose of these tests.
|
|
215
|
+
|
|
216
|
+
## 9. API Coverage Update
|
|
217
|
+
|
|
218
|
+
**IMPORTANT**: When implementing new Puppeteer API methods, update `API_COVERAGE.md`:
|
|
219
|
+
|
|
220
|
+
1. Find the corresponding entry in the table (e.g., `Browser.userAgent`, `Page.setUserAgent`)
|
|
221
|
+
2. Change the status from `❌` to `✅`
|
|
222
|
+
3. Update the coverage count at the top of the file
|
|
223
|
+
|
|
224
|
+
```markdown
|
|
225
|
+
# Before
|
|
226
|
+
- Coverage: `156/274` (`56.93%`)
|
|
227
|
+
| `Browser.userAgent` | `Puppeteer::Bidi::Browser#user_agent` | ❌ |
|
|
228
|
+
|
|
229
|
+
# After (implemented 2 new methods: 156 + 2 = 158)
|
|
230
|
+
- Coverage: `158/274` (`57.66%`)
|
|
231
|
+
| `Browser.userAgent` | `Puppeteer::Bidi::Browser#user_agent` | ✅ |
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**CI will fail** if API_COVERAGE.md is not updated when new methods are implemented. The API Coverage check compares implemented methods against the coverage file.
|