puppeteer-ruby 0.45.6 → 0.50.0.alpha5
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 +169 -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 +575 -0
- data/CLAUDE/rbs_type_checking.md +101 -0
- data/CLAUDE/spec_migration_plans.md +1041 -0
- data/CLAUDE/testing.md +278 -0
- data/CLAUDE.md +242 -0
- data/README.md +8 -0
- data/Rakefile +7 -0
- data/Steepfile +28 -0
- data/docs/api_coverage.md +105 -56
- 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 +231 -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 +162 -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 +58 -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/mouse.rb +273 -64
- data/lib/puppeteer/network_event_manager.rb +7 -0
- data/lib/puppeteer/network_manager.rb +393 -112
- data/lib/puppeteer/page/screenshot_task_queue.rb +14 -4
- data/lib/puppeteer/page.rb +568 -226
- data/lib/puppeteer/puppeteer.rb +171 -64
- data/lib/puppeteer/query_handler_manager.rb +112 -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 +20 -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 +305 -0
- data/sig/puppeteer/execution_context.rbs +87 -0
- data/sig/puppeteer/frame.rbs +226 -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/mouse.rbs +113 -0
- data/sig/puppeteer/page.rbs +515 -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 +116 -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,79 @@ 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
|
|
|
327
462
|
# `$()` in JavaScript.
|
|
328
|
-
# @
|
|
329
|
-
# @return
|
|
463
|
+
# @rbs selector: String -- CSS selector
|
|
464
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
330
465
|
def query_selector(selector)
|
|
331
466
|
main_frame.query_selector(selector)
|
|
332
467
|
end
|
|
@@ -335,18 +470,19 @@ class Puppeteer::Page
|
|
|
335
470
|
define_async_method :async_query_selector
|
|
336
471
|
|
|
337
472
|
# `$$()` in JavaScript.
|
|
338
|
-
# @
|
|
339
|
-
# @
|
|
340
|
-
|
|
341
|
-
|
|
473
|
+
# @rbs selector: String -- CSS selector
|
|
474
|
+
# @rbs isolate: bool? -- Use isolated world for queries
|
|
475
|
+
# @rbs return: Array[Puppeteer::ElementHandle] -- Matching elements
|
|
476
|
+
def query_selector_all(selector, isolate: nil)
|
|
477
|
+
main_frame.query_selector_all(selector, isolate: isolate)
|
|
342
478
|
end
|
|
343
479
|
alias_method :SS, :query_selector_all
|
|
344
480
|
|
|
345
481
|
define_async_method :async_query_selector_all
|
|
346
482
|
|
|
347
|
-
# @
|
|
348
|
-
# @
|
|
349
|
-
# @return
|
|
483
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
484
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
485
|
+
# @rbs return: Puppeteer::JSHandle -- Handle to evaluation result
|
|
350
486
|
def evaluate_handle(page_function, *args)
|
|
351
487
|
context = main_frame.execution_context
|
|
352
488
|
context.evaluate_handle(page_function, *args)
|
|
@@ -354,17 +490,18 @@ class Puppeteer::Page
|
|
|
354
490
|
|
|
355
491
|
define_async_method :async_evaluate_handle
|
|
356
492
|
|
|
357
|
-
# @
|
|
358
|
-
# @return
|
|
493
|
+
# @rbs prototype_handle: Puppeteer::JSHandle -- Prototype handle
|
|
494
|
+
# @rbs return: Puppeteer::JSHandle -- Handle to query result
|
|
359
495
|
def query_objects(prototype_handle)
|
|
360
496
|
context = main_frame.execution_context
|
|
361
497
|
context.query_objects(prototype_handle)
|
|
362
498
|
end
|
|
363
499
|
|
|
364
500
|
# `$eval()` in JavaScript.
|
|
365
|
-
# @
|
|
366
|
-
# @
|
|
367
|
-
# @
|
|
501
|
+
# @rbs selector: String -- CSS selector
|
|
502
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
503
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
504
|
+
# @rbs return: untyped -- Evaluation result
|
|
368
505
|
def eval_on_selector(selector, page_function, *args)
|
|
369
506
|
main_frame.eval_on_selector(selector, page_function, *args)
|
|
370
507
|
end
|
|
@@ -373,9 +510,10 @@ class Puppeteer::Page
|
|
|
373
510
|
define_async_method :async_eval_on_selector
|
|
374
511
|
|
|
375
512
|
# `$$eval()` in JavaScript.
|
|
376
|
-
# @
|
|
377
|
-
# @
|
|
378
|
-
# @
|
|
513
|
+
# @rbs selector: String -- CSS selector
|
|
514
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
515
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
516
|
+
# @rbs return: untyped -- Evaluation result
|
|
379
517
|
def eval_on_selector_all(selector, page_function, *args)
|
|
380
518
|
main_frame.eval_on_selector_all(selector, page_function, *args)
|
|
381
519
|
end
|
|
@@ -384,15 +522,16 @@ class Puppeteer::Page
|
|
|
384
522
|
define_async_method :async_eval_on_selector_all
|
|
385
523
|
|
|
386
524
|
# `$x()` in JavaScript. $ is not allowed to use as a method name in Ruby.
|
|
387
|
-
# @
|
|
388
|
-
# @return
|
|
525
|
+
# @rbs expression: String -- XPath expression
|
|
526
|
+
# @rbs return: Array[Puppeteer::ElementHandle] -- Matching elements
|
|
389
527
|
def Sx(expression)
|
|
390
528
|
main_frame.Sx(expression)
|
|
391
529
|
end
|
|
392
530
|
|
|
393
531
|
define_async_method :async_Sx
|
|
394
532
|
|
|
395
|
-
# @
|
|
533
|
+
# @rbs urls: Array[String] -- URLs to fetch cookies for
|
|
534
|
+
# @rbs return: Array[Hash[String, untyped]] -- Cookies list
|
|
396
535
|
def cookies(*urls)
|
|
397
536
|
@client.send_message('Network.getCookies', urls: (urls.empty? ? [url] : urls))['cookies']
|
|
398
537
|
end
|
|
@@ -406,6 +545,8 @@ class Puppeteer::Page
|
|
|
406
545
|
raise ArgumentError.new("Each coookie must have #{requires.join(" and ")} attribute.")
|
|
407
546
|
end
|
|
408
547
|
|
|
548
|
+
# @rbs cookies: Array[Hash[Symbol | String, untyped]] -- cookies parameter
|
|
549
|
+
# @rbs return: void -- No return value
|
|
409
550
|
def delete_cookie(*cookies)
|
|
410
551
|
assert_cookie_params(cookies, requires: %i(name))
|
|
411
552
|
|
|
@@ -417,6 +558,8 @@ class Puppeteer::Page
|
|
|
417
558
|
end
|
|
418
559
|
end
|
|
419
560
|
|
|
561
|
+
# @rbs cookies: Array[Hash[Symbol | String, untyped]] -- cookies parameter
|
|
562
|
+
# @rbs return: void -- No return value
|
|
420
563
|
def set_cookie(*cookies)
|
|
421
564
|
assert_cookie_params(cookies, requires: %i(name value))
|
|
422
565
|
|
|
@@ -434,24 +577,27 @@ class Puppeteer::Page
|
|
|
434
577
|
end
|
|
435
578
|
end
|
|
436
579
|
|
|
437
|
-
# @
|
|
438
|
-
# @
|
|
439
|
-
# @
|
|
440
|
-
# @
|
|
441
|
-
# @
|
|
580
|
+
# @rbs url: String? -- Script URL
|
|
581
|
+
# @rbs path: String? -- Path to script file
|
|
582
|
+
# @rbs content: String? -- Script contents
|
|
583
|
+
# @rbs type: String? -- Script type
|
|
584
|
+
# @rbs id: String? -- Script element ID
|
|
585
|
+
# @rbs return: Puppeteer::ElementHandle -- Script element handle
|
|
442
586
|
def add_script_tag(url: nil, path: nil, content: nil, type: nil, id: nil)
|
|
443
587
|
main_frame.add_script_tag(url: url, path: path, content: content, type: type, id: id)
|
|
444
588
|
end
|
|
445
589
|
|
|
446
|
-
# @
|
|
447
|
-
# @
|
|
448
|
-
# @
|
|
590
|
+
# @rbs url: String? -- Stylesheet URL
|
|
591
|
+
# @rbs path: String? -- Path to stylesheet file
|
|
592
|
+
# @rbs content: String? -- Stylesheet contents
|
|
593
|
+
# @rbs return: Puppeteer::ElementHandle -- Style element handle
|
|
449
594
|
def add_style_tag(url: nil, path: nil, content: nil)
|
|
450
595
|
main_frame.add_style_tag(url: url, path: path, content: content)
|
|
451
596
|
end
|
|
452
597
|
|
|
453
|
-
# @
|
|
454
|
-
# @
|
|
598
|
+
# @rbs name: String -- Binding name
|
|
599
|
+
# @rbs puppeteer_function: Proc -- Ruby callback
|
|
600
|
+
# @rbs return: void -- No return value
|
|
455
601
|
def expose_function(name, puppeteer_function)
|
|
456
602
|
if @page_bindings[name]
|
|
457
603
|
raise ArgumentError.new("Failed to add page binding with name `#{name}` already exists!")
|
|
@@ -486,42 +632,79 @@ class Puppeteer::Page
|
|
|
486
632
|
|
|
487
633
|
source = JavaScriptFunction.new(add_page_binding, ['exposedFun', name]).source
|
|
488
634
|
@client.send_message('Runtime.addBinding', name: name)
|
|
489
|
-
@client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
|
|
635
|
+
script = @client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
|
|
636
|
+
@page_binding_ids[name] = script['identifier']
|
|
490
637
|
|
|
491
638
|
promises = @frame_manager.frames.map do |frame|
|
|
492
639
|
frame.async_evaluate("() => #{source}")
|
|
493
640
|
end
|
|
494
|
-
|
|
641
|
+
Puppeteer::AsyncUtils.await_promise_all(*promises)
|
|
642
|
+
|
|
643
|
+
nil
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
# @rbs name: String -- Binding name
|
|
647
|
+
# @rbs return: void -- No return value
|
|
648
|
+
def remove_exposed_function(name)
|
|
649
|
+
identifier = @page_binding_ids[name]
|
|
650
|
+
unless identifier
|
|
651
|
+
raise ArgumentError.new("Function with name \"#{name}\" does not exist")
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
@page_binding_ids.delete(name)
|
|
655
|
+
@page_bindings.delete(name)
|
|
656
|
+
|
|
657
|
+
@client.send_message('Runtime.removeBinding', name: name)
|
|
658
|
+
@client.send_message('Page.removeScriptToEvaluateOnNewDocument', identifier: identifier)
|
|
495
659
|
|
|
660
|
+
remove_script = '(name) => { delete window[name]; }'
|
|
661
|
+
@frame_manager.frames.each do |frame|
|
|
662
|
+
frame.evaluate(remove_script, name)
|
|
663
|
+
rescue StandardError
|
|
664
|
+
nil
|
|
665
|
+
end
|
|
496
666
|
nil
|
|
497
667
|
end
|
|
498
668
|
|
|
499
|
-
# @
|
|
500
|
-
# @
|
|
669
|
+
# @rbs username: String? -- HTTP basic auth username
|
|
670
|
+
# @rbs password: String? -- HTTP basic auth password
|
|
671
|
+
# @rbs return: void -- No return value
|
|
501
672
|
def authenticate(username: nil, password: nil)
|
|
502
673
|
@frame_manager.network_manager.authenticate(username: username, password: password)
|
|
503
674
|
end
|
|
504
675
|
|
|
505
|
-
# @
|
|
676
|
+
# @rbs headers: Hash[String, String] -- Extra HTTP headers
|
|
677
|
+
# @rbs return: void -- No return value
|
|
506
678
|
def extra_http_headers=(headers)
|
|
507
679
|
@frame_manager.network_manager.extra_http_headers = headers
|
|
508
680
|
end
|
|
509
681
|
|
|
510
|
-
# @
|
|
511
|
-
# @
|
|
682
|
+
# @rbs user_agent: String -- User agent string
|
|
683
|
+
# @rbs user_agent_metadata: Hash[String, untyped]? -- User agent metadata
|
|
684
|
+
# @rbs return: void -- No return value
|
|
512
685
|
def set_user_agent(user_agent, user_agent_metadata = nil)
|
|
513
686
|
@frame_manager.network_manager.set_user_agent(user_agent, user_agent_metadata)
|
|
514
687
|
end
|
|
515
688
|
alias_method :user_agent=, :set_user_agent
|
|
516
689
|
|
|
690
|
+
# @rbs return: Puppeteer::Page::Metrics -- Page metrics
|
|
517
691
|
def metrics
|
|
518
692
|
response = @client.send_message('Performance.getMetrics')
|
|
519
693
|
Metrics.new(response['metrics'])
|
|
520
694
|
end
|
|
521
695
|
|
|
522
|
-
class PageError <
|
|
696
|
+
class PageError < Puppeteer::Error ; end
|
|
523
697
|
|
|
524
698
|
private def handle_exception(exception_details)
|
|
699
|
+
exception = exception_details['exception']
|
|
700
|
+
if exception
|
|
701
|
+
is_error_object = exception['type'] == 'object' && exception['subtype'] == 'error'
|
|
702
|
+
if !is_error_object && !exception.key?('objectId')
|
|
703
|
+
emit_event(PageEmittedEvents::PageError, Puppeteer::RemoteObject.new(exception).value)
|
|
704
|
+
return
|
|
705
|
+
end
|
|
706
|
+
end
|
|
707
|
+
|
|
525
708
|
message = Puppeteer::ExceptionDetails.new(exception_details).message
|
|
526
709
|
err = PageError.new(message)
|
|
527
710
|
# err.stack = ''; // Don't report clientside error with a node stack attached
|
|
@@ -554,6 +737,8 @@ class Puppeteer::Page
|
|
|
554
737
|
add_console_message(event['type'], values, event['stackTrace'])
|
|
555
738
|
end
|
|
556
739
|
|
|
740
|
+
# @rbs event: Hash[String, untyped] -- Binding called payload
|
|
741
|
+
# @rbs return: void -- No return value
|
|
557
742
|
def handle_binding_called(event)
|
|
558
743
|
execution_context_id = event['executionContextId']
|
|
559
744
|
payload =
|
|
@@ -595,13 +780,18 @@ class Puppeteer::Page
|
|
|
595
780
|
JavaScriptFunction.new(deliver_error, [name, seq, err.message]).source
|
|
596
781
|
end
|
|
597
782
|
|
|
598
|
-
|
|
783
|
+
Async do
|
|
784
|
+
@client.async_send_message('Runtime.evaluate', expression: expression, contextId: execution_context_id).wait
|
|
785
|
+
rescue => error
|
|
599
786
|
debug_puts(error)
|
|
600
787
|
end
|
|
601
788
|
end
|
|
602
789
|
|
|
603
790
|
private def add_console_message(type, args, stack_trace)
|
|
604
|
-
text_tokens = args.map
|
|
791
|
+
text_tokens = args.map do |arg|
|
|
792
|
+
value = arg.remote_object.value
|
|
793
|
+
value.nil? ? arg.to_s : value
|
|
794
|
+
end
|
|
605
795
|
|
|
606
796
|
stack_trace_locations =
|
|
607
797
|
if stack_trace && stack_trace['callFrames']
|
|
@@ -624,7 +814,7 @@ class Puppeteer::Page
|
|
|
624
814
|
unless %w(alert confirm prompt beforeunload).include?(dialog_type)
|
|
625
815
|
raise ArgumentError.new("Unknown javascript dialog type: #{dialog_type}")
|
|
626
816
|
end
|
|
627
|
-
dialog = Puppeteer::
|
|
817
|
+
dialog = Puppeteer::CdpDialog.new(@client,
|
|
628
818
|
type: dialog_type,
|
|
629
819
|
message: event['message'],
|
|
630
820
|
default_value: event['defaultPrompt'])
|
|
@@ -641,72 +831,101 @@ class Puppeteer::Page
|
|
|
641
831
|
@client.send_message('Emulation.setDefaultBackgroundColorOverride')
|
|
642
832
|
end
|
|
643
833
|
|
|
644
|
-
# @return
|
|
834
|
+
# @rbs return: String? -- Page URL
|
|
645
835
|
def url
|
|
646
836
|
main_frame.url
|
|
647
837
|
end
|
|
648
838
|
|
|
649
|
-
# @return
|
|
839
|
+
# @rbs return: String -- Page HTML content
|
|
650
840
|
def content
|
|
651
841
|
main_frame.content
|
|
652
842
|
end
|
|
653
843
|
|
|
654
|
-
# @
|
|
655
|
-
# @
|
|
656
|
-
# @
|
|
844
|
+
# @rbs html: String -- HTML content
|
|
845
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
846
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
847
|
+
# @rbs return: void -- No return value
|
|
657
848
|
def set_content(html, timeout: nil, wait_until: nil)
|
|
658
849
|
main_frame.set_content(html, timeout: timeout, wait_until: wait_until)
|
|
659
850
|
end
|
|
660
851
|
|
|
661
|
-
# @
|
|
852
|
+
# @rbs html: String -- HTML content
|
|
853
|
+
# @rbs return: void -- No return value
|
|
662
854
|
def content=(html)
|
|
663
855
|
main_frame.set_content(html)
|
|
664
856
|
end
|
|
665
857
|
|
|
666
|
-
# @
|
|
667
|
-
# @
|
|
668
|
-
# @
|
|
669
|
-
# @
|
|
670
|
-
|
|
671
|
-
|
|
858
|
+
# @rbs url: String -- URL to navigate
|
|
859
|
+
# @rbs referer: String? -- Referer header value
|
|
860
|
+
# @rbs referer: String? -- Referer header value
|
|
861
|
+
# @rbs referrer_policy: String? -- Referrer policy
|
|
862
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
863
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
864
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
865
|
+
def goto(url, referer: nil, referrer_policy: nil, timeout: nil, wait_until: nil)
|
|
866
|
+
main_frame.goto(
|
|
867
|
+
url,
|
|
868
|
+
referer: referer,
|
|
869
|
+
referrer_policy: referrer_policy,
|
|
870
|
+
timeout: timeout,
|
|
871
|
+
wait_until: wait_until,
|
|
872
|
+
)
|
|
672
873
|
end
|
|
673
874
|
|
|
674
|
-
# @
|
|
675
|
-
# @
|
|
676
|
-
# @
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
875
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
876
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
877
|
+
# @rbs ignore_cache: bool? -- Skip cache when reloading
|
|
878
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
879
|
+
def reload(timeout: nil, wait_until: nil, ignore_cache: nil)
|
|
880
|
+
params = {}
|
|
881
|
+
params[:ignoreCache] = ignore_cache unless ignore_cache.nil?
|
|
882
|
+
|
|
883
|
+
wait_for_navigation(timeout: timeout, wait_until: wait_until, ignore_same_document_navigation: true) do
|
|
884
|
+
if params.empty?
|
|
885
|
+
@client.send_message('Page.reload')
|
|
886
|
+
else
|
|
887
|
+
@client.send_message('Page.reload', **params)
|
|
888
|
+
end
|
|
680
889
|
end
|
|
681
890
|
end
|
|
682
891
|
|
|
683
|
-
|
|
684
|
-
|
|
892
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
893
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
894
|
+
# @rbs ignore_same_document_navigation: bool -- Ignore same-document navigation
|
|
895
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
896
|
+
def wait_for_navigation(timeout: nil, wait_until: nil, ignore_same_document_navigation: false)
|
|
897
|
+
main_frame.send(
|
|
898
|
+
:wait_for_navigation,
|
|
899
|
+
timeout: timeout,
|
|
900
|
+
wait_until: wait_until,
|
|
901
|
+
ignore_same_document_navigation: ignore_same_document_navigation,
|
|
902
|
+
)
|
|
685
903
|
end
|
|
686
904
|
|
|
687
905
|
# @!method async_wait_for_navigation(timeout: nil, wait_until: nil)
|
|
688
906
|
#
|
|
689
|
-
# @param timeout [number|nil]
|
|
690
|
-
# @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
|
|
691
907
|
define_async_method :async_wait_for_navigation
|
|
692
908
|
|
|
693
909
|
private def wait_for_network_manager_event(event_name, predicate:, timeout:)
|
|
694
910
|
option_timeout = timeout || @timeout_settings.timeout
|
|
695
911
|
|
|
696
|
-
promise =
|
|
912
|
+
promise = Async::Promise.new
|
|
697
913
|
|
|
698
914
|
listener_id = @frame_manager.network_manager.add_event_listener(event_name) do |event_target|
|
|
699
|
-
if predicate.call(event_target)
|
|
700
|
-
promise.
|
|
915
|
+
if Puppeteer::AsyncUtils.await(predicate.call(event_target))
|
|
916
|
+
promise.resolve(event_target)
|
|
701
917
|
end
|
|
702
918
|
end
|
|
703
919
|
|
|
704
920
|
begin
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
921
|
+
if option_timeout == 0
|
|
922
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
923
|
+
else
|
|
924
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, -> {
|
|
925
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
926
|
+
}).wait
|
|
708
927
|
end
|
|
709
|
-
rescue
|
|
928
|
+
rescue Async::TimeoutError
|
|
710
929
|
raise Puppeteer::TimeoutError.new("waiting for #{event_name} failed: timeout #{option_timeout}ms exceeded")
|
|
711
930
|
ensure
|
|
712
931
|
@frame_manager.network_manager.remove_event_listener(listener_id)
|
|
@@ -716,22 +935,25 @@ class Puppeteer::Page
|
|
|
716
935
|
private def wait_for_frame_manager_event(*event_names, predicate:, timeout:)
|
|
717
936
|
option_timeout = timeout || @timeout_settings.timeout
|
|
718
937
|
|
|
719
|
-
promise =
|
|
938
|
+
promise = Async::Promise.new
|
|
720
939
|
|
|
721
940
|
listener_ids = event_names.map do |event_name|
|
|
722
941
|
@frame_manager.add_event_listener(event_name) do |event_target|
|
|
723
|
-
if predicate.call(event_target)
|
|
724
|
-
promise.
|
|
942
|
+
if Puppeteer::AsyncUtils.await(predicate.call(event_target))
|
|
943
|
+
promise.resolve(event_target) unless promise.resolved?
|
|
725
944
|
end
|
|
726
945
|
end
|
|
727
946
|
end
|
|
728
947
|
|
|
729
948
|
begin
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
949
|
+
if option_timeout == 0
|
|
950
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
951
|
+
else
|
|
952
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, -> {
|
|
953
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
954
|
+
}).wait
|
|
733
955
|
end
|
|
734
|
-
rescue
|
|
956
|
+
rescue Async::TimeoutError
|
|
735
957
|
raise Puppeteer::TimeoutError.new("waiting for #{event_names.join(" or ")} failed: timeout #{option_timeout}ms exceeded")
|
|
736
958
|
ensure
|
|
737
959
|
listener_ids.each do |listener_id|
|
|
@@ -741,13 +963,17 @@ class Puppeteer::Page
|
|
|
741
963
|
end
|
|
742
964
|
|
|
743
965
|
private def session_close_promise
|
|
744
|
-
@disconnect_promise ||=
|
|
966
|
+
@disconnect_promise ||= Async::Promise.new.tap do |future|
|
|
745
967
|
@client.observe_first(CDPSessionEmittedEvents::Disconnected) do
|
|
746
968
|
future.reject(Puppeteer::CDPSession::Error.new('Target Closed'))
|
|
747
969
|
end
|
|
748
970
|
end
|
|
749
971
|
end
|
|
750
972
|
|
|
973
|
+
# @rbs url: String? -- URL to match
|
|
974
|
+
# @rbs predicate: Proc? -- Predicate to match
|
|
975
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
976
|
+
# @rbs return: Puppeteer::HTTPRequest -- Matching request
|
|
751
977
|
def wait_for_request(url: nil, predicate: nil, timeout: nil)
|
|
752
978
|
if !url && !predicate
|
|
753
979
|
raise ArgumentError.new('url or predicate must be specified')
|
|
@@ -778,10 +1004,12 @@ class Puppeteer::Page
|
|
|
778
1004
|
# Waits until request matches the given predicate
|
|
779
1005
|
# wait_for_request(predicate: -> (req){ req.url.start_with?('https://example.com/search') })
|
|
780
1006
|
#
|
|
781
|
-
# @param url [String]
|
|
782
|
-
# @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)]
|
|
783
1007
|
define_async_method :async_wait_for_request
|
|
784
1008
|
|
|
1009
|
+
# @rbs url: String? -- URL to match
|
|
1010
|
+
# @rbs predicate: Proc? -- Predicate to match
|
|
1011
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
1012
|
+
# @rbs return: Puppeteer::HTTPResponse -- Matching response
|
|
785
1013
|
def wait_for_response(url: nil, predicate: nil, timeout: nil)
|
|
786
1014
|
if !url && !predicate
|
|
787
1015
|
raise ArgumentError.new('url or predicate must be specified')
|
|
@@ -804,10 +1032,68 @@ class Puppeteer::Page
|
|
|
804
1032
|
|
|
805
1033
|
# @!method async_wait_for_response(url: nil, predicate: nil, timeout: nil)
|
|
806
1034
|
#
|
|
807
|
-
# @param url [String]
|
|
808
|
-
# @param predicate [Proc(Puppeteer::HTTPRequest -> Boolean)]
|
|
809
1035
|
define_async_method :async_wait_for_response
|
|
810
1036
|
|
|
1037
|
+
# @rbs idle_time: Numeric -- Idle time to wait for in milliseconds
|
|
1038
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
1039
|
+
# @rbs concurrency: Integer -- Allowed number of concurrent requests
|
|
1040
|
+
# @rbs return: void -- No return value
|
|
1041
|
+
def wait_for_network_idle(idle_time: 500, timeout: nil, concurrency: 0)
|
|
1042
|
+
option_timeout = timeout || @timeout_settings.timeout
|
|
1043
|
+
|
|
1044
|
+
promise = Async::Promise.new
|
|
1045
|
+
idle_timer = nil
|
|
1046
|
+
|
|
1047
|
+
schedule_idle = lambda do
|
|
1048
|
+
return if @inflight_requests.size > concurrency
|
|
1049
|
+
|
|
1050
|
+
idle_timer&.stop
|
|
1051
|
+
idle_timer = Async do
|
|
1052
|
+
Puppeteer::AsyncUtils.sleep_seconds(idle_time / 1000.0)
|
|
1053
|
+
unless promise.resolved? || @inflight_requests.size > concurrency
|
|
1054
|
+
promise.resolve(nil)
|
|
1055
|
+
end
|
|
1056
|
+
end
|
|
1057
|
+
end
|
|
1058
|
+
|
|
1059
|
+
# Use raw listener to avoid request interception queue delaying idle tracking.
|
|
1060
|
+
request_listener = add_event_listener('request') do
|
|
1061
|
+
idle_timer&.stop
|
|
1062
|
+
idle_timer = nil
|
|
1063
|
+
end
|
|
1064
|
+
request_finished_listener = on('requestfinished') do
|
|
1065
|
+
schedule_idle.call
|
|
1066
|
+
end
|
|
1067
|
+
request_failed_listener = on('requestfailed') do
|
|
1068
|
+
schedule_idle.call
|
|
1069
|
+
end
|
|
1070
|
+
|
|
1071
|
+
schedule_idle.call
|
|
1072
|
+
|
|
1073
|
+
begin
|
|
1074
|
+
if option_timeout == 0
|
|
1075
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
1076
|
+
else
|
|
1077
|
+
Puppeteer::AsyncUtils.async_timeout(option_timeout, -> {
|
|
1078
|
+
Puppeteer::AsyncUtils.await_promise_race(promise, session_close_promise)
|
|
1079
|
+
}).wait
|
|
1080
|
+
end
|
|
1081
|
+
rescue Async::TimeoutError
|
|
1082
|
+
raise Puppeteer::TimeoutError.new("waiting for network idle failed: timeout #{option_timeout}ms exceeded")
|
|
1083
|
+
ensure
|
|
1084
|
+
off(request_listener)
|
|
1085
|
+
off(request_finished_listener)
|
|
1086
|
+
off(request_failed_listener)
|
|
1087
|
+
idle_timer&.stop
|
|
1088
|
+
end
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
define_async_method :async_wait_for_network_idle
|
|
1092
|
+
|
|
1093
|
+
# @rbs url: String? -- URL to match
|
|
1094
|
+
# @rbs predicate: Proc? -- Predicate to match
|
|
1095
|
+
# @rbs timeout: Numeric? -- Timeout in milliseconds
|
|
1096
|
+
# @rbs return: Puppeteer::Frame -- Matching frame
|
|
811
1097
|
def wait_for_frame(url: nil, predicate: nil, timeout: nil)
|
|
812
1098
|
if !url && !predicate
|
|
813
1099
|
raise ArgumentError.new('url or predicate must be specified')
|
|
@@ -836,18 +1122,18 @@ class Puppeteer::Page
|
|
|
836
1122
|
|
|
837
1123
|
# @!method async_wait_for_frame(url: nil, predicate: nil, timeout: nil)
|
|
838
1124
|
#
|
|
839
|
-
# @param url [String]
|
|
840
|
-
# @param predicate [Proc(Puppeteer::Frame -> Boolean)]
|
|
841
1125
|
define_async_method :async_wait_for_frame
|
|
842
1126
|
|
|
843
|
-
# @
|
|
844
|
-
# @
|
|
1127
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
1128
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
1129
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
845
1130
|
def go_back(timeout: nil, wait_until: nil)
|
|
846
1131
|
go(-1, timeout: timeout, wait_until: wait_until)
|
|
847
1132
|
end
|
|
848
1133
|
|
|
849
|
-
# @
|
|
850
|
-
# @
|
|
1134
|
+
# @rbs timeout: Numeric? -- Navigation timeout in milliseconds
|
|
1135
|
+
# @rbs wait_until: String | Array[String] | nil -- Lifecycle events to wait for
|
|
1136
|
+
# @rbs return: Puppeteer::HTTPResponse? -- Navigation response
|
|
851
1137
|
def go_forward(timeout: nil, wait_until: nil)
|
|
852
1138
|
go(+1, timeout: timeout, wait_until: wait_until)
|
|
853
1139
|
end
|
|
@@ -856,37 +1142,44 @@ class Puppeteer::Page
|
|
|
856
1142
|
history = @client.send_message('Page.getNavigationHistory')
|
|
857
1143
|
entries = history['entries']
|
|
858
1144
|
index = history['currentIndex'] + delta
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1145
|
+
if index < 0 || index >= entries.length
|
|
1146
|
+
raise Puppeteer::Error.new('History entry to navigate to not found.')
|
|
1147
|
+
end
|
|
1148
|
+
entry = entries[index]
|
|
1149
|
+
wait_for_navigation(timeout: timeout, wait_until: wait_until) do
|
|
1150
|
+
@client.send_message('Page.navigateToHistoryEntry', entryId: entry['id'])
|
|
863
1151
|
end
|
|
864
1152
|
end
|
|
865
1153
|
|
|
866
1154
|
# Brings page to front (activates tab).
|
|
1155
|
+
# @rbs return: void -- No return value
|
|
867
1156
|
def bring_to_front
|
|
868
1157
|
@client.send_message('Page.bringToFront')
|
|
869
1158
|
end
|
|
870
1159
|
|
|
871
|
-
# @
|
|
1160
|
+
# @rbs device: Puppeteer::Device -- Device descriptor
|
|
1161
|
+
# @rbs return: void -- No return value
|
|
872
1162
|
def emulate(device)
|
|
873
1163
|
self.viewport = device.viewport
|
|
874
1164
|
self.user_agent = device.user_agent
|
|
875
1165
|
end
|
|
876
1166
|
|
|
877
|
-
# @
|
|
1167
|
+
# @rbs enabled: bool -- Enable JavaScript
|
|
1168
|
+
# @rbs return: void -- No return value
|
|
878
1169
|
def javascript_enabled=(enabled)
|
|
879
1170
|
return if @javascript_enabled == enabled
|
|
880
1171
|
@javascript_enabled = enabled
|
|
881
1172
|
@client.send_message('Emulation.setScriptExecutionDisabled', value: !enabled)
|
|
882
1173
|
end
|
|
883
1174
|
|
|
884
|
-
# @
|
|
1175
|
+
# @rbs enabled: bool -- Enable bypassing CSP
|
|
1176
|
+
# @rbs return: void -- No return value
|
|
885
1177
|
def bypass_csp=(enabled)
|
|
886
1178
|
@client.send_message('Page.setBypassCSP', enabled: enabled)
|
|
887
1179
|
end
|
|
888
1180
|
|
|
889
|
-
# @
|
|
1181
|
+
# @rbs media_type: (String | Symbol)? -- Media type override
|
|
1182
|
+
# @rbs return: void -- No return value
|
|
890
1183
|
def emulate_media_type(media_type)
|
|
891
1184
|
media_type_str = media_type.to_s
|
|
892
1185
|
unless ['screen', 'print', ''].include?(media_type_str)
|
|
@@ -895,7 +1188,8 @@ class Puppeteer::Page
|
|
|
895
1188
|
@client.send_message('Emulation.setEmulatedMedia', media: media_type_str)
|
|
896
1189
|
end
|
|
897
1190
|
|
|
898
|
-
# @
|
|
1191
|
+
# @rbs factor: Numeric? -- CPU throttling rate
|
|
1192
|
+
# @rbs return: void -- No return value
|
|
899
1193
|
def emulate_cpu_throttling(factor)
|
|
900
1194
|
if factor.nil? || factor >= 1
|
|
901
1195
|
@client.send_message('Emulation.setCPUThrottlingRate', rate: factor || 1)
|
|
@@ -904,7 +1198,8 @@ class Puppeteer::Page
|
|
|
904
1198
|
end
|
|
905
1199
|
end
|
|
906
1200
|
|
|
907
|
-
# @
|
|
1201
|
+
# @rbs features: Array[Hash[Symbol, untyped]]? -- Media feature overrides
|
|
1202
|
+
# @rbs return: void -- No return value
|
|
908
1203
|
def emulate_media_features(features)
|
|
909
1204
|
if features.nil?
|
|
910
1205
|
@client.send_message('Emulation.setEmulatedMedia', features: nil)
|
|
@@ -919,7 +1214,8 @@ class Puppeteer::Page
|
|
|
919
1214
|
end
|
|
920
1215
|
end
|
|
921
1216
|
|
|
922
|
-
# @
|
|
1217
|
+
# @rbs timezone_id: String? -- Timezone ID
|
|
1218
|
+
# @rbs return: void -- No return value
|
|
923
1219
|
def emulate_timezone(timezone_id)
|
|
924
1220
|
@client.send_message('Emulation.setTimezoneOverride', timezoneId: timezone_id || '')
|
|
925
1221
|
rescue => err
|
|
@@ -939,6 +1235,8 @@ class Puppeteer::Page
|
|
|
939
1235
|
tritanopia
|
|
940
1236
|
].freeze
|
|
941
1237
|
|
|
1238
|
+
# @rbs vision_deficiency_type: String? -- Vision deficiency type
|
|
1239
|
+
# @rbs return: void -- No return value
|
|
942
1240
|
def emulate_vision_deficiency(vision_deficiency_type)
|
|
943
1241
|
value = vision_deficiency_type || 'none'
|
|
944
1242
|
unless VISION_DEFICIENCY_TYPES.include?(value)
|
|
@@ -947,8 +1245,9 @@ class Puppeteer::Page
|
|
|
947
1245
|
@client.send_message('Emulation.setEmulatedVisionDeficiency', type: value)
|
|
948
1246
|
end
|
|
949
1247
|
|
|
950
|
-
# @
|
|
951
|
-
# @
|
|
1248
|
+
# @rbs is_user_active: bool? -- User activity override
|
|
1249
|
+
# @rbs is_screen_unlocked: bool? -- Screen unlocked override
|
|
1250
|
+
# @rbs return: void -- No return value
|
|
952
1251
|
def emulate_idle_state(is_user_active: nil, is_screen_unlocked: nil)
|
|
953
1252
|
overrides = {
|
|
954
1253
|
isUserActive: is_user_active,
|
|
@@ -962,7 +1261,8 @@ class Puppeteer::Page
|
|
|
962
1261
|
end
|
|
963
1262
|
end
|
|
964
1263
|
|
|
965
|
-
# @
|
|
1264
|
+
# @rbs viewport: Puppeteer::Viewport? -- Viewport settings
|
|
1265
|
+
# @rbs return: void -- No return value
|
|
966
1266
|
def viewport=(viewport)
|
|
967
1267
|
needs_reload = @emulation_manager.emulate_viewport(viewport)
|
|
968
1268
|
@viewport = viewport
|
|
@@ -971,9 +1271,9 @@ class Puppeteer::Page
|
|
|
971
1271
|
|
|
972
1272
|
attr_reader :viewport
|
|
973
1273
|
|
|
974
|
-
# @
|
|
975
|
-
# @
|
|
976
|
-
# @return
|
|
1274
|
+
# @rbs page_function: String -- page_function parameter
|
|
1275
|
+
# @rbs args: Array[untyped] -- args parameter
|
|
1276
|
+
# @rbs return: untyped -- Result
|
|
977
1277
|
def evaluate(page_function, *args)
|
|
978
1278
|
main_frame.evaluate(page_function, *args)
|
|
979
1279
|
end
|
|
@@ -981,11 +1281,15 @@ class Puppeteer::Page
|
|
|
981
1281
|
define_async_method :async_evaluate
|
|
982
1282
|
|
|
983
1283
|
class JavaScriptFunction
|
|
1284
|
+
# @rbs expression: String -- Function expression
|
|
1285
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
1286
|
+
# @rbs return: void -- No return value
|
|
984
1287
|
def initialize(expression, args)
|
|
985
1288
|
@expression = expression
|
|
986
1289
|
@args = args
|
|
987
1290
|
end
|
|
988
1291
|
|
|
1292
|
+
# @rbs return: String -- Generated source
|
|
989
1293
|
def source
|
|
990
1294
|
"(#{@expression})(#{arguments})"
|
|
991
1295
|
end
|
|
@@ -996,15 +1300,21 @@ class Puppeteer::Page
|
|
|
996
1300
|
end
|
|
997
1301
|
|
|
998
1302
|
class JavaScriptExpression
|
|
1303
|
+
# @rbs expression: String -- Expression to evaluate
|
|
1304
|
+
# @rbs return: void -- No return value
|
|
999
1305
|
def initialize(expression)
|
|
1000
1306
|
@expression = expression
|
|
1001
1307
|
end
|
|
1002
1308
|
|
|
1309
|
+
# @rbs return: String -- Generated source
|
|
1003
1310
|
def source
|
|
1004
1311
|
@expression
|
|
1005
1312
|
end
|
|
1006
1313
|
end
|
|
1007
1314
|
|
|
1315
|
+
# @rbs page_function: String -- page_function parameter
|
|
1316
|
+
# @rbs args: Array[untyped] -- args parameter
|
|
1317
|
+
# @rbs return: Hash[String, untyped] -- CDP response
|
|
1008
1318
|
def evaluate_on_new_document(page_function, *args)
|
|
1009
1319
|
source =
|
|
1010
1320
|
if ['=>', 'async', 'function'].any? { |keyword| page_function.include?(keyword) }
|
|
@@ -1016,23 +1326,32 @@ class Puppeteer::Page
|
|
|
1016
1326
|
@client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
|
|
1017
1327
|
end
|
|
1018
1328
|
|
|
1019
|
-
# @
|
|
1329
|
+
# @rbs identifier: String -- Script identifier to remove
|
|
1330
|
+
# @rbs return: void
|
|
1331
|
+
def remove_script_to_evaluate_on_new_document(identifier)
|
|
1332
|
+
@client.send_message('Page.removeScriptToEvaluateOnNewDocument', identifier: identifier)
|
|
1333
|
+
end
|
|
1334
|
+
|
|
1335
|
+
# @rbs enabled: bool -- Enable cache usage
|
|
1020
1336
|
def cache_enabled=(enabled)
|
|
1021
1337
|
@frame_manager.network_manager.cache_enabled = enabled
|
|
1022
1338
|
end
|
|
1023
1339
|
|
|
1024
|
-
# @return
|
|
1340
|
+
# @rbs return: String -- Page title
|
|
1025
1341
|
def title
|
|
1026
1342
|
main_frame.title
|
|
1027
1343
|
end
|
|
1028
1344
|
|
|
1029
|
-
# @
|
|
1030
|
-
# @
|
|
1031
|
-
# @
|
|
1032
|
-
# @
|
|
1033
|
-
# @
|
|
1034
|
-
# @
|
|
1035
|
-
# @
|
|
1345
|
+
# @rbs type: String? -- Image format
|
|
1346
|
+
# @rbs path: String? -- File path to save
|
|
1347
|
+
# @rbs full_page: bool? -- Capture full page
|
|
1348
|
+
# @rbs clip: Hash[Symbol, Numeric]? -- Clip rectangle
|
|
1349
|
+
# @rbs quality: Integer? -- JPEG quality
|
|
1350
|
+
# @rbs omit_background: bool? -- Omit background for PNG
|
|
1351
|
+
# @rbs encoding: String? -- Encoding (base64 or binary)
|
|
1352
|
+
# @rbs capture_beyond_viewport: bool? -- Capture beyond viewport
|
|
1353
|
+
# @rbs from_surface: bool? -- Capture from surface
|
|
1354
|
+
# @rbs return: String -- Screenshot data
|
|
1036
1355
|
def screenshot(type: nil,
|
|
1037
1356
|
path: nil,
|
|
1038
1357
|
full_page: nil,
|
|
@@ -1055,14 +1374,14 @@ class Puppeteer::Page
|
|
|
1055
1374
|
}.compact
|
|
1056
1375
|
screenshot_options = ScreenshotOptions.new(options)
|
|
1057
1376
|
|
|
1377
|
+
guard = browser_context.start_screenshot
|
|
1058
1378
|
@screenshot_task_queue.post_task do
|
|
1059
1379
|
screenshot_task(screenshot_options.type, screenshot_options)
|
|
1060
1380
|
end
|
|
1381
|
+
ensure
|
|
1382
|
+
guard&.release
|
|
1061
1383
|
end
|
|
1062
1384
|
|
|
1063
|
-
# @param {"png"|"jpeg"} format
|
|
1064
|
-
# @param {!ScreenshotOptions=} options
|
|
1065
|
-
# @return {!Promise<!Buffer|!String>}
|
|
1066
1385
|
private def screenshot_task(format, screenshot_options)
|
|
1067
1386
|
@client.send_message('Target.activateTarget', targetId: @target.target_id)
|
|
1068
1387
|
|
|
@@ -1129,13 +1448,15 @@ class Puppeteer::Page
|
|
|
1129
1448
|
buffer
|
|
1130
1449
|
end
|
|
1131
1450
|
|
|
1132
|
-
class PrintToPdfIsNotImplementedError <
|
|
1451
|
+
class PrintToPdfIsNotImplementedError < Puppeteer::Error
|
|
1452
|
+
# @rbs return: void -- No return value
|
|
1133
1453
|
def initialize
|
|
1134
1454
|
super('pdf() is only available in headless mode. See https://github.com/puppeteer/puppeteer/issues/1829')
|
|
1135
1455
|
end
|
|
1136
1456
|
end
|
|
1137
1457
|
|
|
1138
|
-
# @
|
|
1458
|
+
# @rbs options: Hash[Symbol, untyped] -- PDF options
|
|
1459
|
+
# @rbs return: Enumerable[String] -- PDF data chunks
|
|
1139
1460
|
def create_pdf_stream(options = {})
|
|
1140
1461
|
timeout_helper = Puppeteer::TimeoutHelper.new('Page.printToPDF',
|
|
1141
1462
|
timeout_ms: options[:timeout],
|
|
@@ -1158,7 +1479,8 @@ class Puppeteer::Page
|
|
|
1158
1479
|
).read_as_chunks
|
|
1159
1480
|
end
|
|
1160
1481
|
|
|
1161
|
-
# @
|
|
1482
|
+
# @rbs options: Hash[Symbol, untyped] -- PDF options
|
|
1483
|
+
# @rbs return: String -- PDF data
|
|
1162
1484
|
def pdf(options = {})
|
|
1163
1485
|
chunks = create_pdf_stream(options)
|
|
1164
1486
|
|
|
@@ -1186,65 +1508,79 @@ class Puppeteer::Page
|
|
|
1186
1508
|
end
|
|
1187
1509
|
end
|
|
1188
1510
|
|
|
1189
|
-
# @
|
|
1511
|
+
# @rbs run_before_unload: bool -- Whether to run beforeunload handlers
|
|
1512
|
+
# @rbs return: void -- No return value
|
|
1190
1513
|
def close(run_before_unload: false)
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1514
|
+
guard = browser_context.wait_for_screenshot_operations
|
|
1515
|
+
begin
|
|
1516
|
+
unless @client.connection
|
|
1517
|
+
raise 'Protocol error: Connection closed. Most likely the page has been closed.'
|
|
1518
|
+
end
|
|
1194
1519
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1520
|
+
if run_before_unload
|
|
1521
|
+
@client.send_message('Page.close')
|
|
1522
|
+
else
|
|
1523
|
+
@client.connection.send_message('Target.closeTarget', targetId: @target.target_id)
|
|
1524
|
+
@target.is_closed_promise.wait
|
|
1200
1525
|
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1526
|
+
# @closed sometimes remains false, so wait for @closed = true with 100ms timeout.
|
|
1527
|
+
25.times do
|
|
1528
|
+
break if @closed
|
|
1529
|
+
Puppeteer::AsyncUtils.sleep_seconds(0.004)
|
|
1530
|
+
end
|
|
1205
1531
|
end
|
|
1532
|
+
rescue Puppeteer::Connection::ProtocolError => err
|
|
1533
|
+
raise unless err.message.match?(/Target closed/i)
|
|
1534
|
+
ensure
|
|
1535
|
+
guard&.release
|
|
1206
1536
|
end
|
|
1207
1537
|
end
|
|
1208
1538
|
|
|
1209
|
-
# @return
|
|
1539
|
+
# @rbs return: bool -- Whether the page is closed
|
|
1210
1540
|
def closed?
|
|
1211
1541
|
@closed
|
|
1212
1542
|
end
|
|
1213
1543
|
|
|
1214
1544
|
attr_reader :mouse
|
|
1215
1545
|
|
|
1216
|
-
# @
|
|
1217
|
-
# @
|
|
1218
|
-
# @
|
|
1219
|
-
# @
|
|
1220
|
-
|
|
1221
|
-
|
|
1546
|
+
# @rbs selector: String -- CSS selector
|
|
1547
|
+
# @rbs delay: Numeric? -- Delay between down and up (ms)
|
|
1548
|
+
# @rbs button: String? -- Mouse button
|
|
1549
|
+
# @rbs click_count: Integer? -- Deprecated: use count (click_count only sets clickCount)
|
|
1550
|
+
# @rbs count: Integer? -- Number of clicks to perform
|
|
1551
|
+
# @rbs return: void -- No return value
|
|
1552
|
+
def click(selector, delay: nil, button: nil, click_count: nil, count: nil)
|
|
1553
|
+
main_frame.click(selector, delay: delay, button: button, click_count: click_count, count: count)
|
|
1222
1554
|
end
|
|
1223
1555
|
|
|
1224
1556
|
define_async_method :async_click
|
|
1225
1557
|
|
|
1226
|
-
# @
|
|
1558
|
+
# @rbs selector: String -- CSS selector
|
|
1559
|
+
# @rbs return: void -- No return value
|
|
1227
1560
|
def focus(selector)
|
|
1228
1561
|
main_frame.focus(selector)
|
|
1229
1562
|
end
|
|
1230
1563
|
|
|
1231
1564
|
define_async_method :async_focus
|
|
1232
1565
|
|
|
1233
|
-
# @
|
|
1566
|
+
# @rbs selector: String -- CSS selector
|
|
1567
|
+
# @rbs return: void -- No return value
|
|
1234
1568
|
def hover(selector)
|
|
1235
1569
|
main_frame.hover(selector)
|
|
1236
1570
|
end
|
|
1237
1571
|
|
|
1238
|
-
# @
|
|
1239
|
-
# @
|
|
1240
|
-
# @return
|
|
1572
|
+
# @rbs selector: String -- CSS selector
|
|
1573
|
+
# @rbs values: Array[String] -- Option values to select
|
|
1574
|
+
# @rbs return: Array[String] -- Selected values
|
|
1241
1575
|
def select(selector, *values)
|
|
1242
1576
|
main_frame.select(selector, *values)
|
|
1243
1577
|
end
|
|
1244
1578
|
|
|
1245
1579
|
define_async_method :async_select
|
|
1246
1580
|
|
|
1247
|
-
# @
|
|
1581
|
+
# @rbs selector: String? -- CSS selector
|
|
1582
|
+
# @rbs block: Proc? -- Optional block for Object#tap usage
|
|
1583
|
+
# @rbs return: Puppeteer::Page | nil -- Page instance or nil
|
|
1248
1584
|
def tap(selector: nil, &block)
|
|
1249
1585
|
# resolves double meaning of tap.
|
|
1250
1586
|
if selector.nil? && block
|
|
@@ -1253,54 +1589,60 @@ class Puppeteer::Page
|
|
|
1253
1589
|
# browser.new_page.tap do |page|
|
|
1254
1590
|
# ...
|
|
1255
1591
|
# end
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
# Puppeteer's Page#tap.
|
|
1259
|
-
main_frame.tap(selector)
|
|
1592
|
+
block.call(self)
|
|
1593
|
+
return self
|
|
1260
1594
|
end
|
|
1595
|
+
|
|
1596
|
+
# Puppeteer's Page#tap.
|
|
1597
|
+
main_frame.tap(selector)
|
|
1598
|
+
nil
|
|
1261
1599
|
end
|
|
1262
1600
|
|
|
1263
1601
|
define_async_method :async_tap
|
|
1264
1602
|
|
|
1265
|
-
# @
|
|
1266
|
-
# @
|
|
1267
|
-
# @
|
|
1603
|
+
# @rbs selector: String -- CSS selector
|
|
1604
|
+
# @rbs text: String -- Text to type
|
|
1605
|
+
# @rbs delay: Numeric? -- Delay between key presses (ms)
|
|
1606
|
+
# @rbs return: void -- No return value
|
|
1268
1607
|
def type_text(selector, text, delay: nil)
|
|
1269
1608
|
main_frame.type_text(selector, text, delay: delay)
|
|
1270
1609
|
end
|
|
1271
1610
|
|
|
1272
1611
|
define_async_method :async_type_text
|
|
1273
1612
|
|
|
1274
|
-
# @
|
|
1275
|
-
# @
|
|
1276
|
-
# @
|
|
1277
|
-
# @
|
|
1613
|
+
# @rbs selector: String -- CSS selector
|
|
1614
|
+
# @rbs visible: bool? -- Wait for element to be visible
|
|
1615
|
+
# @rbs hidden: bool? -- Wait for element to be hidden
|
|
1616
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
1617
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
1278
1618
|
def wait_for_selector(selector, visible: nil, hidden: nil, timeout: nil)
|
|
1279
1619
|
main_frame.wait_for_selector(selector, visible: visible, hidden: hidden, timeout: timeout)
|
|
1280
1620
|
end
|
|
1281
1621
|
|
|
1282
1622
|
define_async_method :async_wait_for_selector
|
|
1283
1623
|
|
|
1284
|
-
# @
|
|
1624
|
+
# @rbs milliseconds: Numeric -- Time to wait in milliseconds
|
|
1625
|
+
# @rbs return: void -- No return value
|
|
1285
1626
|
def wait_for_timeout(milliseconds)
|
|
1286
1627
|
main_frame.wait_for_timeout(milliseconds)
|
|
1287
1628
|
end
|
|
1288
1629
|
|
|
1289
|
-
# @
|
|
1290
|
-
# @
|
|
1291
|
-
# @
|
|
1292
|
-
# @
|
|
1630
|
+
# @rbs xpath: String -- XPath expression
|
|
1631
|
+
# @rbs visible: bool? -- Wait for element to be visible
|
|
1632
|
+
# @rbs hidden: bool? -- Wait for element to be hidden
|
|
1633
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
1634
|
+
# @rbs return: Puppeteer::ElementHandle? -- Matching element or nil
|
|
1293
1635
|
def wait_for_xpath(xpath, visible: nil, hidden: nil, timeout: nil)
|
|
1294
1636
|
main_frame.wait_for_xpath(xpath, visible: visible, hidden: hidden, timeout: timeout)
|
|
1295
1637
|
end
|
|
1296
1638
|
|
|
1297
1639
|
define_async_method :async_wait_for_xpath
|
|
1298
1640
|
|
|
1299
|
-
# @
|
|
1300
|
-
# @
|
|
1301
|
-
# @
|
|
1302
|
-
# @
|
|
1303
|
-
# @return
|
|
1641
|
+
# @rbs page_function: String -- Function or expression to evaluate
|
|
1642
|
+
# @rbs args: Array[untyped] -- Arguments for evaluation
|
|
1643
|
+
# @rbs polling: String | Numeric | nil -- Polling strategy
|
|
1644
|
+
# @rbs timeout: Numeric? -- Maximum wait time in milliseconds
|
|
1645
|
+
# @rbs return: Puppeteer::JSHandle -- Handle to evaluation result
|
|
1304
1646
|
def wait_for_function(page_function, args: [], polling: nil, timeout: nil)
|
|
1305
1647
|
main_frame.wait_for_function(page_function, args: args, polling: polling, timeout: timeout)
|
|
1306
1648
|
end
|