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 +4 -4
- data/CHANGELOG.md +10 -1
- data/docs/api_coverage.md +23 -18
- data/lib/puppeteer.rb +1 -1
- data/lib/puppeteer/concurrent_ruby_utils.rb +2 -2
- data/lib/puppeteer/define_async_method.rb +1 -1
- data/lib/puppeteer/devices.rb +2 -0
- data/lib/puppeteer/element_handle.rb +41 -0
- data/lib/puppeteer/mouse.rb +54 -1
- data/lib/puppeteer/network_condition.rb +12 -0
- data/lib/puppeteer/network_conditions.rb +24 -0
- data/lib/puppeteer/network_manager.rb +47 -11
- data/lib/puppeteer/page.rb +132 -88
- data/lib/puppeteer/page/metrics.rb +49 -0
- data/lib/puppeteer/puppeteer.rb +5 -0
- data/lib/puppeteer/version.rb +1 -1
- data/puppeteer-ruby.gemspec +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d4af926ca9ad39046dcc4ce9507622bf72c3263b747516ee1ba642c02e8e38d
|
4
|
+
data.tar.gz: 3eff89952883a47270dee6a9ee7281609dbbe7bac197eada9e6b6dcabbaa1f78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
3
|
-
- puppeteer-ruby version: 0.
|
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
|
-
*
|
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
|
-
*
|
84
|
+
* emulateCPUThrottling => `#emulate_cpu_throttling`
|
85
85
|
* emulateIdleState => `#emulate_idle_state`
|
86
86
|
* emulateMediaFeatures => `#emulate_media_features`
|
87
87
|
* emulateMediaType => `#emulate_media_type`
|
88
|
-
*
|
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
|
-
*
|
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
|
-
*
|
102
|
+
* isDragInterceptionEnabled => `#drag_interception_enabled?`
|
103
103
|
* isJavaScriptEnabled => `#javascript_enabled?`
|
104
104
|
* keyboard
|
105
105
|
* mainFrame => `#main_frame`
|
106
|
-
*
|
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
|
-
*
|
170
|
-
*
|
171
|
-
*
|
172
|
-
*
|
173
|
-
*
|
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
|
-
*
|
276
|
-
*
|
277
|
-
*
|
278
|
-
*
|
279
|
-
*
|
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.
|
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.
|
10
|
+
async_block_call = Concurrent::Promises.delay do
|
11
11
|
block.call
|
12
12
|
rescue => err
|
13
13
|
Logger.new($stderr).warn(err)
|
data/lib/puppeteer/devices.rb
CHANGED
@@ -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) }
|
data/lib/puppeteer/mouse.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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]
|
data/lib/puppeteer/page.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
445
|
-
|
446
|
-
|
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
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
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
|
-
|
538
|
-
|
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
|
-
|
551
|
-
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
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
|
-
|
635
|
-
|
636
|
-
|
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
|
-
|
764
|
-
|
765
|
-
|
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
|
data/lib/puppeteer/puppeteer.rb
CHANGED
@@ -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]
|
data/lib/puppeteer/version.rb
CHANGED
data/puppeteer-ruby.gemspec
CHANGED
@@ -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.
|
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.
|
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-
|
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.
|
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.
|
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
|