puppeteer-ruby 0.0.22 → 0.0.23

Sign up to get free protection for your applications and to get access to all the features.
@@ -134,7 +134,7 @@ class Puppeteer::DOMWorld
134
134
 
135
135
  # @return [String]
136
136
  def content
137
- evaluate <<-JAVASCRIPT
137
+ evaluate(<<-JAVASCRIPT)
138
138
  () => {
139
139
  let retVal = '';
140
140
  if (document.doctype)
@@ -151,7 +151,7 @@ class Puppeteer::DOMWorld
151
151
  # @param wait_until [String|Array<String>]
152
152
  def set_content(html, timeout: nil, wait_until: nil)
153
153
  option_wait_until = [wait_until || 'load'].flatten
154
- option_timeout = @timeout_settings.navigation_timeout
154
+ option_timeout = timeout || @timeout_settings.navigation_timeout
155
155
 
156
156
  # We rely upon the fact that document.open() will reset frame lifecycle with "init"
157
157
  # lifecycle event. @see https://crrev.com/608658
@@ -31,6 +31,8 @@ module Puppeteer::EventCallbackable
31
31
  (@event_listeners[event_name] ||= EventListeners.new).add(&block)
32
32
  end
33
33
 
34
+ alias_method :on, :add_event_listener
35
+
34
36
  def remove_event_listener(*id_args)
35
37
  (@event_listeners ||= {}).each do |event_name, listeners|
36
38
  id_args.each do |id|
@@ -50,6 +52,8 @@ module Puppeteer::EventCallbackable
50
52
  end
51
53
  end
52
54
 
55
+ alias_method :once, :observe_first
56
+
53
57
  def on_event(event_name, &block)
54
58
  @event_callbackable_handlers ||= {}
55
59
  @event_callbackable_handlers[event_name] = block
@@ -0,0 +1,184 @@
1
+ require 'digest/md5'
2
+
3
+ module EventsDefinitionUtils
4
+ refine Kernel do
5
+ # Symbol is used to prevent external parties listening to these events
6
+ def Symbol(str)
7
+ Digest::MD5.hexdigest(str)
8
+ end
9
+ end
10
+
11
+ refine Hash do
12
+ def define_const_into(target_module)
13
+ each do |key, value|
14
+ target_module.const_set(key, value)
15
+ target_module.define_singleton_method(key) { value }
16
+ end
17
+ keyset = Set.new(keys)
18
+ valueset = Set.new(values)
19
+ target_module.define_singleton_method(:keys) { keyset }
20
+ target_module.define_singleton_method(:values) { valueset }
21
+ end
22
+ end
23
+ end
24
+
25
+ using EventsDefinitionUtils
26
+
27
+ # Internal events that the Connection class emits.
28
+ module ConnectionEmittedEvents ; end
29
+
30
+ {
31
+ Disconnected: Symbol('Connection.Disconnected'),
32
+ }.define_const_into(ConnectionEmittedEvents)
33
+
34
+ # Internal events that the CDPSession class emits.
35
+ module CDPSessionEmittedEvents ; end
36
+
37
+ {
38
+ Disconnected: Symbol('CDPSession.Disconnected'),
39
+ }.define_const_into(CDPSessionEmittedEvents)
40
+
41
+ # All the events a Browser may emit.
42
+ module BrowserEmittedEvents ; end
43
+
44
+ {
45
+ # Emitted when Puppeteer gets disconnected from the Chromium instance. This might happen because of one of the following:
46
+ # - Chromium is closed or crashed
47
+ # - The Browser#disconnect method was called.
48
+ Disconnected: 'disconnected',
49
+
50
+ # Emitted when the url of a target changes. Contains a {@link Target} instance.
51
+ TargetChanged: 'targetchanged',
52
+
53
+ # Emitted when a target is created, for example when a new page is opened by
54
+ # window.open or by Browser#newPage
55
+ # Contains a Target instance.
56
+ TargetCreated: 'targetcreated',
57
+
58
+ # Emitted when a target is destroyed, for example when a page is closed.
59
+ # Contains a Target instance.
60
+ TargetDestroyed: 'targetdestroyed',
61
+ }.define_const_into(BrowserEmittedEvents)
62
+
63
+ module BrowserContextEmittedEvents ; end
64
+
65
+ {
66
+ # Emitted when the url of a target inside the browser context changes.
67
+ # Contains a Target instance.
68
+ TargetChanged: 'targetchanged',
69
+
70
+ # Emitted when a target is created, for example when a new page is opened by
71
+ # window.open or by BrowserContext#newPage
72
+ # Contains a Target instance.
73
+ TargetCreated: 'targetcreated',
74
+
75
+ # Emitted when a target is destroyed within the browser context, for example when a page is closed.
76
+ # Contains a Target instance.
77
+ TargetDestroyed: 'targetdestroyed',
78
+ }.define_const_into(BrowserContextEmittedEvents)
79
+
80
+ # We use symbols to prevent any external parties listening to these events.
81
+ # They are internal to Puppeteer.
82
+ module NetworkManagerEmittedEvents ; end
83
+
84
+ {
85
+ Request: Symbol('NetworkManager.Request'),
86
+ Response: Symbol('NetworkManager.Response'),
87
+ RequestFailed: Symbol('NetworkManager.RequestFailed'),
88
+ RequestFinished: Symbol('NetworkManager.RequestFinished'),
89
+ }.define_const_into(NetworkManagerEmittedEvents)
90
+
91
+
92
+ # We use symbols to prevent external parties listening to these events.
93
+ # They are internal to Puppeteer.
94
+ module FrameManagerEmittedEvents ; end
95
+
96
+ {
97
+ FrameAttached: Symbol('FrameManager.FrameAttached'),
98
+ FrameNavigated: Symbol('FrameManager.FrameNavigated'),
99
+ FrameDetached: Symbol('FrameManager.FrameDetached'),
100
+ LifecycleEvent: Symbol('FrameManager.LifecycleEvent'),
101
+ FrameNavigatedWithinDocument: Symbol('FrameManager.FrameNavigatedWithinDocument'),
102
+ ExecutionContextCreated: Symbol('FrameManager.ExecutionContextCreated'),
103
+ ExecutionContextDestroyed: Symbol('FrameManager.ExecutionContextDestroyed'),
104
+ }.define_const_into(FrameManagerEmittedEvents)
105
+
106
+ # All the events that a page instance may emit.
107
+ module PageEmittedEvents ; end
108
+
109
+ {
110
+ # Emitted when the page closes.
111
+ Close: 'close',
112
+
113
+ # Emitted when JavaScript within the page calls one of console API methods,
114
+ # e.g. `console.log` or `console.dir`. Also emitted if the page throws an
115
+ # error or a warning.
116
+ Console: 'console',
117
+
118
+ # Emitted when a JavaScript dialog appears, such as `alert`, `prompt`,
119
+ # `confirm` or `beforeunload`. Puppeteer can respond to the dialog via
120
+ # Dialog#accept or Dialog#dismiss.
121
+ Dialog: 'dialog',
122
+
123
+ # Emitted when the JavaScript
124
+ # {https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded DOMContentLoaded} event is dispatched.
125
+ DOMContentLoaded: 'domcontentloaded',
126
+
127
+ # Emitted when the page crashes. Will contain an `Error`.
128
+ Error: 'error',
129
+
130
+ # Emitted when a frame is attached. Will contain a Frame.
131
+ FrameAttached: 'frameattached',
132
+ # Emitted when a frame is detached. Will contain a Frame.
133
+ FrameDetached: 'framedetached',
134
+ # Emitted when a frame is navigated to a new URL. Will contain a {@link Frame}.
135
+ FrameNavigated: 'framenavigated',
136
+
137
+ # Emitted when the JavaScript
138
+ # {https://developer.mozilla.org/en-US/docs/Web/Events/load | load} event is dispatched.
139
+ Load: 'load',
140
+
141
+ # Emitted when the JavaScript code makes a call to `console.timeStamp`. For
142
+ # the list of metrics see {@link Page.metrics | page.metrics}.
143
+ #
144
+ # Contains an object with two properties:
145
+ # - `title`: the title passed to `console.timeStamp`
146
+ # - `metrics`: objec containing metrics as key/value pairs. The values will be `number`s.
147
+ Metrics: 'metrics',
148
+
149
+ # Emitted when an uncaught exception happens within the page.
150
+ # Contains an `Error`.
151
+ PageError: 'pageerror',
152
+
153
+ # Emitted when the page opens a new tab or window.
154
+ # Contains a Page corresponding to the popup window.
155
+ Popup: 'popup',
156
+
157
+ # Emitted when a page issues a request and contains a HTTPRequest.
158
+ #
159
+ # The object is readonly. See Page#setRequestInterception for intercepting and mutating requests.
160
+ Request: 'request',
161
+
162
+ # Emitted when a request fails, for example by timing out.
163
+ #
164
+ # Contains a HTTPRequest.
165
+ #
166
+ # NOTE: HTTP Error responses, such as 404 or 503, are still successful
167
+ # responses from HTTP standpoint, so request will complete with
168
+ # `requestfinished` event and not with `requestfailed`.
169
+ RequestFailed: 'requestfailed',
170
+
171
+ # Emitted when a request finishes successfully. Contains a HTTPRequest.
172
+ RequestFinished: 'requestfinished',
173
+
174
+ # Emitted when a response is received. Contains a HTTPResponse.
175
+ Response: 'response',
176
+
177
+ # Emitted when a dedicated
178
+ # {https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API WebWorker} is spawned by the page.
179
+ WorkerCreated: 'workercreated',
180
+
181
+ # Emitted when a dedicated
182
+ # {https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API WebWorker} is destroyed by the page.
183
+ WorkerDestroyed: 'workerdestroyed',
184
+ }.define_const_into(PageEmittedEvents)
@@ -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
@@ -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