puppeteer-ruby 0.45.6 → 0.50.0.alpha6
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/.rubocop.yml +1 -3
- data/AGENTS.md +170 -0
- data/CLAUDE/README.md +41 -0
- data/CLAUDE/architecture.md +253 -0
- data/CLAUDE/cdp_protocol.md +230 -0
- data/CLAUDE/concurrency.md +216 -0
- data/CLAUDE/porting_puppeteer.md +605 -0
- data/CLAUDE/rbs_type_checking.md +101 -0
- data/CLAUDE/spec_migration_plans.md +1039 -0
- data/CLAUDE/testing.md +278 -0
- data/CLAUDE.md +242 -0
- data/README.md +9 -0
- data/Rakefile +7 -0
- data/Steepfile +28 -0
- data/docs/api_coverage.md +106 -57
- data/lib/puppeteer/aria_query_handler.rb +3 -2
- data/lib/puppeteer/async_utils.rb +214 -0
- data/lib/puppeteer/browser.rb +98 -56
- data/lib/puppeteer/browser_connector.rb +18 -3
- data/lib/puppeteer/browser_context.rb +196 -3
- data/lib/puppeteer/browser_runner.rb +18 -10
- data/lib/puppeteer/cdp_session.rb +67 -23
- data/lib/puppeteer/chrome_target_manager.rb +65 -40
- data/lib/puppeteer/connection.rb +55 -36
- data/lib/puppeteer/console_message.rb +9 -1
- data/lib/puppeteer/console_patch.rb +47 -0
- data/lib/puppeteer/css_coverage.rb +5 -3
- data/lib/puppeteer/custom_query_handler.rb +80 -33
- data/lib/puppeteer/define_async_method.rb +31 -37
- data/lib/puppeteer/dialog.rb +47 -14
- data/lib/puppeteer/element_handle.rb +236 -62
- data/lib/puppeteer/emulation_manager.rb +1 -1
- data/lib/puppeteer/env.rb +1 -1
- data/lib/puppeteer/errors.rb +25 -2
- data/lib/puppeteer/event_callbackable.rb +15 -0
- data/lib/puppeteer/events.rb +4 -0
- data/lib/puppeteer/execution_context.rb +148 -3
- data/lib/puppeteer/file_chooser.rb +6 -0
- data/lib/puppeteer/frame.rb +177 -91
- data/lib/puppeteer/frame_manager.rb +69 -48
- data/lib/puppeteer/http_request.rb +114 -38
- data/lib/puppeteer/http_response.rb +24 -7
- data/lib/puppeteer/isolated_world.rb +64 -41
- data/lib/puppeteer/js_coverage.rb +5 -3
- data/lib/puppeteer/js_handle.rb +77 -16
- data/lib/puppeteer/keyboard.rb +30 -17
- data/lib/puppeteer/launcher/browser_options.rb +3 -1
- data/lib/puppeteer/launcher/chrome.rb +8 -5
- data/lib/puppeteer/launcher/launch_options.rb +7 -2
- data/lib/puppeteer/launcher.rb +4 -8
- data/lib/puppeteer/lifecycle_watcher.rb +38 -22
- data/lib/puppeteer/locators.rb +733 -0
- data/lib/puppeteer/mouse.rb +273 -64
- data/lib/puppeteer/network_event_manager.rb +7 -0
- data/lib/puppeteer/network_manager.rb +393 -112
- data/lib/puppeteer/p_query_handler.rb +367 -0
- data/lib/puppeteer/p_selector_parser.rb +241 -0
- data/lib/puppeteer/page/screenshot_task_queue.rb +14 -4
- data/lib/puppeteer/page.rb +583 -226
- data/lib/puppeteer/puppeteer.rb +171 -64
- data/lib/puppeteer/query_handler_manager.rb +66 -16
- data/lib/puppeteer/reactor_runner.rb +247 -0
- data/lib/puppeteer/remote_object.rb +127 -47
- data/lib/puppeteer/target.rb +74 -27
- data/lib/puppeteer/task_manager.rb +3 -1
- data/lib/puppeteer/timeout_helper.rb +6 -10
- data/lib/puppeteer/touch_handle.rb +39 -0
- data/lib/puppeteer/touch_screen.rb +72 -22
- data/lib/puppeteer/tracing.rb +3 -3
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer/wait_task.rb +264 -101
- data/lib/puppeteer/web_socket.rb +2 -2
- data/lib/puppeteer/web_socket_transport.rb +91 -27
- data/lib/puppeteer/web_worker.rb +175 -0
- data/lib/puppeteer.rb +23 -4
- data/puppeteer-ruby.gemspec +15 -11
- data/sig/_external.rbs +8 -0
- data/sig/_supplementary.rbs +314 -0
- data/sig/puppeteer/browser.rbs +166 -0
- data/sig/puppeteer/cdp_session.rbs +64 -0
- data/sig/puppeteer/dialog.rbs +41 -0
- data/sig/puppeteer/element_handle.rbs +308 -0
- data/sig/puppeteer/execution_context.rbs +87 -0
- data/sig/puppeteer/frame.rbs +233 -0
- data/sig/puppeteer/http_request.rbs +214 -0
- data/sig/puppeteer/http_response.rbs +89 -0
- data/sig/puppeteer/js_handle.rbs +64 -0
- data/sig/puppeteer/keyboard.rbs +40 -0
- data/sig/puppeteer/locators.rbs +222 -0
- data/sig/puppeteer/mouse.rbs +113 -0
- data/sig/puppeteer/p_query_handler.rbs +73 -0
- data/sig/puppeteer/p_selector_parser.rbs +31 -0
- data/sig/puppeteer/page.rbs +522 -0
- data/sig/puppeteer/puppeteer.rbs +98 -0
- data/sig/puppeteer/remote_object.rbs +78 -0
- data/sig/puppeteer/touch_handle.rbs +21 -0
- data/sig/puppeteer/touch_screen.rbs +35 -0
- data/sig/puppeteer/web_worker.rbs +83 -0
- metadata +122 -45
- data/CHANGELOG.md +0 -397
- data/lib/puppeteer/concurrent_ruby_utils.rb +0 -81
- data/lib/puppeteer/firefox_target_manager.rb +0 -157
- data/lib/puppeteer/launcher/firefox.rb +0 -453
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
1
3
|
require_relative './element_handle/bounding_box'
|
|
2
4
|
require_relative './element_handle/box_model'
|
|
3
5
|
require_relative './element_handle/offset'
|
|
@@ -8,10 +10,10 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
8
10
|
include Puppeteer::IfPresent
|
|
9
11
|
using Puppeteer::DefineAsyncMethod
|
|
10
12
|
|
|
11
|
-
# @
|
|
12
|
-
# @
|
|
13
|
-
# @
|
|
14
|
-
# @
|
|
13
|
+
# @rbs context: Puppeteer::ExecutionContext -- Execution context
|
|
14
|
+
# @rbs client: Puppeteer::CDPSession -- CDP session
|
|
15
|
+
# @rbs remote_object: Puppeteer::RemoteObject -- Remote object handle
|
|
16
|
+
# @rbs frame: Puppeteer::Frame -- Owning frame
|
|
15
17
|
def initialize(context:, client:, remote_object:, frame:)
|
|
16
18
|
super(context: context, client: client, remote_object: remote_object)
|
|
17
19
|
@frame = frame
|
|
@@ -22,6 +24,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
22
24
|
|
|
23
25
|
attr_reader :page, :frame, :frame_manager
|
|
24
26
|
|
|
27
|
+
# @rbs return: String -- Inspection string
|
|
25
28
|
def inspect
|
|
26
29
|
values = %i[context remote_object page disposed].map do |sym|
|
|
27
30
|
value = instance_variable_get(:"@#{sym}")
|
|
@@ -38,11 +41,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
38
41
|
#
|
|
39
42
|
# This method does not work across navigations or if the element is detached from DOM.
|
|
40
43
|
#
|
|
41
|
-
# @param selector - A
|
|
42
44
|
# {@link https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors | selector}
|
|
43
45
|
# of an element to wait for
|
|
44
|
-
# @param options - Optional waiting parameters
|
|
45
|
-
# @returns Promise which resolves when element specified by selector string
|
|
46
46
|
# is added to DOM. Resolves to `null` if waiting for hidden: `true` and
|
|
47
47
|
# selector is not found in DOM.
|
|
48
48
|
# @remarks
|
|
@@ -59,8 +59,18 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
59
59
|
# - `timeout`: maximum time to wait in milliseconds. Defaults to `30000`
|
|
60
60
|
# (30 seconds). Pass `0` to disable timeout. The default value can be changed
|
|
61
61
|
# by using the {@link Page.setDefaultTimeout} method.
|
|
62
|
+
# @rbs selector: String -- CSS selector
|
|
63
|
+
# @rbs visible: bool? -- Wait for element to be visible
|
|
64
|
+
# @rbs hidden: bool? -- Wait for element to be hidden
|
|
65
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
66
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matched element handle
|
|
62
67
|
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
|
|
63
|
-
query_handler_manager.detect_query_handler(selector).wait_for(
|
|
68
|
+
query_handler_manager.detect_query_handler(selector).wait_for(
|
|
69
|
+
self,
|
|
70
|
+
visible: visible,
|
|
71
|
+
hidden: hidden,
|
|
72
|
+
timeout: timeout,
|
|
73
|
+
)
|
|
64
74
|
end
|
|
65
75
|
|
|
66
76
|
define_async_method :async_wait_for_selector
|
|
@@ -92,11 +102,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
92
102
|
# await browser.close();
|
|
93
103
|
# })();
|
|
94
104
|
# ```
|
|
95
|
-
# @param xpath - A
|
|
96
105
|
# {@link https://developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an
|
|
97
106
|
# element to wait for
|
|
98
|
-
# @param options - Optional waiting parameters
|
|
99
|
-
# @returns Promise which resolves when element specified by xpath string is
|
|
100
107
|
# added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is
|
|
101
108
|
# not found in DOM.
|
|
102
109
|
# @remarks
|
|
@@ -113,6 +120,11 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
113
120
|
# - `timeout`: A number which is maximum time to wait for in milliseconds.
|
|
114
121
|
# Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default
|
|
115
122
|
# value can be changed by using the {@link Page.setDefaultTimeout} method.
|
|
123
|
+
# @rbs xpath: String -- XPath expression
|
|
124
|
+
# @rbs visible: bool? -- Wait for element to be visible
|
|
125
|
+
# @rbs hidden: bool? -- Wait for element to be hidden
|
|
126
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
127
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matched element handle
|
|
116
128
|
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
|
|
117
129
|
param_xpath =
|
|
118
130
|
if xpath.start_with?('//')
|
|
@@ -126,6 +138,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
126
138
|
|
|
127
139
|
define_async_method :async_wait_for_xpath
|
|
128
140
|
|
|
141
|
+
# @rbs tag_name: String -- Tag name to assert
|
|
142
|
+
# @rbs return: Puppeteer::ElementHandle -- Element handle
|
|
129
143
|
def to_element(tag_name)
|
|
130
144
|
unless evaluate('(node, tagName) => node.nodeName === tagName.toUpperCase()', tag_name)
|
|
131
145
|
raise ArgumentError.new("Element is not a(n) `#{tag_name}` element")
|
|
@@ -133,10 +147,50 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
133
147
|
self
|
|
134
148
|
end
|
|
135
149
|
|
|
150
|
+
# @rbs return: Puppeteer::ElementHandle -- Element handle
|
|
136
151
|
def as_element
|
|
137
152
|
self
|
|
138
153
|
end
|
|
139
154
|
|
|
155
|
+
# @rbs return: Puppeteer::Locator -- Locator for this element
|
|
156
|
+
def as_locator
|
|
157
|
+
Puppeteer::NodeLocator.create_from_handle(@frame, self)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @rbs return: bool -- Whether element is visible
|
|
161
|
+
def visible?
|
|
162
|
+
check_visibility(true)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# @rbs return: bool -- Whether element is hidden
|
|
166
|
+
def hidden?
|
|
167
|
+
check_visibility(false)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# @rbs visible: bool -- Expected visibility state
|
|
171
|
+
# @rbs return: bool -- Whether visibility matches
|
|
172
|
+
private def check_visibility(visible)
|
|
173
|
+
js = <<~JAVASCRIPT
|
|
174
|
+
(node, visible) => {
|
|
175
|
+
if (!node) return visible === false;
|
|
176
|
+
const element =
|
|
177
|
+
node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
178
|
+
if (!element) return visible === false;
|
|
179
|
+
const style = window.getComputedStyle(element);
|
|
180
|
+
const rect = element.getBoundingClientRect();
|
|
181
|
+
const isVisible =
|
|
182
|
+
style &&
|
|
183
|
+
style.visibility !== 'hidden' &&
|
|
184
|
+
style.visibility !== 'collapse' &&
|
|
185
|
+
rect.width !== 0 &&
|
|
186
|
+
rect.height !== 0;
|
|
187
|
+
return visible === isVisible;
|
|
188
|
+
}
|
|
189
|
+
JAVASCRIPT
|
|
190
|
+
evaluate(js, visible)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# @rbs return: Puppeteer::Frame? -- Frame that owns this element
|
|
140
194
|
def content_frame
|
|
141
195
|
node_info = @remote_object.node_info(@client)
|
|
142
196
|
frame_id = node_info['node']['frameId']
|
|
@@ -147,8 +201,9 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
147
201
|
end
|
|
148
202
|
end
|
|
149
203
|
|
|
150
|
-
class ScrollIntoViewError <
|
|
204
|
+
class ScrollIntoViewError < Puppeteer::Error; end
|
|
151
205
|
|
|
206
|
+
# @rbs return: void -- No return value
|
|
152
207
|
def scroll_into_view_if_needed
|
|
153
208
|
js = <<~JAVASCRIPT
|
|
154
209
|
async(element) => {
|
|
@@ -165,7 +220,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
165
220
|
end
|
|
166
221
|
begin
|
|
167
222
|
@remote_object.scroll_into_view_if_needed(@client)
|
|
168
|
-
rescue
|
|
223
|
+
rescue
|
|
169
224
|
# Fallback to Element.scrollIntoView if DOM.scrollIntoViewIfNeeded is not supported
|
|
170
225
|
js = <<~JAVASCRIPT
|
|
171
226
|
async (element, pageJavascriptEnabled) => {
|
|
@@ -198,26 +253,27 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
198
253
|
sleep 0.16
|
|
199
254
|
end
|
|
200
255
|
|
|
201
|
-
class ElementNotVisibleError <
|
|
256
|
+
class ElementNotVisibleError < Puppeteer::Error
|
|
202
257
|
def initialize
|
|
203
258
|
super("Node is either not visible or not an HTMLElement")
|
|
204
259
|
end
|
|
205
260
|
end
|
|
206
261
|
|
|
207
|
-
class ElementNotClickableError <
|
|
262
|
+
class ElementNotClickableError < Puppeteer::Error
|
|
208
263
|
def initialize
|
|
209
264
|
super("Node is either not clickable or not an HTMLElement")
|
|
210
265
|
end
|
|
211
266
|
end
|
|
212
267
|
|
|
213
|
-
# @
|
|
214
|
-
# @
|
|
268
|
+
# @rbs quad: Array[Point] -- Quad points
|
|
269
|
+
# @rbs offset: Point -- Offset to apply
|
|
270
|
+
# @rbs return: Array[Point] -- Offset quad points
|
|
215
271
|
private def apply_offsets_to_quad(quad, offset)
|
|
216
272
|
quad.map { |part| part + offset }
|
|
217
273
|
end
|
|
218
274
|
|
|
219
|
-
# @
|
|
220
|
-
# @return
|
|
275
|
+
# @rbs frame: Puppeteer::Frame -- Frame to calculate offsets for
|
|
276
|
+
# @rbs return: Point -- Calculated offset
|
|
221
277
|
private def oopif_offsets(frame)
|
|
222
278
|
offset = Point.new(x: 0, y: 0)
|
|
223
279
|
while frame.parent_frame
|
|
@@ -235,6 +291,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
235
291
|
offset
|
|
236
292
|
end
|
|
237
293
|
|
|
294
|
+
# @rbs offset: Puppeteer::ElementHandle::Offset | Hash[Symbol, Numeric] | nil -- Click offset
|
|
295
|
+
# @rbs return: Puppeteer::ElementHandle::Point -- Clickable point
|
|
238
296
|
def clickable_point(offset = nil)
|
|
239
297
|
offset_param = Offset.from(offset)
|
|
240
298
|
|
|
@@ -258,7 +316,7 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
258
316
|
end
|
|
259
317
|
|
|
260
318
|
# Filter out quads that have too small area to click into.
|
|
261
|
-
#
|
|
319
|
+
# Prefer cssLayoutViewport when available.
|
|
262
320
|
layout_viewport = layout_metrics["cssLayoutViewport"] || layout_metrics["layoutViewport"]
|
|
263
321
|
client_width = layout_viewport["clientWidth"]
|
|
264
322
|
client_height = layout_viewport["clientHeight"]
|
|
@@ -290,18 +348,14 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
290
348
|
quads.first.reduce(:+) / 4
|
|
291
349
|
end
|
|
292
350
|
|
|
293
|
-
# @
|
|
294
|
-
# @return [
|
|
351
|
+
# @rbs quad: Array[Numeric] -- Protocol quad coordinates
|
|
352
|
+
# @rbs return: Array[Point] -- Point array
|
|
295
353
|
private def from_protocol_quad(quad)
|
|
296
354
|
quad.each_slice(2).map do |x, y|
|
|
297
355
|
Point.new(x: x, y: y)
|
|
298
356
|
end
|
|
299
357
|
end
|
|
300
358
|
|
|
301
|
-
# @param quad [Array<Point>]
|
|
302
|
-
# @param width [number]
|
|
303
|
-
# @param height [number]
|
|
304
|
-
# @return [Array<Point>]
|
|
305
359
|
private def intersect_quad_with_viewport(quad, width, height)
|
|
306
360
|
quad.map do |point|
|
|
307
361
|
Point.new(
|
|
@@ -311,30 +365,67 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
311
365
|
end
|
|
312
366
|
end
|
|
313
367
|
|
|
368
|
+
# @rbs return: void -- No return value
|
|
314
369
|
def hover
|
|
315
370
|
scroll_into_view_if_needed
|
|
316
371
|
point = clickable_point
|
|
317
372
|
@page.mouse.move(point.x, point.y)
|
|
318
373
|
end
|
|
319
374
|
|
|
320
|
-
# @
|
|
321
|
-
# @
|
|
322
|
-
# @
|
|
323
|
-
# @
|
|
324
|
-
|
|
375
|
+
# @rbs delay: Numeric? -- Delay between down and up (ms)
|
|
376
|
+
# @rbs button: String? -- Mouse button
|
|
377
|
+
# @rbs click_count: Integer? -- Deprecated: use count (click_count only sets clickCount)
|
|
378
|
+
# @rbs count: Integer? -- Number of clicks to perform
|
|
379
|
+
# @rbs offset: Puppeteer::ElementHandle::Offset | Hash[Symbol, Numeric] | nil -- Click offset
|
|
380
|
+
# @rbs return: void -- No return value
|
|
381
|
+
def click(delay: nil, button: nil, click_count: nil, count: nil, offset: nil)
|
|
325
382
|
scroll_into_view_if_needed
|
|
326
383
|
point = clickable_point(offset)
|
|
327
|
-
@page.mouse.click(point.x, point.y, delay: delay, button: button, click_count: click_count)
|
|
384
|
+
@page.mouse.click(point.x, point.y, delay: delay, button: button, click_count: click_count, count: count)
|
|
328
385
|
end
|
|
329
386
|
|
|
330
387
|
define_async_method :async_click
|
|
331
388
|
|
|
332
|
-
|
|
389
|
+
# @rbs return: Puppeteer::TouchHandle -- Touch handle
|
|
390
|
+
def touch_start
|
|
391
|
+
scroll_into_view_if_needed
|
|
392
|
+
point = clickable_point
|
|
393
|
+
@page.touchscreen.touch_start(point.x, point.y)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
define_async_method :async_touch_start
|
|
397
|
+
|
|
398
|
+
# @rbs touch: Puppeteer::TouchHandle? -- Optional touch handle
|
|
399
|
+
# @rbs return: void -- No return value
|
|
400
|
+
def touch_move(touch = nil)
|
|
401
|
+
scroll_into_view_if_needed
|
|
402
|
+
point = clickable_point
|
|
403
|
+
if touch
|
|
404
|
+
touch.move(point.x, point.y)
|
|
405
|
+
else
|
|
406
|
+
@page.touchscreen.touch_move(point.x, point.y)
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
define_async_method :async_touch_move
|
|
411
|
+
|
|
412
|
+
# @rbs return: void -- No return value
|
|
413
|
+
def touch_end
|
|
414
|
+
scroll_into_view_if_needed
|
|
415
|
+
@page.touchscreen.touch_end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
define_async_method :async_touch_end
|
|
419
|
+
|
|
420
|
+
class DragInterceptionNotEnabledError < Puppeteer::Error
|
|
333
421
|
def initialize
|
|
334
422
|
super('Drag Interception is not enabled!')
|
|
335
423
|
end
|
|
336
424
|
end
|
|
337
425
|
|
|
426
|
+
# @rbs x: Numeric -- Drag end X coordinate
|
|
427
|
+
# @rbs y: Numeric -- Drag end Y coordinate
|
|
428
|
+
# @rbs return: void -- No return value
|
|
338
429
|
def drag(x:, y:)
|
|
339
430
|
unless @page.drag_interception_enabled?
|
|
340
431
|
raise DragInterceptionNotEnabledError.new
|
|
@@ -344,25 +435,33 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
344
435
|
@page.mouse.drag(start, Point.new(x: x, y: y))
|
|
345
436
|
end
|
|
346
437
|
|
|
438
|
+
# @rbs data: Hash[String, untyped] -- Drag data payload
|
|
439
|
+
# @rbs return: void -- No return value
|
|
347
440
|
def drag_enter(data)
|
|
348
441
|
scroll_into_view_if_needed
|
|
349
442
|
target = clickable_point
|
|
350
443
|
@page.mouse.drag_enter(target, data)
|
|
351
444
|
end
|
|
352
445
|
|
|
446
|
+
# @rbs data: Hash[String, untyped] -- Drag data payload
|
|
447
|
+
# @rbs return: void -- No return value
|
|
353
448
|
def drag_over(data)
|
|
354
449
|
scroll_into_view_if_needed
|
|
355
450
|
target = clickable_point
|
|
356
451
|
@page.mouse.drag_over(target, data)
|
|
357
452
|
end
|
|
358
453
|
|
|
454
|
+
# @rbs data: Hash[String, untyped] -- Drag data payload
|
|
455
|
+
# @rbs return: void -- No return value
|
|
359
456
|
def drop(data)
|
|
360
457
|
scroll_into_view_if_needed
|
|
361
458
|
target = clickable_point
|
|
362
459
|
@page.mouse.drop(target, data)
|
|
363
460
|
end
|
|
364
461
|
|
|
365
|
-
# @
|
|
462
|
+
# @rbs target: Puppeteer::ElementHandle -- Drop target element
|
|
463
|
+
# @rbs delay: Numeric? -- Delay before dropping (ms)
|
|
464
|
+
# @rbs return: void -- No return value
|
|
366
465
|
def drag_and_drop(target, delay: nil)
|
|
367
466
|
scroll_into_view_if_needed
|
|
368
467
|
start_point = clickable_point
|
|
@@ -370,7 +469,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
370
469
|
@page.mouse.drag_and_drop(start_point, target_point, delay: delay)
|
|
371
470
|
end
|
|
372
471
|
|
|
373
|
-
# @
|
|
472
|
+
# @rbs values: Array[String] -- Option values to select
|
|
473
|
+
# @rbs return: Array[String] -- Selected values
|
|
374
474
|
def select(*values)
|
|
375
475
|
if nonstring = values.find { |value| !value.is_a?(String) }
|
|
376
476
|
raise ArgumentError.new("Values must be strings. Found value \"#{nonstring}\" of type \"#{nonstring.class}\"")
|
|
@@ -411,7 +511,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
411
511
|
evaluate(fn, values)
|
|
412
512
|
end
|
|
413
513
|
|
|
414
|
-
# @
|
|
514
|
+
# @rbs file_paths: Array[String] -- Files to upload
|
|
515
|
+
# @rbs return: void -- No return value
|
|
415
516
|
def upload_file(*file_paths)
|
|
416
517
|
is_multiple = evaluate("el => el.multiple")
|
|
417
518
|
if !is_multiple && file_paths.length >= 2
|
|
@@ -433,12 +534,14 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
433
534
|
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
434
535
|
}
|
|
435
536
|
JAVASCRIPT
|
|
436
|
-
|
|
537
|
+
evaluate(fn)
|
|
437
538
|
else
|
|
438
539
|
@remote_object.set_file_input_files(@client, file_paths.map { |path| File.expand_path(path) }, backend_node_id)
|
|
439
540
|
end
|
|
440
541
|
end
|
|
441
542
|
|
|
543
|
+
# @rbs block: Proc? -- Optional block for Object#tap usage
|
|
544
|
+
# @rbs return: Puppeteer::ElementHandle | nil -- Element handle or nil
|
|
442
545
|
def tap(&block)
|
|
443
546
|
return super(&block) if block
|
|
444
547
|
|
|
@@ -449,14 +552,16 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
449
552
|
|
|
450
553
|
define_async_method :async_tap
|
|
451
554
|
|
|
555
|
+
# @rbs return: void -- No return value
|
|
452
556
|
def focus
|
|
453
557
|
evaluate('element => element.focus()')
|
|
454
558
|
end
|
|
455
559
|
|
|
456
560
|
define_async_method :async_focus
|
|
457
561
|
|
|
458
|
-
# @
|
|
459
|
-
# @
|
|
562
|
+
# @rbs text: String -- Text to type
|
|
563
|
+
# @rbs delay: Numeric? -- Delay between key presses (ms)
|
|
564
|
+
# @rbs return: void -- No return value
|
|
460
565
|
def type_text(text, delay: nil)
|
|
461
566
|
focus
|
|
462
567
|
@page.keyboard.type_text(text, delay: delay)
|
|
@@ -464,17 +569,18 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
464
569
|
|
|
465
570
|
define_async_method :async_type_text
|
|
466
571
|
|
|
467
|
-
# @
|
|
468
|
-
# @
|
|
469
|
-
# @
|
|
572
|
+
# @rbs key: String -- Key name
|
|
573
|
+
# @rbs delay: Numeric? -- Delay between key events (ms)
|
|
574
|
+
# @rbs text: String? -- Text to input
|
|
575
|
+
# @rbs return: void -- No return value
|
|
470
576
|
def press(key, delay: nil, text: nil)
|
|
471
577
|
focus
|
|
472
|
-
@page.keyboard.press(key, delay: delay
|
|
578
|
+
@page.keyboard.press(key, delay: delay)
|
|
473
579
|
end
|
|
474
580
|
|
|
475
581
|
define_async_method :async_press
|
|
476
582
|
|
|
477
|
-
# @return
|
|
583
|
+
# @rbs return: Puppeteer::ElementHandle::BoundingBox? -- Bounding box or nil
|
|
478
584
|
def bounding_box
|
|
479
585
|
if_present(box_model) do |result_model|
|
|
480
586
|
offset = oopif_offsets(@frame)
|
|
@@ -491,13 +597,23 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
491
597
|
end
|
|
492
598
|
end
|
|
493
599
|
|
|
494
|
-
# @return
|
|
600
|
+
# @rbs return: Puppeteer::ElementHandle::BoxModel? -- Box model or nil
|
|
495
601
|
def box_model
|
|
496
602
|
if_present(@remote_object.box_model(@client)) do |result|
|
|
497
603
|
BoxModel.new(result['model'], offset: oopif_offsets(@frame))
|
|
498
604
|
end
|
|
499
605
|
end
|
|
500
606
|
|
|
607
|
+
# @rbs type: String? -- Image format
|
|
608
|
+
# @rbs path: String? -- File path to save
|
|
609
|
+
# @rbs full_page: bool? -- Capture full page
|
|
610
|
+
# @rbs clip: Hash[Symbol, Numeric]? -- Clip rectangle
|
|
611
|
+
# @rbs quality: Integer? -- JPEG quality
|
|
612
|
+
# @rbs omit_background: bool? -- Omit background for PNG
|
|
613
|
+
# @rbs encoding: String? -- Encoding (base64 or binary)
|
|
614
|
+
# @rbs capture_beyond_viewport: bool? -- Capture beyond viewport
|
|
615
|
+
# @rbs from_surface: bool? -- Capture from surface
|
|
616
|
+
# @rbs return: String -- Screenshot data
|
|
501
617
|
def screenshot(type: nil,
|
|
502
618
|
path: nil,
|
|
503
619
|
full_page: nil,
|
|
@@ -571,30 +687,85 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
571
687
|
Puppeteer::QueryHandlerManager.instance
|
|
572
688
|
end
|
|
573
689
|
|
|
690
|
+
private def query_selector_in_isolated_world(selector)
|
|
691
|
+
puppeteer_world = frame.puppeteer_world
|
|
692
|
+
if execution_context == puppeteer_world.execution_context
|
|
693
|
+
return query_handler_manager.detect_query_handler(selector).query_one(self)
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
adopted = puppeteer_world.adopt_handle(self)
|
|
697
|
+
begin
|
|
698
|
+
result = query_handler_manager.detect_query_handler(selector).query_one(adopted)
|
|
699
|
+
return nil unless result
|
|
700
|
+
|
|
701
|
+
if result.execution_context == frame.main_world.execution_context
|
|
702
|
+
return result
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
frame.main_world.transfer_handle(result)
|
|
706
|
+
ensure
|
|
707
|
+
adopted.dispose
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
private def query_selector_all_in_isolated_world(selector)
|
|
712
|
+
puppeteer_world = frame.puppeteer_world
|
|
713
|
+
if execution_context == puppeteer_world.execution_context
|
|
714
|
+
results = query_handler_manager.detect_query_handler(selector).query_all(self)
|
|
715
|
+
return results || []
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
adopted = puppeteer_world.adopt_handle(self)
|
|
719
|
+
begin
|
|
720
|
+
results = query_handler_manager.detect_query_handler(selector).query_all(adopted)
|
|
721
|
+
return [] unless results
|
|
722
|
+
|
|
723
|
+
results.map do |handle|
|
|
724
|
+
if handle.execution_context == frame.main_world.execution_context
|
|
725
|
+
handle
|
|
726
|
+
else
|
|
727
|
+
frame.main_world.transfer_handle(handle)
|
|
728
|
+
end
|
|
729
|
+
end
|
|
730
|
+
ensure
|
|
731
|
+
adopted.dispose
|
|
732
|
+
end
|
|
733
|
+
end
|
|
734
|
+
|
|
574
735
|
# `$()` in JavaScript.
|
|
575
|
-
# @
|
|
736
|
+
# @rbs selector: String -- CSS selector
|
|
737
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
576
738
|
def query_selector(selector)
|
|
577
|
-
|
|
739
|
+
query_selector_in_isolated_world(selector)
|
|
578
740
|
end
|
|
579
741
|
alias_method :S, :query_selector
|
|
580
742
|
|
|
581
743
|
# `$$()` in JavaScript.
|
|
582
|
-
# @
|
|
583
|
-
|
|
584
|
-
|
|
744
|
+
# @rbs selector: String -- CSS selector
|
|
745
|
+
# @rbs isolate: bool? -- Use isolated world for queries
|
|
746
|
+
# @rbs return: Array[Puppeteer::ElementHandle] -- Matching elements
|
|
747
|
+
def query_selector_all(selector, isolate: nil)
|
|
748
|
+
if isolate == false
|
|
749
|
+
results = query_handler_manager.detect_query_handler(selector).query_all(self)
|
|
750
|
+
return results || []
|
|
751
|
+
end
|
|
752
|
+
|
|
753
|
+
query_selector_all_in_isolated_world(selector)
|
|
585
754
|
end
|
|
586
755
|
alias_method :SS, :query_selector_all
|
|
587
756
|
|
|
588
|
-
class ElementNotFoundError <
|
|
757
|
+
class ElementNotFoundError < Puppeteer::Error
|
|
758
|
+
# @rbs selector: String -- CSS selector
|
|
589
759
|
def initialize(selector)
|
|
590
760
|
super("failed to find element matching selector \"#{selector}\"")
|
|
591
761
|
end
|
|
592
762
|
end
|
|
593
763
|
|
|
594
764
|
# `$eval()` in JavaScript.
|
|
595
|
-
# @
|
|
596
|
-
# @
|
|
597
|
-
# @
|
|
765
|
+
# @rbs selector: String -- CSS selector
|
|
766
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
767
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
768
|
+
# @rbs return: untyped -- Evaluation result
|
|
598
769
|
def eval_on_selector(selector, page_function, *args)
|
|
599
770
|
element_handle = query_selector(selector)
|
|
600
771
|
unless element_handle
|
|
@@ -610,9 +781,10 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
610
781
|
define_async_method :async_eval_on_selector
|
|
611
782
|
|
|
612
783
|
# `$$eval()` in JavaScript.
|
|
613
|
-
# @
|
|
614
|
-
# @
|
|
615
|
-
# @
|
|
784
|
+
# @rbs selector: String -- CSS selector
|
|
785
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
786
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
787
|
+
# @rbs return: untyped -- Evaluation result
|
|
616
788
|
def eval_on_selector_all(selector, page_function, *args)
|
|
617
789
|
handles = query_handler_manager.detect_query_handler(selector).query_all_array(self)
|
|
618
790
|
result = handles.evaluate(page_function, *args)
|
|
@@ -625,8 +797,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
625
797
|
define_async_method :async_eval_on_selector_all
|
|
626
798
|
|
|
627
799
|
# `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
|
|
628
|
-
# @
|
|
629
|
-
# @return [
|
|
800
|
+
# @rbs expression: String -- XPath expression
|
|
801
|
+
# @rbs return: Array[Puppeteer::ElementHandle] -- Matching elements
|
|
630
802
|
def Sx(expression)
|
|
631
803
|
param_xpath =
|
|
632
804
|
if expression.start_with?('//')
|
|
@@ -641,8 +813,8 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
641
813
|
define_async_method :async_Sx
|
|
642
814
|
|
|
643
815
|
# in JS, #isIntersectingViewport.
|
|
644
|
-
# @
|
|
645
|
-
# @return
|
|
816
|
+
# @rbs threshold: Numeric? -- Visibility threshold
|
|
817
|
+
# @rbs return: bool -- Whether element intersects viewport
|
|
646
818
|
def intersecting_viewport?(threshold: nil)
|
|
647
819
|
option_threshold = threshold || 0
|
|
648
820
|
js = <<~JAVASCRIPT
|
|
@@ -662,7 +834,6 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
662
834
|
evaluate(js, option_threshold)
|
|
663
835
|
end
|
|
664
836
|
|
|
665
|
-
# @param quad [Array<Point>]
|
|
666
837
|
private def compute_quad_area(quad)
|
|
667
838
|
# Compute sum of all directed areas of adjacent triangles
|
|
668
839
|
# https://en.wikipedia.org/wiki/Polygon#Simple_polygons
|
|
@@ -670,6 +841,9 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
670
841
|
end
|
|
671
842
|
|
|
672
843
|
# used in AriaQueryHandler
|
|
844
|
+
# @rbs accessible_name: String? -- Accessible name filter
|
|
845
|
+
# @rbs role: String? -- Accessible role filter
|
|
846
|
+
# @rbs return: Hash[String, untyped] -- Accessibility tree result
|
|
673
847
|
def query_ax_tree(accessible_name: nil, role: nil)
|
|
674
848
|
@remote_object.query_ax_tree(@client,
|
|
675
849
|
accessible_name: accessible_name, role: role)
|
data/lib/puppeteer/env.rb
CHANGED
data/lib/puppeteer/errors.rb
CHANGED
|
@@ -1,2 +1,25 @@
|
|
|
1
|
-
# ref: https://github.com/puppeteer/puppeteer/blob/
|
|
2
|
-
|
|
1
|
+
# ref: https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/common/Errors.ts
|
|
2
|
+
|
|
3
|
+
# The base class for all Puppeteer-specific errors
|
|
4
|
+
class Puppeteer::Error < StandardError
|
|
5
|
+
attr_writer :cause
|
|
6
|
+
|
|
7
|
+
def cause
|
|
8
|
+
return nil if @cause.equal?(self)
|
|
9
|
+
|
|
10
|
+
stack = Thread.current[:puppeteer_cause_stack] ||= []
|
|
11
|
+
return nil if stack.include?(object_id)
|
|
12
|
+
|
|
13
|
+
stack << object_id
|
|
14
|
+
begin
|
|
15
|
+
@cause || super
|
|
16
|
+
ensure
|
|
17
|
+
stack.pop
|
|
18
|
+
Thread.current[:puppeteer_cause_stack] = nil if stack.empty?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Puppeteer::TimeoutError < Puppeteer::Error; end
|
|
24
|
+
|
|
25
|
+
class Puppeteer::TouchError < Puppeteer::Error; end
|
|
@@ -20,6 +20,12 @@ module Puppeteer::EventCallbackable
|
|
|
20
20
|
@listeners.delete(id)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# @param handler [Proc] Listener proc
|
|
24
|
+
def delete_by_handler(handler)
|
|
25
|
+
id, = @listeners.find { |_, listener| listener == handler }
|
|
26
|
+
@listeners.delete(id) if id
|
|
27
|
+
end
|
|
28
|
+
|
|
23
29
|
# @implement Enumerable#each
|
|
24
30
|
def each(&block)
|
|
25
31
|
@listeners.values.each(&block)
|
|
@@ -33,6 +39,15 @@ module Puppeteer::EventCallbackable
|
|
|
33
39
|
|
|
34
40
|
alias_method :on, :add_event_listener
|
|
35
41
|
|
|
42
|
+
def off(event_name_or_id, listener = nil, &block)
|
|
43
|
+
listener ||= block
|
|
44
|
+
if listener
|
|
45
|
+
(@event_listeners ||= {})[event_name_or_id]&.delete_by_handler(listener)
|
|
46
|
+
else
|
|
47
|
+
remove_event_listener(event_name_or_id)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
36
51
|
def remove_event_listener(*id_args)
|
|
37
52
|
(@event_listeners ||= {}).each do |event_name, listeners|
|
|
38
53
|
id_args.each do |id|
|
data/lib/puppeteer/events.rb
CHANGED
|
@@ -34,6 +34,7 @@ module CDPSessionEmittedEvents ; end
|
|
|
34
34
|
|
|
35
35
|
{
|
|
36
36
|
Disconnected: EventsDefinitionUtils.symbol('CDPSession.Disconnected'),
|
|
37
|
+
Ready: EventsDefinitionUtils.symbol('CDPSession.Ready'),
|
|
37
38
|
}.define_const_into(CDPSessionEmittedEvents)
|
|
38
39
|
|
|
39
40
|
# All the events a Browser may emit.
|
|
@@ -159,6 +160,9 @@ module PageEmittedEvents ; end
|
|
|
159
160
|
# The object is readonly. See Page#setRequestInterception for intercepting and mutating requests.
|
|
160
161
|
Request: 'request',
|
|
161
162
|
|
|
163
|
+
# Emitted when a request is served from cache.
|
|
164
|
+
RequestServedFromCache: 'requestservedfromcache',
|
|
165
|
+
|
|
162
166
|
# Emitted when a request fails, for example by timing out.
|
|
163
167
|
#
|
|
164
168
|
# Contains a HTTPRequest.
|