puppeteer-ruby 0.35.1 → 0.36.0

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