puppeteer-ruby 0.35.1 → 0.36.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6261fae36fcaf6fad5ebcc8a6721496ea1ba7e76cdc770885f1b9cb6c49582b
4
- data.tar.gz: cd7c4dbc2370568973c4319158e8c0d2f79248f83a7d7fd0a1f00d8c9f38ffa8
3
+ metadata.gz: 1d4af926ca9ad39046dcc4ce9507622bf72c3263b747516ee1ba642c02e8e38d
4
+ data.tar.gz: 3eff89952883a47270dee6a9ee7281609dbbe7bac197eada9e6b6dcabbaa1f78
5
5
  SHA512:
6
- metadata.gz: 0cb8cb7bebc28260088f3f558225c83f8e15bfbe539c0eabbca2fdcf324e941d3c4f61ebd08b57600bf3a366219bcb2f9c0930e250f796caca367601baab9daf
7
- data.tar.gz: e3532e2a9b2da82d819b6946a7c4b2b870312dfb359739b997a7f2c72bc13772998a89453a3e244e64d0db0a6c9f6f70b95d86ba2d02c1b7d8cd5411a67c482f
6
+ metadata.gz: d83085eb31bb6d0d9f125e4c82fe172f46676bc373bd7652b2be430984570f7facff20bd3b1da20dbcb41beca52a740064b9b6d5cb6f51a69bf8f4f97e699947
7
+ data.tar.gz: 141bed0475d5e40a0ddb9ec0cb2a82f44313a14e835a7d5cc271a413dad0c9881658a81f0ec0567d7896a7658539fc131bb5069c1c07c6573f6b633c99a53f8e
data/CHANGELOG.md CHANGED
@@ -1,7 +1,16 @@
1
- ### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.35.1...master)]
1
+ ### master [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.36.0...master)]
2
2
 
3
3
  * xxx
4
4
 
5
+ ### 0.36.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.35.1...0.36.0)]
6
+
7
+ New features:
8
+
9
+ * Drag and Drop feature introduced in Puppeteer 10.1
10
+ * `Page#emulateNetworkConditions`, `Page#emulateCPUThrottling`
11
+ * `Page#exposeFunction`
12
+ * Metrics
13
+
5
14
  ### 0.35.1 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.35.0...0.35.1)]
6
15
 
7
16
  New features:
data/docs/api_coverage.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # API coverages
2
- - Puppeteer version: v10.1.0
3
- - puppeteer-ruby version: 0.35.1
2
+ - Puppeteer version: v10.2.0
3
+ - puppeteer-ruby version: 0.36.0
4
4
 
5
5
  ## Puppeteer
6
6
 
@@ -13,7 +13,7 @@
13
13
  * ~~errors~~
14
14
  * executablePath => `#executable_path`
15
15
  * launch
16
- * ~~networkConditions~~
16
+ * networkConditions => `#network_conditions`
17
17
  * product
18
18
  * ~~registerCustomQueryHandler~~
19
19
  * ~~unregisterCustomQueryHandler~~
@@ -81,17 +81,17 @@
81
81
  * createPDFStream => `#create_pdf_stream`
82
82
  * deleteCookie => `#delete_cookie`
83
83
  * emulate
84
- * ~~emulateCPUThrottling~~
84
+ * emulateCPUThrottling => `#emulate_cpu_throttling`
85
85
  * emulateIdleState => `#emulate_idle_state`
86
86
  * emulateMediaFeatures => `#emulate_media_features`
87
87
  * emulateMediaType => `#emulate_media_type`
88
- * ~~emulateNetworkConditions~~
88
+ * emulateNetworkConditions => `#emulate_network_conditions`
89
89
  * emulateTimezone => `#emulate_timezone`
90
90
  * emulateVisionDeficiency => `#emulate_vision_deficiency`
91
91
  * evaluate
92
92
  * evaluateHandle => `#evaluate_handle`
93
93
  * evaluateOnNewDocument => `#evaluate_on_new_document`
94
- * ~~exposeFunction~~
94
+ * exposeFunction => `#expose_function`
95
95
  * focus
96
96
  * frames
97
97
  * goBack => `#go_back`
@@ -99,11 +99,11 @@
99
99
  * goto
100
100
  * hover
101
101
  * isClosed => `#closed?`
102
- * ~~isDragInterceptionEnabled~~
102
+ * isDragInterceptionEnabled => `#drag_interception_enabled?`
103
103
  * isJavaScriptEnabled => `#javascript_enabled?`
104
104
  * keyboard
105
105
  * mainFrame => `#main_frame`
106
- * ~~metrics~~
106
+ * metrics
107
107
  * mouse
108
108
  * pdf
109
109
  * queryObjects => `#query_objects`
@@ -166,11 +166,11 @@
166
166
 
167
167
  * click
168
168
  * down
169
- * ~~drag~~
170
- * ~~dragAndDrop~~
171
- * ~~dragEnter~~
172
- * ~~dragOver~~
173
- * ~~drop~~
169
+ * drag
170
+ * dragAndDrop => `#drag_and_drop`
171
+ * dragEnter => `#drag_enter`
172
+ * dragOver => `#drag_over`
173
+ * drop
174
174
  * move
175
175
  * up
176
176
  * wheel
@@ -272,11 +272,11 @@
272
272
  * clickablePoint => `#clickable_point`
273
273
  * contentFrame => `#content_frame`
274
274
  * dispose
275
- * ~~drag~~
276
- * ~~dragAndDrop~~
277
- * ~~dragEnter~~
278
- * ~~dragOver~~
279
- * ~~drop~~
275
+ * drag
276
+ * dragAndDrop => `#drag_and_drop`
277
+ * dragEnter => `#drag_enter`
278
+ * dragOver => `#drag_over`
279
+ * drop
280
280
  * evaluate
281
281
  * evaluateHandle => `#evaluate_handle`
282
282
  * executionContext => `#execution_context`
@@ -297,8 +297,12 @@
297
297
  ## ~~HTTPRequest~~
298
298
 
299
299
  * ~~abort~~
300
+ * ~~abortErrorReason~~
300
301
  * ~~continue~~
302
+ * ~~continueRequestOverrides~~
303
+ * ~~enqueueInterceptAction~~
301
304
  * ~~failure~~
305
+ * ~~finalizeInterceptions~~
302
306
  * ~~frame~~
303
307
  * ~~headers~~
304
308
  * ~~isNavigationRequest~~
@@ -308,6 +312,7 @@
308
312
  * ~~resourceType~~
309
313
  * ~~respond~~
310
314
  * ~~response~~
315
+ * ~~responseForRequest~~
311
316
  * ~~url~~
312
317
 
313
318
  ## ~~HTTPResponse~~
data/lib/puppeteer.rb CHANGED
@@ -5,7 +5,6 @@ module Puppeteer; end
5
5
  require 'puppeteer/env'
6
6
 
7
7
  # Custom data types.
8
- require 'puppeteer/device'
9
8
  require 'puppeteer/events'
10
9
  require 'puppeteer/errors'
11
10
  require 'puppeteer/geolocation'
@@ -44,6 +43,7 @@ require 'puppeteer/keyboard'
44
43
  require 'puppeteer/launcher'
45
44
  require 'puppeteer/lifecycle_watcher'
46
45
  require 'puppeteer/mouse'
46
+ require 'puppeteer/network_conditions'
47
47
  require 'puppeteer/network_manager'
48
48
  require 'puppeteer/page'
49
49
  require 'puppeteer/protocol_stream_reader'
@@ -3,11 +3,11 @@ module Puppeteer::ConcurrentRubyUtils
3
3
  module ConcurrentPromisesFutureExtension
4
4
  # Extension for describing 2 concurrent tasks smartly.
5
5
  #
6
- # page.async_for_navigation.with_waiting_for_complete do
6
+ # page.async_wait_for_navigation.with_waiting_for_complete do
7
7
  # page.click('#submit')
8
8
  # end
9
9
  def with_waiting_for_complete(&block)
10
- async_block_call = Concurrent::Promises.future do
10
+ async_block_call = Concurrent::Promises.delay do
11
11
  block.call
12
12
  rescue => err
13
13
  Logger.new($stderr).warn(err)
@@ -40,7 +40,7 @@ module Puppeteer::DefineAsyncMethod
40
40
  end
41
41
  end
42
42
 
43
- async_block_call = Concurrent::Promises.future do
43
+ async_block_call = Concurrent::Promises.delay do
44
44
  block.call
45
45
  rescue => err
46
46
  Logger.new($stderr).warn(err)
@@ -1,3 +1,5 @@
1
+ require_relative './device'
2
+
1
3
  Puppeteer::DEVICES = Hash[
2
4
  [
3
5
  {
@@ -147,6 +147,47 @@ class Puppeteer::ElementHandle < Puppeteer::JSHandle
147
147
 
148
148
  define_async_method :async_click
149
149
 
150
+ class DragInterceptionNotEnabledError < StandardError
151
+ def initialize
152
+ super('Drag Interception is not enabled!')
153
+ end
154
+ end
155
+
156
+ def drag(x:, y:)
157
+ unless @page.drag_interception_enabled?
158
+ raise DragInterceptionNotEnabledError.new
159
+ end
160
+ scroll_into_view_if_needed
161
+ start = clickable_point
162
+ @page.mouse.drag(start, Point.new(x: x, y: y))
163
+ end
164
+
165
+ def drag_enter(data)
166
+ scroll_into_view_if_needed
167
+ target = clickable_point
168
+ @page.mouse.drag_enter(target, data)
169
+ end
170
+
171
+ def drag_over(data)
172
+ scroll_into_view_if_needed
173
+ target = clickable_point
174
+ @page.mouse.drag_over(target, data)
175
+ end
176
+
177
+ def drop(data)
178
+ scroll_into_view_if_needed
179
+ target = clickable_point
180
+ @page.mouse.drop(target, data)
181
+ end
182
+
183
+ # @param target [ElementHandle]
184
+ def drag_and_drop(target, delay: nil)
185
+ scroll_into_view_if_needed
186
+ start_point = clickable_point
187
+ target_point = target.clickable_point
188
+ @page.mouse.drag_and_drop(start_point, target_point, delay: delay)
189
+ end
190
+
150
191
  # @return [Array<String>]
151
192
  def select(*values)
152
193
  if nonstring = values.find { |value| !value.is_a?(String) }
@@ -94,6 +94,8 @@ class Puppeteer::Mouse
94
94
  )
95
95
  end
96
96
 
97
+ define_async_method :async_up
98
+
97
99
  # Dispatches a `mousewheel` event.
98
100
  #
99
101
  # @param delta_x [Integer]
@@ -110,5 +112,56 @@ class Puppeteer::Mouse
110
112
  )
111
113
  end
112
114
 
113
- define_async_method :async_up
115
+ def drag(start, target)
116
+ promise = resolvable_future do |f|
117
+ @client.once('Input.dragIntercepted') do |event|
118
+ f.fulfill(event['data'])
119
+ end
120
+ end
121
+ move(start.x, start.y)
122
+ down
123
+ move(target.x, target.y)
124
+ promise.value!
125
+ end
126
+
127
+ def drag_enter(target, data)
128
+ @client.send_message('Input.dispatchDragEvent',
129
+ type: 'dragEnter',
130
+ x: target.x,
131
+ y: target.y,
132
+ modifiers: @keyboard.modifiers,
133
+ data: data,
134
+ )
135
+ end
136
+
137
+ def drag_over(target, data)
138
+ @client.send_message('Input.dispatchDragEvent',
139
+ type: 'dragOver',
140
+ x: target.x,
141
+ y: target.y,
142
+ modifiers: @keyboard.modifiers,
143
+ data: data,
144
+ )
145
+ end
146
+
147
+ def drop(target, data)
148
+ @client.send_message('Input.dispatchDragEvent',
149
+ type: 'drop',
150
+ x: target.x,
151
+ y: target.y,
152
+ modifiers: @keyboard.modifiers,
153
+ data: data,
154
+ )
155
+ end
156
+
157
+ def drag_and_drop(start, target, delay: nil)
158
+ data = drag(start, target)
159
+ drag_enter(target, data)
160
+ drag_over(target, data)
161
+ if delay
162
+ sleep(delay / 1000.0)
163
+ end
164
+ drop(target, data)
165
+ up
166
+ end
114
167
  end
@@ -0,0 +1,12 @@
1
+ class Puppeteer::NetworkCondition
2
+ # @param download [Number] Download speed (bytes/s)
3
+ # @param upload [Number] Upload speed (bytes/s)
4
+ # @param latency [Number] Latency (ms)
5
+ def initialize(download:, upload:, latency:)
6
+ @download = download
7
+ @upload = upload
8
+ @latency = latency
9
+ end
10
+
11
+ attr_reader :download, :upload, :latency
12
+ end
@@ -0,0 +1,24 @@
1
+ require_relative './network_condition'
2
+
3
+ Puppeteer::NETWORK_CONDITIONS = {
4
+ 'Slow 3G' => Puppeteer::NetworkCondition.new(
5
+ download: ((500 * 1000) / 8) * 0.8,
6
+ upload: ((500 * 1000) / 8) * 0.8,
7
+ latency: 400 * 5,
8
+ ),
9
+ 'Fast 3G' => Puppeteer::NetworkCondition.new(
10
+ download: ((1.6 * 1000 * 1000) / 8) * 0.9,
11
+ upload: ((750 * 1000) / 8) * 0.9,
12
+ latency: 150 * 3.75,
13
+ ),
14
+ }
15
+
16
+ module Puppeteer::NetworkConditions
17
+ module_function def slow_3g
18
+ Puppeteer::NETWORK_CONDITIONS['Slow 3G']
19
+ end
20
+
21
+ module_function def fast_3g
22
+ Puppeteer::NETWORK_CONDITIONS['Fast 3G']
23
+ end
24
+ end
@@ -13,6 +13,46 @@ class Puppeteer::NetworkManager
13
13
  attr_reader :username, :password
14
14
  end
15
15
 
16
+ class InternalNetworkCondition
17
+ attr_writer :offline, :upload, :download, :latency
18
+
19
+ def initialize(client)
20
+ @client = client
21
+ @offline = false
22
+ @upload = -1
23
+ @download = -1
24
+ @latency = 0
25
+ end
26
+
27
+ def offline_mode=(value)
28
+ return if @offline == value
29
+ @offline = value
30
+ update_network_conditions
31
+ end
32
+
33
+ def network_condition=(network_condition)
34
+ if network_condition
35
+ @upload = network_condition.upload
36
+ @download = network_condition.download
37
+ @latency = network_condition.latency
38
+ else
39
+ @upload = -1
40
+ @download = -1
41
+ @latency = 0
42
+ end
43
+ update_network_conditions
44
+ end
45
+
46
+ private def update_network_conditions
47
+ @client.send_message('Network.emulateNetworkConditions',
48
+ offline: @offline,
49
+ latency: @latency,
50
+ downloadThroughput: @download,
51
+ uploadThroughput: @upload,
52
+ )
53
+ end
54
+ end
55
+
16
56
  # @param {!Puppeteer.CDPSession} client
17
57
  # @param {boolean} ignoreHTTPSErrors
18
58
  # @param {!Puppeteer.FrameManager} frameManager
@@ -29,13 +69,12 @@ class Puppeteer::NetworkManager
29
69
 
30
70
  @extra_http_headers = {}
31
71
 
32
- @offline = false
33
-
34
72
  @attempted_authentications = Set.new
35
73
  @user_request_interception_enabled = false
36
74
  @protocol_request_interception_enabled = false
37
75
  @user_cache_disabled = false
38
76
  @request_id_to_interception_id = {}
77
+ @internal_network_condition = InternalNetworkCondition.new(@client)
39
78
 
40
79
  @client.on_event('Fetch.requestPaused') do |event|
41
80
  handle_request_paused(event)
@@ -94,15 +133,12 @@ class Puppeteer::NetworkManager
94
133
 
95
134
  # @param value [TrueClass|FalseClass]
96
135
  def offline_mode=(value)
97
- return if @offline == value
98
- @offline = value
99
- @client.send_message('Network.emulateNetworkConditions',
100
- offline: @offline,
101
- # values of 0 remove any active throttling. crbug.com/456324#c9
102
- latency: 0,
103
- downloadThroughput: -1,
104
- uploadThroughput: -1,
105
- )
136
+ @internal_network_condition.offline_mode=(value)
137
+ end
138
+
139
+ # @param network_condition [Puppeteer::NetworkCondition|nil]
140
+ def emulate_network_conditions(network_condition)
141
+ @internal_network_condition.network_condition = network_condition
106
142
  end
107
143
 
108
144
  # @param user_agent [String]
@@ -2,11 +2,13 @@ require 'base64'
2
2
  require 'json'
3
3
  require "stringio"
4
4
 
5
+ require_relative './page/metrics'
5
6
  require_relative './page/pdf_options'
6
7
  require_relative './page/screenshot_options'
7
8
  require_relative './page/screenshot_task_queue'
8
9
 
9
10
  class Puppeteer::Page
11
+ include Puppeteer::DebugPrint
10
12
  include Puppeteer::EventCallbackable
11
13
  include Puppeteer::IfPresent
12
14
  using Puppeteer::DefineAsyncMethod
@@ -46,6 +48,8 @@ class Puppeteer::Page
46
48
  @screenshot_task_queue = ScreenshotTaskQueue.new
47
49
 
48
50
  @workers = {}
51
+ @user_drag_interception_enabled = false
52
+
49
53
  @client.on_event('Target.attachedToTarget') do |event|
50
54
  if event['targetInfo']['type'] != 'worker'
51
55
  # If we don't detach from service workers, they will never die.
@@ -102,7 +106,9 @@ class Puppeteer::Page
102
106
  @client.on('Runtime.consoleAPICalled') do |event|
103
107
  handle_console_api(event)
104
108
  end
105
- # client.on('Runtime.bindingCalled', event => this._onBindingCalled(event));
109
+ @client.on('Runtime.bindingCalled') do |event|
110
+ handle_binding_called(event)
111
+ end
106
112
  @client.on_event('Page.javascriptDialogOpening') do |event|
107
113
  handle_dialog_opening(event)
108
114
  end
@@ -112,7 +118,9 @@ class Puppeteer::Page
112
118
  @client.on_event('Inspector.targetCrashed') do |event|
113
119
  handle_target_crashed
114
120
  end
115
- # client.on('Performance.metrics', event => this._emitMetrics(event));
121
+ @client.on_event('Performance.metrics') do |event|
122
+ emit_event(PageEmittedEvents::Metrics, MetricsEvent.new(event))
123
+ end
116
124
  @client.on_event('Log.entryAdded') do |event|
117
125
  handle_log_entry_added(event)
118
126
  end
@@ -134,6 +142,11 @@ class Puppeteer::Page
134
142
  )
135
143
  end
136
144
 
145
+ def drag_interception_enabled?
146
+ @user_drag_interception_enabled
147
+ end
148
+ alias_method :drag_interception_enabled, :drag_interception_enabled?
149
+
137
150
  # @param event_name [Symbol]
138
151
  def on(event_name, &block)
139
152
  unless PageEmittedEvents.values.include?(event_name.to_s)
@@ -266,10 +279,20 @@ class Puppeteer::Page
266
279
  @frame_manager.network_manager.request_interception = value
267
280
  end
268
281
 
282
+ def drag_interception_enabled=(enabled)
283
+ @user_drag_interception_enabled = enabled
284
+ @client.send_message('Input.setInterceptDrags', enabled: enabled)
285
+ end
286
+
269
287
  def offline_mode=(enabled)
270
288
  @frame_manager.network_manager.offline_mode = enabled
271
289
  end
272
290
 
291
+ # @param network_condition [Puppeteer::NetworkCondition|nil]
292
+ def emulate_network_conditions(network_condition)
293
+ @frame_manager.network_manager.emulate_network_conditions(network_condition)
294
+ end
295
+
273
296
  # @param {number} timeout
274
297
  def default_navigation_timeout=(timeout)
275
298
  @timeout_settings.default_navigation_timeout = timeout
@@ -392,6 +415,51 @@ class Puppeteer::Page
392
415
  main_frame.add_style_tag(url: url, path: path, content: content)
393
416
  end
394
417
 
418
+ # @param name [String]
419
+ # @param puppeteer_function [Proc]
420
+ def expose_function(name, puppeteer_function)
421
+ if @page_bindings[name]
422
+ raise ArgumentError.new("Failed to add page binding with name `#{name}` already exists!")
423
+ end
424
+ @page_bindings[name] = puppeteer_function
425
+
426
+ add_page_binding = <<~JAVASCRIPT
427
+ function (type, bindingName) {
428
+ /* Cast window to any here as we're about to add properties to it
429
+ * via win[bindingName] which TypeScript doesn't like.
430
+ */
431
+ const win = window;
432
+ const binding = win[bindingName];
433
+
434
+ win[bindingName] = (...args) => {
435
+ const me = window[bindingName];
436
+ let callbacks = me.callbacks;
437
+ if (!callbacks) {
438
+ callbacks = new Map();
439
+ me.callbacks = callbacks;
440
+ }
441
+ const seq = (me.lastSeq || 0) + 1;
442
+ me.lastSeq = seq;
443
+ const promise = new Promise((resolve, reject) =>
444
+ callbacks.set(seq, { resolve, reject })
445
+ );
446
+ binding(JSON.stringify({ type, name: bindingName, seq, args }));
447
+ return promise;
448
+ };
449
+ }
450
+ JAVASCRIPT
451
+
452
+ source = JavaScriptFunction.new(add_page_binding, ['exposedFun', name]).source
453
+ @client.send_message('Runtime.addBinding', name: name)
454
+ @client.send_message('Page.addScriptToEvaluateOnNewDocument', source: source)
455
+
456
+ promises = @frame_manager.frames.map do |frame|
457
+ frame.async_evaluate("() => #{source}")
458
+ end
459
+ await_all(*promises)
460
+
461
+ nil
462
+ end
395
463
  # /**
396
464
  # * @param {string} name
397
465
  # * @param {Function} puppeteerFunction
@@ -440,36 +508,10 @@ class Puppeteer::Page
440
508
  @frame_manager.network_manager.user_agent = user_agent
441
509
  end
442
510
 
443
- # /**
444
- # * @return {!Promise<!Metrics>}
445
- # */
446
- # async metrics() {
447
- # const response = await this._client.send('Performance.getMetrics');
448
- # return this._buildMetricsObject(response.metrics);
449
- # }
450
-
451
- # /**
452
- # * @param {!Protocol.Performance.metricsPayload} event
453
- # */
454
- # _emitMetrics(event) {
455
- # this.emit(PageEmittedEvents::Metrics, {
456
- # title: event.title,
457
- # metrics: this._buildMetricsObject(event.metrics)
458
- # });
459
- # }
460
-
461
- # /**
462
- # * @param {?Array<!Protocol.Performance.Metric>} metrics
463
- # * @return {!Metrics}
464
- # */
465
- # _buildMetricsObject(metrics) {
466
- # const result = {};
467
- # for (const metric of metrics || []) {
468
- # if (supportedMetrics.has(metric.name))
469
- # result[metric.name] = metric.value;
470
- # }
471
- # return result;
472
- # }
511
+ def metrics
512
+ response = @client.send_message('Performance.getMetrics')
513
+ Metrics.new(response['metrics'])
514
+ end
473
515
 
474
516
  class PageError < StandardError ; end
475
517
 
@@ -506,56 +548,51 @@ class Puppeteer::Page
506
548
  add_console_message(event['type'], values, event['stackTrace'])
507
549
  end
508
550
 
509
- # /**
510
- # * @param {!Protocol.Runtime.bindingCalledPayload} event
511
- # */
512
- # async _onBindingCalled(event) {
513
- # const {name, seq, args} = JSON.parse(event.payload);
514
- # let expression = null;
515
- # try {
516
- # const result = await this._pageBindings.get(name)(...args);
517
- # expression = helper.evaluationString(deliverResult, name, seq, result);
518
- # } catch (error) {
519
- # if (error instanceof Error)
520
- # expression = helper.evaluationString(deliverError, name, seq, error.message, error.stack);
521
- # else
522
- # expression = helper.evaluationString(deliverErrorValue, name, seq, error);
523
- # }
524
- # this._client.send('Runtime.evaluate', { expression, contextId: event.executionContextId }).catch(debugError);
525
-
526
- # /**
527
- # * @param {string} name
528
- # * @param {number} seq
529
- # * @param {*} result
530
- # */
531
- # function deliverResult(name, seq, result) {
532
- # window[name]['callbacks'].get(seq).resolve(result);
533
- # window[name]['callbacks'].delete(seq);
534
- # }
551
+ def handle_binding_called(event)
552
+ execution_context_id = event['executionContextId']
553
+ payload =
554
+ begin
555
+ JSON.parse(event['payload'])
556
+ rescue
557
+ # The binding was either called by something in the page or it was
558
+ # called before our wrapper was initialized.
559
+ return
560
+ end
561
+ name = payload['name']
562
+ seq = payload['seq']
563
+ args = payload['args']
535
564
 
536
- # /**
537
- # * @param {string} name
538
- # * @param {number} seq
539
- # * @param {string} message
540
- # * @param {string} stack
541
- # */
542
- # function deliverError(name, seq, message, stack) {
543
- # const error = new Error(message);
544
- # error.stack = stack;
545
- # window[name]['callbacks'].get(seq).reject(error);
546
- # window[name]['callbacks'].delete(seq);
547
- # }
565
+ if payload['type'] != 'exposedFun' || !@page_bindings[name]
566
+ return
567
+ end
548
568
 
549
- # /**
550
- # * @param {string} name
551
- # * @param {number} seq
552
- # * @param {*} value
553
- # */
554
- # function deliverErrorValue(name, seq, value) {
555
- # window[name]['callbacks'].get(seq).reject(value);
556
- # window[name]['callbacks'].delete(seq);
557
- # }
558
- # }
569
+ expression =
570
+ begin
571
+ result = @page_bindings[name].call(*args)
572
+
573
+ deliver_result = <<~JAVASCRIPT
574
+ function (name, seq, result) {
575
+ window[name].callbacks.get(seq).resolve(result);
576
+ window[name].callbacks.delete(seq);
577
+ }
578
+ JAVASCRIPT
579
+
580
+ JavaScriptFunction.new(deliver_result, [name, seq, result]).source
581
+ rescue => err
582
+ deliver_error = <<~JAVASCRIPT
583
+ function (name, seq, message) {
584
+ const error = new Error(message);
585
+ window[name].callbacks.get(seq).reject(error);
586
+ window[name].callbacks.delete(seq);
587
+ }
588
+ JAVASCRIPT
589
+ JavaScriptFunction.new(deliver_error, [name, seq, err.message]).source
590
+ end
591
+
592
+ @client.async_send_message('Runtime.evaluate', expression: expression, contextId: execution_context_id).rescue do |error|
593
+ debug_puts(error)
594
+ end
595
+ end
559
596
 
560
597
  private def add_console_message(type, args, stack_trace)
561
598
  text_tokens = args.map { |arg| arg.remote_object.value }
@@ -631,10 +668,9 @@ class Puppeteer::Page
631
668
  # @param wait_until [string|nil] 'load' | 'domcontentloaded' | 'networkidle0' | 'networkidle2'
632
669
  # @return [Puppeteer::Response]
633
670
  def reload(timeout: nil, wait_until: nil)
634
- await_all(
635
- async_wait_for_navigation(timeout: timeout, wait_until: wait_until),
636
- @client.async_send_message('Page.reload'),
637
- ).first
671
+ wait_for_navigation(timeout: timeout, wait_until: wait_until) do
672
+ @client.send_message('Page.reload')
673
+ end
638
674
  end
639
675
 
640
676
  def wait_for_navigation(timeout: nil, wait_until: nil)
@@ -760,10 +796,9 @@ class Puppeteer::Page
760
796
  entries = history['entries']
761
797
  index = history['currentIndex'] + delta
762
798
  if_present(entries[index]) do |entry|
763
- await_all(
764
- async_wait_for_navigation(timeout: timeout, wait_until: wait_until),
765
- @client.async_send_message('Page.navigateToHistoryEntry', entryId: entry['id']),
766
- )
799
+ wait_for_navigation(timeout: timeout, wait_until: wait_until) do
800
+ @client.send_message('Page.navigateToHistoryEntry', entryId: entry['id'])
801
+ end
767
802
  end
768
803
  end
769
804
 
@@ -799,6 +834,15 @@ class Puppeteer::Page
799
834
  @client.send_message('Emulation.setEmulatedMedia', media: media_type_str)
800
835
  end
801
836
 
837
+ # @param factor [Number|nil] Factor at which the CPU will be throttled (2x, 2.5x. 3x, ...). Passing `nil` disables cpu throttling.
838
+ def emulate_cpu_throttling(factor)
839
+ if factor.nil? || factor >= 1
840
+ @client.send_message('Emulation.setCPUThrottlingRate', rate: factor || 1)
841
+ else
842
+ raise ArgumentError.new('Throttling rate should be greater or equal to 1')
843
+ end
844
+ end
845
+
802
846
  # @param features [Array]
803
847
  def emulate_media_features(features)
804
848
  if features.nil?
@@ -0,0 +1,49 @@
1
+ class Puppeteer::Page
2
+ class Metrics
3
+ SUPPORTED_KEYS = Set.new([
4
+ 'Timestamp',
5
+ 'Documents',
6
+ 'Frames',
7
+ 'JSEventListeners',
8
+ 'Nodes',
9
+ 'LayoutCount',
10
+ 'RecalcStyleCount',
11
+ 'LayoutDuration',
12
+ 'RecalcStyleDuration',
13
+ 'ScriptDuration',
14
+ 'TaskDuration',
15
+ 'JSHeapUsedSize',
16
+ 'JSHeapTotalSize',
17
+ ]).freeze
18
+
19
+ SUPPORTED_KEYS.each do |key|
20
+ attr_reader key
21
+ end
22
+
23
+ # @param metrics_result [Hash] response for Performance.getMetrics
24
+ def initialize(metrics_response)
25
+ metrics_response.each do |metric|
26
+ if SUPPORTED_KEYS.include?(metric['name'])
27
+ instance_variable_set(:"@#{metric['name']}", metric['value'])
28
+ end
29
+ end
30
+ end
31
+
32
+ def [](key)
33
+ if SUPPORTED_KEYS.include?(key.to_s)
34
+ instance_variable_get(:"@#{key}")
35
+ else
36
+ raise ArgumentError.new("invalid metric key specified: #{key}")
37
+ end
38
+ end
39
+ end
40
+
41
+ class MetricsEvent
42
+ def initialize(metrics_event)
43
+ @title = metrics_event['title']
44
+ @metrics = Metrics.new(metrics_event['metrics'])
45
+ end
46
+
47
+ attr_reader :title, :metrics
48
+ end
49
+ end
@@ -149,6 +149,11 @@ class Puppeteer::Puppeteer
149
149
  # # ???
150
150
  # end
151
151
 
152
+ # @return [Puppeteer::NetworkConditions]
153
+ def network_conditions
154
+ Puppeteer::NetworkConditions
155
+ end
156
+
152
157
  # @param args [Array<String>]
153
158
  # @param user_data_dir [String]
154
159
  # @param devtools [Boolean]
@@ -1,3 +1,3 @@
1
1
  module Puppeteer
2
- VERSION = '0.35.1'
2
+ VERSION = '0.36.0'
3
3
  end
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_development_dependency 'rollbar'
33
33
  spec.add_development_dependency 'rspec', '~> 3.10.0 '
34
34
  spec.add_development_dependency 'rspec_junit_formatter' # for CircleCI.
35
- spec.add_development_dependency 'rubocop', '~> 1.18.0'
35
+ spec.add_development_dependency 'rubocop', '~> 1.19.0'
36
36
  spec.add_development_dependency 'rubocop-rspec'
37
37
  spec.add_development_dependency 'sinatra'
38
38
  spec.add_development_dependency 'webrick'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppeteer-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.35.1
4
+ version: 0.36.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-07-23 00:00:00.000000000 Z
11
+ date: 2021-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -170,14 +170,14 @@ dependencies:
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: 1.18.0
173
+ version: 1.19.0
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: 1.18.0
180
+ version: 1.19.0
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: rubocop-rspec
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -300,8 +300,11 @@ files:
300
300
  - lib/puppeteer/launcher/launch_options.rb
301
301
  - lib/puppeteer/lifecycle_watcher.rb
302
302
  - lib/puppeteer/mouse.rb
303
+ - lib/puppeteer/network_condition.rb
304
+ - lib/puppeteer/network_conditions.rb
303
305
  - lib/puppeteer/network_manager.rb
304
306
  - lib/puppeteer/page.rb
307
+ - lib/puppeteer/page/metrics.rb
305
308
  - lib/puppeteer/page/pdf_options.rb
306
309
  - lib/puppeteer/page/screenshot_options.rb
307
310
  - lib/puppeteer/page/screenshot_task_queue.rb