puppeteer-ruby 0.51.0 → 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 +34 -5
- data/lib/puppeteer/browser.rb +75 -0
- data/lib/puppeteer/browser_connector.rb +2 -0
- data/lib/puppeteer/chrome_target_manager.rb +49 -2
- 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/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 +16 -1
- data/lib/puppeteer/launcher/chrome_arg_options.rb +2 -1
- data/lib/puppeteer/network_manager.rb +16 -16
- data/lib/puppeteer/page.rb +36 -4
- 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 -2
- data/lib/puppeteer.rb +2 -0
- data/sig/_supplementary.rbs +5 -0
- data/sig/puppeteer/browser.rbs +29 -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/issue.rbs +13 -0
- data/sig/puppeteer/page.rbs +14 -0
- data/sig/puppeteer/puppeteer.rbs +7 -2
- data/sig/puppeteer/remote_object.rbs +2 -0
- metadata +6 -2
|
@@ -5,6 +5,7 @@ class Puppeteer::FrameManager
|
|
|
5
5
|
using Puppeteer::DefineAsyncMethod
|
|
6
6
|
|
|
7
7
|
UTILITY_WORLD_NAME = '__puppeteer_utility_world__'
|
|
8
|
+
CHROME_EXTENSION_PREFIX = 'chrome-extension://'
|
|
8
9
|
|
|
9
10
|
# @param {!Puppeteer.CDPSession} client
|
|
10
11
|
# @param {!Puppeteer.Page} page
|
|
@@ -89,6 +90,9 @@ class Puppeteer::FrameManager
|
|
|
89
90
|
handle_lifecycle_event(event)
|
|
90
91
|
end
|
|
91
92
|
end
|
|
93
|
+
client.on_event('Audits.issueAdded') do |event|
|
|
94
|
+
@page.emit_event(PageEmittedEvents::Issue, Puppeteer::Issue.new(event['issue']))
|
|
95
|
+
end
|
|
92
96
|
end
|
|
93
97
|
|
|
94
98
|
attr_reader :client, :timeout_settings
|
|
@@ -109,7 +113,9 @@ class Puppeteer::FrameManager
|
|
|
109
113
|
Puppeteer::AsyncUtils.await_promise_all(
|
|
110
114
|
client.async_send_message('Page.setLifecycleEventsEnabled', enabled: true),
|
|
111
115
|
client.async_send_message('Runtime.enable'),
|
|
116
|
+
@page.browser.issues_enabled? ? client.async_send_message('Audits.enable') : nil,
|
|
112
117
|
)
|
|
118
|
+
maybe_setup_block_list(client)
|
|
113
119
|
ensure_isolated_world(client, UTILITY_WORLD_NAME)
|
|
114
120
|
@network_manager.init unless cdp_session
|
|
115
121
|
rescue => err
|
|
@@ -224,7 +230,11 @@ class Puppeteer::FrameManager
|
|
|
224
230
|
session = target.session
|
|
225
231
|
frame&.send(:update_client, session)
|
|
226
232
|
setup_listeners(session)
|
|
227
|
-
|
|
233
|
+
Async do
|
|
234
|
+
async_init(target.target_info.target_id, session).wait
|
|
235
|
+
rescue => err
|
|
236
|
+
debug_puts(err)
|
|
237
|
+
end
|
|
228
238
|
end
|
|
229
239
|
|
|
230
240
|
# @param event [Hash]
|
|
@@ -493,6 +503,7 @@ class Puppeteer::FrameManager
|
|
|
493
503
|
# @pram session [Puppeteer::CDPSession]
|
|
494
504
|
def handle_execution_context_created(context_payload, session)
|
|
495
505
|
frame = if_present(context_payload.dig('auxData', 'frameId')) { |frame_id| @frames[frame_id] }
|
|
506
|
+
origin = context_payload['origin']
|
|
496
507
|
|
|
497
508
|
world = nil
|
|
498
509
|
if frame
|
|
@@ -508,6 +519,17 @@ class Puppeteer::FrameManager
|
|
|
508
519
|
# connections so we might end up creating multiple isolated worlds.
|
|
509
520
|
# We can use either.
|
|
510
521
|
world = frame.puppeteer_world
|
|
522
|
+
elsif extension_origin?(origin)
|
|
523
|
+
extension_id = extract_extension_id(origin)
|
|
524
|
+
if extension_id
|
|
525
|
+
world = frame.extension_worlds[extension_id]
|
|
526
|
+
unless world
|
|
527
|
+
world = Puppeteer::IsolaatedWorld.new(frame._client || @client, self, frame, @timeout_settings)
|
|
528
|
+
frame.extension_worlds[extension_id] = world
|
|
529
|
+
end
|
|
530
|
+
world.origin = origin
|
|
531
|
+
world.world_id = extension_id
|
|
532
|
+
end
|
|
511
533
|
end
|
|
512
534
|
end
|
|
513
535
|
|
|
@@ -523,6 +545,19 @@ class Puppeteer::FrameManager
|
|
|
523
545
|
@context_id_to_context[key] = context
|
|
524
546
|
end
|
|
525
547
|
|
|
548
|
+
private def extension_origin?(origin)
|
|
549
|
+
origin.is_a?(String) && origin.start_with?(CHROME_EXTENSION_PREFIX)
|
|
550
|
+
end
|
|
551
|
+
|
|
552
|
+
private def extract_extension_id(origin)
|
|
553
|
+
return nil unless extension_origin?(origin)
|
|
554
|
+
|
|
555
|
+
path_part = origin[CHROME_EXTENSION_PREFIX.length..]
|
|
556
|
+
return nil unless path_part
|
|
557
|
+
slash_index = path_part.index('/')
|
|
558
|
+
slash_index ? path_part[0...slash_index] : path_part
|
|
559
|
+
end
|
|
560
|
+
|
|
526
561
|
# @param execution_context_id [Integer]
|
|
527
562
|
# @param session [Puppeteer::CDPSEssion]
|
|
528
563
|
def handle_execution_context_destroyed(execution_context_id, session)
|
|
@@ -537,7 +572,7 @@ class Puppeteer::FrameManager
|
|
|
537
572
|
def handle_execution_contexts_cleared(session)
|
|
538
573
|
session_id = session.id
|
|
539
574
|
@context_id_to_context.select! do |execution_context_id, context|
|
|
540
|
-
key_session_id,
|
|
575
|
+
key_session_id, _context_id = execution_context_id.split(':', 2)
|
|
541
576
|
# Make sure to only clear execution contexts that belong
|
|
542
577
|
# to the current session.
|
|
543
578
|
if key_session_id != session_id
|
|
@@ -554,6 +589,31 @@ class Puppeteer::FrameManager
|
|
|
554
589
|
@context_id_to_context[key] or raise "INTERNAL ERROR: missing context with id = #{context_id}"
|
|
555
590
|
end
|
|
556
591
|
|
|
592
|
+
private def maybe_setup_block_list(client)
|
|
593
|
+
block_list = @page.browser.block_list
|
|
594
|
+
return if block_list.nil? || block_list.empty?
|
|
595
|
+
|
|
596
|
+
client.send_message('Network.enable')
|
|
597
|
+
matched_network_conditions = block_list.map do |pattern|
|
|
598
|
+
{
|
|
599
|
+
urlPattern: pattern,
|
|
600
|
+
latency: 0,
|
|
601
|
+
downloadThroughput: -1,
|
|
602
|
+
uploadThroughput: -1,
|
|
603
|
+
}
|
|
604
|
+
end
|
|
605
|
+
client.send_message('Network.emulateNetworkConditionsByRule', {
|
|
606
|
+
matchedNetworkConditions: matched_network_conditions,
|
|
607
|
+
offline: true,
|
|
608
|
+
})
|
|
609
|
+
rescue Puppeteer::Connection::ProtocolError => err
|
|
610
|
+
if err.message.include?('Method not available') || err.message.include?("wasn't found")
|
|
611
|
+
client.send_message('Network.setBlockedURLs', urls: block_list)
|
|
612
|
+
else
|
|
613
|
+
raise
|
|
614
|
+
end
|
|
615
|
+
end
|
|
616
|
+
|
|
557
617
|
# @param {!Frame} frame
|
|
558
618
|
private def remove_frame_recursively(frame)
|
|
559
619
|
frame.child_frames.each do |child|
|
|
@@ -62,11 +62,13 @@ class Puppeteer::IsolaatedWorld
|
|
|
62
62
|
@ctx_bindings = Set.new
|
|
63
63
|
@detached = false
|
|
64
64
|
@context = nil
|
|
65
|
+
@origin = nil
|
|
66
|
+
@world_id = nil
|
|
65
67
|
|
|
66
68
|
@client.on_event('Runtime.bindingCalled', &method(:handle_binding_called))
|
|
67
69
|
end
|
|
68
70
|
|
|
69
|
-
attr_reader :frame, :task_manager
|
|
71
|
+
attr_reader :frame, :task_manager, :origin, :world_id
|
|
70
72
|
|
|
71
73
|
# only used in Puppeteer::WaitTask#initialize
|
|
72
74
|
private def _bound_functions
|
|
@@ -109,6 +111,25 @@ class Puppeteer::IsolaatedWorld
|
|
|
109
111
|
@task_manager.terminate_all(Puppeteer::WaitTask::TerminatedError.new('waitForFunction failed: frame got detached.'))
|
|
110
112
|
end
|
|
111
113
|
|
|
114
|
+
# @rbs origin: String -- Origin for this realm
|
|
115
|
+
# @rbs return: String -- Origin
|
|
116
|
+
def origin=(origin)
|
|
117
|
+
@origin = origin
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# @rbs world_id: String -- World id for this realm
|
|
121
|
+
# @rbs return: String -- World id
|
|
122
|
+
def world_id=(world_id)
|
|
123
|
+
@world_id = world_id
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# @rbs return: Puppeteer::Extension? -- Owning extension for this realm
|
|
127
|
+
def extension
|
|
128
|
+
return nil unless @world_id.is_a?(String)
|
|
129
|
+
|
|
130
|
+
frame.page.browser.extensions[@world_id]
|
|
131
|
+
end
|
|
132
|
+
|
|
112
133
|
def detached?
|
|
113
134
|
@detached
|
|
114
135
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
3
|
+
class Puppeteer::Issue
|
|
4
|
+
# @rbs issue: Hash[String, untyped] -- CDP issue payload
|
|
5
|
+
# @rbs return: void -- No return value
|
|
6
|
+
def initialize(issue)
|
|
7
|
+
@code = issue['code']
|
|
8
|
+
@details = issue['details']
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# @rbs return: String -- Issue code
|
|
12
|
+
attr_reader :code
|
|
13
|
+
|
|
14
|
+
# @rbs return: Hash[String, untyped] -- Issue details payload
|
|
15
|
+
attr_reader :details
|
|
16
|
+
end
|
|
@@ -26,6 +26,7 @@ class Puppeteer::JSCoverage
|
|
|
26
26
|
@enabled = false
|
|
27
27
|
@script_urls = {}
|
|
28
28
|
@script_sources = {}
|
|
29
|
+
@script_parsed_tasks = []
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def start(
|
|
@@ -52,9 +53,10 @@ class Puppeteer::JSCoverage
|
|
|
52
53
|
@enabled = true
|
|
53
54
|
@script_urls.clear
|
|
54
55
|
@script_sources.clear
|
|
56
|
+
@script_parsed_tasks.clear
|
|
55
57
|
@event_listeners = []
|
|
56
58
|
@event_listeners << @client.add_event_listener('Debugger.scriptParsed') do |event|
|
|
57
|
-
Async do
|
|
59
|
+
@script_parsed_tasks << Async do
|
|
58
60
|
Puppeteer::AsyncUtils.future_with_logging { on_script_parsed(event) }.call
|
|
59
61
|
end
|
|
60
62
|
end
|
|
@@ -95,11 +97,22 @@ class Puppeteer::JSCoverage
|
|
|
95
97
|
response = @client.send_message('Debugger.getScriptSource', scriptId: event['scriptId'])
|
|
96
98
|
@script_urls[event['scriptId']] = url
|
|
97
99
|
@script_sources[event['scriptId']] = response['scriptSource']
|
|
100
|
+
rescue Puppeteer::Connection::ProtocolError
|
|
101
|
+
# The page can navigate while we are fetching sources for coverage.
|
|
102
|
+
# This matches upstream behavior that ignores these transient failures.
|
|
103
|
+
nil
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private def drain_script_parsed_tasks
|
|
107
|
+
pending_tasks = @script_parsed_tasks
|
|
108
|
+
@script_parsed_tasks = []
|
|
109
|
+
pending_tasks.each(&:wait)
|
|
98
110
|
end
|
|
99
111
|
|
|
100
112
|
def stop
|
|
101
113
|
raise 'JSCoverage is not enabled' unless @enabled
|
|
102
114
|
@enabled = false
|
|
115
|
+
drain_script_parsed_tasks
|
|
103
116
|
|
|
104
117
|
results = Puppeteer::AsyncUtils.await_promise_all(
|
|
105
118
|
@client.async_send_message('Profiler.takePreciseCoverage'),
|
|
@@ -32,7 +32,12 @@ module Puppeteer::Launcher
|
|
|
32
32
|
@default_viewport = options.key?(:default_viewport) ? options[:default_viewport] : Puppeteer::Viewport.new(width: 800, height: 600)
|
|
33
33
|
@slow_mo = options[:slow_mo] || 0
|
|
34
34
|
@network_enabled = options.fetch(:network_enabled, true)
|
|
35
|
+
@issues_enabled = options.fetch(:issues_enabled, true)
|
|
35
36
|
@protocol_timeout = options[:protocol_timeout]
|
|
37
|
+
@block_list = options[:block_list]
|
|
38
|
+
if @block_list && !@block_list.is_a?(Array)
|
|
39
|
+
raise ArgumentError.new('block_list must be an Array of URL patterns')
|
|
40
|
+
end
|
|
36
41
|
|
|
37
42
|
# only for Puppeteer.connect
|
|
38
43
|
@target_filter = options[:target_filter]
|
|
@@ -46,7 +51,7 @@ module Puppeteer::Launcher
|
|
|
46
51
|
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
|
-
attr_reader :default_viewport, :slow_mo, :target_filter, :is_page_target, :network_enabled, :protocol_timeout
|
|
54
|
+
attr_reader :default_viewport, :slow_mo, :target_filter, :is_page_target, :network_enabled, :issues_enabled, :protocol_timeout, :block_list
|
|
50
55
|
|
|
51
56
|
def ignore_https_errors?
|
|
52
57
|
@ignore_https_errors
|
|
@@ -87,6 +87,8 @@ module Puppeteer::Launcher
|
|
|
87
87
|
ignore_https_errors: @browser_options.ignore_https_errors?,
|
|
88
88
|
default_viewport: @browser_options.default_viewport,
|
|
89
89
|
network_enabled: @browser_options.network_enabled,
|
|
90
|
+
issues_enabled: @browser_options.issues_enabled,
|
|
91
|
+
block_list: @browser_options.block_list,
|
|
90
92
|
process: runner.proc,
|
|
91
93
|
close_callback: -> { runner.close },
|
|
92
94
|
target_filter_callback: nil,
|
|
@@ -129,7 +131,6 @@ module Puppeteer::Launcher
|
|
|
129
131
|
'--disable-component-update',
|
|
130
132
|
'--disable-default-apps',
|
|
131
133
|
'--disable-dev-shm-usage',
|
|
132
|
-
'--disable-extensions',
|
|
133
134
|
# AcceptCHFrame disabled because of crbug.com/1348106.
|
|
134
135
|
'--disable-features=Translate,BackForwardCache,AcceptCHFrame,MediaRouter,OptimizationHints,IPH_ReadingModePageActionLabel,ReadAnythingOmniboxChip',
|
|
135
136
|
'--disable-hang-monitor',
|
|
@@ -177,6 +178,20 @@ module Puppeteer::Launcher
|
|
|
177
178
|
end
|
|
178
179
|
end
|
|
179
180
|
|
|
181
|
+
if chrome_arg_options.enable_extensions
|
|
182
|
+
chrome_arguments << '--enable-unsafe-extension-debugging'
|
|
183
|
+
if chrome_arg_options.enable_extensions.is_a?(Array) && !chrome_arg_options.enable_extensions.empty?
|
|
184
|
+
extension_paths = chrome_arg_options.enable_extensions.map do |path|
|
|
185
|
+
File.expand_path(path)
|
|
186
|
+
end
|
|
187
|
+
joined_paths = extension_paths.join(',')
|
|
188
|
+
chrome_arguments << "--disable-extensions-except=#{joined_paths}"
|
|
189
|
+
chrome_arguments << "--load-extension=#{joined_paths}"
|
|
190
|
+
end
|
|
191
|
+
else
|
|
192
|
+
chrome_arguments << '--disable-extensions'
|
|
193
|
+
end
|
|
194
|
+
|
|
180
195
|
if chrome_arg_options.args.all? { |arg| arg.start_with?('-') }
|
|
181
196
|
chrome_arguments << 'about:blank'
|
|
182
197
|
end
|
|
@@ -31,13 +31,14 @@ module Puppeteer::Launcher
|
|
|
31
31
|
@user_data_dir = options[:user_data_dir]
|
|
32
32
|
@devtools = options[:devtools] || false
|
|
33
33
|
@headless = options[:headless]
|
|
34
|
+
@enable_extensions = options[:enable_extensions] || false
|
|
34
35
|
if @headless.nil?
|
|
35
36
|
@headless = !@devtools
|
|
36
37
|
end
|
|
37
38
|
@debugging_port = options[:debugging_port] || 0
|
|
38
39
|
end
|
|
39
40
|
|
|
40
|
-
attr_reader :args, :user_data_dir, :debugging_port
|
|
41
|
+
attr_reader :args, :user_data_dir, :debugging_port, :enable_extensions
|
|
41
42
|
|
|
42
43
|
def headless?
|
|
43
44
|
@headless
|
|
@@ -54,6 +54,10 @@ class Puppeteer::NetworkManager
|
|
|
54
54
|
}
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
+
def active?
|
|
58
|
+
@offline || @latency != 0 || @download != -1 || @upload != -1
|
|
59
|
+
end
|
|
60
|
+
|
|
57
61
|
def refresh
|
|
58
62
|
update_network_conditions
|
|
59
63
|
end
|
|
@@ -102,7 +106,6 @@ class Puppeteer::NetworkManager
|
|
|
102
106
|
@user_cache_disabled = nil
|
|
103
107
|
@internal_network_condition = InternalNetworkCondition.new(method(:send_to_clients))
|
|
104
108
|
@interception_semaphore = Async::Semaphore.new(1)
|
|
105
|
-
|
|
106
109
|
add_client(@client)
|
|
107
110
|
end
|
|
108
111
|
|
|
@@ -138,10 +141,8 @@ class Puppeteer::NetworkManager
|
|
|
138
141
|
"#<Puppeteer::HTTPRequest #{values.join(' ')}>"
|
|
139
142
|
end
|
|
140
143
|
|
|
141
|
-
private def apply_to_clients
|
|
142
|
-
@clients.each
|
|
143
|
-
yield client
|
|
144
|
-
end
|
|
144
|
+
private def apply_to_clients(&block)
|
|
145
|
+
@clients.each(&block)
|
|
145
146
|
end
|
|
146
147
|
|
|
147
148
|
private def ignore_client_error?(error)
|
|
@@ -192,7 +193,7 @@ class Puppeteer::NetworkManager
|
|
|
192
193
|
if @protocol_request_interception_enabled
|
|
193
194
|
safe_send_message(client, 'Fetch.enable',
|
|
194
195
|
handleAuthRequests: true,
|
|
195
|
-
patterns: [{ urlPattern: '*' }]
|
|
196
|
+
patterns: [{ urlPattern: '*' }]
|
|
196
197
|
)
|
|
197
198
|
else
|
|
198
199
|
safe_send_message(client, 'Fetch.disable')
|
|
@@ -208,7 +209,9 @@ class Puppeteer::NetworkManager
|
|
|
208
209
|
apply_user_agent(client)
|
|
209
210
|
apply_protocol_cache_disabled(client)
|
|
210
211
|
apply_protocol_request_interception(client)
|
|
211
|
-
|
|
212
|
+
if @internal_network_condition.active?
|
|
213
|
+
safe_send_message(client, 'Network.emulateNetworkConditions', @internal_network_condition.params)
|
|
214
|
+
end
|
|
212
215
|
end
|
|
213
216
|
|
|
214
217
|
# @param username [String|NilClass]
|
|
@@ -343,11 +346,9 @@ class Puppeteer::NetworkManager
|
|
|
343
346
|
private def dispatch_intercepted_request(event, fetch_request_id, client:)
|
|
344
347
|
if Async::Task.current?
|
|
345
348
|
Async do
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
debug_puts(err)
|
|
350
|
-
end
|
|
349
|
+
handle_request(event, fetch_request_id, client: client)
|
|
350
|
+
rescue => err
|
|
351
|
+
debug_puts(err)
|
|
351
352
|
end
|
|
352
353
|
else
|
|
353
354
|
handle_request(event, fetch_request_id, client: client)
|
|
@@ -365,6 +366,7 @@ class Puppeteer::NetworkManager
|
|
|
365
366
|
if existing_request &&
|
|
366
367
|
existing_request.url == event_url &&
|
|
367
368
|
existing_request.method == event.dig('request', 'method')
|
|
369
|
+
|
|
368
370
|
if_present(@network_event_manager.request_extra_info(network_request_id).shift) do |extra_info|
|
|
369
371
|
existing_request.update_headers(extra_info['headers'])
|
|
370
372
|
end
|
|
@@ -474,12 +476,10 @@ class Puppeteer::NetworkManager
|
|
|
474
476
|
end
|
|
475
477
|
end
|
|
476
478
|
|
|
477
|
-
private def with_interception_lock
|
|
479
|
+
private def with_interception_lock(&block)
|
|
478
480
|
return yield unless Async::Task.current?
|
|
479
481
|
|
|
480
|
-
@interception_semaphore.acquire
|
|
481
|
-
yield
|
|
482
|
-
end
|
|
482
|
+
@interception_semaphore.acquire(&block)
|
|
483
483
|
end
|
|
484
484
|
|
|
485
485
|
private def handle_request_without_network_instrumentation(event, client)
|
data/lib/puppeteer/page.rb
CHANGED
|
@@ -359,6 +359,22 @@ class Puppeteer::Page
|
|
|
359
359
|
@target.browser_context
|
|
360
360
|
end
|
|
361
361
|
|
|
362
|
+
# @rbs return: bool -- Whether DevTools is attached to this page
|
|
363
|
+
def has_devtools
|
|
364
|
+
!!browser._has_devtools_target(@target.target_id)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
# @rbs extension: Puppeteer::Extension -- Extension to trigger
|
|
368
|
+
# @rbs return: void -- No return value
|
|
369
|
+
def trigger_extension_action(extension)
|
|
370
|
+
extension.trigger_action(self)
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
# @rbs return: Array[untyped] -- Extension execution realms on the main frame
|
|
374
|
+
def extension_realms
|
|
375
|
+
main_frame.extension_realms
|
|
376
|
+
end
|
|
377
|
+
|
|
362
378
|
class TargetCrashedError < Puppeteer::Error; end
|
|
363
379
|
|
|
364
380
|
private def handle_target_crashed
|
|
@@ -854,10 +870,7 @@ class Puppeteer::Page
|
|
|
854
870
|
end
|
|
855
871
|
|
|
856
872
|
private def add_console_message(type, args, stack_trace)
|
|
857
|
-
text_tokens = args.map
|
|
858
|
-
value = arg.remote_object.value
|
|
859
|
-
value.nil? ? arg.to_s : value
|
|
860
|
-
end
|
|
873
|
+
text_tokens = args.map { |arg| console_value_from_js_handle(arg) }
|
|
861
874
|
|
|
862
875
|
stack_trace_locations =
|
|
863
876
|
if stack_trace && stack_trace['callFrames']
|
|
@@ -875,6 +888,25 @@ class Puppeteer::Page
|
|
|
875
888
|
emit_event(PageEmittedEvents::Console, console_message)
|
|
876
889
|
end
|
|
877
890
|
|
|
891
|
+
private def console_value_from_js_handle(handle)
|
|
892
|
+
remote_object = handle.remote_object
|
|
893
|
+
return remote_object.value unless remote_object.object_id?
|
|
894
|
+
|
|
895
|
+
value_from_remote_object_reference(remote_object)
|
|
896
|
+
end
|
|
897
|
+
|
|
898
|
+
private def value_from_remote_object_reference(remote_object)
|
|
899
|
+
description = remote_object.description.to_s
|
|
900
|
+
if remote_object.sub_type == 'error' && !description.empty?
|
|
901
|
+
newline_index = description.index("\n")
|
|
902
|
+
return newline_index ? description[0...newline_index] : description
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
type = remote_object.sub_type || remote_object.type
|
|
906
|
+
class_name = remote_object.class_name || remote_object.description || 'Object'
|
|
907
|
+
"[#{type} #{class_name}]"
|
|
908
|
+
end
|
|
909
|
+
|
|
878
910
|
private def handle_dialog_opening(event)
|
|
879
911
|
dialog_type = event['type']
|
|
880
912
|
unless %w(alert confirm prompt beforeunload).include?(dialog_type)
|
data/lib/puppeteer/puppeteer.rb
CHANGED
|
@@ -31,6 +31,9 @@ class Puppeteer::Puppeteer
|
|
|
31
31
|
# @rbs headless: bool? -- Run browser in headless mode
|
|
32
32
|
# @rbs ignore_https_errors: bool? -- Ignore HTTPS errors
|
|
33
33
|
# @rbs network_enabled: bool? -- Enable network domain
|
|
34
|
+
# @rbs issues_enabled: bool? -- Enable issues domain
|
|
35
|
+
# @rbs block_list: Array[String]? -- URL block list patterns
|
|
36
|
+
# @rbs enable_extensions: (bool | Array[String])? -- Enable extensions or load unpacked extensions
|
|
34
37
|
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
|
|
35
38
|
# @rbs slow_mo: Integer? -- Delay between operations (ms)
|
|
36
39
|
# @rbs protocol_timeout: Integer? -- CDP protocol timeout in milliseconds
|
|
@@ -56,6 +59,9 @@ class Puppeteer::Puppeteer
|
|
|
56
59
|
headless: nil,
|
|
57
60
|
ignore_https_errors: nil,
|
|
58
61
|
network_enabled: true,
|
|
62
|
+
issues_enabled: true,
|
|
63
|
+
block_list: nil,
|
|
64
|
+
enable_extensions: false,
|
|
59
65
|
default_viewport: NoViewport.new,
|
|
60
66
|
slow_mo: nil,
|
|
61
67
|
protocol_timeout: nil,
|
|
@@ -85,6 +91,9 @@ class Puppeteer::Puppeteer
|
|
|
85
91
|
headless: headless,
|
|
86
92
|
ignore_https_errors: ignore_https_errors,
|
|
87
93
|
network_enabled: network_enabled,
|
|
94
|
+
issues_enabled: issues_enabled,
|
|
95
|
+
block_list: block_list,
|
|
96
|
+
enable_extensions: enable_extensions,
|
|
88
97
|
default_viewport: default_viewport,
|
|
89
98
|
slow_mo: slow_mo,
|
|
90
99
|
protocol_timeout: protocol_timeout,
|
|
@@ -133,6 +142,8 @@ class Puppeteer::Puppeteer
|
|
|
133
142
|
# @rbs transport: Puppeteer::WebSocketTransport? -- Pre-connected transport
|
|
134
143
|
# @rbs ignore_https_errors: bool? -- Ignore HTTPS errors
|
|
135
144
|
# @rbs network_enabled: bool? -- Enable network domain
|
|
145
|
+
# @rbs issues_enabled: bool? -- Enable issues domain
|
|
146
|
+
# @rbs block_list: Array[String]? -- URL block list patterns
|
|
136
147
|
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
|
|
137
148
|
# @rbs slow_mo: Integer? -- Delay between operations (ms)
|
|
138
149
|
# @rbs protocol_timeout: Integer? -- CDP protocol timeout in milliseconds
|
|
@@ -144,6 +155,8 @@ class Puppeteer::Puppeteer
|
|
|
144
155
|
transport: nil,
|
|
145
156
|
ignore_https_errors: nil,
|
|
146
157
|
network_enabled: true,
|
|
158
|
+
issues_enabled: true,
|
|
159
|
+
block_list: nil,
|
|
147
160
|
default_viewport: nil,
|
|
148
161
|
slow_mo: nil,
|
|
149
162
|
protocol_timeout: nil,
|
|
@@ -155,6 +168,8 @@ class Puppeteer::Puppeteer
|
|
|
155
168
|
transport: transport,
|
|
156
169
|
ignore_https_errors: ignore_https_errors,
|
|
157
170
|
network_enabled: network_enabled,
|
|
171
|
+
issues_enabled: issues_enabled,
|
|
172
|
+
block_list: block_list,
|
|
158
173
|
default_viewport: default_viewport,
|
|
159
174
|
slow_mo: slow_mo,
|
|
160
175
|
protocol_timeout: protocol_timeout,
|
|
@@ -16,12 +16,13 @@ class Puppeteer::RemoteObject
|
|
|
16
16
|
@object_id = payload['objectId']
|
|
17
17
|
@type = payload['type']
|
|
18
18
|
@sub_type = payload['subtype']
|
|
19
|
+
@class_name = payload['className']
|
|
19
20
|
@unserializable_value = payload['unserializableValue']
|
|
20
21
|
@value = payload['value']
|
|
21
22
|
@description = payload['description']
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
attr_reader :sub_type, :type, :description
|
|
25
|
+
attr_reader :sub_type, :type, :class_name, :description
|
|
25
26
|
|
|
26
27
|
# @rbs return: bool
|
|
27
28
|
def object_id?
|
data/lib/puppeteer/target.rb
CHANGED
|
@@ -142,6 +142,23 @@ class Puppeteer::Target
|
|
|
142
142
|
@page
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
# @return [Puppeteer::Page]
|
|
146
|
+
def as_page
|
|
147
|
+
existing_page = page
|
|
148
|
+
return existing_page if existing_page
|
|
149
|
+
return @as_page if @as_page
|
|
150
|
+
|
|
151
|
+
client = @session || @session_factory.call(false)
|
|
152
|
+
client.wait_for_ready if client.respond_to?(:wait_for_ready)
|
|
153
|
+
@as_page = Puppeteer::Page.create(
|
|
154
|
+
client,
|
|
155
|
+
self,
|
|
156
|
+
@ignore_https_errors,
|
|
157
|
+
nil,
|
|
158
|
+
network_enabled: @network_enabled,
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
|
|
145
162
|
# @return [Puppeteer::CdpWebWorker|nil]
|
|
146
163
|
def worker
|
|
147
164
|
return nil unless ['service_worker', 'shared_worker'].include?(@target_info.type)
|
data/lib/puppeteer/version.rb
CHANGED
data/lib/puppeteer.rb
CHANGED
|
@@ -23,6 +23,7 @@ require 'puppeteer/env'
|
|
|
23
23
|
require 'puppeteer/events'
|
|
24
24
|
require 'puppeteer/errors'
|
|
25
25
|
require 'puppeteer/geolocation'
|
|
26
|
+
require 'puppeteer/issue'
|
|
26
27
|
require 'puppeteer/viewport'
|
|
27
28
|
|
|
28
29
|
# Modules
|
|
@@ -49,6 +50,7 @@ require 'puppeteer/custom_query_handler'
|
|
|
49
50
|
require 'puppeteer/devices'
|
|
50
51
|
require 'puppeteer/dialog'
|
|
51
52
|
require 'puppeteer/emulation_manager'
|
|
53
|
+
require 'puppeteer/extension'
|
|
52
54
|
require 'puppeteer/exception_details'
|
|
53
55
|
require 'puppeteer/executable_path_finder'
|
|
54
56
|
require 'puppeteer/execution_context'
|
data/sig/_supplementary.rbs
CHANGED
|
@@ -189,6 +189,10 @@ class Puppeteer::FrameManager
|
|
|
189
189
|
def remove_frame_recursively: (Puppeteer::Frame frame) -> void
|
|
190
190
|
def referrer_policy_to_protocol: (String? referrer_policy) -> String?
|
|
191
191
|
def assert_no_legacy_navigation_options: (wait_until: (String | Array[String] | nil)) -> void
|
|
192
|
+
def extension_origin?: (untyped origin) -> bool
|
|
193
|
+
def extract_extension_id: (untyped origin) -> String?
|
|
194
|
+
def maybe_setup_block_list: (Puppeteer::CDPSession client) -> void
|
|
195
|
+
def blocked_url?: (String? url) -> bool
|
|
192
196
|
end
|
|
193
197
|
|
|
194
198
|
class Puppeteer::NetworkCondition
|
|
@@ -226,6 +230,7 @@ class Puppeteer::Target
|
|
|
226
230
|
def browser: () -> Puppeteer::Browser
|
|
227
231
|
def browser_context: () -> Puppeteer::BrowserContext
|
|
228
232
|
def opener: () -> Puppeteer::Target?
|
|
233
|
+
def as_page: () -> Puppeteer::Page?
|
|
229
234
|
def handle_target_info_changed: (Puppeteer::Target::TargetInfo target_info) -> void
|
|
230
235
|
|
|
231
236
|
private
|
data/sig/puppeteer/browser.rbs
CHANGED
|
@@ -13,12 +13,14 @@ class Puppeteer::Browser
|
|
|
13
13
|
# @rbs ignore_https_errors: bool -- Ignore HTTPS errors
|
|
14
14
|
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
|
|
15
15
|
# @rbs network_enabled: bool -- Whether network events are enabled
|
|
16
|
+
# @rbs issues_enabled: bool -- Whether issues events are enabled
|
|
17
|
+
# @rbs block_list: Array[String]? -- URL block list patterns
|
|
16
18
|
# @rbs process: Puppeteer::BrowserRunner::BrowserProcess? -- Browser process handle
|
|
17
19
|
# @rbs close_callback: Proc -- Close callback
|
|
18
20
|
# @rbs target_filter_callback: Proc? -- Target filter callback
|
|
19
21
|
# @rbs is_page_target_callback: Proc? -- Page target predicate
|
|
20
22
|
# @rbs return: Puppeteer::Browser -- Browser instance
|
|
21
|
-
def self.create: (product: String?, connection: Puppeteer::Connection, context_ids: Array[String], ignore_https_errors: bool, default_viewport: Puppeteer::Viewport?, process: Puppeteer::BrowserRunner::BrowserProcess?, close_callback: Proc, target_filter_callback: Proc?, is_page_target_callback: Proc?, ?network_enabled: bool) -> Puppeteer::Browser
|
|
23
|
+
def self.create: (product: String?, connection: Puppeteer::Connection, context_ids: Array[String], ignore_https_errors: bool, default_viewport: Puppeteer::Viewport?, process: Puppeteer::BrowserRunner::BrowserProcess?, close_callback: Proc, target_filter_callback: Proc?, is_page_target_callback: Proc?, ?network_enabled: bool, ?issues_enabled: bool, ?block_list: Array[String]?) -> Puppeteer::Browser
|
|
22
24
|
|
|
23
25
|
# @rbs product: String? -- Browser product (chrome only)
|
|
24
26
|
# @rbs connection: Puppeteer::Connection -- CDP connection
|
|
@@ -26,12 +28,14 @@ class Puppeteer::Browser
|
|
|
26
28
|
# @rbs ignore_https_errors: bool -- Ignore HTTPS errors
|
|
27
29
|
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport
|
|
28
30
|
# @rbs network_enabled: bool -- Whether network events are enabled
|
|
31
|
+
# @rbs issues_enabled: bool -- Whether issues events are enabled
|
|
32
|
+
# @rbs block_list: Array[String]? -- URL block list patterns
|
|
29
33
|
# @rbs process: Puppeteer::BrowserRunner::BrowserProcess? -- Browser process handle
|
|
30
34
|
# @rbs close_callback: Proc -- Close callback
|
|
31
35
|
# @rbs target_filter_callback: Proc? -- Target filter callback
|
|
32
36
|
# @rbs is_page_target_callback: Proc? -- Page target predicate
|
|
33
37
|
# @rbs return: void -- No return value
|
|
34
|
-
def initialize: (product: String?, connection: Puppeteer::Connection, context_ids: Array[String], ignore_https_errors: bool, default_viewport: Puppeteer::Viewport?, process: Puppeteer::BrowserRunner::BrowserProcess?, close_callback: Proc, target_filter_callback: Proc?, is_page_target_callback: Proc?, ?network_enabled: bool) -> void
|
|
38
|
+
def initialize: (product: String?, connection: Puppeteer::Connection, context_ids: Array[String], ignore_https_errors: bool, default_viewport: Puppeteer::Viewport?, process: Puppeteer::BrowserRunner::BrowserProcess?, close_callback: Proc, target_filter_callback: Proc?, is_page_target_callback: Proc?, ?network_enabled: bool, ?issues_enabled: bool, ?block_list: Array[String]?) -> void
|
|
35
39
|
|
|
36
40
|
private def default_target_filter_callback: (untyped target_info) -> untyped
|
|
37
41
|
|
|
@@ -58,6 +62,8 @@ class Puppeteer::Browser
|
|
|
58
62
|
|
|
59
63
|
private def target_manager: () -> untyped
|
|
60
64
|
|
|
65
|
+
private def connection: () -> untyped
|
|
66
|
+
|
|
61
67
|
# @rbs return: Puppeteer::BrowserContext -- New incognito browser context
|
|
62
68
|
def create_incognito_browser_context: () -> Puppeteer::BrowserContext
|
|
63
69
|
|
|
@@ -140,6 +146,27 @@ class Puppeteer::Browser
|
|
|
140
146
|
# @rbs return: String -- Browser user agent string
|
|
141
147
|
def user_agent: () -> String
|
|
142
148
|
|
|
149
|
+
# @rbs page_target_id: String -- Page target id
|
|
150
|
+
# @rbs return: String? -- DevTools target id for page
|
|
151
|
+
def _has_devtools_target: (String page_target_id) -> String?
|
|
152
|
+
|
|
153
|
+
# @rbs path: String -- Extension path
|
|
154
|
+
# @rbs return: String -- Installed extension id
|
|
155
|
+
def install_extension: (String path) -> String
|
|
156
|
+
|
|
157
|
+
# @rbs extension_id: String -- Extension id
|
|
158
|
+
# @rbs return: void -- No return value
|
|
159
|
+
def uninstall_extension: (String extension_id) -> void
|
|
160
|
+
|
|
161
|
+
# @rbs return: Hash[String, Puppeteer::Extension] -- Installed extensions
|
|
162
|
+
def extensions: () -> Hash[String, Puppeteer::Extension]
|
|
163
|
+
|
|
164
|
+
# @rbs return: bool -- Whether issue events are enabled
|
|
165
|
+
def issues_enabled?: () -> bool
|
|
166
|
+
|
|
167
|
+
# @rbs return: Array[String]? -- URL block list patterns
|
|
168
|
+
def block_list: () -> Array[String]?
|
|
169
|
+
|
|
143
170
|
# @rbs return: void -- No return value
|
|
144
171
|
def close: () -> void
|
|
145
172
|
|
|
@@ -216,6 +216,11 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
|
|
|
216
216
|
# @rbs return: void -- No return value
|
|
217
217
|
def upload_file: (*untyped file_paths) -> void
|
|
218
218
|
|
|
219
|
+
# @rbs credit_card: Hash[Symbol | String, untyped]? -- Credit card autofill payload
|
|
220
|
+
# @rbs address: Hash[Symbol | String, untyped]? -- Address autofill payload
|
|
221
|
+
# @rbs return: void -- No return value
|
|
222
|
+
def autofill: (?credit_card: Hash[Symbol | String, untyped]?, ?address: Hash[Symbol | String, untyped]?) -> void
|
|
223
|
+
|
|
219
224
|
# @rbs block: Proc? -- Optional block for Object#tap usage
|
|
220
225
|
# @rbs return: Puppeteer::ElementHandle | nil -- Element handle or nil
|
|
221
226
|
def tap: () ?{ (?) -> untyped } -> (Puppeteer::ElementHandle | nil)
|