puppeteer-ruby 0.0.22 → 0.0.23
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/.rubocop.yml +37 -0
- data/README.md +15 -0
- data/lib/puppeteer.rb +3 -0
- data/lib/puppeteer/browser.rb +19 -26
- data/lib/puppeteer/browser_context.rb +48 -49
- data/lib/puppeteer/browser_runner.rb +0 -1
- data/lib/puppeteer/cdp_session.rb +11 -7
- data/lib/puppeteer/connection.rb +30 -11
- data/lib/puppeteer/dom_world.rb +2 -2
- data/lib/puppeteer/event_callbackable.rb +4 -0
- data/lib/puppeteer/events.rb +184 -0
- data/lib/puppeteer/exception_details.rb +38 -0
- data/lib/puppeteer/frame_manager.rb +20 -16
- data/lib/puppeteer/geolocation.rb +24 -0
- data/lib/puppeteer/lifecycle_watcher.rb +6 -6
- data/lib/puppeteer/network_manager.rb +6 -6
- data/lib/puppeteer/page.rb +65 -86
- data/lib/puppeteer/target.rb +2 -2
- data/lib/puppeteer/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 133c14dccb0da46cc01bff747bb2e36c1de1f7619ee4a8a898e798b3eb3947af
|
4
|
+
data.tar.gz: a7cc95948ddef2b45cc3b4a8faa82497334daeb1e47d92c97a7cbcbf9dccd887
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de40a672ecbf7089e43463497bd877ed671e366ae022d6224bef0068c9ecbc5a949d4649454348af8d9823abf0e0effe84255ce3e73f8bd6f13c2d4ce2dd514e
|
7
|
+
data.tar.gz: 96821b3ec521338f6c2a08c31a65113acb1aa808e1f50b9fece78bcdcc19cb6f4072b2dd6d778a5a413f47733b1efa7d3f9ded85fdd0e0dce6c1729f53be47ab
|
data/.rubocop.yml
CHANGED
@@ -116,6 +116,43 @@ Style/DefWithParentheses:
|
|
116
116
|
Style/MethodDefParentheses:
|
117
117
|
Enabled: true
|
118
118
|
|
119
|
+
Style/MethodCallWithArgsParentheses:
|
120
|
+
Enabled: true
|
121
|
+
IgnoredMethods:
|
122
|
+
# Gemfile, gemspec
|
123
|
+
- source
|
124
|
+
- add_dependency
|
125
|
+
- add_development_dependency
|
126
|
+
|
127
|
+
# include/require
|
128
|
+
- require
|
129
|
+
- require_relative
|
130
|
+
- include
|
131
|
+
|
132
|
+
# fundamental methods
|
133
|
+
- raise
|
134
|
+
- sleep
|
135
|
+
|
136
|
+
# RSpec
|
137
|
+
- describe
|
138
|
+
- it
|
139
|
+
- to
|
140
|
+
- not_to
|
141
|
+
- be
|
142
|
+
- eq
|
143
|
+
|
144
|
+
# async/await
|
145
|
+
- define_async_method
|
146
|
+
- await
|
147
|
+
- future
|
148
|
+
|
149
|
+
# utils
|
150
|
+
- debug_print
|
151
|
+
- debug_puts
|
152
|
+
|
153
|
+
Style/MethodCallWithoutArgsParentheses:
|
154
|
+
Enabled: true
|
155
|
+
|
119
156
|
Style/RedundantFreeze:
|
120
157
|
Enabled: true
|
121
158
|
|
data/README.md
CHANGED
@@ -72,6 +72,21 @@ end
|
|
72
72
|
|
73
73
|
More usage examples can be found [here](https://github.com/YusukeIwaki/puppeteer-ruby-example)
|
74
74
|
|
75
|
+
## :whale: Running in Docker
|
76
|
+
|
77
|
+
Following packages are required.
|
78
|
+
|
79
|
+
* Google Chrome or Chromium
|
80
|
+
* In Debian-based images, `google-chrome-stable`
|
81
|
+
* In Alpine-based images, `chromium`
|
82
|
+
|
83
|
+
Also, CJK font will be required for Chinese, Japanese, Korean sites.
|
84
|
+
|
85
|
+
### References
|
86
|
+
|
87
|
+
* Puppeteer official README: https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#running-puppeteer-in-docker
|
88
|
+
* puppeteer-ruby example: https://github.com/YusukeIwaki/puppeteer-ruby-example/tree/master/docker_chromium
|
89
|
+
|
75
90
|
## :bulb: Collaboration with Selenium or Capybara
|
76
91
|
|
77
92
|
It is really remarkable that we can use puppeteer functions in existing Selenium or Capybara codes, with a few configuration in advance.
|
data/lib/puppeteer.rb
CHANGED
@@ -6,7 +6,9 @@ require 'puppeteer/env'
|
|
6
6
|
|
7
7
|
# Custom data types.
|
8
8
|
require 'puppeteer/device'
|
9
|
+
require 'puppeteer/events'
|
9
10
|
require 'puppeteer/errors'
|
11
|
+
require 'puppeteer/geolocation'
|
10
12
|
require 'puppeteer/viewport'
|
11
13
|
|
12
14
|
# Modules
|
@@ -27,6 +29,7 @@ require 'puppeteer/devices'
|
|
27
29
|
require 'puppeteer/dialog'
|
28
30
|
require 'puppeteer/dom_world'
|
29
31
|
require 'puppeteer/emulation_manager'
|
32
|
+
require 'puppeteer/exception_details'
|
30
33
|
require 'puppeteer/execution_context'
|
31
34
|
require 'puppeteer/file_chooser'
|
32
35
|
require 'puppeteer/frame'
|
data/lib/puppeteer/browser.rb
CHANGED
@@ -46,37 +46,30 @@ class Puppeteer::Browser
|
|
46
46
|
@contexts[context_id] = Puppeteer::BrowserContext.new(@connection, self, context_id)
|
47
47
|
end
|
48
48
|
@targets = {}
|
49
|
-
@connection.on_event
|
50
|
-
emit_event
|
49
|
+
@connection.on_event(ConnectionEmittedEvents::Disconnected) do
|
50
|
+
emit_event(BrowserEmittedEvents::Disconnected)
|
51
51
|
end
|
52
|
-
@connection.on_event
|
53
|
-
@connection.on_event
|
54
|
-
@connection.on_event
|
52
|
+
@connection.on_event('Target.targetCreated', &method(:handle_target_created))
|
53
|
+
@connection.on_event('Target.targetDestroyed', &method(:handle_target_destroyed))
|
54
|
+
@connection.on_event('Target.targetInfoChanged', &method(:handle_target_info_changed))
|
55
55
|
end
|
56
56
|
|
57
|
-
EVENT_MAPPINGS = {
|
58
|
-
disconnected: 'Events.Browser.Disconnected',
|
59
|
-
targetcreated: 'Events.Browser.TargetCreated',
|
60
|
-
targetchanged: 'Events.Browser.TargetChanged',
|
61
|
-
targetdestroyed: 'Events.Browser.TargetDestroyed',
|
62
|
-
}
|
63
|
-
|
64
57
|
# @param event_name [Symbol] either of :disconnected, :targetcreated, :targetchanged, :targetdestroyed
|
65
58
|
def on(event_name, &block)
|
66
|
-
unless
|
67
|
-
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{
|
59
|
+
unless BrowserEmittedEvents.values.include?(event_name.to_s)
|
60
|
+
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{BrowserEmittedEvents.values.to_a.join(", ")}")
|
68
61
|
end
|
69
62
|
|
70
|
-
|
63
|
+
super(event_name.to_s, &block)
|
71
64
|
end
|
72
65
|
|
73
66
|
# @param event_name [Symbol]
|
74
67
|
def once(event_name, &block)
|
75
|
-
unless
|
76
|
-
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{
|
68
|
+
unless BrowserEmittedEvents.values.include?(event_name.to_s)
|
69
|
+
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{BrowserEmittedEvents.values.to_a.join(", ")}")
|
77
70
|
end
|
78
71
|
|
79
|
-
|
72
|
+
super(event_name.to_s, &block)
|
80
73
|
end
|
81
74
|
|
82
75
|
# @return [Puppeteer::BrowserRunner::BrowserProcess]
|
@@ -137,8 +130,8 @@ class Puppeteer::Browser
|
|
137
130
|
# assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
138
131
|
@targets[target_info.target_id] = target
|
139
132
|
if await target.initialized_promise
|
140
|
-
emit_event
|
141
|
-
context.emit_event
|
133
|
+
emit_event(BrowserEmittedEvents::TargetCreated, target)
|
134
|
+
context.emit_event(BrowserContextEmittedEvents::TargetCreated, target)
|
142
135
|
end
|
143
136
|
end
|
144
137
|
|
@@ -150,8 +143,8 @@ class Puppeteer::Browser
|
|
150
143
|
@targets.delete(target_id)
|
151
144
|
target.closed_callback
|
152
145
|
if await target.initialized_promise
|
153
|
-
emit_event
|
154
|
-
target.browser_context.emit_event
|
146
|
+
emit_event(BrowserEmittedEvents::TargetDestroyed, target)
|
147
|
+
target.browser_context.emit_event(BrowserContextEmittedEvents::TargetDestroyed, target)
|
155
148
|
end
|
156
149
|
end
|
157
150
|
|
@@ -169,8 +162,8 @@ class Puppeteer::Browser
|
|
169
162
|
was_initialized = target.initialized?
|
170
163
|
target.handle_target_info_changed(target_info)
|
171
164
|
if was_initialized && previous_url != target.url
|
172
|
-
emit_event
|
173
|
-
target.browser_context.emit_event
|
165
|
+
emit_event(BrowserEmittedEvents::TargetChanged, target)
|
166
|
+
target.browser_context.emit_event(BrowserContextEmittedEvents::TargetChanged, target)
|
174
167
|
end
|
175
168
|
end
|
176
169
|
|
@@ -222,12 +215,12 @@ class Puppeteer::Browser
|
|
222
215
|
|
223
216
|
event_listening_ids = []
|
224
217
|
target_promise = resolvable_future
|
225
|
-
event_listening_ids << add_event_listener(
|
218
|
+
event_listening_ids << add_event_listener(BrowserEmittedEvents::TargetCreated) do |target|
|
226
219
|
if predicate.call(target)
|
227
220
|
target_promise.fulfill(target)
|
228
221
|
end
|
229
222
|
end
|
230
|
-
event_listening_ids << add_event_listener(
|
223
|
+
event_listening_ids << add_event_listener(BrowserEmittedEvents::TargetChanged) do |target|
|
231
224
|
if predicate.call(target)
|
232
225
|
target_promise.fulfill(target)
|
233
226
|
end
|
@@ -11,29 +11,22 @@ class Puppeteer::BrowserContext
|
|
11
11
|
@id = context_id
|
12
12
|
end
|
13
13
|
|
14
|
-
EVENT_MAPPINGS = {
|
15
|
-
disconnected: 'Events.BrowserContext.Disconnected',
|
16
|
-
targetcreated: 'Events.BrowserContext.TargetCreated',
|
17
|
-
targetchanged: 'Events.BrowserContext.TargetChanged',
|
18
|
-
targetdestroyed: 'Events.BrowserContext.TargetDestroyed',
|
19
|
-
}
|
20
|
-
|
21
14
|
# @param event_name [Symbol] either of :disconnected, :targetcreated, :targetchanged, :targetdestroyed
|
22
15
|
def on(event_name, &block)
|
23
|
-
unless
|
24
|
-
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{
|
16
|
+
unless BrowserContextEmittedEvents.values.include?(event_name.to_s)
|
17
|
+
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{BrowserContextEmittedEvents.values.to_a.join(", ")}")
|
25
18
|
end
|
26
19
|
|
27
|
-
|
20
|
+
super(event_name.to_s, &block)
|
28
21
|
end
|
29
22
|
|
30
23
|
# @param event_name [Symbol]
|
31
24
|
def once(event_name, &block)
|
32
|
-
unless
|
33
|
-
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{
|
25
|
+
unless BrowserContextEmittedEvents.values.include?(event_name.to_s)
|
26
|
+
raise ArgumentError.new("Unknown event name: #{event_name}. Known events are #{BrowserContextEmittedEvents.values.to_a.join(", ")}")
|
34
27
|
end
|
35
28
|
|
36
|
-
|
29
|
+
super(event_name.to_s, &block)
|
37
30
|
end
|
38
31
|
|
39
32
|
# @return {!Array<!Target>} target
|
@@ -64,42 +57,48 @@ class Puppeteer::BrowserContext
|
|
64
57
|
!!@id
|
65
58
|
end
|
66
59
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
#
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
60
|
+
WEB_PERMISSION_TO_PROTOCOL = {
|
61
|
+
'geolocation' => 'geolocation',
|
62
|
+
'midi' => 'midi',
|
63
|
+
'notifications' => 'notifications',
|
64
|
+
# TODO: push isn't a valid type?
|
65
|
+
# 'push' => 'push',
|
66
|
+
'camera' => 'videoCapture',
|
67
|
+
'microphone' => 'audioCapture',
|
68
|
+
'background-sync' => 'backgroundSync',
|
69
|
+
'ambient-light-sensor' => 'sensors',
|
70
|
+
'accelerometer' => 'sensors',
|
71
|
+
'gyroscope' => 'sensors',
|
72
|
+
'magnetometer' => 'sensors',
|
73
|
+
'accessibility-events' => 'accessibilityEvents',
|
74
|
+
'clipboard-read' => 'clipboardReadWrite',
|
75
|
+
'clipboard-write' => 'clipboardReadWrite',
|
76
|
+
'payment-handler' => 'paymentHandler',
|
77
|
+
'idle-detection' => 'idleDetection',
|
78
|
+
# chrome-specific permissions we have.
|
79
|
+
'midi-sysex' => 'midiSysex',
|
80
|
+
}.freeze
|
81
|
+
|
82
|
+
# @param origin [String]
|
83
|
+
# @param permissions [Array<String>]
|
84
|
+
def override_permissions(origin, permissions)
|
85
|
+
protocol_permissions = permissions.map do |permission|
|
86
|
+
WEB_PERMISSION_TO_PROTOCOL[permission] or raise ArgumentError.new("Unknown permission: #{permission}")
|
87
|
+
end
|
88
|
+
@connection.send_message('Browser.grantPermissions', {
|
89
|
+
origin: origin,
|
90
|
+
browserContextId: @id,
|
91
|
+
permissions: protocol_permissions,
|
92
|
+
}.compact)
|
93
|
+
end
|
94
|
+
|
95
|
+
def clear_permission_overrides
|
96
|
+
if @id
|
97
|
+
@connection.send_message('Browser.resetPermissions', browserContextId: @id)
|
98
|
+
else
|
99
|
+
@connection.send_message('Browser.resetPermissions')
|
100
|
+
end
|
101
|
+
end
|
103
102
|
|
104
103
|
# @return [Future<Puppeteer::Page>]
|
105
104
|
def new_page
|
@@ -9,7 +9,7 @@ class Puppeteer::CDPSession
|
|
9
9
|
# @param {string} targetType
|
10
10
|
# @param {string} sessionId
|
11
11
|
def initialize(connection, target_type, session_id)
|
12
|
-
@callbacks =
|
12
|
+
@callbacks = Concurrent::Hash.new
|
13
13
|
@connection = connection
|
14
14
|
@target_type = target_type
|
15
15
|
@session_id = session_id
|
@@ -31,10 +31,14 @@ class Puppeteer::CDPSession
|
|
31
31
|
if !@connection
|
32
32
|
raise Error.new("Protocol error (#{method}): Session closed. Most likely the #{@target_type} has been closed.")
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
promise = resolvable_future
|
36
|
-
|
37
|
-
@
|
36
|
+
|
37
|
+
@connection.generate_id do |id|
|
38
|
+
@callbacks[id] = Puppeteer::Connection::MessageCallback.new(method: method, promise: promise)
|
39
|
+
@connection.raw_send(id: id, message: { sessionId: @session_id, method: method, params: params })
|
40
|
+
end
|
41
|
+
|
38
42
|
promise
|
39
43
|
end
|
40
44
|
|
@@ -44,10 +48,10 @@ class Puppeteer::CDPSession
|
|
44
48
|
if callback = @callbacks.delete(message['id'])
|
45
49
|
callback_with_message(callback, message)
|
46
50
|
else
|
47
|
-
raise Error.new("unknown id: #{id}")
|
51
|
+
raise Error.new("unknown id: #{message['id']}")
|
48
52
|
end
|
49
53
|
else
|
50
|
-
emit_event
|
54
|
+
emit_event(message['method'], message['params'])
|
51
55
|
end
|
52
56
|
end
|
53
57
|
|
@@ -79,7 +83,7 @@ class Puppeteer::CDPSession
|
|
79
83
|
end
|
80
84
|
@callbacks.clear
|
81
85
|
@connection = nil
|
82
|
-
emit_event
|
86
|
+
emit_event(CDPSessionEmittedEvents::Disconnected)
|
83
87
|
end
|
84
88
|
|
85
89
|
# @param event_name [String]
|
data/lib/puppeteer/connection.rb
CHANGED
@@ -39,7 +39,7 @@ class Puppeteer::Connection
|
|
39
39
|
def initialize(url, transport, delay = 0)
|
40
40
|
@url = url
|
41
41
|
@last_id = 0
|
42
|
-
@callbacks =
|
42
|
+
@callbacks = Concurrent::Hash.new
|
43
43
|
@delay = delay
|
44
44
|
|
45
45
|
@transport = transport
|
@@ -52,7 +52,7 @@ class Puppeteer::Connection
|
|
52
52
|
handle_close
|
53
53
|
end
|
54
54
|
|
55
|
-
@sessions =
|
55
|
+
@sessions = Concurrent::Hash.new
|
56
56
|
@closed = false
|
57
57
|
end
|
58
58
|
|
@@ -92,22 +92,41 @@ class Puppeteer::Connection
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def async_send_message(method, params = {})
|
95
|
-
id = raw_send(message: { method: method, params: params })
|
96
95
|
promise = resolvable_future
|
97
|
-
|
96
|
+
|
97
|
+
generate_id do |id|
|
98
|
+
@callbacks[id] = MessageCallback.new(method: method, promise: promise)
|
99
|
+
raw_send(id: id, message: { method: method, params: params })
|
100
|
+
end
|
101
|
+
|
98
102
|
promise
|
99
103
|
end
|
100
104
|
|
101
|
-
private
|
102
|
-
|
105
|
+
# package private. not intended to use externally.
|
106
|
+
#
|
107
|
+
# ```usage
|
108
|
+
# connection.generate_id do |generated_id|
|
109
|
+
# # play with generated_id
|
110
|
+
# end
|
111
|
+
# ````
|
112
|
+
#
|
113
|
+
def generate_id(&block)
|
114
|
+
block.call(@last_id += 1)
|
103
115
|
end
|
104
116
|
|
105
|
-
|
106
|
-
|
117
|
+
# package private. not intended to use externally.
|
118
|
+
def raw_send(id:, message:)
|
119
|
+
# In original puppeteer (JS) implementation,
|
120
|
+
# id is generated here using #generate_id and the id argument is not passed to #raw_send.
|
121
|
+
#
|
122
|
+
# However with concurrent-ruby, '#handle_message' is sometimes called
|
123
|
+
# just soon after @transport.send_text and **before returning the id.**
|
124
|
+
#
|
125
|
+
# So we have to know the message id in advance before send_text.
|
126
|
+
#
|
107
127
|
payload = JSON.fast_generate(message.compact.merge(id: id))
|
108
128
|
@transport.send_text(payload)
|
109
129
|
request_debug_printer.handle_payload(payload)
|
110
|
-
id
|
111
130
|
end
|
112
131
|
|
113
132
|
# Just for effective debugging :)
|
@@ -211,7 +230,7 @@ class Puppeteer::Connection
|
|
211
230
|
end
|
212
231
|
end
|
213
232
|
else
|
214
|
-
emit_event
|
233
|
+
emit_event(message['method'], message['params'])
|
215
234
|
end
|
216
235
|
end
|
217
236
|
|
@@ -233,7 +252,7 @@ class Puppeteer::Connection
|
|
233
252
|
session.handle_closed
|
234
253
|
end
|
235
254
|
@sessions.clear
|
236
|
-
emit_event
|
255
|
+
emit_event(ConnectionEmittedEvents::Disconnected)
|
237
256
|
end
|
238
257
|
|
239
258
|
def on_close(&block)
|