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
data/lib/puppeteer/page.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
1
3
|
require 'base64'
|
|
2
4
|
require 'json'
|
|
5
|
+
require 'objspace'
|
|
3
6
|
require "stringio"
|
|
4
7
|
|
|
5
8
|
require_relative './page/metrics'
|
|
@@ -13,13 +16,14 @@ class Puppeteer::Page
|
|
|
13
16
|
include Puppeteer::IfPresent
|
|
14
17
|
using Puppeteer::DefineAsyncMethod
|
|
15
18
|
|
|
16
|
-
# @
|
|
17
|
-
# @
|
|
18
|
-
# @
|
|
19
|
-
# @
|
|
20
|
-
# @
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
# @rbs client: Puppeteer::CDPSession -- CDP session for the page
|
|
20
|
+
# @rbs target: Puppeteer::Target -- Target associated with the page
|
|
21
|
+
# @rbs ignore_https_errors: bool -- Ignore HTTPS errors
|
|
22
|
+
# @rbs default_viewport: Puppeteer::Viewport? -- Default viewport for new pages
|
|
23
|
+
# @rbs network_enabled: bool -- Whether network events are enabled
|
|
24
|
+
# @rbs return: Puppeteer::Page -- Created page instance
|
|
25
|
+
def self.create(client, target, ignore_https_errors, default_viewport, network_enabled: true)
|
|
26
|
+
page = Puppeteer::Page.new(client, target, ignore_https_errors, network_enabled: network_enabled)
|
|
23
27
|
page.init
|
|
24
28
|
if default_viewport
|
|
25
29
|
page.viewport = default_viewport
|
|
@@ -27,30 +31,40 @@ class Puppeteer::Page
|
|
|
27
31
|
page
|
|
28
32
|
end
|
|
29
33
|
|
|
30
|
-
# @
|
|
31
|
-
# @
|
|
32
|
-
# @
|
|
33
|
-
|
|
34
|
+
# @rbs client: Puppeteer::CDPSession -- CDP session for the page
|
|
35
|
+
# @rbs target: Puppeteer::Target -- Target associated with the page
|
|
36
|
+
# @rbs ignore_https_errors: bool -- Ignore HTTPS errors
|
|
37
|
+
# @rbs network_enabled: bool -- Whether network events are enabled
|
|
38
|
+
# @rbs return: void -- No return value
|
|
39
|
+
def initialize(client, target, ignore_https_errors, network_enabled: true)
|
|
34
40
|
@closed = false
|
|
35
41
|
@client = client
|
|
36
42
|
@target = target
|
|
43
|
+
@tab_id = nil
|
|
37
44
|
@keyboard = Puppeteer::Keyboard.new(client)
|
|
38
45
|
@mouse = Puppeteer::Mouse.new(client, @keyboard)
|
|
39
46
|
@timeout_settings = Puppeteer::TimeoutSettings.new
|
|
40
47
|
@touchscreen = Puppeteer::TouchScreen.new(client, @keyboard)
|
|
41
48
|
# @accessibility = Accessibility.new(client)
|
|
42
|
-
@frame_manager = Puppeteer::FrameManager.new(client, self, ignore_https_errors, @timeout_settings)
|
|
49
|
+
@frame_manager = Puppeteer::FrameManager.new(client, self, ignore_https_errors, @timeout_settings, network_enabled: network_enabled)
|
|
43
50
|
@emulation_manager = Puppeteer::EmulationManager.new(client)
|
|
44
51
|
@tracing = Puppeteer::Tracing.new(client)
|
|
45
52
|
@page_bindings = {}
|
|
53
|
+
@page_binding_ids = {}
|
|
46
54
|
@coverage = Puppeteer::Coverage.new(client)
|
|
47
55
|
@javascript_enabled = true
|
|
48
56
|
@screenshot_task_queue = ScreenshotTaskQueue.new
|
|
57
|
+
@inflight_requests = Set.new
|
|
58
|
+
@request_intercepted_listener_map = ObjectSpace::WeakMap.new
|
|
59
|
+
@attached_sessions = Set.new
|
|
49
60
|
|
|
50
61
|
@workers = {}
|
|
51
62
|
@user_drag_interception_enabled = false
|
|
63
|
+
@service_worker_bypassed = false
|
|
52
64
|
|
|
53
|
-
@
|
|
65
|
+
@attached_session_listener_id = @client.add_event_listener(CDPSessionEmittedEvents::Ready) do |session|
|
|
66
|
+
handle_attached_to_session(session)
|
|
67
|
+
end
|
|
54
68
|
@target_gone_listener_id = @target.target_manager.add_event_listener(
|
|
55
69
|
TargetManagerEmittedEvents::TargetGone,
|
|
56
70
|
&method(:handle_detached_from_target)
|
|
@@ -68,15 +82,21 @@ class Puppeteer::Page
|
|
|
68
82
|
|
|
69
83
|
network_manager = @frame_manager.network_manager
|
|
70
84
|
network_manager.on_event(NetworkManagerEmittedEvents::Request) do |event|
|
|
85
|
+
@inflight_requests.add(event)
|
|
71
86
|
emit_event(PageEmittedEvents::Request, event)
|
|
72
87
|
end
|
|
73
88
|
network_manager.on_event(NetworkManagerEmittedEvents::Response) do |event|
|
|
74
89
|
emit_event(PageEmittedEvents::Response, event)
|
|
75
90
|
end
|
|
91
|
+
network_manager.on_event(NetworkManagerEmittedEvents::RequestServedFromCache) do |event|
|
|
92
|
+
emit_event(PageEmittedEvents::RequestServedFromCache, event)
|
|
93
|
+
end
|
|
76
94
|
network_manager.on_event(NetworkManagerEmittedEvents::RequestFailed) do |event|
|
|
95
|
+
@inflight_requests.delete(event)
|
|
77
96
|
emit_event(PageEmittedEvents::RequestFailed, event)
|
|
78
97
|
end
|
|
79
98
|
network_manager.on_event(NetworkManagerEmittedEvents::RequestFinished) do |event|
|
|
99
|
+
@inflight_requests.delete(event)
|
|
80
100
|
emit_event(PageEmittedEvents::RequestFinished, event)
|
|
81
101
|
end
|
|
82
102
|
@file_chooser_interception_is_disabled = false
|
|
@@ -112,8 +132,9 @@ class Puppeteer::Page
|
|
|
112
132
|
@client.on_event('Page.fileChooserOpened') do |event|
|
|
113
133
|
handle_file_chooser(event)
|
|
114
134
|
end
|
|
115
|
-
|
|
116
|
-
@target.
|
|
135
|
+
Async do
|
|
136
|
+
@target.is_closed_promise.wait
|
|
137
|
+
@client.remove_event_listener(@attached_session_listener_id)
|
|
117
138
|
@target.target_manager.remove_event_listener(@target_gone_listener_id)
|
|
118
139
|
|
|
119
140
|
emit_event(PageEmittedEvents::Close)
|
|
@@ -130,63 +151,137 @@ class Puppeteer::Page
|
|
|
130
151
|
emit_event(PageEmittedEvents::WorkerDestroyed, worker)
|
|
131
152
|
end
|
|
132
153
|
|
|
133
|
-
private def
|
|
154
|
+
private def handle_attached_to_session(session)
|
|
155
|
+
return if @attached_sessions.include?(session)
|
|
156
|
+
@attached_sessions << session
|
|
157
|
+
session.on(CDPSessionEmittedEvents::Ready) do |child_session|
|
|
158
|
+
handle_attached_to_session(child_session)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
target = session.target
|
|
162
|
+
return unless target
|
|
163
|
+
handle_attached_to_target(target)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private def handle_attached_to_target(target)
|
|
134
167
|
@frame_manager.handle_attached_to_target(target)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# const worker = new WebWorker(
|
|
139
|
-
# session,
|
|
140
|
-
# createdTarget.url(),
|
|
141
|
-
# this.#addConsoleMessage.bind(this),
|
|
142
|
-
# this.#handleException.bind(this)
|
|
143
|
-
# );
|
|
144
|
-
# this.#workers.set(session.id(), worker);
|
|
145
|
-
# this.emit(PageEmittedEvents.WorkerCreated, worker);
|
|
168
|
+
session = target.session
|
|
169
|
+
if session && target.raw_type != 'worker'
|
|
170
|
+
@frame_manager.network_manager.add_client(session)
|
|
146
171
|
end
|
|
172
|
+
if target.raw_type == 'worker'
|
|
173
|
+
return unless session
|
|
147
174
|
|
|
148
|
-
|
|
149
|
-
|
|
175
|
+
console_api_called = lambda do |world, event|
|
|
176
|
+
values = event['args'].map do |arg|
|
|
177
|
+
remote_object = Puppeteer::RemoteObject.new(arg)
|
|
178
|
+
Puppeteer::JSHandle.create(context: world.execution_context, remote_object: remote_object)
|
|
179
|
+
end
|
|
180
|
+
add_console_message(event['type'], values, event['stackTrace'])
|
|
181
|
+
end
|
|
182
|
+
exception_thrown = method(:handle_exception)
|
|
183
|
+
|
|
184
|
+
worker = Puppeteer::CdpWebWorker.new(
|
|
185
|
+
session,
|
|
186
|
+
target.url,
|
|
187
|
+
target.target_id,
|
|
188
|
+
target.raw_type,
|
|
189
|
+
console_api_called,
|
|
190
|
+
exception_thrown,
|
|
191
|
+
network_manager: @frame_manager.network_manager,
|
|
192
|
+
)
|
|
193
|
+
@workers[session.id] = worker
|
|
194
|
+
emit_event(PageEmittedEvents::WorkerCreated, worker)
|
|
150
195
|
end
|
|
196
|
+
|
|
151
197
|
end
|
|
152
198
|
|
|
199
|
+
# @rbs return: Array[untyped] -- Initialization results
|
|
153
200
|
def init
|
|
154
|
-
|
|
201
|
+
Puppeteer::AsyncUtils.await_promise_all(
|
|
155
202
|
@frame_manager.async_init(@target.target_id),
|
|
156
203
|
@client.async_send_message('Performance.enable'),
|
|
157
204
|
@client.async_send_message('Log.enable'),
|
|
158
205
|
)
|
|
159
206
|
end
|
|
160
207
|
|
|
208
|
+
# @rbs return: bool -- Whether drag interception is enabled
|
|
161
209
|
def drag_interception_enabled?
|
|
162
210
|
@user_drag_interception_enabled
|
|
163
211
|
end
|
|
164
212
|
alias_method :drag_interception_enabled, :drag_interception_enabled?
|
|
165
213
|
|
|
166
|
-
# @
|
|
214
|
+
# @rbs event_name: (String | Symbol) -- Page event name
|
|
215
|
+
# @rbs &block: ^(untyped) -> void -- Event handler
|
|
216
|
+
# @rbs return: String -- Listener ID
|
|
167
217
|
def on(event_name, &block)
|
|
168
218
|
unless PageEmittedEvents.values.include?(event_name.to_s)
|
|
169
219
|
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{PageEmittedEvents.values.to_a.join(", ")}")
|
|
170
220
|
end
|
|
171
221
|
|
|
172
222
|
if event_name.to_s == 'request'
|
|
173
|
-
|
|
174
|
-
|
|
223
|
+
wrapped = ->(req) { req.enqueue_intercept_action(-> { block.call(req) }) }
|
|
224
|
+
if (listeners = @request_intercepted_listener_map[block])
|
|
225
|
+
listeners << wrapped
|
|
226
|
+
else
|
|
227
|
+
@request_intercepted_listener_map[block] = [wrapped]
|
|
175
228
|
end
|
|
229
|
+
super('request', &wrapped)
|
|
230
|
+
else
|
|
231
|
+
super(event_name.to_s, &block)
|
|
176
232
|
end
|
|
177
|
-
|
|
178
|
-
super(event_name.to_s, &block)
|
|
179
233
|
end
|
|
180
234
|
|
|
181
|
-
# @
|
|
235
|
+
# @rbs event_name: (String | Symbol) -- Page event name
|
|
236
|
+
# @rbs &block: ^(untyped) -> void -- Event handler
|
|
237
|
+
# @rbs return: String -- Listener ID
|
|
182
238
|
def once(event_name, &block)
|
|
183
239
|
unless PageEmittedEvents.values.include?(event_name.to_s)
|
|
184
240
|
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{PageEmittedEvents.values.to_a.join(", ")}")
|
|
185
241
|
end
|
|
186
242
|
|
|
187
|
-
|
|
243
|
+
if event_name.to_s == 'request'
|
|
244
|
+
wrapped = ->(req) { req.enqueue_intercept_action(-> { block.call(req) }) }
|
|
245
|
+
if (listeners = @request_intercepted_listener_map[block])
|
|
246
|
+
listeners << wrapped
|
|
247
|
+
else
|
|
248
|
+
@request_intercepted_listener_map[block] = [wrapped]
|
|
249
|
+
end
|
|
250
|
+
super('request', &wrapped)
|
|
251
|
+
else
|
|
252
|
+
super(event_name.to_s, &block)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# @rbs event_name_or_id: (String | Symbol) -- Page event name or listener ID
|
|
257
|
+
# @rbs listener: Proc? -- Event handler to remove
|
|
258
|
+
# @rbs return: void -- No return value
|
|
259
|
+
def off(event_name_or_id, listener = nil, &block)
|
|
260
|
+
listener ||= block
|
|
261
|
+
if listener && PageEmittedEvents.values.include?(event_name_or_id.to_s)
|
|
262
|
+
event_name = event_name_or_id.to_s
|
|
263
|
+
if event_name == 'request'
|
|
264
|
+
listeners = @request_intercepted_listener_map[listener]
|
|
265
|
+
wrapped = listeners&.shift
|
|
266
|
+
return unless wrapped
|
|
267
|
+
if listeners.empty?
|
|
268
|
+
if @request_intercepted_listener_map.respond_to?(:delete)
|
|
269
|
+
@request_intercepted_listener_map.delete(listener)
|
|
270
|
+
else
|
|
271
|
+
@request_intercepted_listener_map[listener] = nil
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
super(event_name, wrapped)
|
|
275
|
+
else
|
|
276
|
+
super(event_name, listener)
|
|
277
|
+
end
|
|
278
|
+
else
|
|
279
|
+
super(event_name_or_id)
|
|
280
|
+
end
|
|
188
281
|
end
|
|
189
282
|
|
|
283
|
+
# @rbs event: Hash[String, untyped] -- File chooser event payload
|
|
284
|
+
# @rbs return: void -- No return value
|
|
190
285
|
def handle_file_chooser(event)
|
|
191
286
|
return if @file_chooser_interceptors.empty?
|
|
192
287
|
|
|
@@ -196,33 +291,29 @@ class Puppeteer::Page
|
|
|
196
291
|
@file_chooser_interceptors.clear
|
|
197
292
|
file_chooser = Puppeteer::FileChooser.new(element, event)
|
|
198
293
|
interceptors.each do |promise|
|
|
199
|
-
promise.
|
|
294
|
+
promise.resolve(file_chooser)
|
|
200
295
|
end
|
|
201
296
|
end
|
|
202
297
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
super("waiting for filechooser failed: timeout #{timeout}ms exceeded")
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
# @param timeout [Integer]
|
|
210
|
-
# @return [Puppeteer::FileChooser]
|
|
298
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
299
|
+
# @rbs return: Puppeteer::FileChooser -- File chooser handle
|
|
211
300
|
def wait_for_file_chooser(timeout: nil)
|
|
212
301
|
if @file_chooser_interceptors.empty?
|
|
213
302
|
@client.send_message('Page.setInterceptFileChooserDialog', enabled: true)
|
|
214
303
|
end
|
|
215
304
|
|
|
216
305
|
option_timeout = timeout || @timeout_settings.timeout
|
|
217
|
-
promise =
|
|
306
|
+
promise = Async::Promise.new
|
|
218
307
|
@file_chooser_interceptors << promise
|
|
219
308
|
|
|
220
309
|
begin
|
|
221
|
-
|
|
222
|
-
promise.
|
|
310
|
+
if option_timeout == 0
|
|
311
|
+
promise.wait
|
|
312
|
+
else
|
|
313
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, promise).wait
|
|
223
314
|
end
|
|
224
|
-
rescue
|
|
225
|
-
raise
|
|
315
|
+
rescue Async::TimeoutError
|
|
316
|
+
raise Puppeteer::TimeoutError.new("Waiting for `FileChooser` failed: #{option_timeout}ms exceeded")
|
|
226
317
|
ensure
|
|
227
318
|
@file_chooser_interceptors.delete(promise)
|
|
228
319
|
end
|
|
@@ -230,23 +321,46 @@ class Puppeteer::Page
|
|
|
230
321
|
|
|
231
322
|
define_async_method :async_wait_for_file_chooser
|
|
232
323
|
|
|
233
|
-
# @
|
|
324
|
+
# @rbs geolocation: Puppeteer::Geolocation -- Geolocation override
|
|
325
|
+
# @rbs return: void -- No return value
|
|
234
326
|
def geolocation=(geolocation)
|
|
235
327
|
@client.send_message('Emulation.setGeolocationOverride', geolocation.to_h)
|
|
236
328
|
end
|
|
237
329
|
|
|
238
|
-
attr_reader :javascript_enabled, :target, :client
|
|
330
|
+
attr_reader :javascript_enabled, :service_worker_bypassed, :target, :client
|
|
331
|
+
|
|
332
|
+
# @rbs return: String -- Tab target id
|
|
333
|
+
def _tab_id
|
|
334
|
+
return @tab_id if @tab_id
|
|
335
|
+
|
|
336
|
+
parent_session = @client.respond_to?(:parent_session) ? @client.parent_session : nil
|
|
337
|
+
@tab_id = parent_session&.target&.target_id || @target.target_id
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# @rbs other: Object -- Other object to compare
|
|
341
|
+
# @rbs return: bool -- Equality result
|
|
342
|
+
def ==(other)
|
|
343
|
+
other = other.__getobj__ if other.is_a?(Puppeteer::ReactorRunner::Proxy)
|
|
344
|
+
return true if equal?(other)
|
|
345
|
+
return false unless other.is_a?(Puppeteer::Page)
|
|
346
|
+
return false unless @target&.target_id && other.target&.target_id
|
|
347
|
+
|
|
348
|
+
@target.target_id == other.target.target_id
|
|
349
|
+
end
|
|
239
350
|
alias_method :javascript_enabled?, :javascript_enabled
|
|
351
|
+
alias_method :service_worker_bypassed?, :service_worker_bypassed
|
|
240
352
|
|
|
353
|
+
# @rbs return: Puppeteer::Browser -- Owning browser
|
|
241
354
|
def browser
|
|
242
355
|
@target.browser
|
|
243
356
|
end
|
|
244
357
|
|
|
358
|
+
# @rbs return: Puppeteer::BrowserContext -- Owning browser context
|
|
245
359
|
def browser_context
|
|
246
360
|
@target.browser_context
|
|
247
361
|
end
|
|
248
362
|
|
|
249
|
-
class TargetCrashedError <
|
|
363
|
+
class TargetCrashedError < Puppeteer::Error; end
|
|
250
364
|
|
|
251
365
|
private def handle_target_crashed
|
|
252
366
|
emit_event(PageEmittedEvents::Error, TargetCrashedError.new('Page crashed!'))
|
|
@@ -275,58 +389,94 @@ class Puppeteer::Page
|
|
|
275
389
|
end
|
|
276
390
|
end
|
|
277
391
|
|
|
392
|
+
# @rbs return: Puppeteer::Frame -- Main frame
|
|
278
393
|
def main_frame
|
|
279
394
|
@frame_manager.main_frame
|
|
280
395
|
end
|
|
281
396
|
|
|
282
|
-
attr_reader :
|
|
397
|
+
attr_reader :touchscreen, :coverage, :tracing, :accessibility
|
|
398
|
+
alias_method :touch_screen, :touchscreen
|
|
283
399
|
|
|
400
|
+
# @rbs block: Proc? -- Optional block for instance_eval
|
|
401
|
+
# @rbs return: Puppeteer::Keyboard -- Keyboard instance
|
|
284
402
|
def keyboard(&block)
|
|
285
403
|
@keyboard.instance_eval(&block) unless block.nil?
|
|
286
404
|
|
|
287
405
|
@keyboard
|
|
288
406
|
end
|
|
289
407
|
|
|
408
|
+
# @rbs return: Array[Puppeteer::Frame] -- All frames
|
|
290
409
|
def frames
|
|
291
410
|
@frame_manager.frames
|
|
292
411
|
end
|
|
293
412
|
|
|
413
|
+
# @rbs return: Array[untyped] -- Active web workers
|
|
294
414
|
def workers
|
|
295
415
|
@workers.values
|
|
296
416
|
end
|
|
297
417
|
|
|
298
|
-
# @
|
|
418
|
+
# @rbs value: bool -- Enable request interception
|
|
419
|
+
# @rbs return: void -- No return value
|
|
299
420
|
def request_interception=(value)
|
|
300
421
|
@frame_manager.network_manager.request_interception = value
|
|
301
422
|
end
|
|
302
423
|
|
|
424
|
+
# @rbs enabled: bool -- Enable drag interception
|
|
425
|
+
# @rbs return: void -- No return value
|
|
303
426
|
def drag_interception_enabled=(enabled)
|
|
304
427
|
@user_drag_interception_enabled = enabled
|
|
305
428
|
@client.send_message('Input.setInterceptDrags', enabled: enabled)
|
|
306
429
|
end
|
|
307
430
|
|
|
431
|
+
# @rbs bypass: bool -- Bypass service workers
|
|
432
|
+
# @rbs return: void -- No return value
|
|
433
|
+
def service_worker_bypassed=(bypass)
|
|
434
|
+
@service_worker_bypassed = bypass
|
|
435
|
+
@client.send_message('Network.setBypassServiceWorker', bypass: bypass)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# @rbs enabled: bool -- Enable offline mode
|
|
439
|
+
# @rbs return: void -- No return value
|
|
308
440
|
def offline_mode=(enabled)
|
|
309
441
|
@frame_manager.network_manager.offline_mode = enabled
|
|
310
442
|
end
|
|
311
443
|
|
|
312
|
-
# @
|
|
444
|
+
# @rbs network_condition: Puppeteer::NetworkCondition? -- Network condition override
|
|
445
|
+
# @rbs return: void -- No return value
|
|
313
446
|
def emulate_network_conditions(network_condition)
|
|
314
447
|
@frame_manager.network_manager.emulate_network_conditions(network_condition)
|
|
315
448
|
end
|
|
316
449
|
|
|
317
|
-
# @
|
|
450
|
+
# @rbs timeout: Numeric? -- Default navigation timeout in milliseconds
|
|
451
|
+
# @rbs return: void -- No return value
|
|
318
452
|
def default_navigation_timeout=(timeout)
|
|
319
453
|
@timeout_settings.default_navigation_timeout = timeout
|
|
320
454
|
end
|
|
321
455
|
|
|
322
|
-
# @
|
|
456
|
+
# @rbs timeout: Numeric? -- Default timeout in milliseconds
|
|
457
|
+
# @rbs return: void -- No return value
|
|
323
458
|
def default_timeout=(timeout)
|
|
324
459
|
@timeout_settings.default_timeout = timeout
|
|
325
460
|
end
|
|
326
461
|
|
|
462
|
+
# @rbs return: Numeric -- Default timeout in milliseconds
|
|
463
|
+
def default_timeout
|
|
464
|
+
@timeout_settings.timeout
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# @rbs selector_or_function: String -- Selector or JS function
|
|
468
|
+
# @rbs return: Puppeteer::Locator -- Locator for selector or function
|
|
469
|
+
def locator(selector_or_function)
|
|
470
|
+
if Puppeteer::Locator.function_string?(selector_or_function)
|
|
471
|
+
Puppeteer::FunctionLocator.create(self, selector_or_function)
|
|
472
|
+
else
|
|
473
|
+
Puppeteer::NodeLocator.create(self, selector_or_function)
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
|
|
327
477
|
# `$()` in JavaScript.
|
|
328
|
-
# @
|
|
329
|
-
# @return
|
|
478
|
+
# @rbs selector: String -- CSS selector
|
|
479
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
330
480
|
def query_selector(selector)
|
|
331
481
|
main_frame.query_selector(selector)
|
|
332
482
|
end
|
|
@@ -335,18 +485,19 @@ class Puppeteer::Page
|
|
|
335
485
|
define_async_method :async_query_selector
|
|
336
486
|
|
|
337
487
|
# `$$()` in JavaScript.
|
|
338
|
-
# @
|
|
339
|
-
# @
|
|
340
|
-
|
|
341
|
-
|
|
488
|
+
# @rbs selector: String -- CSS selector
|
|
489
|
+
# @rbs isolate: bool? -- Use isolated world for queries
|
|
490
|
+
# @rbs return: Array[Puppeteer::ElementHandle] -- Matching elements
|
|
491
|
+
def query_selector_all(selector, isolate: nil)
|
|
492
|
+
main_frame.query_selector_all(selector, isolate: isolate)
|
|
342
493
|
end
|
|
343
494
|
alias_method :SS, :query_selector_all
|
|
344
495
|
|
|
345
496
|
define_async_method :async_query_selector_all
|
|
346
497
|
|
|
347
|
-
# @
|
|
348
|
-
# @
|
|
349
|
-
# @return
|
|
498
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
499
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
500
|
+
# @rbs return: Puppeteer::JSHandle -- Handle to evaluation result
|
|
350
501
|
def evaluate_handle(page_function, *args)
|
|
351
502
|
context = main_frame.execution_context
|
|
352
503
|
context.evaluate_handle(page_function, *args)
|
|
@@ -354,17 +505,18 @@ class Puppeteer::Page
|
|
|
354
505
|
|
|
355
506
|
define_async_method :async_evaluate_handle
|
|
356
507
|
|
|
357
|
-
# @
|
|
358
|
-
# @return
|
|
508
|
+
# @rbs prototype_handle: Puppeteer::JSHandle -- Prototype handle
|
|
509
|
+
# @rbs return: Puppeteer::JSHandle -- Handle to query result
|
|
359
510
|
def query_objects(prototype_handle)
|
|
360
511
|
context = main_frame.execution_context
|
|
361
512
|
context.query_objects(prototype_handle)
|
|
362
513
|
end
|
|
363
514
|
|
|
364
515
|
# `$eval()` in JavaScript.
|
|
365
|
-
# @
|
|
366
|
-
# @
|
|
367
|
-
# @
|
|
516
|
+
# @rbs selector: String -- CSS selector
|
|
517
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
518
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
519
|
+
# @rbs return: untyped -- Evaluation result
|
|
368
520
|
def eval_on_selector(selector, page_function, *args)
|
|
369
521
|
main_frame.eval_on_selector(selector, page_function, *args)
|
|
370
522
|
end
|
|
@@ -373,9 +525,10 @@ class Puppeteer::Page
|
|
|
373
525
|
define_async_method :async_eval_on_selector
|
|
374
526
|
|
|
375
527
|
# `$$eval()` in JavaScript.
|
|
376
|
-
# @
|
|
377
|
-
# @
|
|
378
|
-
# @
|
|
528
|
+
# @rbs selector: String -- CSS selector
|
|
529
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
530
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
531
|
+
# @rbs return: untyped -- Evaluation result
|
|
379
532
|
def eval_on_selector_all(selector, page_function, *args)
|
|
380
533
|
main_frame.eval_on_selector_all(selector, page_function, *args)
|
|
381
534
|
end
|
|
@@ -384,15 +537,16 @@ class Puppeteer::Page
|
|
|
384
537
|
define_async_method :async_eval_on_selector_all
|
|
385
538
|
|
|
386
539
|
# `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
|
|
387
|
-
# @
|
|
388
|
-
# @return
|
|
540
|
+
# @rbs expression: String -- XPath expression
|
|
541
|
+
# @rbs return: Array[Puppeteer::ElementHandle] -- Matching elements
|
|
389
542
|
def Sx(expression)
|
|
390
543
|
main_frame.Sx(expression)
|
|
391
544
|
end
|
|
392
545
|
|
|
393
546
|
define_async_method :async_Sx
|
|
394
547
|
|
|
395
|
-
# @
|
|
548
|
+
# @rbs urls: Array[String] -- URLs to fetch cookies for
|
|
549
|
+
# @rbs return: Array[Hash[String, untyped]] -- Cookies list
|
|
396
550
|
def cookies(*urls)
|
|
397
551
|
@client.send_message('Network.getCookies', urls: (urls.empty? ? [url] : urls))['cookies']
|
|
398
552
|
end
|
|
@@ -406,6 +560,8 @@ class Puppeteer::Page
|
|
|
406
560
|
raise ArgumentError.new("Each coookie must have #{requires.join(" and ")} attribute.")
|
|
407
561
|
end
|
|
408
562
|
|
|
563
|
+
# @rbs cookies: Array[Hash[Symbol | String, untyped]] -- cookies parameter
|
|
564
|
+
# @rbs return: void -- No return value
|
|
409
565
|
def delete_cookie(*cookies)
|
|
410
566
|
assert_cookie_params(cookies, requires: %i(name))
|
|
411
567
|
|
|
@@ -417,6 +573,8 @@ class Puppeteer::Page
|
|
|
417
573
|
end
|
|
418
574
|
end
|
|
419
575
|
|
|
576
|
+
# @rbs cookies: Array[Hash[Symbol | String, untyped]] -- cookies parameter
|
|
577
|
+
# @rbs return: void -- No return value
|
|
420
578
|
def set_cookie(*cookies)
|
|
421
579
|
assert_cookie_params(cookies, requires: %i(name value))
|
|
422
580
|
|
|
@@ -434,24 +592,27 @@ class Puppeteer::Page
|
|
|
434
592
|
end
|
|
435
593
|
end
|
|
436
594
|
|
|
437
|
-
# @
|
|
438
|
-
# @
|
|
439
|
-
# @
|
|
440
|
-
# @
|
|
441
|
-
# @
|
|
595
|
+
# @rbs url: String? -- Script URL
|
|
596
|
+
# @rbs path: String? -- Path to script file
|
|
597
|
+
# @rbs content: String? -- Script contents
|
|
598
|
+
# @rbs type: String? -- Script type
|
|
599
|
+
# @rbs id: String? -- Script element ID
|
|
600
|
+
# @rbs return: Puppeteer::ElementHandle -- Script element handle
|
|
442
601
|
def add_script_tag(url: nil, path: nil, content: nil, type: nil, id: nil)
|
|
443
602
|
main_frame.add_script_tag(url: url, path: path, content: content, type: type, id: id)
|
|
444
603
|
end
|
|
445
604
|
|
|
446
|
-
# @
|
|
447
|
-
# @
|
|
448
|
-
# @
|
|
605
|
+
# @rbs url: String? -- Stylesheet URL
|
|
606
|
+
# @rbs path: String? -- Path to stylesheet file
|
|
607
|
+
# @rbs content: String? -- Stylesheet contents
|
|
608
|
+
# @rbs return: Puppeteer::ElementHandle -- Style element handle
|
|
449
609
|
def add_style_tag(url: nil, path: nil, content: nil)
|
|
450
610
|
main_frame.add_style_tag(url: url, path: path, content: content)
|
|
451
611
|
end
|
|
452
612
|
|
|
453
|
-
# @
|
|
454
|
-
# @
|
|
613
|
+
# @rbs name: String -- Binding name
|
|
614
|
+
# @rbs puppeteer_function: Proc -- Ruby callback
|
|
615
|
+
# @rbs return: void -- No return value
|
|
455
616
|
def expose_function(name, puppeteer_function)
|
|
456
617
|
if @page_bindings[name]
|
|
457
618
|
raise ArgumentError.new("Failed to add page binding with name `#{name}` already exists!")
|
|
@@ -486,42 +647,79 @@ class Puppeteer::Page
|
|
|
486
647
|
|
|
487
648
|
source = JavaScriptFunction.new(add_page_binding, ['exposedFun', name]).source
|
|
488
649
|
@client.send_message('Runtime.addBinding', name: name)
|
|
489
|
-
@client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
|
|
650
|
+
script = @client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
|
|
651
|
+
@page_binding_ids[name] = script['identifier']
|
|
490
652
|
|
|
491
653
|
promises = @frame_manager.frames.map do |frame|
|
|
492
654
|
frame.async_evaluate("() => #{source}")
|
|
493
655
|
end
|
|
494
|
-
|
|
656
|
+
Puppeteer::AsyncUtils.await_promise_all(*promises)
|
|
495
657
|
|
|
496
658
|
nil
|
|
497
659
|
end
|
|
498
660
|
|
|
499
|
-
# @
|
|
500
|
-
# @
|
|
661
|
+
# @rbs name: String -- Binding name
|
|
662
|
+
# @rbs return: void -- No return value
|
|
663
|
+
def remove_exposed_function(name)
|
|
664
|
+
identifier = @page_binding_ids[name]
|
|
665
|
+
unless identifier
|
|
666
|
+
raise ArgumentError.new("Function with name \"#{name}\" does not exist")
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
@page_binding_ids.delete(name)
|
|
670
|
+
@page_bindings.delete(name)
|
|
671
|
+
|
|
672
|
+
@client.send_message('Runtime.removeBinding', name: name)
|
|
673
|
+
@client.send_message('Page.removeScriptToEvaluateOnNewDocument', identifier: identifier)
|
|
674
|
+
|
|
675
|
+
remove_script = '(name) => { delete window[name]; }'
|
|
676
|
+
@frame_manager.frames.each do |frame|
|
|
677
|
+
frame.evaluate(remove_script, name)
|
|
678
|
+
rescue StandardError
|
|
679
|
+
nil
|
|
680
|
+
end
|
|
681
|
+
nil
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
# @rbs username: String? -- HTTP basic auth username
|
|
685
|
+
# @rbs password: String? -- HTTP basic auth password
|
|
686
|
+
# @rbs return: void -- No return value
|
|
501
687
|
def authenticate(username: nil, password: nil)
|
|
502
688
|
@frame_manager.network_manager.authenticate(username: username, password: password)
|
|
503
689
|
end
|
|
504
690
|
|
|
505
|
-
# @
|
|
691
|
+
# @rbs headers: Hash[String, String] -- Extra HTTP headers
|
|
692
|
+
# @rbs return: void -- No return value
|
|
506
693
|
def extra_http_headers=(headers)
|
|
507
694
|
@frame_manager.network_manager.extra_http_headers = headers
|
|
508
695
|
end
|
|
509
696
|
|
|
510
|
-
# @
|
|
511
|
-
# @
|
|
697
|
+
# @rbs user_agent: String -- User agent string
|
|
698
|
+
# @rbs user_agent_metadata: Hash[String, untyped]? -- User agent metadata
|
|
699
|
+
# @rbs return: void -- No return value
|
|
512
700
|
def set_user_agent(user_agent, user_agent_metadata = nil)
|
|
513
701
|
@frame_manager.network_manager.set_user_agent(user_agent, user_agent_metadata)
|
|
514
702
|
end
|
|
515
703
|
alias_method :user_agent=, :set_user_agent
|
|
516
704
|
|
|
705
|
+
# @rbs return: Puppeteer::Page::Metrics -- Page metrics
|
|
517
706
|
def metrics
|
|
518
707
|
response = @client.send_message('Performance.getMetrics')
|
|
519
708
|
Metrics.new(response['metrics'])
|
|
520
709
|
end
|
|
521
710
|
|
|
522
|
-
class PageError <
|
|
711
|
+
class PageError < Puppeteer::Error ; end
|
|
523
712
|
|
|
524
713
|
private def handle_exception(exception_details)
|
|
714
|
+
exception = exception_details['exception']
|
|
715
|
+
if exception
|
|
716
|
+
is_error_object = exception['type'] == 'object' && exception['subtype'] == 'error'
|
|
717
|
+
if !is_error_object && !exception.key?('objectId')
|
|
718
|
+
emit_event(PageEmittedEvents::PageError, Puppeteer::RemoteObject.new(exception).value)
|
|
719
|
+
return
|
|
720
|
+
end
|
|
721
|
+
end
|
|
722
|
+
|
|
525
723
|
message = Puppeteer::ExceptionDetails.new(exception_details).message
|
|
526
724
|
err = PageError.new(message)
|
|
527
725
|
# err.stack = ''; // Don't report clientside error with a node stack attached
|
|
@@ -554,6 +752,8 @@ class Puppeteer::Page
|
|
|
554
752
|
add_console_message(event['type'], values, event['stackTrace'])
|
|
555
753
|
end
|
|
556
754
|
|
|
755
|
+
# @rbs event: Hash[String, untyped] -- Binding called payload
|
|
756
|
+
# @rbs return: void -- No return value
|
|
557
757
|
def handle_binding_called(event)
|
|
558
758
|
execution_context_id = event['executionContextId']
|
|
559
759
|
payload =
|
|
@@ -595,13 +795,18 @@ class Puppeteer::Page
|
|
|
595
795
|
JavaScriptFunction.new(deliver_error, [name, seq, err.message]).source
|
|
596
796
|
end
|
|
597
797
|
|
|
598
|
-
|
|
798
|
+
Async do
|
|
799
|
+
@client.async_send_message('Runtime.evaluate', expression: expression, contextId: execution_context_id).wait
|
|
800
|
+
rescue => error
|
|
599
801
|
debug_puts(error)
|
|
600
802
|
end
|
|
601
803
|
end
|
|
602
804
|
|
|
603
805
|
private def add_console_message(type, args, stack_trace)
|
|
604
|
-
text_tokens = args.map
|
|
806
|
+
text_tokens = args.map do |arg|
|
|
807
|
+
value = arg.remote_object.value
|
|
808
|
+
value.nil? ? arg.to_s : value
|
|
809
|
+
end
|
|
605
810
|
|
|
606
811
|
stack_trace_locations =
|
|
607
812
|
if stack_trace && stack_trace['callFrames']
|
|
@@ -624,7 +829,7 @@ class Puppeteer::Page
|
|
|
624
829
|
unless %w(alert confirm prompt beforeunload).include?(dialog_type)
|
|
625
830
|
raise ArgumentError.new("Unknown javascript dialog type: #{dialog_type}")
|
|
626
831
|
end
|
|
627
|
-
dialog = Puppeteer::
|
|
832
|
+
dialog = Puppeteer::CdpDialog.new(@client,
|
|
628
833
|
type: dialog_type,
|
|
629
834
|
message: event['message'],
|
|
630
835
|
default_value: event['defaultPrompt'])
|
|
@@ -641,72 +846,101 @@ class Puppeteer::Page
|
|
|
641
846
|
@client.send_message('Emulation.setDefaultBackgroundColorOverride')
|
|
642
847
|
end
|
|
643
848
|
|
|
644
|
-
# @return
|
|
849
|
+
# @rbs return: String? -- Page URL
|
|
645
850
|
def url
|
|
646
851
|
main_frame.url
|
|
647
852
|
end
|
|
648
853
|
|
|
649
|
-
# @return
|
|
854
|
+
# @rbs return: String -- Page HTML content
|
|
650
855
|
def content
|
|
651
856
|
main_frame.content
|
|
652
857
|
end
|
|
653
858
|
|
|
654
|
-
# @
|
|
655
|
-
# @
|
|
656
|
-
# @
|
|
859
|
+
# @rbs html: String -- HTML content
|
|
860
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
861
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
862
|
+
# @rbs return: void -- No return value
|
|
657
863
|
def set_content(html, timeout: nil, wait_until: nil)
|
|
658
864
|
main_frame.set_content(html, timeout: timeout, wait_until: wait_until)
|
|
659
865
|
end
|
|
660
866
|
|
|
661
|
-
# @
|
|
867
|
+
# @rbs html: String -- HTML content
|
|
868
|
+
# @rbs return: void -- No return value
|
|
662
869
|
def content=(html)
|
|
663
870
|
main_frame.set_content(html)
|
|
664
871
|
end
|
|
665
872
|
|
|
666
|
-
# @
|
|
667
|
-
# @
|
|
668
|
-
# @
|
|
669
|
-
# @
|
|
670
|
-
|
|
671
|
-
|
|
873
|
+
# @rbs url: String -- URL to navigate
|
|
874
|
+
# @rbs referer: String? -- Referer header value
|
|
875
|
+
# @rbs referer: String? -- Referer header value
|
|
876
|
+
# @rbs referrer_policy: String? -- Referrer policy
|
|
877
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
878
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
879
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
880
|
+
def goto(url, referer: nil, referrer_policy: nil, timeout: nil, wait_until: nil)
|
|
881
|
+
main_frame.goto(
|
|
882
|
+
url,
|
|
883
|
+
referer: referer,
|
|
884
|
+
referrer_policy: referrer_policy,
|
|
885
|
+
timeout: timeout,
|
|
886
|
+
wait_until: wait_until,
|
|
887
|
+
)
|
|
672
888
|
end
|
|
673
889
|
|
|
674
|
-
# @
|
|
675
|
-
# @
|
|
676
|
-
# @
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
890
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
891
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
892
|
+
# @rbs ignore_cache: bool? -- Skip cache when reloading
|
|
893
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
894
|
+
def reload(timeout: nil, wait_until: nil, ignore_cache: nil)
|
|
895
|
+
params = {}
|
|
896
|
+
params[:ignoreCache] = ignore_cache unless ignore_cache.nil?
|
|
897
|
+
|
|
898
|
+
wait_for_navigation(timeout: timeout, wait_until: wait_until, ignore_same_document_navigation: true) do
|
|
899
|
+
if params.empty?
|
|
900
|
+
@client.send_message('Page.reload')
|
|
901
|
+
else
|
|
902
|
+
@client.send_message('Page.reload', **params)
|
|
903
|
+
end
|
|
680
904
|
end
|
|
681
905
|
end
|
|
682
906
|
|
|
683
|
-
|
|
684
|
-
|
|
907
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
908
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
909
|
+
# @rbs ignore_same_document_navigation: bool -- Ignore same-document navigation
|
|
910
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
911
|
+
def wait_for_navigation(timeout: nil, wait_until: nil, ignore_same_document_navigation: false)
|
|
912
|
+
main_frame.send(
|
|
913
|
+
:wait_for_navigation,
|
|
914
|
+
timeout: timeout,
|
|
915
|
+
wait_until: wait_until,
|
|
916
|
+
ignore_same_document_navigation: ignore_same_document_navigation,
|
|
917
|
+
)
|
|
685
918
|
end
|
|
686
919
|
|
|
687
920
|
# @!method async_wait_for_navigation(timeout: nil, wait_until: nil)
|
|
688
921
|
#
|
|
689
|
-
# @param timeout [number|nil]
|
|
690
|
-
# @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
|
|
691
922
|
define_async_method :async_wait_for_navigation
|
|
692
923
|
|
|
693
924
|
private def wait_for_network_manager_event(event_name, predicate:, timeout:)
|
|
694
925
|
option_timeout = timeout || @timeout_settings.timeout
|
|
695
926
|
|
|
696
|
-
promise =
|
|
927
|
+
promise = Async::Promise.new
|
|
697
928
|
|
|
698
929
|
listener_id = @frame_manager.network_manager.add_event_listener(event_name) do |event_target|
|
|
699
|
-
if predicate.call(event_target)
|
|
700
|
-
promise.
|
|
930
|
+
if Puppeteer::AsyncUtils.await(predicate.call(event_target))
|
|
931
|
+
promise.resolve(event_target)
|
|
701
932
|
end
|
|
702
933
|
end
|
|
703
934
|
|
|
704
935
|
begin
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
936
|
+
if option_timeout == 0
|
|
937
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
938
|
+
else
|
|
939
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, -> {
|
|
940
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
941
|
+
}).wait
|
|
708
942
|
end
|
|
709
|
-
rescue
|
|
943
|
+
rescue Async::TimeoutError
|
|
710
944
|
raise Puppeteer::TimeoutError.new("waiting for #{event_name} failed: timeout #{option_timeout}ms exceeded")
|
|
711
945
|
ensure
|
|
712
946
|
@frame_manager.network_manager.remove_event_listener(listener_id)
|
|
@@ -716,22 +950,25 @@ class Puppeteer::Page
|
|
|
716
950
|
private def wait_for_frame_manager_event(*event_names, predicate:, timeout:)
|
|
717
951
|
option_timeout = timeout || @timeout_settings.timeout
|
|
718
952
|
|
|
719
|
-
promise =
|
|
953
|
+
promise = Async::Promise.new
|
|
720
954
|
|
|
721
955
|
listener_ids = event_names.map do |event_name|
|
|
722
956
|
@frame_manager.add_event_listener(event_name) do |event_target|
|
|
723
|
-
if predicate.call(event_target)
|
|
724
|
-
promise.
|
|
957
|
+
if Puppeteer::AsyncUtils.await(predicate.call(event_target))
|
|
958
|
+
promise.resolve(event_target) unless promise.resolved?
|
|
725
959
|
end
|
|
726
960
|
end
|
|
727
961
|
end
|
|
728
962
|
|
|
729
963
|
begin
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
964
|
+
if option_timeout == 0
|
|
965
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
966
|
+
else
|
|
967
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, -> {
|
|
968
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
969
|
+
}).wait
|
|
733
970
|
end
|
|
734
|
-
rescue
|
|
971
|
+
rescue Async::TimeoutError
|
|
735
972
|
raise Puppeteer::TimeoutError.new("waiting for #{event_names.join(" or ")} failed: timeout #{option_timeout}ms exceeded")
|
|
736
973
|
ensure
|
|
737
974
|
listener_ids.each do |listener_id|
|
|
@@ -741,13 +978,17 @@ class Puppeteer::Page
|
|
|
741
978
|
end
|
|
742
979
|
|
|
743
980
|
private def session_close_promise
|
|
744
|
-
@disconnect_promise ||=
|
|
981
|
+
@disconnect_promise ||= Async::Promise.new.tap do |future|
|
|
745
982
|
@client.observe_first(CDPSessionEmittedEvents::Disconnected) do
|
|
746
983
|
future.reject(Puppeteer::CDPSession::Error.new('Target Closed'))
|
|
747
984
|
end
|
|
748
985
|
end
|
|
749
986
|
end
|
|
750
987
|
|
|
988
|
+
# @rbs url: String? -- URL to match
|
|
989
|
+
# @rbs predicate: Proc? -- Predicate to match
|
|
990
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
991
|
+
# @rbs return: Puppeteer::HTTPRequest -- Matching request
|
|
751
992
|
def wait_for_request(url: nil, predicate: nil, timeout: nil)
|
|
752
993
|
if !url && !predicate
|
|
753
994
|
raise ArgumentError.new('url or predicate must be specified')
|
|
@@ -778,10 +1019,12 @@ class Puppeteer::Page
|
|
|
778
1019
|
# Waits until request matches the given predicate
|
|
779
1020
|
# wait_for_request(predicate: -> (req){ req.url.start_with?('https://example.com/search') })
|
|
780
1021
|
#
|
|
781
|
-
# @param url [String]
|
|
782
|
-
# @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)]
|
|
783
1022
|
define_async_method :async_wait_for_request
|
|
784
1023
|
|
|
1024
|
+
# @rbs url: String? -- URL to match
|
|
1025
|
+
# @rbs predicate: Proc? -- Predicate to match
|
|
1026
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
1027
|
+
# @rbs return: Puppeteer::HTTPResponse -- Matching response
|
|
785
1028
|
def wait_for_response(url: nil, predicate: nil, timeout: nil)
|
|
786
1029
|
if !url && !predicate
|
|
787
1030
|
raise ArgumentError.new('url or predicate must be specified')
|
|
@@ -804,10 +1047,68 @@ class Puppeteer::Page
|
|
|
804
1047
|
|
|
805
1048
|
# @!method async_wait_for_response(url: nil, predicate: nil, timeout: nil)
|
|
806
1049
|
#
|
|
807
|
-
# @param url [String]
|
|
808
|
-
# @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)]
|
|
809
1050
|
define_async_method :async_wait_for_response
|
|
810
1051
|
|
|
1052
|
+
# @rbs idle_time: Numeric -- Idle time to wait for in milliseconds
|
|
1053
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
1054
|
+
# @rbs concurrency: Integer -- Allowed number of concurrent requests
|
|
1055
|
+
# @rbs return: void -- No return value
|
|
1056
|
+
def wait_for_network_idle(idle_time: 500, timeout: nil, concurrency: 0)
|
|
1057
|
+
option_timeout = timeout || @timeout_settings.timeout
|
|
1058
|
+
|
|
1059
|
+
promise = Async::Promise.new
|
|
1060
|
+
idle_timer = nil
|
|
1061
|
+
|
|
1062
|
+
schedule_idle = lambda do
|
|
1063
|
+
return if @inflight_requests.size > concurrency
|
|
1064
|
+
|
|
1065
|
+
idle_timer&.stop
|
|
1066
|
+
idle_timer = Async do
|
|
1067
|
+
Puppeteer::AsyncUtils.sleep_seconds(idle_time / 1000.0)
|
|
1068
|
+
unless promise.resolved? || @inflight_requests.size > concurrency
|
|
1069
|
+
promise.resolve(nil)
|
|
1070
|
+
end
|
|
1071
|
+
end
|
|
1072
|
+
end
|
|
1073
|
+
|
|
1074
|
+
# Use raw listener to avoid request interception queue delaying idle tracking.
|
|
1075
|
+
request_listener = add_event_listener('request') do
|
|
1076
|
+
idle_timer&.stop
|
|
1077
|
+
idle_timer = nil
|
|
1078
|
+
end
|
|
1079
|
+
request_finished_listener = on('requestfinished') do
|
|
1080
|
+
schedule_idle.call
|
|
1081
|
+
end
|
|
1082
|
+
request_failed_listener = on('requestfailed') do
|
|
1083
|
+
schedule_idle.call
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
schedule_idle.call
|
|
1087
|
+
|
|
1088
|
+
begin
|
|
1089
|
+
if option_timeout == 0
|
|
1090
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
1091
|
+
else
|
|
1092
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, -> {
|
|
1093
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
1094
|
+
}).wait
|
|
1095
|
+
end
|
|
1096
|
+
rescue Async::TimeoutError
|
|
1097
|
+
raise Puppeteer::TimeoutError.new("waiting for network idle failed: timeout #{option_timeout}ms exceeded")
|
|
1098
|
+
ensure
|
|
1099
|
+
off(request_listener)
|
|
1100
|
+
off(request_finished_listener)
|
|
1101
|
+
off(request_failed_listener)
|
|
1102
|
+
idle_timer&.stop
|
|
1103
|
+
end
|
|
1104
|
+
end
|
|
1105
|
+
|
|
1106
|
+
define_async_method :async_wait_for_network_idle
|
|
1107
|
+
|
|
1108
|
+
# @rbs url: String? -- URL to match
|
|
1109
|
+
# @rbs predicate: Proc? -- Predicate to match
|
|
1110
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
1111
|
+
# @rbs return: Puppeteer::Frame -- Matching frame
|
|
811
1112
|
def wait_for_frame(url: nil, predicate: nil, timeout: nil)
|
|
812
1113
|
if !url && !predicate
|
|
813
1114
|
raise ArgumentError.new('url or predicate must be specified')
|
|
@@ -836,18 +1137,18 @@ class Puppeteer::Page
|
|
|
836
1137
|
|
|
837
1138
|
# @!method async_wait_for_frame(url: nil, predicate: nil, timeout: nil)
|
|
838
1139
|
#
|
|
839
|
-
# @param url [String]
|
|
840
|
-
# @param predicate [Proc(Puppeteer::Frame -> Boolean)]
|
|
841
1140
|
define_async_method :async_wait_for_frame
|
|
842
1141
|
|
|
843
|
-
# @
|
|
844
|
-
# @
|
|
1142
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
1143
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
1144
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
845
1145
|
def go_back(timeout: nil, wait_until: nil)
|
|
846
1146
|
go(-1, timeout: timeout, wait_until: wait_until)
|
|
847
1147
|
end
|
|
848
1148
|
|
|
849
|
-
# @
|
|
850
|
-
# @
|
|
1149
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
1150
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
1151
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
851
1152
|
def go_forward(timeout: nil, wait_until: nil)
|
|
852
1153
|
go(+1, timeout: timeout, wait_until: wait_until)
|
|
853
1154
|
end
|
|
@@ -856,37 +1157,44 @@ class Puppeteer::Page
|
|
|
856
1157
|
history = @client.send_message('Page.getNavigationHistory')
|
|
857
1158
|
entries = history['entries']
|
|
858
1159
|
index = history['currentIndex'] + delta
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1160
|
+
if index < 0 || index >= entries.length
|
|
1161
|
+
raise Puppeteer::Error.new('History entry to navigate to not found.')
|
|
1162
|
+
end
|
|
1163
|
+
entry = entries[index]
|
|
1164
|
+
wait_for_navigation(timeout: timeout, wait_until: wait_until) do
|
|
1165
|
+
@client.send_message('Page.navigateToHistoryEntry', entryId: entry['id'])
|
|
863
1166
|
end
|
|
864
1167
|
end
|
|
865
1168
|
|
|
866
1169
|
# Brings page to front (activates tab).
|
|
1170
|
+
# @rbs return: void -- No return value
|
|
867
1171
|
def bring_to_front
|
|
868
1172
|
@client.send_message('Page.bringToFront')
|
|
869
1173
|
end
|
|
870
1174
|
|
|
871
|
-
# @
|
|
1175
|
+
# @rbs device: Puppeteer::Device -- Device descriptor
|
|
1176
|
+
# @rbs return: void -- No return value
|
|
872
1177
|
def emulate(device)
|
|
873
1178
|
self.viewport = device.viewport
|
|
874
1179
|
self.user_agent = device.user_agent
|
|
875
1180
|
end
|
|
876
1181
|
|
|
877
|
-
# @
|
|
1182
|
+
# @rbs enabled: bool -- Enable JavaScript
|
|
1183
|
+
# @rbs return: void -- No return value
|
|
878
1184
|
def javascript_enabled=(enabled)
|
|
879
1185
|
return if @javascript_enabled == enabled
|
|
880
1186
|
@javascript_enabled = enabled
|
|
881
1187
|
@client.send_message('Emulation.setScriptExecutionDisabled', value: !enabled)
|
|
882
1188
|
end
|
|
883
1189
|
|
|
884
|
-
# @
|
|
1190
|
+
# @rbs enabled: bool -- Enable bypassing CSP
|
|
1191
|
+
# @rbs return: void -- No return value
|
|
885
1192
|
def bypass_csp=(enabled)
|
|
886
1193
|
@client.send_message('Page.setBypassCSP', enabled: enabled)
|
|
887
1194
|
end
|
|
888
1195
|
|
|
889
|
-
# @
|
|
1196
|
+
# @rbs media_type: (String | Symbol)? -- Media type override
|
|
1197
|
+
# @rbs return: void -- No return value
|
|
890
1198
|
def emulate_media_type(media_type)
|
|
891
1199
|
media_type_str = media_type.to_s
|
|
892
1200
|
unless ['screen', 'print', ''].include?(media_type_str)
|
|
@@ -895,7 +1203,8 @@ class Puppeteer::Page
|
|
|
895
1203
|
@client.send_message('Emulation.setEmulatedMedia', media: media_type_str)
|
|
896
1204
|
end
|
|
897
1205
|
|
|
898
|
-
# @
|
|
1206
|
+
# @rbs factor: Numeric? -- CPU throttling rate
|
|
1207
|
+
# @rbs return: void -- No return value
|
|
899
1208
|
def emulate_cpu_throttling(factor)
|
|
900
1209
|
if factor.nil? || factor >= 1
|
|
901
1210
|
@client.send_message('Emulation.setCPUThrottlingRate', rate: factor || 1)
|
|
@@ -904,7 +1213,8 @@ class Puppeteer::Page
|
|
|
904
1213
|
end
|
|
905
1214
|
end
|
|
906
1215
|
|
|
907
|
-
# @
|
|
1216
|
+
# @rbs features: Array[Hash[Symbol, untyped]]? -- Media feature overrides
|
|
1217
|
+
# @rbs return: void -- No return value
|
|
908
1218
|
def emulate_media_features(features)
|
|
909
1219
|
if features.nil?
|
|
910
1220
|
@client.send_message('Emulation.setEmulatedMedia', features: nil)
|
|
@@ -919,7 +1229,8 @@ class Puppeteer::Page
|
|
|
919
1229
|
end
|
|
920
1230
|
end
|
|
921
1231
|
|
|
922
|
-
# @
|
|
1232
|
+
# @rbs timezone_id: String? -- Timezone ID
|
|
1233
|
+
# @rbs return: void -- No return value
|
|
923
1234
|
def emulate_timezone(timezone_id)
|
|
924
1235
|
@client.send_message('Emulation.setTimezoneOverride', timezoneId: timezone_id || '')
|
|
925
1236
|
rescue => err
|
|
@@ -939,6 +1250,8 @@ class Puppeteer::Page
|
|
|
939
1250
|
tritanopia
|
|
940
1251
|
].freeze
|
|
941
1252
|
|
|
1253
|
+
# @rbs vision_deficiency_type: String? -- Vision deficiency type
|
|
1254
|
+
# @rbs return: void -- No return value
|
|
942
1255
|
def emulate_vision_deficiency(vision_deficiency_type)
|
|
943
1256
|
value = vision_deficiency_type || 'none'
|
|
944
1257
|
unless VISION_DEFICIENCY_TYPES.include?(value)
|
|
@@ -947,8 +1260,9 @@ class Puppeteer::Page
|
|
|
947
1260
|
@client.send_message('Emulation.setEmulatedVisionDeficiency', type: value)
|
|
948
1261
|
end
|
|
949
1262
|
|
|
950
|
-
# @
|
|
951
|
-
# @
|
|
1263
|
+
# @rbs is_user_active: bool? -- User activity override
|
|
1264
|
+
# @rbs is_screen_unlocked: bool? -- Screen unlocked override
|
|
1265
|
+
# @rbs return: void -- No return value
|
|
952
1266
|
def emulate_idle_state(is_user_active: nil, is_screen_unlocked: nil)
|
|
953
1267
|
overrides = {
|
|
954
1268
|
isUserActive: is_user_active,
|
|
@@ -962,7 +1276,8 @@ class Puppeteer::Page
|
|
|
962
1276
|
end
|
|
963
1277
|
end
|
|
964
1278
|
|
|
965
|
-
# @
|
|
1279
|
+
# @rbs viewport: Puppeteer::Viewport? -- Viewport settings
|
|
1280
|
+
# @rbs return: void -- No return value
|
|
966
1281
|
def viewport=(viewport)
|
|
967
1282
|
needs_reload = @emulation_manager.emulate_viewport(viewport)
|
|
968
1283
|
@viewport = viewport
|
|
@@ -971,9 +1286,9 @@ class Puppeteer::Page
|
|
|
971
1286
|
|
|
972
1287
|
attr_reader :viewport
|
|
973
1288
|
|
|
974
|
-
# @
|
|
975
|
-
# @
|
|
976
|
-
# @return
|
|
1289
|
+
# @rbs page_function: String -- page_function parameter
|
|
1290
|
+
# @rbs args: Array[untyped] -- args parameter
|
|
1291
|
+
# @rbs return: untyped -- Result
|
|
977
1292
|
def evaluate(page_function, *args)
|
|
978
1293
|
main_frame.evaluate(page_function, *args)
|
|
979
1294
|
end
|
|
@@ -981,11 +1296,15 @@ class Puppeteer::Page
|
|
|
981
1296
|
define_async_method :async_evaluate
|
|
982
1297
|
|
|
983
1298
|
class JavaScriptFunction
|
|
1299
|
+
# @rbs expression: String -- Function expression
|
|
1300
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
1301
|
+
# @rbs return: void -- No return value
|
|
984
1302
|
def initialize(expression, args)
|
|
985
1303
|
@expression = expression
|
|
986
1304
|
@args = args
|
|
987
1305
|
end
|
|
988
1306
|
|
|
1307
|
+
# @rbs return: String -- Generated source
|
|
989
1308
|
def source
|
|
990
1309
|
"(#{@expression})(#{arguments})"
|
|
991
1310
|
end
|
|
@@ -996,15 +1315,21 @@ class Puppeteer::Page
|
|
|
996
1315
|
end
|
|
997
1316
|
|
|
998
1317
|
class JavaScriptExpression
|
|
1318
|
+
# @rbs expression: String -- Expression to evaluate
|
|
1319
|
+
# @rbs return: void -- No return value
|
|
999
1320
|
def initialize(expression)
|
|
1000
1321
|
@expression = expression
|
|
1001
1322
|
end
|
|
1002
1323
|
|
|
1324
|
+
# @rbs return: String -- Generated source
|
|
1003
1325
|
def source
|
|
1004
1326
|
@expression
|
|
1005
1327
|
end
|
|
1006
1328
|
end
|
|
1007
1329
|
|
|
1330
|
+
# @rbs page_function: String -- page_function parameter
|
|
1331
|
+
# @rbs args: Array[untyped] -- args parameter
|
|
1332
|
+
# @rbs return: Hash[String, untyped] -- CDP response
|
|
1008
1333
|
def evaluate_on_new_document(page_function, *args)
|
|
1009
1334
|
source =
|
|
1010
1335
|
if ['=>', 'async', 'function'].any? { |keyword| page_function.include?(keyword) }
|
|
@@ -1016,23 +1341,32 @@ class Puppeteer::Page
|
|
|
1016
1341
|
@client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
|
|
1017
1342
|
end
|
|
1018
1343
|
|
|
1019
|
-
# @
|
|
1344
|
+
# @rbs identifier: String -- Script identifier to remove
|
|
1345
|
+
# @rbs return: void
|
|
1346
|
+
def remove_script_to_evaluate_on_new_document(identifier)
|
|
1347
|
+
@client.send_message('Page.removeScriptToEvaluateOnNewDocument', identifier: identifier)
|
|
1348
|
+
end
|
|
1349
|
+
|
|
1350
|
+
# @rbs enabled: bool -- Enable cache usage
|
|
1020
1351
|
def cache_enabled=(enabled)
|
|
1021
1352
|
@frame_manager.network_manager.cache_enabled = enabled
|
|
1022
1353
|
end
|
|
1023
1354
|
|
|
1024
|
-
# @return
|
|
1355
|
+
# @rbs return: String -- Page title
|
|
1025
1356
|
def title
|
|
1026
1357
|
main_frame.title
|
|
1027
1358
|
end
|
|
1028
1359
|
|
|
1029
|
-
# @
|
|
1030
|
-
# @
|
|
1031
|
-
# @
|
|
1032
|
-
# @
|
|
1033
|
-
# @
|
|
1034
|
-
# @
|
|
1035
|
-
# @
|
|
1360
|
+
# @rbs type: String? -- Image format
|
|
1361
|
+
# @rbs path: String? -- File path to save
|
|
1362
|
+
# @rbs full_page: bool? -- Capture full page
|
|
1363
|
+
# @rbs clip: Hash[Symbol, Numeric]? -- Clip rectangle
|
|
1364
|
+
# @rbs quality: Integer? -- JPEG quality
|
|
1365
|
+
# @rbs omit_background: bool? -- Omit background for PNG
|
|
1366
|
+
# @rbs encoding: String? -- Encoding (base64 or binary)
|
|
1367
|
+
# @rbs capture_beyond_viewport: bool? -- Capture beyond viewport
|
|
1368
|
+
# @rbs from_surface: bool? -- Capture from surface
|
|
1369
|
+
# @rbs return: String -- Screenshot data
|
|
1036
1370
|
def screenshot(type: nil,
|
|
1037
1371
|
path: nil,
|
|
1038
1372
|
full_page: nil,
|
|
@@ -1055,14 +1389,14 @@ class Puppeteer::Page
|
|
|
1055
1389
|
}.compact
|
|
1056
1390
|
screenshot_options = ScreenshotOptions.new(options)
|
|
1057
1391
|
|
|
1392
|
+
guard = browser_context.start_screenshot
|
|
1058
1393
|
@screenshot_task_queue.post_task do
|
|
1059
1394
|
screenshot_task(screenshot_options.type, screenshot_options)
|
|
1060
1395
|
end
|
|
1396
|
+
ensure
|
|
1397
|
+
guard&.release
|
|
1061
1398
|
end
|
|
1062
1399
|
|
|
1063
|
-
# @param {"png"|"jpeg"} format
|
|
1064
|
-
# @param {!ScreenshotOptions=} options
|
|
1065
|
-
# @return {!Promise<!Buffer|!String>}
|
|
1066
1400
|
private def screenshot_task(format, screenshot_options)
|
|
1067
1401
|
@client.send_message('Target.activateTarget', targetId: @target.target_id)
|
|
1068
1402
|
|
|
@@ -1129,13 +1463,15 @@ class Puppeteer::Page
|
|
|
1129
1463
|
buffer
|
|
1130
1464
|
end
|
|
1131
1465
|
|
|
1132
|
-
class PrintToPdfIsNotImplementedError <
|
|
1466
|
+
class PrintToPdfIsNotImplementedError < Puppeteer::Error
|
|
1467
|
+
# @rbs return: void -- No return value
|
|
1133
1468
|
def initialize
|
|
1134
1469
|
super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
|
|
1135
1470
|
end
|
|
1136
1471
|
end
|
|
1137
1472
|
|
|
1138
|
-
# @
|
|
1473
|
+
# @rbs options: Hash[Symbol, untyped] -- PDF options
|
|
1474
|
+
# @rbs return: Enumerable[String] -- PDF data chunks
|
|
1139
1475
|
def create_pdf_stream(options = {})
|
|
1140
1476
|
timeout_helper = Puppeteer::TimeoutHelper.new('Page.printToPDF',
|
|
1141
1477
|
timeout_ms: options[:timeout],
|
|
@@ -1158,7 +1494,8 @@ class Puppeteer::Page
|
|
|
1158
1494
|
).read_as_chunks
|
|
1159
1495
|
end
|
|
1160
1496
|
|
|
1161
|
-
# @
|
|
1497
|
+
# @rbs options: Hash[Symbol, untyped] -- PDF options
|
|
1498
|
+
# @rbs return: String -- PDF data
|
|
1162
1499
|
def pdf(options = {})
|
|
1163
1500
|
chunks = create_pdf_stream(options)
|
|
1164
1501
|
|
|
@@ -1186,65 +1523,79 @@ class Puppeteer::Page
|
|
|
1186
1523
|
end
|
|
1187
1524
|
end
|
|
1188
1525
|
|
|
1189
|
-
# @
|
|
1526
|
+
# @rbs run_before_unload: bool -- Whether to run beforeunload handlers
|
|
1527
|
+
# @rbs return: void -- No return value
|
|
1190
1528
|
def close(run_before_unload: false)
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1529
|
+
guard = browser_context.wait_for_screenshot_operations
|
|
1530
|
+
begin
|
|
1531
|
+
unless @client.connection
|
|
1532
|
+
raise 'Protocol error: Connection closed. Most likely the page has been closed.'
|
|
1533
|
+
end
|
|
1194
1534
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1535
|
+
if run_before_unload
|
|
1536
|
+
@client.send_message('Page.close')
|
|
1537
|
+
else
|
|
1538
|
+
@client.connection.send_message('Target.closeTarget', targetId: @target.target_id)
|
|
1539
|
+
@target.is_closed_promise.wait
|
|
1200
1540
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1541
|
+
# @closed sometimes remains false, so wait for @closed = true with 100ms timeout.
|
|
1542
|
+
25.times do
|
|
1543
|
+
break if @closed
|
|
1544
|
+
Puppeteer::AsyncUtils.sleep_seconds(0.004)
|
|
1545
|
+
end
|
|
1205
1546
|
end
|
|
1547
|
+
rescue Puppeteer::Connection::ProtocolError => err
|
|
1548
|
+
raise unless err.message.match?(/Target closed/i)
|
|
1549
|
+
ensure
|
|
1550
|
+
guard&.release
|
|
1206
1551
|
end
|
|
1207
1552
|
end
|
|
1208
1553
|
|
|
1209
|
-
# @return
|
|
1554
|
+
# @rbs return: bool -- Whether the page is closed
|
|
1210
1555
|
def closed?
|
|
1211
1556
|
@closed
|
|
1212
1557
|
end
|
|
1213
1558
|
|
|
1214
1559
|
attr_reader :mouse
|
|
1215
1560
|
|
|
1216
|
-
# @
|
|
1217
|
-
# @
|
|
1218
|
-
# @
|
|
1219
|
-
# @
|
|
1220
|
-
|
|
1221
|
-
|
|
1561
|
+
# @rbs selector: String -- CSS selector
|
|
1562
|
+
# @rbs delay: Numeric? -- Delay between down and up (ms)
|
|
1563
|
+
# @rbs button: String? -- Mouse button
|
|
1564
|
+
# @rbs click_count: Integer? -- Deprecated: use count (click_count only sets clickCount)
|
|
1565
|
+
# @rbs count: Integer? -- Number of clicks to perform
|
|
1566
|
+
# @rbs return: void -- No return value
|
|
1567
|
+
def click(selector, delay: nil, button: nil, click_count: nil, count: nil)
|
|
1568
|
+
main_frame.click(selector, delay: delay, button: button, click_count: click_count, count: count)
|
|
1222
1569
|
end
|
|
1223
1570
|
|
|
1224
1571
|
define_async_method :async_click
|
|
1225
1572
|
|
|
1226
|
-
# @
|
|
1573
|
+
# @rbs selector: String -- CSS selector
|
|
1574
|
+
# @rbs return: void -- No return value
|
|
1227
1575
|
def focus(selector)
|
|
1228
1576
|
main_frame.focus(selector)
|
|
1229
1577
|
end
|
|
1230
1578
|
|
|
1231
1579
|
define_async_method :async_focus
|
|
1232
1580
|
|
|
1233
|
-
# @
|
|
1581
|
+
# @rbs selector: String -- CSS selector
|
|
1582
|
+
# @rbs return: void -- No return value
|
|
1234
1583
|
def hover(selector)
|
|
1235
1584
|
main_frame.hover(selector)
|
|
1236
1585
|
end
|
|
1237
1586
|
|
|
1238
|
-
# @
|
|
1239
|
-
# @
|
|
1240
|
-
# @return
|
|
1587
|
+
# @rbs selector: String -- CSS selector
|
|
1588
|
+
# @rbs values: Array[String] -- Option values to select
|
|
1589
|
+
# @rbs return: Array[String] -- Selected values
|
|
1241
1590
|
def select(selector, *values)
|
|
1242
1591
|
main_frame.select(selector, *values)
|
|
1243
1592
|
end
|
|
1244
1593
|
|
|
1245
1594
|
define_async_method :async_select
|
|
1246
1595
|
|
|
1247
|
-
# @
|
|
1596
|
+
# @rbs selector: String? -- CSS selector
|
|
1597
|
+
# @rbs block: Proc? -- Optional block for Object#tap usage
|
|
1598
|
+
# @rbs return: Puppeteer::Page | nil -- Page instance or nil
|
|
1248
1599
|
def tap(selector: nil, &block)
|
|
1249
1600
|
# resolves double meaning of tap.
|
|
1250
1601
|
if selector.nil? && block
|
|
@@ -1253,54 +1604,60 @@ class Puppeteer::Page
|
|
|
1253
1604
|
# browser.new_page.tap do |page|
|
|
1254
1605
|
# ...
|
|
1255
1606
|
# end
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
# Puppeteer's Page#tap.
|
|
1259
|
-
main_frame.tap(selector)
|
|
1607
|
+
block.call(self)
|
|
1608
|
+
return self
|
|
1260
1609
|
end
|
|
1610
|
+
|
|
1611
|
+
# Puppeteer's Page#tap.
|
|
1612
|
+
main_frame.tap(selector)
|
|
1613
|
+
nil
|
|
1261
1614
|
end
|
|
1262
1615
|
|
|
1263
1616
|
define_async_method :async_tap
|
|
1264
1617
|
|
|
1265
|
-
# @
|
|
1266
|
-
# @
|
|
1267
|
-
# @
|
|
1618
|
+
# @rbs selector: String -- CSS selector
|
|
1619
|
+
# @rbs text: String -- Text to type
|
|
1620
|
+
# @rbs delay: Numeric? -- Delay between key presses (ms)
|
|
1621
|
+
# @rbs return: void -- No return value
|
|
1268
1622
|
def type_text(selector, text, delay: nil)
|
|
1269
1623
|
main_frame.type_text(selector, text, delay: delay)
|
|
1270
1624
|
end
|
|
1271
1625
|
|
|
1272
1626
|
define_async_method :async_type_text
|
|
1273
1627
|
|
|
1274
|
-
# @
|
|
1275
|
-
# @
|
|
1276
|
-
# @
|
|
1277
|
-
# @
|
|
1628
|
+
# @rbs selector: String -- CSS selector
|
|
1629
|
+
# @rbs visible: bool? -- Wait for element to be visible
|
|
1630
|
+
# @rbs hidden: bool? -- Wait for element to be hidden
|
|
1631
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
1632
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
1278
1633
|
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
|
|
1279
1634
|
main_frame.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
|
|
1280
1635
|
end
|
|
1281
1636
|
|
|
1282
1637
|
define_async_method :async_wait_for_selector
|
|
1283
1638
|
|
|
1284
|
-
# @
|
|
1639
|
+
# @rbs milliseconds: Numeric -- Time to wait in milliseconds
|
|
1640
|
+
# @rbs return: void -- No return value
|
|
1285
1641
|
def wait_for_timeout(milliseconds)
|
|
1286
1642
|
main_frame.wait_for_timeout(milliseconds)
|
|
1287
1643
|
end
|
|
1288
1644
|
|
|
1289
|
-
# @
|
|
1290
|
-
# @
|
|
1291
|
-
# @
|
|
1292
|
-
# @
|
|
1645
|
+
# @rbs xpath: String -- XPath expression
|
|
1646
|
+
# @rbs visible: bool? -- Wait for element to be visible
|
|
1647
|
+
# @rbs hidden: bool? -- Wait for element to be hidden
|
|
1648
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
1649
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
1293
1650
|
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
|
|
1294
1651
|
main_frame.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
|
|
1295
1652
|
end
|
|
1296
1653
|
|
|
1297
1654
|
define_async_method :async_wait_for_xpath
|
|
1298
1655
|
|
|
1299
|
-
# @
|
|
1300
|
-
# @
|
|
1301
|
-
# @
|
|
1302
|
-
# @
|
|
1303
|
-
# @return
|
|
1656
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
1657
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
1658
|
+
# @rbs polling: String | Numeric | nil -- Polling strategy
|
|
1659
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
1660
|
+
# @rbs return: Puppeteer::JSHandle -- Handle to evaluation result
|
|
1304
1661
|
def wait_for_function(page_function, args: [], polling: nil, timeout: nil)
|
|
1305
1662
|
main_frame.wait_for_function(page_function, args: args, polling: polling, timeout: timeout)
|
|
1306
1663
|
end
|