puppeteer-ruby 0.0.21 → 0.0.27

Sign up to get free protection for your applications and to get access to all the features.
@@ -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