puppeteer-ruby 0.0.21 → 0.0.27
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/.circleci/config.yml +32 -22
- data/.github/workflows/reviewdog.yml +1 -1
- data/.rubocop.yml +37 -0
- data/CHANGELOG.md +74 -0
- data/Dockerfile +1 -1
- data/README.md +15 -0
- data/lib/puppeteer.rb +9 -2
- data/lib/puppeteer/browser.rb +19 -28
- data/lib/puppeteer/browser_context.rb +48 -49
- data/lib/puppeteer/browser_runner.rb +19 -5
- data/lib/puppeteer/cdp_session.rb +11 -7
- data/lib/puppeteer/concurrent_ruby_utils.rb +22 -6
- data/lib/puppeteer/connection.rb +30 -11
- data/lib/puppeteer/define_async_method.rb +15 -6
- data/lib/puppeteer/dom_world.rb +2 -2
- data/lib/puppeteer/event_callbackable.rb +4 -0
- data/lib/puppeteer/events.rb +184 -0
- data/lib/puppeteer/exception_details.rb +38 -0
- data/lib/puppeteer/frame_manager.rb +20 -16
- data/lib/puppeteer/geolocation.rb +24 -0
- data/lib/puppeteer/launcher.rb +0 -1
- data/lib/puppeteer/launcher/browser_options.rb +2 -1
- data/lib/puppeteer/launcher/chrome.rb +4 -8
- data/lib/puppeteer/launcher/firefox.rb +4 -8
- data/lib/puppeteer/lifecycle_watcher.rb +6 -6
- data/lib/puppeteer/network_manager.rb +6 -6
- data/lib/puppeteer/page.rb +119 -141
- data/lib/puppeteer/page/screenshot_options.rb +2 -2
- data/lib/puppeteer/page/screenshot_task_queue.rb +13 -0
- data/lib/puppeteer/target.rb +4 -6
- data/lib/puppeteer/version.rb +1 -1
- data/puppeteer-ruby.gemspec +6 -4
- metadata +43 -10
@@ -0,0 +1,38 @@
|
|
1
|
+
# Original implementation, helpers.getExceptionMessage
|
2
|
+
class Puppeteer::ExceptionDetails
|
3
|
+
# @param exception_details [Hash]
|
4
|
+
def initialize(exception_details)
|
5
|
+
@exception_details = exception_details
|
6
|
+
end
|
7
|
+
|
8
|
+
def message
|
9
|
+
# "exceptionDetails"=>{"exceptionId"=>1, "text"=>"Uncaught", "lineNumber"=>12, "columnNumber"=>10, "url"=>"http://127.0.0.1:4567/error.html",
|
10
|
+
# "stackTrace"=>{"callFrames"=>[
|
11
|
+
# {"functionName"=>"c", "scriptId"=>"6", "url"=>"http://127.0.0.1:4567/error.html", "lineNumber"=>12, "columnNumber"=>10},
|
12
|
+
# {"functionName"=>"b", "scriptId"=>"6", "url"=>"http://127.0.0.1:4567/error.html", "lineNumber"=>8, "columnNumber"=>4},
|
13
|
+
# {"functionName"=>"a", "scriptId"=>"6", "url"=>"http://127.0.0.1:4567/error.html", "lineNumber"=>4, "columnNumber"=>4},
|
14
|
+
# {"functionName"=>"", "scriptId"=>"6", "url"=>"http://127.0.0.1:4567/error.html", "lineNumber"=>1, "columnNumber"=>0}
|
15
|
+
# ]},
|
16
|
+
# "exception"=>{"type"=>"object", "subtype"=>"error", "className"=>"Error", "description"=>"Error: Fancy error!\n at c (http://127.0.0.1:4567/error.html:13:11)\n at b (http://127.0.0.1:4567/error.html:9:5)\n at a (http://127.0.0.1:4567/error.html:5:5)\n at http://127.0.0.1:4567/error.html:2:1", "objectId"=>"{\"injectedScriptId\":3,\"id\":1}", "preview"=>{"type"=>"object", "subtype"=>"error", "description"=>"Error: Fancy error!\n at c (http://127.0.0.1:4567/error.html:13:11)\n at b (http://127.0.0.1:4567/error.html:9:5)\n at a (http://127.0.0.1:4567/error.html:5:5)\n at http://127.0.0.1:4567/error.html:2:1", "overflow"=>false, "properties"=>[{"name"=>"stack", "type"=>"string", "value"=>"Error: Fancy error!\n at c (http://127.0.0.1:456…:5:5)\n at http://127.0.0.1:4567/error.html:2:1"}, {"name"=>"message", "type"=>"string", "value"=>"Fancy error!"}]}}
|
17
|
+
if @exception_details['exception']
|
18
|
+
return exception_description_or_value(@exception_details['exception'])
|
19
|
+
end
|
20
|
+
|
21
|
+
messages = []
|
22
|
+
messages << @exception_details['text']
|
23
|
+
|
24
|
+
if @exception_details['stackTrace']
|
25
|
+
@exception_details['stackTrace']['callFrames'].each do |call_frame|
|
26
|
+
location = "#{call_frame['url']}:#{call_frame['lineNumber']}:#{call_frame['columnNumber']}"
|
27
|
+
function_name = call_frame['functionName'] || '<anonymous>'
|
28
|
+
messages << "at #{function_name} (#{location})"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
messages.join("\n ")
|
33
|
+
end
|
34
|
+
|
35
|
+
private def exception_description_or_value(exception)
|
36
|
+
exception['description'] || exception['value']
|
37
|
+
end
|
38
|
+
end
|
@@ -27,31 +27,31 @@ class Puppeteer::FrameManager
|
|
27
27
|
# @type {!Set<string>}
|
28
28
|
@isolated_worlds = Set.new
|
29
29
|
|
30
|
-
@client.on_event
|
30
|
+
@client.on_event('Page.frameAttached') do |event|
|
31
31
|
handle_frame_attached(event['frameId'], event['parentFrameId'])
|
32
32
|
end
|
33
|
-
@client.on_event
|
33
|
+
@client.on_event('Page.frameNavigated') do |event|
|
34
34
|
handle_frame_navigated(event['frame'])
|
35
35
|
end
|
36
|
-
@client.on_event
|
36
|
+
@client.on_event('Page.navigatedWithinDocument') do |event|
|
37
37
|
handle_frame_navigated_within_document(event['frameId'], event['url'])
|
38
38
|
end
|
39
|
-
@client.on_event
|
39
|
+
@client.on_event('Page.frameDetached') do |event|
|
40
40
|
handle_frame_detached(event['frameId'])
|
41
41
|
end
|
42
|
-
@client.on_event
|
42
|
+
@client.on_event('Page.frameStoppedLoading') do |event|
|
43
43
|
handle_frame_stopped_loading(event['frameId'])
|
44
44
|
end
|
45
|
-
@client.on_event
|
45
|
+
@client.on_event('Runtime.executionContextCreated') do |event|
|
46
46
|
handle_execution_context_created(event['context'])
|
47
47
|
end
|
48
|
-
@client.on_event
|
48
|
+
@client.on_event('Runtime.executionContextDestroyed') do |event|
|
49
49
|
handle_execution_context_destroyed(event['executionContextId'])
|
50
50
|
end
|
51
|
-
@client.on_event
|
51
|
+
@client.on_event('Runtime.executionContextsCleared') do |event|
|
52
52
|
handle_execution_contexts_cleared
|
53
53
|
end
|
54
|
-
@client.on_event
|
54
|
+
@client.on_event('Page.lifecycleEvent') do |event|
|
55
55
|
handle_lifecycle_event(event)
|
56
56
|
end
|
57
57
|
end
|
@@ -121,6 +121,8 @@ class Puppeteer::FrameManager
|
|
121
121
|
document_navigation_promise,
|
122
122
|
watcher.timeout_or_termination_promise,
|
123
123
|
)
|
124
|
+
rescue Puppeteer::TimeoutError => err
|
125
|
+
raise NavigationError.new(err)
|
124
126
|
ensure
|
125
127
|
watcher.dispose
|
126
128
|
end
|
@@ -143,6 +145,8 @@ class Puppeteer::FrameManager
|
|
143
145
|
watcher.same_document_navigation_promise,
|
144
146
|
watcher.new_document_navigation_promise,
|
145
147
|
)
|
148
|
+
rescue Puppeteer::TimeoutError => err
|
149
|
+
raise NavigationError.new(err)
|
146
150
|
ensure
|
147
151
|
watcher.dispose
|
148
152
|
end
|
@@ -155,7 +159,7 @@ class Puppeteer::FrameManager
|
|
155
159
|
frame = @frames[event['frameId']]
|
156
160
|
return if !frame
|
157
161
|
frame.handle_lifecycle_event(event['loaderId'], event['name'])
|
158
|
-
emit_event
|
162
|
+
emit_event(FrameManagerEmittedEvents::LifecycleEvent, frame)
|
159
163
|
end
|
160
164
|
|
161
165
|
# @param {string} frameId
|
@@ -163,7 +167,7 @@ class Puppeteer::FrameManager
|
|
163
167
|
frame = @frames[frame_id]
|
164
168
|
return if !frame
|
165
169
|
frame.handle_loading_stopped
|
166
|
-
emit_event
|
170
|
+
emit_event(FrameManagerEmittedEvents::LifecycleEvent, frame)
|
167
171
|
end
|
168
172
|
|
169
173
|
# @param frame_tree [Hash]
|
@@ -211,7 +215,7 @@ class Puppeteer::FrameManager
|
|
211
215
|
frame = Puppeteer::Frame.new(self, @client, parent_frame, frame_id)
|
212
216
|
@frames[frame_id] = frame
|
213
217
|
|
214
|
-
emit_event
|
218
|
+
emit_event(FrameManagerEmittedEvents::FrameAttached, frame)
|
215
219
|
end
|
216
220
|
|
217
221
|
# @param frame_payload [Hash]
|
@@ -252,7 +256,7 @@ class Puppeteer::FrameManager
|
|
252
256
|
# Update frame payload.
|
253
257
|
frame.navigated(frame_payload)
|
254
258
|
|
255
|
-
emit_event
|
259
|
+
emit_event(FrameManagerEmittedEvents::FrameNavigated, frame)
|
256
260
|
end
|
257
261
|
|
258
262
|
# @param name [String]
|
@@ -280,8 +284,8 @@ class Puppeteer::FrameManager
|
|
280
284
|
frame = @frames[frame_id]
|
281
285
|
return unless frame
|
282
286
|
frame.navigated_within_document(url)
|
283
|
-
emit_event
|
284
|
-
emit_event
|
287
|
+
emit_event(FrameManagerEmittedEvents::FrameNavigatedWithinDocument, frame)
|
288
|
+
emit_event(FrameManagerEmittedEvents::FrameNavigated, frame)
|
285
289
|
end
|
286
290
|
|
287
291
|
# @param frame_id [String]
|
@@ -349,7 +353,7 @@ class Puppeteer::FrameManager
|
|
349
353
|
end
|
350
354
|
frame.detach
|
351
355
|
@frames.delete(frame.id)
|
352
|
-
emit_event
|
356
|
+
emit_event(FrameManagerEmittedEvents::FrameDetached, frame)
|
353
357
|
end
|
354
358
|
|
355
359
|
private def assert_no_legacy_navigation_options(wait_until:)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Puppeteer::Geolocation
|
2
|
+
# @param latitude [Fixnum]
|
3
|
+
# @param longitude [Fixnum]
|
4
|
+
# @param accuracy [Fixnum]
|
5
|
+
def initialize(latitude:, longitude:, accuracy: 0)
|
6
|
+
unless (-180..180).include?(longitude)
|
7
|
+
raise ArgumentError.new("Invalid longitude \"#{longitude}\": precondition -180 <= LONGITUDE <= 180 failed.")
|
8
|
+
end
|
9
|
+
unless (-90..90).include?(latitude)
|
10
|
+
raise ArgumentError.new("Invalid latitude \"#{latitude}\": precondition -90 <= LATITUDE <= 90 failed.")
|
11
|
+
end
|
12
|
+
if accuracy < 0
|
13
|
+
raise ArgumentError.new("Invalid accuracy \"#{longitude}\": precondition 0 <= ACCURACY failed.")
|
14
|
+
end
|
15
|
+
|
16
|
+
@latitude = latitude
|
17
|
+
@longitude = longitude
|
18
|
+
@accuracy = accuracy
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_h
|
22
|
+
{ latitude: @latitude, longitude: @longitude, accuracy: @accuracy }
|
23
|
+
end
|
24
|
+
end
|
data/lib/puppeteer/launcher.rb
CHANGED
@@ -28,7 +28,8 @@ module Puppeteer::Launcher
|
|
28
28
|
# @property {number=} slowMo
|
29
29
|
def initialize(options)
|
30
30
|
@ignore_https_errors = options[:ignore_https_errors] || false
|
31
|
-
|
31
|
+
# `default_viewport: nil` must be respected here.
|
32
|
+
@default_viewport = options.key?(:default_viewport) ? options[:default_viewport] : Puppeteer::Viewport.new(width: 800, height: 600)
|
32
33
|
@slow_mo = options[:slow_mo] || 0
|
33
34
|
end
|
34
35
|
|
@@ -12,9 +12,9 @@ module Puppeteer::Launcher
|
|
12
12
|
|
13
13
|
chrome_arguments =
|
14
14
|
if !@launch_options.ignore_default_args
|
15
|
-
default_args.to_a
|
15
|
+
default_args(options).to_a
|
16
16
|
elsif @launch_options.ignore_default_args.is_a?(Enumerable)
|
17
|
-
default_args.reject do |arg|
|
17
|
+
default_args(options).reject do |arg|
|
18
18
|
@launch_options.ignore_default_args.include?(arg)
|
19
19
|
end.to_a
|
20
20
|
else
|
@@ -141,11 +141,7 @@ module Puppeteer::Launcher
|
|
141
141
|
|
142
142
|
# @return [DefaultArgs]
|
143
143
|
def default_args(options = nil)
|
144
|
-
|
145
|
-
@default_args ||= DefaultArgs.new(@chrome_arg_options)
|
146
|
-
else
|
147
|
-
DefaultArgs.new(ChromeArgOptions.new(options))
|
148
|
-
end
|
144
|
+
DefaultArgs.new(ChromeArgOptions.new(options || {}))
|
149
145
|
end
|
150
146
|
|
151
147
|
# @return [Puppeteer::Browser]
|
@@ -206,7 +202,7 @@ module Puppeteer::Launcher
|
|
206
202
|
resolve_executable_path
|
207
203
|
end
|
208
204
|
|
209
|
-
|
205
|
+
def product
|
210
206
|
'chrome'
|
211
207
|
end
|
212
208
|
end
|
@@ -12,9 +12,9 @@ module Puppeteer::Launcher
|
|
12
12
|
|
13
13
|
firefox_arguments =
|
14
14
|
if !@launch_options.ignore_default_args
|
15
|
-
default_args.to_a
|
15
|
+
default_args(options).to_a
|
16
16
|
elsif @launch_options.ignore_default_args.is_a?(Enumerable)
|
17
|
-
default_args.reject do |arg|
|
17
|
+
default_args(options).reject do |arg|
|
18
18
|
@launch_options.ignore_default_args.include?(arg)
|
19
19
|
end.to_a
|
20
20
|
else
|
@@ -127,7 +127,7 @@ module Puppeteer::Launcher
|
|
127
127
|
resolve_executable_path
|
128
128
|
end
|
129
129
|
|
130
|
-
|
130
|
+
def product
|
131
131
|
'firefox'
|
132
132
|
end
|
133
133
|
|
@@ -173,11 +173,7 @@ module Puppeteer::Launcher
|
|
173
173
|
|
174
174
|
# @return [DefaultArgs]
|
175
175
|
def default_args(options = nil)
|
176
|
-
|
177
|
-
@default_args ||= DefaultArgs.new(@chrome_arg_options)
|
178
|
-
else
|
179
|
-
DefaultArgs.new(ChromeArgOptions.new(options))
|
180
|
-
end
|
176
|
+
DefaultArgs.new(ChromeArgOptions.new(options || {}))
|
181
177
|
end
|
182
178
|
|
183
179
|
private def create_profile(extra_prefs = {})
|
@@ -69,17 +69,17 @@ class Puppeteer::LifecycleWatcher
|
|
69
69
|
@timeout = timeout
|
70
70
|
|
71
71
|
@listener_ids = {}
|
72
|
-
@listener_ids['client'] = @frame_manager.client.add_event_listener(
|
72
|
+
@listener_ids['client'] = @frame_manager.client.add_event_listener(CDPSessionEmittedEvents::Disconnected) do
|
73
73
|
terminate(TerminatedError.new('Navigation failed because browser has disconnected!'))
|
74
74
|
end
|
75
75
|
@listener_ids['frame_manager'] = [
|
76
|
-
@frame_manager.add_event_listener(
|
76
|
+
@frame_manager.add_event_listener(FrameManagerEmittedEvents::LifecycleEvent) do |_|
|
77
77
|
check_lifecycle_complete
|
78
78
|
end,
|
79
|
-
@frame_manager.add_event_listener(
|
80
|
-
@frame_manager.add_event_listener(
|
79
|
+
@frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameNavigatedWithinDocument, &method(:navigated_within_document)),
|
80
|
+
@frame_manager.add_event_listener(FrameManagerEmittedEvents::FrameDetached, &method(:handle_frame_detached)),
|
81
81
|
]
|
82
|
-
@listener_ids['network_manager'] = @frame_manager.network_manager.add_event_listener(
|
82
|
+
@listener_ids['network_manager'] = @frame_manager.network_manager.add_event_listener(NetworkManagerEmittedEvents::Request, &method(:handle_request))
|
83
83
|
|
84
84
|
@same_document_navigation_promise = resolvable_future
|
85
85
|
@lifecycle_promise = resolvable_future
|
@@ -128,7 +128,7 @@ class Puppeteer::LifecycleWatcher
|
|
128
128
|
@termination_promise.value!
|
129
129
|
end
|
130
130
|
rescue Timeout::Error
|
131
|
-
raise Puppeteer::
|
131
|
+
raise Puppeteer::TimeoutError.new("Navigation timeout of #{@timeout}ms exceeded")
|
132
132
|
end
|
133
133
|
else
|
134
134
|
@termination_promise
|
@@ -212,7 +212,7 @@ class Puppeteer::NetworkManager
|
|
212
212
|
frame = if_present(event['frameId']) { |frame_id| @frame_manager.frame(frame_id) }
|
213
213
|
request = Puppeteer::Request.new(@client, frame, interception_id, @user_request_interception_enabled, event, redirect_chain)
|
214
214
|
@request_id_to_request[event['requestId']] = request
|
215
|
-
emit_event
|
215
|
+
emit_event(NetworkManagerEmittedEvents::Request, request)
|
216
216
|
end
|
217
217
|
|
218
218
|
private def handle_request_served_from_cache(event)
|
@@ -230,8 +230,8 @@ class Puppeteer::NetworkManager
|
|
230
230
|
response.internal.body_loaded_promise.reject(Puppeteer::Response::Redirected.new)
|
231
231
|
@request_id_to_request.delete(request.internal.request_id)
|
232
232
|
@attempted_authentications.delete(request.internal.interception_id)
|
233
|
-
emit_event
|
234
|
-
emit_event
|
233
|
+
emit_event(NetworkManagerEmittedEvents::Response, response)
|
234
|
+
emit_event(NetworkManagerEmittedEvents::RequestFinished, request)
|
235
235
|
end
|
236
236
|
|
237
237
|
# @param event [Hash]
|
@@ -242,7 +242,7 @@ class Puppeteer::NetworkManager
|
|
242
242
|
|
243
243
|
response = Puppeteer::Response.new(@client, request, event['response'])
|
244
244
|
request.internal.response = response
|
245
|
-
emit_event
|
245
|
+
emit_event(NetworkManagerEmittedEvents::Response, response)
|
246
246
|
end
|
247
247
|
|
248
248
|
private def handle_loading_finished(event)
|
@@ -260,7 +260,7 @@ class Puppeteer::NetworkManager
|
|
260
260
|
|
261
261
|
@request_id_to_request.delete(request.internal.request_id)
|
262
262
|
@attempted_authentications.delete(request.internal.interception_id)
|
263
|
-
emit_event
|
263
|
+
emit_event(NetworkManagerEmittedEvents::RequestFinished, request)
|
264
264
|
end
|
265
265
|
|
266
266
|
private def handle_loading_failed(event)
|
@@ -275,6 +275,6 @@ class Puppeteer::NetworkManager
|
|
275
275
|
end
|
276
276
|
@request_id_to_request.delete(request.internal.request_id)
|
277
277
|
@attempted_authentications.delete(request.internal.interception_id)
|
278
|
-
emit_event
|
278
|
+
emit_event(NetworkManagerEmittedEvents::RequestFailed, request)
|
279
279
|
end
|
280
280
|
end
|
data/lib/puppeteer/page.rb
CHANGED
@@ -3,6 +3,7 @@ require "stringio"
|
|
3
3
|
|
4
4
|
require_relative './page/pdf_options'
|
5
5
|
require_relative './page/screenshot_options'
|
6
|
+
require_relative './page/screenshot_task_queue'
|
6
7
|
|
7
8
|
class Puppeteer::Page
|
8
9
|
include Puppeteer::EventCallbackable
|
@@ -13,10 +14,9 @@ class Puppeteer::Page
|
|
13
14
|
# @param {!Puppeteer.Target} target
|
14
15
|
# @param {boolean} ignoreHTTPSErrors
|
15
16
|
# @param {?Puppeteer.Viewport} defaultViewport
|
16
|
-
# @param {!Puppeteer.TaskQueue} screenshotTaskQueue
|
17
17
|
# @return {!Promise<!Page>}
|
18
|
-
def self.create(client, target, ignore_https_errors, default_viewport
|
19
|
-
page = Puppeteer::Page.new(client, target, ignore_https_errors
|
18
|
+
def self.create(client, target, ignore_https_errors, default_viewport)
|
19
|
+
page = Puppeteer::Page.new(client, target, ignore_https_errors)
|
20
20
|
page.init
|
21
21
|
if default_viewport
|
22
22
|
page.viewport = default_viewport
|
@@ -27,8 +27,7 @@ class Puppeteer::Page
|
|
27
27
|
# @param {!Puppeteer.CDPSession} client
|
28
28
|
# @param {!Puppeteer.Target} target
|
29
29
|
# @param {boolean} ignoreHTTPSErrors
|
30
|
-
|
31
|
-
def initialize(client, target, ignore_https_errors, screenshot_task_queue)
|
30
|
+
def initialize(client, target, ignore_https_errors)
|
32
31
|
@closed = false
|
33
32
|
@client = client
|
34
33
|
@target = target
|
@@ -43,10 +42,10 @@ class Puppeteer::Page
|
|
43
42
|
@page_bindings = {}
|
44
43
|
# @coverage = Coverage.new(client)
|
45
44
|
@javascript_enabled = true
|
46
|
-
@screenshot_task_queue =
|
45
|
+
@screenshot_task_queue = ScreenshotTaskQueue.new
|
47
46
|
|
48
47
|
@workers = {}
|
49
|
-
@client.on_event
|
48
|
+
@client.on_event('Target.attachedToTarget') do |event|
|
50
49
|
if event['targetInfo']['type'] != 'worker'
|
51
50
|
# If we don't detach from service workers, they will never die.
|
52
51
|
await @client.send_message('Target.detachFromTarget', sessionId: event['sessionId'])
|
@@ -56,65 +55,69 @@ class Puppeteer::Page
|
|
56
55
|
session = Puppeteer::Connection.from_session(@client).session(event['sessionId']) # rubocop:disable Lint/UselessAssignment
|
57
56
|
# const worker = new Worker(session, event.targetInfo.url, this._addConsoleMessage.bind(this), this._handleException.bind(this));
|
58
57
|
# this._workers.set(event.sessionId, worker);
|
59
|
-
# this.emit(
|
58
|
+
# this.emit(PageEmittedEvents::WorkerCreated, worker);
|
60
59
|
end
|
61
|
-
@client.on_event
|
60
|
+
@client.on_event('Target.detachedFromTarget') do |event|
|
62
61
|
session_id = event['sessionId']
|
63
62
|
worker = @workers[session_id]
|
64
63
|
next unless worker
|
65
64
|
|
66
|
-
emit_event(
|
65
|
+
emit_event(PageEmittedEvents::WorkerDestroyed, worker)
|
67
66
|
@workers.delete(session_id)
|
68
67
|
end
|
69
68
|
|
70
|
-
@frame_manager.on_event
|
71
|
-
emit_event
|
69
|
+
@frame_manager.on_event(FrameManagerEmittedEvents::FrameAttached) do |event|
|
70
|
+
emit_event(PageEmittedEvents::FrameAttached, event)
|
72
71
|
end
|
73
|
-
@frame_manager.on_event
|
74
|
-
emit_event
|
72
|
+
@frame_manager.on_event(FrameManagerEmittedEvents::FrameDetached) do |event|
|
73
|
+
emit_event(PageEmittedEvents::FrameDetached, event)
|
75
74
|
end
|
76
|
-
@frame_manager.on_event
|
77
|
-
emit_event
|
75
|
+
@frame_manager.on_event(FrameManagerEmittedEvents::FrameNavigated) do |event|
|
76
|
+
emit_event(PageEmittedEvents::FrameNavigated, event)
|
78
77
|
end
|
79
78
|
|
80
79
|
network_manager = @frame_manager.network_manager
|
81
|
-
network_manager.on_event
|
82
|
-
emit_event
|
80
|
+
network_manager.on_event(NetworkManagerEmittedEvents::Request) do |event|
|
81
|
+
emit_event(PageEmittedEvents::Request, event)
|
83
82
|
end
|
84
|
-
network_manager.on_event
|
85
|
-
emit_event
|
83
|
+
network_manager.on_event(NetworkManagerEmittedEvents::Response) do |event|
|
84
|
+
emit_event(PageEmittedEvents::Response, event)
|
86
85
|
end
|
87
|
-
network_manager.on_event
|
88
|
-
emit_event
|
86
|
+
network_manager.on_event(NetworkManagerEmittedEvents::RequestFailed) do |event|
|
87
|
+
emit_event(PageEmittedEvents::RequestFailed, event)
|
89
88
|
end
|
90
|
-
network_manager.on_event
|
91
|
-
emit_event
|
89
|
+
network_manager.on_event(NetworkManagerEmittedEvents::RequestFinished) do |event|
|
90
|
+
emit_event(PageEmittedEvents::RequestFinished, event)
|
92
91
|
end
|
93
92
|
@file_chooser_interception_is_disabled = false
|
94
93
|
@file_chooser_interceptors = Set.new
|
95
94
|
|
96
|
-
@client.on_event
|
97
|
-
emit_event
|
95
|
+
@client.on_event('Page.domContentEventFired') do |event|
|
96
|
+
emit_event(PageEmittedEvents::DOMContentLoaded)
|
98
97
|
end
|
99
|
-
@client.on_event
|
100
|
-
emit_event
|
98
|
+
@client.on_event('Page.loadEventFired') do |event|
|
99
|
+
emit_event(PageEmittedEvents::Load)
|
101
100
|
end
|
102
101
|
# client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
103
102
|
# client.on('Runtime.bindingCalled', event => this._onBindingCalled(event));
|
104
|
-
@client.on_event
|
103
|
+
@client.on_event('Page.javascriptDialogOpening') do |event|
|
105
104
|
handle_dialog_opening(event)
|
106
105
|
end
|
107
|
-
|
108
|
-
|
106
|
+
@client.on_event('Runtime.exceptionThrown') do |exception|
|
107
|
+
handle_exception(exception['exceptionDetails'])
|
108
|
+
end
|
109
|
+
@client.on_event('Inspector.targetCrashed') do |event|
|
110
|
+
handle_target_crashed
|
111
|
+
end
|
109
112
|
# client.on('Performance.metrics', event => this._emitMetrics(event));
|
110
|
-
@client.on_event
|
113
|
+
@client.on_event('Log.entryAdded') do |event|
|
111
114
|
handle_log_entry_added(event)
|
112
115
|
end
|
113
|
-
@client.on_event
|
116
|
+
@client.on_event('Page.fileChooserOpened') do |event|
|
114
117
|
handle_file_chooser(event)
|
115
118
|
end
|
116
119
|
@target.is_closed_promise.then do
|
117
|
-
emit_event
|
120
|
+
emit_event(PageEmittedEvents::Close)
|
118
121
|
@closed = true
|
119
122
|
end
|
120
123
|
end
|
@@ -128,43 +131,22 @@ class Puppeteer::Page
|
|
128
131
|
)
|
129
132
|
end
|
130
133
|
|
131
|
-
EVENT_MAPPINGS = {
|
132
|
-
close: 'Events.Page.Close',
|
133
|
-
# console: 'Events.Page.Console',
|
134
|
-
dialog: 'Events.Page.Dialog',
|
135
|
-
domcontentloaded: 'Events.Page.DOMContentLoaded',
|
136
|
-
# error:
|
137
|
-
frameattached: 'Events.Page.FrameAttached',
|
138
|
-
framedetached: 'Events.Page.FrameDetached',
|
139
|
-
framenavigated: 'Events.Page.FrameNavigated',
|
140
|
-
load: 'Events.Page.Load',
|
141
|
-
# metrics: 'Events.Page.Metrics',
|
142
|
-
# pageerror: 'Events.Page.PageError',
|
143
|
-
popup: 'Events.Page.Popup',
|
144
|
-
request: 'Events.Page.Request',
|
145
|
-
requestfailed: 'Events.Page.RequestFailed',
|
146
|
-
requestfinished: 'Events.Page.RequestFinished',
|
147
|
-
response: 'Events.Page.Response',
|
148
|
-
# workercreated: 'Events.Page.WorkerCreated',
|
149
|
-
# workerdestroyed: 'Events.Page.WorkerDestroyed',
|
150
|
-
}
|
151
|
-
|
152
134
|
# @param event_name [Symbol]
|
153
135
|
def on(event_name, &block)
|
154
|
-
unless
|
155
|
-
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{
|
136
|
+
unless PageEmittedEvents.values.include?(event_name.to_s)
|
137
|
+
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{PageEmittedEvents.values.to_a.join(", ")}")
|
156
138
|
end
|
157
139
|
|
158
|
-
|
140
|
+
super(event_name.to_s, &block)
|
159
141
|
end
|
160
142
|
|
161
143
|
# @param event_name [Symbol]
|
162
144
|
def once(event_name, &block)
|
163
|
-
unless
|
164
|
-
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{
|
145
|
+
unless PageEmittedEvents.values.include?(event_name.to_s)
|
146
|
+
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{PageEmittedEvents.values.to_a.join(", ")}")
|
165
147
|
end
|
166
148
|
|
167
|
-
|
149
|
+
super(event_name.to_s, &block)
|
168
150
|
end
|
169
151
|
|
170
152
|
def handle_file_chooser(event)
|
@@ -211,19 +193,10 @@ class Puppeteer::Page
|
|
211
193
|
|
212
194
|
define_async_method :async_wait_for_file_chooser
|
213
195
|
|
214
|
-
#
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
# const { longitude, latitude, accuracy = 0} = options;
|
219
|
-
# if (longitude < -180 || longitude > 180)
|
220
|
-
# throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
221
|
-
# if (latitude < -90 || latitude > 90)
|
222
|
-
# throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
223
|
-
# if (accuracy < 0)
|
224
|
-
# throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
225
|
-
# await this._client.send('Emulation.setGeolocationOverride', {longitude, latitude, accuracy});
|
226
|
-
# }
|
196
|
+
# @param [Puppeteer::Geolocation]
|
197
|
+
def geolocation=(geolocation)
|
198
|
+
@client.send_message('Emulation.setGeolocationOverride', geolocation.to_h)
|
199
|
+
end
|
227
200
|
|
228
201
|
attr_reader :javascript_enabled, :target
|
229
202
|
|
@@ -238,7 +211,7 @@ class Puppeteer::Page
|
|
238
211
|
class TargetCrashedError < StandardError; end
|
239
212
|
|
240
213
|
private def handle_target_crashed
|
241
|
-
emit_event
|
214
|
+
emit_event(PageEmittedEvents::Error, TargetCrashedError.new('Page crashed!'))
|
242
215
|
end
|
243
216
|
|
244
217
|
private def handle_log_entry_added(event)
|
@@ -259,7 +232,7 @@ class Puppeteer::Page
|
|
259
232
|
url: url,
|
260
233
|
line_number: line_number,
|
261
234
|
)
|
262
|
-
emit_event(
|
235
|
+
emit_event(PageEmittedEvents::Console,
|
263
236
|
Puppeteer::ConsoleMessage.new(level, text, [], console_message_location))
|
264
237
|
end
|
265
238
|
end
|
@@ -367,47 +340,34 @@ class Puppeteer::Page
|
|
367
340
|
|
368
341
|
define_async_method :async_Sx
|
369
342
|
|
370
|
-
#
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
# async cookies(...urls) {
|
375
|
-
# return (await this._client.send('Network.getCookies', {
|
376
|
-
# urls: urls.length ? urls : [this.url()]
|
377
|
-
# })).cookies;
|
378
|
-
# }
|
343
|
+
# @return [Array<Hash>]
|
344
|
+
def cookies(*urls)
|
345
|
+
@client.send_message('Network.getCookies', urls: (urls.empty? ? [url] : urls))['cookies']
|
346
|
+
end
|
379
347
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
# item.url = pageURL;
|
389
|
-
# await this._client.send('Network.deleteCookies', item);
|
390
|
-
# }
|
391
|
-
# }
|
348
|
+
def delete_cookie(*cookies)
|
349
|
+
page_url = url
|
350
|
+
starts_with_http = page_url.start_with?("http")
|
351
|
+
cookies.each do |cookie|
|
352
|
+
item = (starts_with_http ? { url: page_url } : {}).merge(cookie)
|
353
|
+
@client.send_message("Network.deleteCookies", item)
|
354
|
+
end
|
355
|
+
end
|
392
356
|
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
# await this.deleteCookie(...items);
|
408
|
-
# if (items.length)
|
409
|
-
# await this._client.send('Network.setCookies', { cookies: items });
|
410
|
-
# }
|
357
|
+
def set_cookie(*cookies)
|
358
|
+
page_url = url
|
359
|
+
starts_with_http = page_url.start_with?("http")
|
360
|
+
items = cookies.map do |cookie|
|
361
|
+
(starts_with_http ? { url: page_url } : {}).merge(cookie).tap do |item|
|
362
|
+
raise ArgumentError.new("Blank page can not have cookie \"#{item[:name]}\"") if item[:url] == "about:blank"
|
363
|
+
raise ArgumetnError.new("Data URL page can not have cookie \"#{item[:name]}\"") if item[:url]&.start_with?("data:")
|
364
|
+
end
|
365
|
+
end
|
366
|
+
delete_cookie(*items)
|
367
|
+
unless items.empty?
|
368
|
+
@client.send_message("Network.setCookies", cookies: items)
|
369
|
+
end
|
370
|
+
end
|
411
371
|
|
412
372
|
class ScriptTag
|
413
373
|
# @param {!{content?: string, path?: string, type?: string, url?: string}} options
|
@@ -502,7 +462,7 @@ class Puppeteer::Page
|
|
502
462
|
# * @param {!Protocol.Performance.metricsPayload} event
|
503
463
|
# */
|
504
464
|
# _emitMetrics(event) {
|
505
|
-
# this.emit(
|
465
|
+
# this.emit(PageEmittedEvents::Metrics, {
|
506
466
|
# title: event.title,
|
507
467
|
# metrics: this._buildMetricsObject(event.metrics)
|
508
468
|
# });
|
@@ -521,15 +481,14 @@ class Puppeteer::Page
|
|
521
481
|
# return result;
|
522
482
|
# }
|
523
483
|
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
# }
|
484
|
+
class PageError < StandardError ; end
|
485
|
+
|
486
|
+
private def handle_exception(exception_details)
|
487
|
+
message = Puppeteer::ExceptionDetails.new(exception_details).message
|
488
|
+
err = PageError.new(message)
|
489
|
+
# err.stack = ''; // Don't report clientside error with a node stack attached
|
490
|
+
emit_event(PageEmittedEvents::PageError, err)
|
491
|
+
end
|
533
492
|
|
534
493
|
# /**
|
535
494
|
# * @param {!Protocol.Runtime.consoleAPICalledPayload} event
|
@@ -613,7 +572,7 @@ class Puppeteer::Page
|
|
613
572
|
# * @param {Protocol.Runtime.StackTrace=} stackTrace
|
614
573
|
# */
|
615
574
|
# _addConsoleMessage(type, args, stackTrace) {
|
616
|
-
# if (!this.listenerCount(
|
575
|
+
# if (!this.listenerCount(PageEmittedEvents::Console)) {
|
617
576
|
# args.forEach(arg => arg.dispose());
|
618
577
|
# return;
|
619
578
|
# }
|
@@ -631,7 +590,7 @@ class Puppeteer::Page
|
|
631
590
|
# columnNumber: stackTrace.callFrames[0].columnNumber,
|
632
591
|
# } : {};
|
633
592
|
# const message = new ConsoleMessage(type, textTokens.join(' '), args, location);
|
634
|
-
# this.emit(
|
593
|
+
# this.emit(PageEmittedEvents::Console, message);
|
635
594
|
# }
|
636
595
|
|
637
596
|
private def handle_dialog_opening(event)
|
@@ -643,7 +602,7 @@ class Puppeteer::Page
|
|
643
602
|
type: dialog_type,
|
644
603
|
message: event['message'],
|
645
604
|
default_value: event['defaultPrompt'])
|
646
|
-
emit_event(
|
605
|
+
emit_event(PageEmittedEvents::Dialog, dialog)
|
647
606
|
end
|
648
607
|
|
649
608
|
# @return [String]
|
@@ -726,7 +685,7 @@ class Puppeteer::Page
|
|
726
685
|
|
727
686
|
private def session_close_promise
|
728
687
|
@disconnect_promise ||= resolvable_future do |future|
|
729
|
-
@client.observe_first(
|
688
|
+
@client.observe_first(CDPSessionEmittedEvents::Disconnected) do
|
730
689
|
future.reject(Puppeteer::CDPSession::Error.new('Target Closed'))
|
731
690
|
end
|
732
691
|
end
|
@@ -746,7 +705,7 @@ class Puppeteer::Page
|
|
746
705
|
-> (request) { predicate.call(request) }
|
747
706
|
end
|
748
707
|
|
749
|
-
wait_for_network_manager_event(
|
708
|
+
wait_for_network_manager_event(NetworkManagerEmittedEvents::Request,
|
750
709
|
predicate: request_predicate,
|
751
710
|
timeout: timeout,
|
752
711
|
)
|
@@ -780,7 +739,7 @@ class Puppeteer::Page
|
|
780
739
|
-> (response) { predicate.call(response) }
|
781
740
|
end
|
782
741
|
|
783
|
-
wait_for_network_manager_event(
|
742
|
+
wait_for_network_manager_event(NetworkManagerEmittedEvents::Response,
|
784
743
|
predicate: response_predicate,
|
785
744
|
timeout: timeout,
|
786
745
|
)
|
@@ -907,15 +866,28 @@ class Puppeteer::Page
|
|
907
866
|
main_frame.title
|
908
867
|
end
|
909
868
|
|
910
|
-
#
|
911
|
-
#
|
912
|
-
#
|
913
|
-
#
|
914
|
-
|
869
|
+
# @param type [String] "png"|"jpeg"
|
870
|
+
# @param path [String]
|
871
|
+
# @param full_page [Boolean]
|
872
|
+
# @param clip [Hash]
|
873
|
+
# @param quality [Integer]
|
874
|
+
# @param omit_background [Boolean]
|
875
|
+
# @param encoding [String]
|
876
|
+
def screenshot(type: nil, path: nil, full_page: nil, clip: nil, quality: nil, omit_background: nil, encoding: nil)
|
877
|
+
options = {
|
878
|
+
type: type,
|
879
|
+
path: path,
|
880
|
+
full_page: full_page,
|
881
|
+
clip: clip,
|
882
|
+
quality: quality,
|
883
|
+
omit_background: omit_background,
|
884
|
+
encoding: encoding,
|
885
|
+
}.compact
|
915
886
|
screenshot_options = ScreenshotOptions.new(options)
|
916
887
|
|
917
|
-
|
918
|
-
|
888
|
+
@screenshot_task_queue.post_task do
|
889
|
+
screenshot_task(screenshot_options.type, screenshot_options)
|
890
|
+
end
|
919
891
|
end
|
920
892
|
|
921
893
|
# @param {"png"|"jpeg"} format
|
@@ -939,16 +911,16 @@ class Puppeteer::Page
|
|
939
911
|
clip = { x: 0, y: 0, width: width, height: height, scale: 1 }
|
940
912
|
|
941
913
|
screen_orientation =
|
942
|
-
if @viewport
|
914
|
+
if @viewport&.landscape?
|
943
915
|
{ angle: 90, type: 'landscapePrimary' }
|
944
916
|
else
|
945
917
|
{ angle: 0, type: 'portraitPrimary' }
|
946
918
|
end
|
947
919
|
@client.send_message('Emulation.setDeviceMetricsOverride',
|
948
|
-
mobile: @viewport
|
920
|
+
mobile: @viewport&.mobile? || false,
|
949
921
|
width: width,
|
950
922
|
height: height,
|
951
|
-
deviceScaleFactor: @viewport
|
923
|
+
deviceScaleFactor: @viewport&.device_scale_factor || 1,
|
952
924
|
screenOrientation: screen_orientation)
|
953
925
|
end
|
954
926
|
|
@@ -1043,6 +1015,12 @@ class Puppeteer::Page
|
|
1043
1015
|
else
|
1044
1016
|
@client.connection.send_message('Target.closeTarget', targetId: @target.target_id)
|
1045
1017
|
await @target.is_closed_promise
|
1018
|
+
|
1019
|
+
# @closed sometimes remains false, so wait for @closed = true with 100ms timeout.
|
1020
|
+
25.times do
|
1021
|
+
break if @closed
|
1022
|
+
sleep 0.004
|
1023
|
+
end
|
1046
1024
|
end
|
1047
1025
|
end
|
1048
1026
|
|