puppeteer-ruby 0.50.1 → 0.52.0
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/docs/api_coverage.md +38 -6
- data/lib/puppeteer/browser.rb +82 -0
- data/lib/puppeteer/browser_connector.rb +2 -0
- data/lib/puppeteer/browser_context.rb +51 -3
- data/lib/puppeteer/chrome_target_manager.rb +53 -6
- data/lib/puppeteer/debug_print.rb +9 -13
- data/lib/puppeteer/element_handle.rb +16 -0
- data/lib/puppeteer/events.rb +3 -0
- data/lib/puppeteer/execution_context.rb +37 -15
- data/lib/puppeteer/extension.rb +70 -0
- data/lib/puppeteer/frame.rb +8 -1
- data/lib/puppeteer/frame_manager.rb +62 -2
- data/lib/puppeteer/http_request.rb +15 -1
- data/lib/puppeteer/http_response.rb +6 -1
- data/lib/puppeteer/isolated_world.rb +22 -1
- data/lib/puppeteer/issue.rb +16 -0
- data/lib/puppeteer/js_coverage.rb +14 -1
- data/lib/puppeteer/launcher/browser_options.rb +6 -1
- data/lib/puppeteer/launcher/chrome.rb +17 -2
- data/lib/puppeteer/launcher/chrome_arg_options.rb +2 -1
- data/lib/puppeteer/locators.rb +56 -37
- data/lib/puppeteer/network_manager.rb +16 -16
- data/lib/puppeteer/page.rb +90 -7
- data/lib/puppeteer/puppeteer.rb +15 -0
- data/lib/puppeteer/remote_object.rb +2 -1
- data/lib/puppeteer/target.rb +17 -0
- data/lib/puppeteer/version.rb +2 -1
- data/lib/puppeteer.rb +2 -0
- data/puppeteer-ruby.gemspec +1 -1
- data/sig/_supplementary.rbs +7 -0
- data/sig/puppeteer/browser.rbs +34 -2
- data/sig/puppeteer/element_handle.rbs +5 -0
- data/sig/puppeteer/execution_context.rbs +4 -0
- data/sig/puppeteer/extension.rbs +37 -0
- data/sig/puppeteer/frame.rbs +5 -0
- data/sig/puppeteer/http_request.rbs +2 -0
- data/sig/puppeteer/issue.rbs +13 -0
- data/sig/puppeteer/locators.rbs +5 -2
- data/sig/puppeteer/page.rbs +20 -0
- data/sig/puppeteer/puppeteer.rbs +7 -2
- data/sig/puppeteer/remote_object.rbs +2 -0
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc3601ac8a64edb1a6054663944d5244eb0fcbc774b2d2f73529eef194031689
|
|
4
|
+
data.tar.gz: c364f8b73fc4dc6a591baca37e2e116d3147c5572f42dd026de77e36b0842851
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4de65d9249823c8532150d9cbeace0670d5e0c35de747193bee4923edb751b3dcc7a46aa6ec5f58393e20fabe9933b12eaca0ac47ead1d91c5793fec8ca0e9b
|
|
7
|
+
data.tar.gz: 4d5d39fe7dac8d74a3f90478f3bae1bd7e9dcb14018518551ca4ca7d59f48ed1e9f9e7205c33956d82591b2985b4fecd5f1eb4f0af8efc03f4a1b3f22a0b2281
|
data/docs/api_coverage.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# API coverages
|
|
2
|
-
- Puppeteer version: v24.
|
|
3
|
-
- puppeteer-ruby version: 0.
|
|
2
|
+
- Puppeteer version: v24.42.0
|
|
3
|
+
- puppeteer-ruby version: 0.51.0
|
|
4
4
|
|
|
5
5
|
## Puppeteer
|
|
6
6
|
|
|
@@ -25,8 +25,9 @@
|
|
|
25
25
|
* ~~deleteCookie~~
|
|
26
26
|
* ~~deleteMatchingCookies~~
|
|
27
27
|
* disconnect
|
|
28
|
+
* extensions
|
|
28
29
|
* ~~getWindowBounds~~
|
|
29
|
-
*
|
|
30
|
+
* installExtension => `#install_extension`
|
|
30
31
|
* isConnected => `#connected?`
|
|
31
32
|
* newPage => `#new_page`
|
|
32
33
|
* pages
|
|
@@ -34,10 +35,11 @@
|
|
|
34
35
|
* ~~removeScreen~~
|
|
35
36
|
* ~~screens~~
|
|
36
37
|
* ~~setCookie~~
|
|
38
|
+
* setPermission => `#set_permission`
|
|
37
39
|
* ~~setWindowBounds~~
|
|
38
40
|
* target
|
|
39
41
|
* targets
|
|
40
|
-
*
|
|
42
|
+
* uninstallExtension => `#uninstall_extension`
|
|
41
43
|
* userAgent => `#user_agent`
|
|
42
44
|
* version
|
|
43
45
|
* waitForTarget => `#wait_for_target`
|
|
@@ -55,6 +57,7 @@
|
|
|
55
57
|
* overridePermissions => `#override_permissions`
|
|
56
58
|
* pages
|
|
57
59
|
* setCookie => `#set_cookie`
|
|
60
|
+
* setPermission => `#set_permission`
|
|
58
61
|
* targets
|
|
59
62
|
* waitForTarget => `#wait_for_target`
|
|
60
63
|
|
|
@@ -124,7 +127,7 @@
|
|
|
124
127
|
* $$eval => `#eval_on_selector_all`
|
|
125
128
|
* $eval => `#eval_on_selector`
|
|
126
129
|
* asLocator => `#as_locator`
|
|
127
|
-
*
|
|
130
|
+
* autofill
|
|
128
131
|
* ~~backendNodeId~~
|
|
129
132
|
* boundingBox => `#bounding_box`
|
|
130
133
|
* boxModel => `#box_model`
|
|
@@ -163,6 +166,12 @@
|
|
|
163
166
|
* ~~once~~
|
|
164
167
|
* ~~removeAllListeners~~
|
|
165
168
|
|
|
169
|
+
## Extension
|
|
170
|
+
|
|
171
|
+
* pages
|
|
172
|
+
* triggerAction => `#trigger_action`
|
|
173
|
+
* workers
|
|
174
|
+
|
|
166
175
|
## ~~ExtensionTransport~~
|
|
167
176
|
|
|
168
177
|
* ~~close~~
|
|
@@ -188,6 +197,7 @@
|
|
|
188
197
|
* content
|
|
189
198
|
* evaluate
|
|
190
199
|
* evaluateHandle => `#evaluate_handle`
|
|
200
|
+
* extensionRealms => `#extension_realms`
|
|
191
201
|
* focus
|
|
192
202
|
* frameElement => `#frame_element`
|
|
193
203
|
* goto
|
|
@@ -321,6 +331,7 @@
|
|
|
321
331
|
* bringToFront => `#bring_to_front`
|
|
322
332
|
* browser
|
|
323
333
|
* browserContext => `#browser_context`
|
|
334
|
+
* captureHeapSnapshot => `#capture_heap_snapshot`
|
|
324
335
|
* click
|
|
325
336
|
* close
|
|
326
337
|
* content
|
|
@@ -341,6 +352,7 @@
|
|
|
341
352
|
* evaluateHandle => `#evaluate_handle`
|
|
342
353
|
* evaluateOnNewDocument => `#evaluate_on_new_document`
|
|
343
354
|
* exposeFunction => `#expose_function`
|
|
355
|
+
* extensionRealms => `#extension_realms`
|
|
344
356
|
* focus
|
|
345
357
|
* frames
|
|
346
358
|
* ~~getDefaultNavigationTimeout~~
|
|
@@ -348,6 +360,7 @@
|
|
|
348
360
|
* goBack => `#go_back`
|
|
349
361
|
* goForward => `#go_forward`
|
|
350
362
|
* goto
|
|
363
|
+
* ~~hasDevTools~~
|
|
351
364
|
* hover
|
|
352
365
|
* isClosed => `#closed?`
|
|
353
366
|
* isDragInterceptionEnabled => `#drag_interception_enabled?`
|
|
@@ -384,6 +397,7 @@
|
|
|
384
397
|
* tap
|
|
385
398
|
* target
|
|
386
399
|
* title
|
|
400
|
+
* triggerExtensionAction => `#trigger_extension_action`
|
|
387
401
|
* type => `#type_text`
|
|
388
402
|
* url
|
|
389
403
|
* viewport
|
|
@@ -402,6 +416,13 @@
|
|
|
402
416
|
## ~~ProtocolError~~
|
|
403
417
|
|
|
404
418
|
|
|
419
|
+
## ~~Realm~~
|
|
420
|
+
|
|
421
|
+
* ~~evaluate~~
|
|
422
|
+
* ~~evaluateHandle~~
|
|
423
|
+
* ~~extension~~
|
|
424
|
+
* ~~waitForFunction~~
|
|
425
|
+
|
|
405
426
|
## ~~ScreenRecorder~~
|
|
406
427
|
|
|
407
428
|
* ~~stop~~
|
|
@@ -417,7 +438,7 @@
|
|
|
417
438
|
|
|
418
439
|
## Target
|
|
419
440
|
|
|
420
|
-
*
|
|
441
|
+
* asPage => `#as_page`
|
|
421
442
|
* browser
|
|
422
443
|
* browserContext => `#browser_context`
|
|
423
444
|
* createCDPSession => `#create_cdp_session`
|
|
@@ -448,6 +469,17 @@
|
|
|
448
469
|
## ~~UnsupportedOperation~~
|
|
449
470
|
|
|
450
471
|
|
|
472
|
+
## ~~WebMCP~~
|
|
473
|
+
|
|
474
|
+
* ~~tools~~
|
|
475
|
+
|
|
476
|
+
## ~~WebMCPTool~~
|
|
477
|
+
|
|
478
|
+
* ~~execute~~
|
|
479
|
+
|
|
480
|
+
## ~~WebMCPToolCall~~
|
|
481
|
+
|
|
482
|
+
|
|
451
483
|
## WebWorker
|
|
452
484
|
|
|
453
485
|
* close
|
data/lib/puppeteer/browser.rb
CHANGED
|
@@ -14,6 +14,8 @@ class Puppeteer::Browser
|
|
|
14
14
|
# @rbs ignore_https_errors: bool -- Ignore HTTPS errors
|
|
15
15
|
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
|
|
16
16
|
# @rbs network_enabled: bool -- Whether network events are enabled
|
|
17
|
+
# @rbs issues_enabled: bool -- Whether issues events are enabled
|
|
18
|
+
# @rbs block_list: Array[String]? -- URL block list patterns
|
|
17
19
|
# @rbs process: Puppeteer::BrowserRunner::BrowserProcess? -- Browser process handle
|
|
18
20
|
# @rbs close_callback: Proc -- Close callback
|
|
19
21
|
# @rbs target_filter_callback: Proc? -- Target filter callback
|
|
@@ -25,6 +27,8 @@ class Puppeteer::Browser
|
|
|
25
27
|
ignore_https_errors:,
|
|
26
28
|
default_viewport:,
|
|
27
29
|
network_enabled: true,
|
|
30
|
+
issues_enabled: true,
|
|
31
|
+
block_list: nil,
|
|
28
32
|
process:,
|
|
29
33
|
close_callback:,
|
|
30
34
|
target_filter_callback:,
|
|
@@ -36,6 +40,8 @@ class Puppeteer::Browser
|
|
|
36
40
|
ignore_https_errors: ignore_https_errors,
|
|
37
41
|
default_viewport: default_viewport,
|
|
38
42
|
network_enabled: network_enabled,
|
|
43
|
+
issues_enabled: issues_enabled,
|
|
44
|
+
block_list: block_list,
|
|
39
45
|
process: process,
|
|
40
46
|
close_callback: close_callback,
|
|
41
47
|
target_filter_callback: target_filter_callback,
|
|
@@ -51,6 +57,8 @@ class Puppeteer::Browser
|
|
|
51
57
|
# @rbs ignore_https_errors: bool -- Ignore HTTPS errors
|
|
52
58
|
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
|
|
53
59
|
# @rbs network_enabled: bool -- Whether network events are enabled
|
|
60
|
+
# @rbs issues_enabled: bool -- Whether issues events are enabled
|
|
61
|
+
# @rbs block_list: Array[String]? -- URL block list patterns
|
|
54
62
|
# @rbs process: Puppeteer::BrowserRunner::BrowserProcess? -- Browser process handle
|
|
55
63
|
# @rbs close_callback: Proc -- Close callback
|
|
56
64
|
# @rbs target_filter_callback: Proc? -- Target filter callback
|
|
@@ -62,6 +70,8 @@ class Puppeteer::Browser
|
|
|
62
70
|
ignore_https_errors:,
|
|
63
71
|
default_viewport:,
|
|
64
72
|
network_enabled: true,
|
|
73
|
+
issues_enabled: true,
|
|
74
|
+
block_list: nil,
|
|
65
75
|
process:,
|
|
66
76
|
close_callback:,
|
|
67
77
|
target_filter_callback:,
|
|
@@ -73,6 +83,8 @@ class Puppeteer::Browser
|
|
|
73
83
|
@ignore_https_errors = ignore_https_errors
|
|
74
84
|
@default_viewport = default_viewport
|
|
75
85
|
@network_enabled = network_enabled
|
|
86
|
+
@issues_enabled = issues_enabled
|
|
87
|
+
@block_list = block_list
|
|
76
88
|
@process = process
|
|
77
89
|
@connection = connection
|
|
78
90
|
@close_callback = close_callback
|
|
@@ -89,7 +101,9 @@ class Puppeteer::Browser
|
|
|
89
101
|
connection: connection,
|
|
90
102
|
target_factory: method(:create_target),
|
|
91
103
|
target_filter_callback: @target_filter_callback,
|
|
104
|
+
block_list: block_list,
|
|
92
105
|
)
|
|
106
|
+
@extensions = {}
|
|
93
107
|
end
|
|
94
108
|
|
|
95
109
|
private def default_target_filter_callback(target_info)
|
|
@@ -163,6 +177,10 @@ class Puppeteer::Browser
|
|
|
163
177
|
@target_manager
|
|
164
178
|
end
|
|
165
179
|
|
|
180
|
+
private def connection
|
|
181
|
+
@connection
|
|
182
|
+
end
|
|
183
|
+
|
|
166
184
|
# @rbs return: Puppeteer::BrowserContext -- New incognito browser context
|
|
167
185
|
def create_incognito_browser_context
|
|
168
186
|
result = @connection.send_message('Target.createBrowserContext')
|
|
@@ -196,6 +214,13 @@ class Puppeteer::Browser
|
|
|
196
214
|
@default_context
|
|
197
215
|
end
|
|
198
216
|
|
|
217
|
+
# @rbs origin: String -- Origin URL, or '*' for all origins
|
|
218
|
+
# @rbs permissions: Array[Hash[Symbol | String, untyped]] -- Permission descriptors and states
|
|
219
|
+
# @rbs return: void -- No return value
|
|
220
|
+
def set_permission(origin, *permissions)
|
|
221
|
+
@default_context.set_permission(origin, *permissions)
|
|
222
|
+
end
|
|
223
|
+
|
|
199
224
|
# @rbs context_id: String? -- Browser context ID
|
|
200
225
|
# @rbs return: void -- No return value
|
|
201
226
|
def dispose_context(context_id)
|
|
@@ -369,6 +394,63 @@ class Puppeteer::Browser
|
|
|
369
394
|
Version.fetch(@connection).user_agent
|
|
370
395
|
end
|
|
371
396
|
|
|
397
|
+
# @rbs page_target_id: String -- Page target id
|
|
398
|
+
# @rbs return: String? -- DevTools target id for page
|
|
399
|
+
def _has_devtools_target(page_target_id)
|
|
400
|
+
result = @connection.send_message('Target.getDevToolsTarget', targetId: page_target_id)
|
|
401
|
+
result['targetId']
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# @rbs path: String -- Extension path
|
|
405
|
+
# @rbs return: String -- Installed extension id
|
|
406
|
+
def install_extension(path)
|
|
407
|
+
result = @connection.send_message('Extensions.loadUnpacked', path: path)
|
|
408
|
+
extension_id = result['id']
|
|
409
|
+
@extensions.delete(extension_id)
|
|
410
|
+
extension_id
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# @rbs extension_id: String -- Extension id
|
|
414
|
+
# @rbs return: void -- No return value
|
|
415
|
+
def uninstall_extension(extension_id)
|
|
416
|
+
@connection.send_message('Extensions.uninstall', id: extension_id)
|
|
417
|
+
@extensions.delete(extension_id)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# @rbs return: Hash[String, Puppeteer::Extension] -- Installed extensions
|
|
421
|
+
def extensions
|
|
422
|
+
response = @connection.send_message('Extensions.getExtensions')
|
|
423
|
+
extension_map = {}
|
|
424
|
+
response.fetch('extensions', []).each do |payload|
|
|
425
|
+
extension_id = payload['id']
|
|
426
|
+
existing_extension = @extensions[extension_id]
|
|
427
|
+
extension_map[extension_id] =
|
|
428
|
+
if existing_extension
|
|
429
|
+
existing_extension
|
|
430
|
+
else
|
|
431
|
+
Puppeteer::Extension.new(
|
|
432
|
+
id: extension_id,
|
|
433
|
+
version: payload['version'],
|
|
434
|
+
name: payload['name'],
|
|
435
|
+
path: payload['path'],
|
|
436
|
+
enabled: payload['enabled'],
|
|
437
|
+
browser: self,
|
|
438
|
+
)
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
@extensions = extension_map
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
# @rbs return: bool -- Whether issue events are enabled
|
|
445
|
+
def issues_enabled?
|
|
446
|
+
@issues_enabled
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# @rbs return: Array[String]? -- URL block list patterns
|
|
450
|
+
def block_list
|
|
451
|
+
@block_list
|
|
452
|
+
end
|
|
453
|
+
|
|
372
454
|
# @rbs return: void -- No return value
|
|
373
455
|
def close
|
|
374
456
|
@close_callback.call
|
|
@@ -28,6 +28,8 @@ class Puppeteer::BrowserConnector
|
|
|
28
28
|
ignore_https_errors: @browser_options.ignore_https_errors?,
|
|
29
29
|
default_viewport: @browser_options.default_viewport,
|
|
30
30
|
network_enabled: @browser_options.network_enabled,
|
|
31
|
+
issues_enabled: @browser_options.issues_enabled,
|
|
32
|
+
block_list: @browser_options.block_list,
|
|
31
33
|
process: nil,
|
|
32
34
|
close_callback: -> { connection.send_message('Browser.close') },
|
|
33
35
|
target_filter_callback: @browser_options.target_filter,
|
|
@@ -153,6 +153,44 @@ class Puppeteer::BrowserContext
|
|
|
153
153
|
}.compact)
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
+
# @param origin [String] Use '*' to apply to all origins.
|
|
157
|
+
# @param permissions [Array<Hash>] { permission: { name:, userVisibleOnly:, sysex:, panTiltZoom:, allowWithoutSanitization: }, state: 'granted'|'denied'|'prompt' }
|
|
158
|
+
def set_permission(origin, *permissions)
|
|
159
|
+
permissions.each do |permission|
|
|
160
|
+
descriptor = hash_value(permission, 'permission')
|
|
161
|
+
state = hash_value(permission, 'state')
|
|
162
|
+
unless descriptor.is_a?(Hash)
|
|
163
|
+
raise ArgumentError.new('Each permission entry must include a permission descriptor')
|
|
164
|
+
end
|
|
165
|
+
unless state
|
|
166
|
+
raise ArgumentError.new('Each permission entry must include state')
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
name = hash_value(descriptor, 'name')
|
|
170
|
+
unless name
|
|
171
|
+
raise ArgumentError.new('Permission descriptor must include name')
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
protocol_permission = { name: name.to_s }
|
|
175
|
+
{
|
|
176
|
+
userVisibleOnly: %w[userVisibleOnly user_visible_only],
|
|
177
|
+
sysex: %w[sysex],
|
|
178
|
+
panTiltZoom: %w[panTiltZoom pan_tilt_zoom],
|
|
179
|
+
allowWithoutSanitization: %w[allowWithoutSanitization allow_without_sanitization],
|
|
180
|
+
}.each do |protocol_key, keys|
|
|
181
|
+
value = hash_value(descriptor, *keys)
|
|
182
|
+
protocol_permission[protocol_key] = value unless value.nil?
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
@connection.send_message('Browser.setPermission', {
|
|
186
|
+
origin: origin == '*' ? nil : origin,
|
|
187
|
+
browserContextId: @id,
|
|
188
|
+
permission: protocol_permission,
|
|
189
|
+
setting: state.to_s,
|
|
190
|
+
}.compact)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
156
194
|
def clear_permission_overrides
|
|
157
195
|
if @id
|
|
158
196
|
@connection.send_message('Browser.resetPermissions', browserContextId: @id)
|
|
@@ -214,6 +252,9 @@ class Puppeteer::BrowserContext
|
|
|
214
252
|
normalized = normalize_cookie_hash(cookie)
|
|
215
253
|
partition_key = normalized.delete('partitionKey') || normalized.delete('partition_key')
|
|
216
254
|
normalized['partitionKey'] = convert_partition_key_for_cdp(partition_key) if partition_key
|
|
255
|
+
same_site = normalized.delete('sameSite') || normalized.delete('same_site')
|
|
256
|
+
converted_same_site = convert_same_site_for_cdp(same_site)
|
|
257
|
+
normalized['sameSite'] = converted_same_site if converted_same_site
|
|
217
258
|
normalized
|
|
218
259
|
end
|
|
219
260
|
@connection.send_message('Storage.setCookies', {
|
|
@@ -277,9 +318,7 @@ class Puppeteer::BrowserContext
|
|
|
277
318
|
end
|
|
278
319
|
|
|
279
320
|
private def normalize_cookie_hash(cookie)
|
|
280
|
-
cookie.
|
|
281
|
-
normalized[key.to_s] = value
|
|
282
|
-
end
|
|
321
|
+
cookie.transform_keys(&:to_s)
|
|
283
322
|
end
|
|
284
323
|
|
|
285
324
|
private def hash_value(hash, *keys)
|
|
@@ -305,6 +344,15 @@ class Puppeteer::BrowserContext
|
|
|
305
344
|
}
|
|
306
345
|
end
|
|
307
346
|
|
|
347
|
+
private def convert_same_site_for_cdp(same_site)
|
|
348
|
+
case same_site
|
|
349
|
+
when 'Strict', 'Lax', 'None'
|
|
350
|
+
same_site
|
|
351
|
+
else
|
|
352
|
+
nil
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
308
356
|
private def convert_partition_key_from_cdp(partition_key)
|
|
309
357
|
return nil if partition_key.nil?
|
|
310
358
|
return partition_key if partition_key.is_a?(String)
|
|
@@ -2,7 +2,7 @@ class Puppeteer::ChromeTargetManager
|
|
|
2
2
|
include Puppeteer::DebugPrint
|
|
3
3
|
include Puppeteer::EventCallbackable
|
|
4
4
|
|
|
5
|
-
def initialize(connection:, target_factory:, target_filter_callback:)
|
|
5
|
+
def initialize(connection:, target_factory:, target_filter_callback:, block_list: nil)
|
|
6
6
|
@discovered_targets_by_target_id = {}
|
|
7
7
|
@attached_targets_by_target_id = {}
|
|
8
8
|
@attached_targets_by_session_id = {}
|
|
@@ -13,8 +13,13 @@ class Puppeteer::ChromeTargetManager
|
|
|
13
13
|
@connection = connection
|
|
14
14
|
@target_filter_callback = target_filter_callback
|
|
15
15
|
@target_factory = target_factory
|
|
16
|
+
@block_list = block_list
|
|
17
|
+
if @block_list && !@block_list.is_a?(Array)
|
|
18
|
+
raise ArgumentError.new('block_list must be an Array of URL patterns')
|
|
19
|
+
end
|
|
16
20
|
@target_interceptors = {}
|
|
17
21
|
@initialize_promise = Async::Promise.new
|
|
22
|
+
@initial_attach_done = false
|
|
18
23
|
|
|
19
24
|
@connection_event_listeners = []
|
|
20
25
|
@connection_event_listeners << @connection.add_event_listener(
|
|
@@ -52,7 +57,7 @@ class Puppeteer::ChromeTargetManager
|
|
|
52
57
|
|
|
53
58
|
private def store_existing_targets_for_init
|
|
54
59
|
@discovered_targets_by_target_id.each do |target_id, target_info|
|
|
55
|
-
if @target_filter_callback.call(target_info) && target_info.type != 'browser'
|
|
60
|
+
if @target_filter_callback.call(target_info) && target_info.type != 'browser' && url_allowed?(target_info.url)
|
|
56
61
|
@target_ids_for_init << target_id
|
|
57
62
|
end
|
|
58
63
|
end
|
|
@@ -64,6 +69,7 @@ class Puppeteer::ChromeTargetManager
|
|
|
64
69
|
flatten: true,
|
|
65
70
|
autoAttach: true,
|
|
66
71
|
})
|
|
72
|
+
@initial_attach_done = true
|
|
67
73
|
finish_initialization_if_ready
|
|
68
74
|
@initialize_promise.wait
|
|
69
75
|
end
|
|
@@ -149,11 +155,11 @@ class Puppeteer::ChromeTargetManager
|
|
|
149
155
|
target_info = @discovered_targets_by_target_id.delete(target_id)
|
|
150
156
|
finish_initialization_if_ready(target_id)
|
|
151
157
|
|
|
152
|
-
if target_info
|
|
158
|
+
if target_info&.type == 'service_worker'
|
|
153
159
|
# Special case for service workers: report TargetGone event when
|
|
154
160
|
# the worker is destroyed.
|
|
155
161
|
target = @attached_targets_by_target_id.delete(target_id)
|
|
156
|
-
emit_event(TargetManagerEmittedEvents::TargetGone, target)
|
|
162
|
+
emit_event(TargetManagerEmittedEvents::TargetGone, target) if target
|
|
157
163
|
end
|
|
158
164
|
end
|
|
159
165
|
|
|
@@ -161,11 +167,11 @@ class Puppeteer::ChromeTargetManager
|
|
|
161
167
|
target_info = Puppeteer::Target::TargetInfo.new(event['targetInfo'])
|
|
162
168
|
@discovered_targets_by_target_id[target_info.target_id] = target_info
|
|
163
169
|
|
|
164
|
-
if @ignored_targets.include?(target_info.target_id) ||
|
|
170
|
+
if @ignored_targets.include?(target_info.target_id) || !target_info.attached
|
|
165
171
|
return
|
|
166
172
|
end
|
|
167
173
|
original_target = @attached_targets_by_target_id[target_info.target_id]
|
|
168
|
-
emit_event(TargetManagerEmittedEvents::TargetChanged, original_target, target_info)
|
|
174
|
+
emit_event(TargetManagerEmittedEvents::TargetChanged, original_target, target_info) if original_target
|
|
169
175
|
end
|
|
170
176
|
|
|
171
177
|
class SessionNotCreatedError < Puppeteer::Error ; end
|
|
@@ -204,6 +210,12 @@ class Puppeteer::ChromeTargetManager
|
|
|
204
210
|
|
|
205
211
|
return unless @connection.auto_attached?(target_info.target_id)
|
|
206
212
|
|
|
213
|
+
if !@initial_attach_done && !url_allowed?(target_info.url)
|
|
214
|
+
finish_initialization_if_ready(target_info.target_id)
|
|
215
|
+
silent_detach.call
|
|
216
|
+
return
|
|
217
|
+
end
|
|
218
|
+
|
|
207
219
|
# Special case for service workers: being attached to service workers will
|
|
208
220
|
# prevent them from ever being destroyed. Therefore, we silently detach
|
|
209
221
|
# from service workers unless the connection was manually created via
|
|
@@ -270,6 +282,7 @@ class Puppeteer::ChromeTargetManager
|
|
|
270
282
|
flatten: true,
|
|
271
283
|
autoAttach: true,
|
|
272
284
|
}))
|
|
285
|
+
maybe_setup_network_conditions(session)
|
|
273
286
|
Puppeteer::AsyncUtils.await(session.async_send_message('Runtime.runIfWaitingForDebugger'))
|
|
274
287
|
rescue => err
|
|
275
288
|
Logger.new($stderr).warn(err)
|
|
@@ -292,4 +305,38 @@ class Puppeteer::ChromeTargetManager
|
|
|
292
305
|
@attached_targets_by_target_id.delete(target.target_id)
|
|
293
306
|
emit_event(TargetManagerEmittedEvents::TargetGone, target)
|
|
294
307
|
end
|
|
308
|
+
|
|
309
|
+
private def url_allowed?(url)
|
|
310
|
+
return true if @block_list.nil? || @block_list.empty?
|
|
311
|
+
return true if url.nil? || url.empty? || url == 'about:blank'
|
|
312
|
+
|
|
313
|
+
@block_list.none? do |pattern|
|
|
314
|
+
File.fnmatch?(pattern, url, File::FNM_EXTGLOB)
|
|
315
|
+
rescue ArgumentError
|
|
316
|
+
false
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
private def maybe_setup_network_conditions(session)
|
|
321
|
+
return if @block_list.nil? || @block_list.empty?
|
|
322
|
+
|
|
323
|
+
matched_network_conditions = @block_list.map do |pattern|
|
|
324
|
+
{
|
|
325
|
+
urlPattern: pattern,
|
|
326
|
+
latency: 0,
|
|
327
|
+
downloadThroughput: -1,
|
|
328
|
+
uploadThroughput: -1,
|
|
329
|
+
}
|
|
330
|
+
end
|
|
331
|
+
Puppeteer::AsyncUtils.await(session.async_send_message('Network.enable'))
|
|
332
|
+
Puppeteer::AsyncUtils.await(session.async_send_message('Network.emulateNetworkConditionsByRule', {
|
|
333
|
+
matchedNetworkConditions: matched_network_conditions,
|
|
334
|
+
offline: true,
|
|
335
|
+
}))
|
|
336
|
+
rescue => err
|
|
337
|
+
message = err.message.to_s.downcase
|
|
338
|
+
return if message.include?('target closed') || message.include?('session closed') || message.include?('not found')
|
|
339
|
+
|
|
340
|
+
raise
|
|
341
|
+
end
|
|
295
342
|
end
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
require 'logger'
|
|
2
2
|
|
|
3
3
|
module Puppeteer::DebugPrint
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
@__debug_logger ||= Logger.new($stdout)
|
|
7
|
-
@__debug_logger.debug(*args, **kwargs)
|
|
8
|
-
end
|
|
4
|
+
def debug_puts(*args, **kwargs)
|
|
5
|
+
return unless Puppeteer.env.debug?
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
@__debug_logger ||= Logger.new($stdout)
|
|
8
|
+
@__debug_logger.debug(*args, **kwargs)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def debug_print(*args)
|
|
12
|
+
return unless Puppeteer.env.debug?
|
|
16
13
|
|
|
17
|
-
|
|
18
|
-
end
|
|
14
|
+
print(*args)
|
|
19
15
|
end
|
|
20
16
|
end
|
|
@@ -540,6 +540,22 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
540
540
|
end
|
|
541
541
|
end
|
|
542
542
|
|
|
543
|
+
# @rbs credit_card: Hash[Symbol | String, untyped]? -- Credit card autofill payload
|
|
544
|
+
# @rbs address: Hash[Symbol | String, untyped]? -- Address autofill payload
|
|
545
|
+
# @rbs return: void -- No return value
|
|
546
|
+
def autofill(credit_card: nil, address: nil)
|
|
547
|
+
node_info = @remote_object.node_info(@client)
|
|
548
|
+
field_id = node_info.dig('node', 'backendNodeId')
|
|
549
|
+
@client.send_message('Autofill.trigger', {
|
|
550
|
+
fieldId: field_id,
|
|
551
|
+
frameId: @frame.id,
|
|
552
|
+
card: credit_card,
|
|
553
|
+
address: address,
|
|
554
|
+
}.compact)
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
define_async_method :async_autofill
|
|
558
|
+
|
|
543
559
|
# @rbs block: Proc? -- Optional block for Object#tap usage
|
|
544
560
|
# @rbs return: Puppeteer::ElementHandle | nil -- Element handle or nil
|
|
545
561
|
def tap(&block)
|
data/lib/puppeteer/events.rb
CHANGED
|
@@ -135,6 +135,9 @@ module PageEmittedEvents ; end
|
|
|
135
135
|
# Emitted when a frame is navigated to a new URL. Will contain a {@link Frame}.
|
|
136
136
|
FrameNavigated: 'framenavigated',
|
|
137
137
|
|
|
138
|
+
# Emitted when a DevTools issue is reported.
|
|
139
|
+
Issue: 'issue',
|
|
140
|
+
|
|
138
141
|
# Emitted when the JavaScript
|
|
139
142
|
# {https://developer.mozilla.org/en-US/docs/Web/Events/load | load} event is dispatched.
|
|
140
143
|
Load: 'load',
|
|
@@ -52,13 +52,18 @@ class Puppeteer::ExecutionContext
|
|
|
52
52
|
# @param context_id [String]
|
|
53
53
|
# @return [Object|JSHandle]
|
|
54
54
|
def evaluate_with(client:, context_id:)
|
|
55
|
-
result =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
result =
|
|
56
|
+
begin
|
|
57
|
+
client.send_message('Runtime.evaluate',
|
|
58
|
+
expression: expression_with_source_url,
|
|
59
|
+
contextId: context_id,
|
|
60
|
+
returnByValue: @return_by_value,
|
|
61
|
+
awaitPromise: true,
|
|
62
|
+
userGesture: true,
|
|
63
|
+
)
|
|
64
|
+
rescue Puppeteer::Connection::ProtocolError => err
|
|
65
|
+
raise @execution_context.send(:rewrite_evaluation_error, err)
|
|
66
|
+
end
|
|
62
67
|
# }).catch(rewriteError);
|
|
63
68
|
|
|
64
69
|
exception_details = result['exceptionDetails']
|
|
@@ -110,14 +115,19 @@ class Puppeteer::ExecutionContext
|
|
|
110
115
|
# Original puppeteer implementation take it into consideration.
|
|
111
116
|
# But we don't support the syntax here.
|
|
112
117
|
|
|
113
|
-
result =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
result =
|
|
119
|
+
begin
|
|
120
|
+
client.send_message('Runtime.callFunctionOn',
|
|
121
|
+
functionDeclaration: function_declaration,
|
|
122
|
+
executionContextId: context_id,
|
|
123
|
+
arguments: converted_args,
|
|
124
|
+
returnByValue: @return_by_value,
|
|
125
|
+
awaitPromise: true,
|
|
126
|
+
userGesture: true,
|
|
127
|
+
)
|
|
128
|
+
rescue Puppeteer::Connection::ProtocolError => err
|
|
129
|
+
raise @execution_context.send(:rewrite_evaluation_error, err)
|
|
130
|
+
end # .catch(rewriteError);
|
|
121
131
|
|
|
122
132
|
exception_details = result['exceptionDetails']
|
|
123
133
|
if exception_details
|
|
@@ -337,6 +347,18 @@ class Puppeteer::ExecutionContext
|
|
|
337
347
|
)
|
|
338
348
|
end
|
|
339
349
|
|
|
350
|
+
# @param error [Puppeteer::Connection::ProtocolError]
|
|
351
|
+
# @return [StandardError]
|
|
352
|
+
private def rewrite_evaluation_error(error)
|
|
353
|
+
message = error.message
|
|
354
|
+
if message.end_with?('Cannot find context with specified id') ||
|
|
355
|
+
message.end_with?('Inspected target navigated or closed')
|
|
356
|
+
return Puppeteer::Error.new('Execution context was destroyed, most likely because of a navigation.')
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
error
|
|
360
|
+
end
|
|
361
|
+
|
|
340
362
|
# @rbs page_function: String -- JavaScript code to check
|
|
341
363
|
# @rbs return: bool -- Whether the code represents a function
|
|
342
364
|
private def function_string?(page_function)
|