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.
@@ -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 'Page.frameAttached' do |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 'Page.frameNavigated' do |event|
33
+ @client.on_event('Page.frameNavigated') do |event|
34
34
  handle_frame_navigated(event['frame'])
35
35
  end
36
- @client.on_event 'Page.navigatedWithinDocument' do |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 'Page.frameDetached' do |event|
39
+ @client.on_event('Page.frameDetached') do |event|
40
40
  handle_frame_detached(event['frameId'])
41
41
  end
42
- @client.on_event 'Page.frameStoppedLoading' do |event|
42
+ @client.on_event('Page.frameStoppedLoading') do |event|
43
43
  handle_frame_stopped_loading(event['frameId'])
44
44
  end
45
- @client.on_event 'Runtime.executionContextCreated' do |event|
45
+ @client.on_event('Runtime.executionContextCreated') do |event|
46
46
  handle_execution_context_created(event['context'])
47
47
  end
48
- @client.on_event 'Runtime.executionContextDestroyed' do |event|
48
+ @client.on_event('Runtime.executionContextDestroyed') do |event|
49
49
  handle_execution_context_destroyed(event['executionContextId'])
50
50
  end
51
- @client.on_event 'Runtime.executionContextsCleared' do |event|
51
+ @client.on_event('Runtime.executionContextsCleared') do |event|
52
52
  handle_execution_contexts_cleared
53
53
  end
54
- @client.on_event 'Page.lifecycleEvent' do |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 'Events.FrameManager.LifecycleEvent', frame
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 'Events.FrameManager.LifecycleEvent', frame
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 'Events.FrameManager.FrameAttached', frame
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 'Events.FrameManager.FrameNavigated', frame
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 'Events.FrameManager.FrameNavigatedWithinDocument', frame
284
- emit_event 'Events.FrameManager.FrameNavigated', frame
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 'Events.FrameManager.FrameDetached', frame
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
@@ -23,7 +23,6 @@ module Puppeteer::Launcher
23
23
  preferred_revision: preferred_revision,
24
24
  is_puppeteer_core: is_puppeteer_core,
25
25
  )
26
- raise NotImplementedError.new('FirefoxLauncher is not implemented yet.')
27
26
  end
28
27
 
29
28
  Chrome.new(
@@ -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
- @default_viewport = options[:default_viewport] || Puppeteer::Viewport.new(width: 800, height: 600)
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
- if options.nil?
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
- private def product
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
- private def product
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
- if options.nil?
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('Events.CDPSession.Disconnected') do
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('Events.FrameManager.LifecycleEvent') do |_|
76
+ @frame_manager.add_event_listener(FrameManagerEmittedEvents::LifecycleEvent) do |_|
77
77
  check_lifecycle_complete
78
78
  end,
79
- @frame_manager.add_event_listener('Events.FrameManager.FrameNavigatedWithinDocument', &method(:navigated_within_document)),
80
- @frame_manager.add_event_listener('Events.FrameManager.FrameDetached', &method(:handle_frame_detached)),
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('Events.NetworkManager.Request', &method(:handle_request))
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::FrameManager::NavigationError.new("Navigation timeout of #{@timeout}ms exceeded")
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 'Events.NetworkManager.Request', request
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 'Events.NetworkManager.Response', response
234
- emit_event 'Events.NetworkManager.RequestFinished', request
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 'Events.NetworkManager.Response', response
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 'Events.NetworkManager.RequestFinished', request
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 'Events.NetworkManager.RequestFailed', request
278
+ emit_event(NetworkManagerEmittedEvents::RequestFailed, request)
279
279
  end
280
280
  end
@@ -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, screenshot_task_queue)
19
- page = Puppeteer::Page.new(client, target, ignore_https_errors, screenshot_task_queue)
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
- # @param {!Puppeteer.TaskQueue} screenshotTaskQueue
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 = screenshot_task_queue
45
+ @screenshot_task_queue = ScreenshotTaskQueue.new
47
46
 
48
47
  @workers = {}
49
- @client.on_event 'Target.attachedToTarget' do |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(Events.Page.WorkerCreated, worker);
58
+ # this.emit(PageEmittedEvents::WorkerCreated, worker);
60
59
  end
61
- @client.on_event 'Target.detachedFromTarget' do |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('Events.Page.WorkerDestroyed', worker)
65
+ emit_event(PageEmittedEvents::WorkerDestroyed, worker)
67
66
  @workers.delete(session_id)
68
67
  end
69
68
 
70
- @frame_manager.on_event 'Events.FrameManager.FrameAttached' do |event|
71
- emit_event 'Events.Page.FrameAttached', event
69
+ @frame_manager.on_event(FrameManagerEmittedEvents::FrameAttached) do |event|
70
+ emit_event(PageEmittedEvents::FrameAttached, event)
72
71
  end
73
- @frame_manager.on_event 'Events.FrameManager.FrameDetached' do |event|
74
- emit_event 'Events.Page.FrameDetached', event
72
+ @frame_manager.on_event(FrameManagerEmittedEvents::FrameDetached) do |event|
73
+ emit_event(PageEmittedEvents::FrameDetached, event)
75
74
  end
76
- @frame_manager.on_event 'Events.FrameManager.FrameNavigated' do |event|
77
- emit_event 'Events.Page.FrameNavigated', 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 'Events.NetworkManager.Request' do |event|
82
- emit_event 'Events.Page.Request', event
80
+ network_manager.on_event(NetworkManagerEmittedEvents::Request) do |event|
81
+ emit_event(PageEmittedEvents::Request, event)
83
82
  end
84
- network_manager.on_event 'Events.NetworkManager.Response' do |event|
85
- emit_event 'Events.Page.Response', event
83
+ network_manager.on_event(NetworkManagerEmittedEvents::Response) do |event|
84
+ emit_event(PageEmittedEvents::Response, event)
86
85
  end
87
- network_manager.on_event 'Events.NetworkManager.RequestFailed' do |event|
88
- emit_event 'Events.Page.RequestFailed', event
86
+ network_manager.on_event(NetworkManagerEmittedEvents::RequestFailed) do |event|
87
+ emit_event(PageEmittedEvents::RequestFailed, event)
89
88
  end
90
- network_manager.on_event 'Events.NetworkManager.RequestFinished' do |event|
91
- emit_event 'Events.Page.RequestFinished', 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 'Page.domContentEventFired' do |event|
97
- emit_event 'Events.Page.DOMContentLoaded'
95
+ @client.on_event('Page.domContentEventFired') do |event|
96
+ emit_event(PageEmittedEvents::DOMContentLoaded)
98
97
  end
99
- @client.on_event 'Page.loadEventFired' do |event|
100
- emit_event 'Events.Page.Load'
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 'Page.javascriptDialogOpening' do |event|
103
+ @client.on_event('Page.javascriptDialogOpening') do |event|
105
104
  handle_dialog_opening(event)
106
105
  end
107
- # client.on('Runtime.exceptionThrown', exception => this._handleException(exception.exceptionDetails));
108
- # client.on('Inspector.targetCrashed', event => this._onTargetCrashed());
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 'Log.entryAdded' do |event|
113
+ @client.on_event('Log.entryAdded') do |event|
111
114
  handle_log_entry_added(event)
112
115
  end
113
- @client.on_event 'Page.fileChooserOpened' do |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 'Events.Page.Close'
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 EVENT_MAPPINGS.has_key?(event_name.to_sym)
155
- raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
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
- add_event_listener(EVENT_MAPPINGS[event_name.to_sym], &block)
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 EVENT_MAPPINGS.has_key?(event_name.to_sym)
164
- raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{EVENT_MAPPINGS.keys.join(", ")}")
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
- observe_first(EVENT_MAPPINGS[event_name.to_sym], &block)
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
- # * @param {!{longitude: number, latitude: number, accuracy: (number|undefined)}} options
216
- # */
217
- # async setGeolocation(options) {
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 'error', TargetCrashedError.new('Page crashed!')
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('Events.Page.Console',
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
- # * @param {!Array<string>} urls
372
- # * @return {!Promise<!Array<Network.Cookie>>}
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
- # * @param {Array<Protocol.Network.deleteCookiesParameters>} cookies
382
- # */
383
- # async deleteCookie(...cookies) {
384
- # const pageURL = this.url();
385
- # for (const cookie of cookies) {
386
- # const item = Object.assign({}, cookie);
387
- # if (!cookie.url && pageURL.startsWith('http'))
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
- # * @param {Array<Network.CookieParam>} cookies
395
- # */
396
- # async setCookie(...cookies) {
397
- # const pageURL = this.url();
398
- # const startsWithHTTP = pageURL.startsWith('http');
399
- # const items = cookies.map(cookie => {
400
- # const item = Object.assign({}, cookie);
401
- # if (!item.url && startsWithHTTP)
402
- # item.url = pageURL;
403
- # assert(item.url !== 'about:blank', `Blank page can not have cookie "${item.name}"`);
404
- # assert(!String.prototype.startsWith.call(item.url || '', 'data:'), `Data URL page can not have cookie "${item.name}"`);
405
- # return item;
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(Events.Page.Metrics, {
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
- # * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails
526
- # */
527
- # _handleException(exceptionDetails) {
528
- # const message = helper.getExceptionMessage(exceptionDetails);
529
- # const err = new Error(message);
530
- # err.stack = ''; // Don't report clientside error with a node stack attached
531
- # this.emit(Events.Page.PageError, err);
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(Events.Page.Console)) {
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(Events.Page.Console, message);
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('Events.Page.Dialog', dialog)
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('Events.CDPSession.Disconnected') do
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('Events.NetworkManager.Request',
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('Events.NetworkManager.Response',
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
- # * @param {!ScreenshotOptions=} options
912
- # * @return {!Promise<!Buffer|!String>}
913
- # */
914
- def screenshot(options = {})
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
- # @screenshot_task_queue.post_task(-> { screenshot_task(screenshot_options.type, screenshot_options) })
918
- screenshot_task(screenshot_options.type, screenshot_options)
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.landscape?
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.mobile?,
920
+ mobile: @viewport&.mobile? || false,
949
921
  width: width,
950
922
  height: height,
951
- deviceScaleFactor: @viewport.device_scale_factor,
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