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 +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
|