puppeteer-ruby 0.42.0 → 0.43.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 +5 -1
- data/docs/api_coverage.md +2 -2
- data/lib/puppeteer/browser.rb +109 -75
- data/lib/puppeteer/browser_connector.rb +67 -0
- data/lib/puppeteer/chrome_target_manager.rb +256 -0
- data/lib/puppeteer/connection.rb +16 -0
- data/lib/puppeteer/events.rb +9 -0
- data/lib/puppeteer/firefox_target_manager.rb +158 -0
- data/lib/puppeteer/frame_manager.rb +69 -38
- data/lib/puppeteer/launcher/chrome.rb +3 -56
- data/lib/puppeteer/launcher/firefox.rb +1 -55
- data/lib/puppeteer/lifecycle_watcher.rb +2 -1
- data/lib/puppeteer/page.rb +38 -29
- data/lib/puppeteer/puppeteer.rb +1 -1
- data/lib/puppeteer/target.rb +14 -1
- data/lib/puppeteer/version.rb +1 -1
- data/lib/puppeteer.rb +3 -0
- 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: da17d477ad97a3197fa0eacf237e784204439efdca104e2a304d5cfb58c449d3
|
4
|
+
data.tar.gz: f56c151a8d7ebd8ac0ff71feafe5a90dcdd14ed997aaa9ea8e198ddd345b93dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3db33eea6388dd6c743395c9e3cd583d95a3f2147627ae371390e40bf6269f32d689687e8adf0fdaef8b99134b6bad2ee55061ca66c037407ec681ee62b61188
|
7
|
+
data.tar.gz: 2b169890fd8c5dde6850fff2439e5d43c03fa1b65e906ce443389c0599cf31d0e88af1ca0203c12cff90b2789c14af7a992d1c6b95caf7e78b9dadbf4d4a0f09
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.
|
1
|
+
### main [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.43.0...main)]
|
2
2
|
|
3
3
|
- xxx
|
4
4
|
|
5
|
+
### 0.43.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.42.0...0.43.0)]
|
6
|
+
|
7
|
+
- Port Puppeteer v16.0 features. Increasing stability.
|
8
|
+
|
5
9
|
### 0.42.0 [[diff](https://github.com/YusukeIwaki/puppeteer-ruby/compare/0.41.0...0.42.0)]
|
6
10
|
|
7
11
|
- Port Puppeteer v15.3-v15.5 features, including `Frame#page`
|
data/docs/api_coverage.md
CHANGED
data/lib/puppeteer/browser.rb
CHANGED
@@ -7,13 +7,15 @@ class Puppeteer::Browser
|
|
7
7
|
include Puppeteer::IfPresent
|
8
8
|
using Puppeteer::DefineAsyncMethod
|
9
9
|
|
10
|
+
# @param product [String|nil] 'chrome' or 'firefox'
|
10
11
|
# @param {!Puppeteer.Connection} connection
|
11
12
|
# @param {!Array<string>} contextIds
|
12
13
|
# @param {boolean} ignoreHTTPSErrors
|
13
14
|
# @param {?Puppeteer.Viewport} defaultViewport
|
14
15
|
# @param process [Puppeteer::BrowserRunner::BrowserProcess|NilClass]
|
15
16
|
# @param {function()=} closeCallback
|
16
|
-
def self.create(
|
17
|
+
def self.create(product:,
|
18
|
+
connection:,
|
17
19
|
context_ids:,
|
18
20
|
ignore_https_errors:,
|
19
21
|
default_viewport:,
|
@@ -22,6 +24,7 @@ class Puppeteer::Browser
|
|
22
24
|
target_filter_callback:,
|
23
25
|
is_page_target_callback:)
|
24
26
|
browser = Puppeteer::Browser.new(
|
27
|
+
product: product,
|
25
28
|
connection: connection,
|
26
29
|
context_ids: context_ids,
|
27
30
|
ignore_https_errors: ignore_https_errors,
|
@@ -31,17 +34,19 @@ class Puppeteer::Browser
|
|
31
34
|
target_filter_callback: target_filter_callback,
|
32
35
|
is_page_target_callback: is_page_target_callback,
|
33
36
|
)
|
34
|
-
|
37
|
+
browser.send(:attach)
|
35
38
|
browser
|
36
39
|
end
|
37
40
|
|
41
|
+
# @param product [String|nil] 'chrome' or 'firefox'
|
38
42
|
# @param {!Puppeteer.Connection} connection
|
39
43
|
# @param {!Array<string>} contextIds
|
40
44
|
# @param {boolean} ignoreHTTPSErrors
|
41
45
|
# @param {?Puppeteer.Viewport} defaultViewport
|
42
46
|
# @param {?Puppeteer.ChildProcess} process
|
43
47
|
# @param {(function():Promise)=} closeCallback
|
44
|
-
def initialize(
|
48
|
+
def initialize(product:,
|
49
|
+
connection:,
|
45
50
|
context_ids:,
|
46
51
|
ignore_https_errors:,
|
47
52
|
default_viewport:,
|
@@ -49,6 +54,7 @@ class Puppeteer::Browser
|
|
49
54
|
close_callback:,
|
50
55
|
target_filter_callback:,
|
51
56
|
is_page_target_callback:)
|
57
|
+
@product = product || 'chrome'
|
52
58
|
@ignore_https_errors = ignore_https_errors
|
53
59
|
@default_viewport = default_viewport
|
54
60
|
@process = process
|
@@ -56,20 +62,26 @@ class Puppeteer::Browser
|
|
56
62
|
@close_callback = close_callback
|
57
63
|
@target_filter_callback = target_filter_callback || method(:default_target_filter_callback)
|
58
64
|
@is_page_target_callback = is_page_target_callback || method(:default_is_page_target_callback)
|
59
|
-
|
60
65
|
@default_context = Puppeteer::BrowserContext.new(@connection, self, nil)
|
61
66
|
@contexts = {}
|
67
|
+
|
62
68
|
context_ids.each do |context_id|
|
63
69
|
@contexts[context_id] = Puppeteer::BrowserContext.new(@connection, self, context_id)
|
64
70
|
end
|
65
|
-
|
66
|
-
@
|
67
|
-
|
68
|
-
|
71
|
+
|
72
|
+
if @product == 'firefox'
|
73
|
+
@target_manager = Puppeteer::FirefoxTargetManager.new(
|
74
|
+
connection: connection,
|
75
|
+
target_factory: method(:create_target),
|
76
|
+
target_filter_callback: @target_filter_callback,
|
77
|
+
)
|
78
|
+
else
|
79
|
+
@target_manager = Puppeteer::ChromeTargetManager.new(
|
80
|
+
connection: connection,
|
81
|
+
target_factory: method(:create_target),
|
82
|
+
target_filter_callback: @target_filter_callback,
|
83
|
+
)
|
69
84
|
end
|
70
|
-
@connection.on_event('Target.targetCreated', &method(:handle_target_created))
|
71
|
-
@connection.on_event('Target.targetDestroyed', &method(:handle_target_destroyed))
|
72
|
-
@connection.on_event('Target.targetInfoChanged', &method(:handle_target_info_changed))
|
73
85
|
end
|
74
86
|
|
75
87
|
private def default_target_filter_callback(target_info)
|
@@ -100,11 +112,45 @@ class Puppeteer::Browser
|
|
100
112
|
super(event_name.to_s, &block)
|
101
113
|
end
|
102
114
|
|
115
|
+
private def attach
|
116
|
+
@connection_event_listeners ||= []
|
117
|
+
@connection_event_listeners << @connection.add_event_listener(ConnectionEmittedEvents::Disconnected) do
|
118
|
+
emit_event(BrowserEmittedEvents::Disconnected)
|
119
|
+
end
|
120
|
+
@target_manager_event_listeners ||= []
|
121
|
+
@target_manager.add_event_listener(
|
122
|
+
TargetManagerEmittedEvents::TargetAvailable,
|
123
|
+
&method(:handle_attached_to_target)
|
124
|
+
)
|
125
|
+
@target_manager.add_event_listener(
|
126
|
+
TargetManagerEmittedEvents::TargetGone,
|
127
|
+
&method(:handle_detached_from_target)
|
128
|
+
)
|
129
|
+
@target_manager.add_event_listener(
|
130
|
+
TargetManagerEmittedEvents::TargetChanged,
|
131
|
+
&method(:handle_target_changed)
|
132
|
+
)
|
133
|
+
@target_manager.add_event_listener(
|
134
|
+
TargetManagerEmittedEvents::TargetDiscovered,
|
135
|
+
&method(:handle_target_discovered)
|
136
|
+
)
|
137
|
+
@target_manager.init
|
138
|
+
end
|
139
|
+
|
140
|
+
private def detach
|
141
|
+
@connection.remove_event_listener(*@connection_event_listeners)
|
142
|
+
@target_manager.remove_event_listener(*@target_manager_event_listeners)
|
143
|
+
end
|
144
|
+
|
103
145
|
# @return [Puppeteer::BrowserRunner::BrowserProcess]
|
104
146
|
def process
|
105
147
|
@process
|
106
148
|
end
|
107
149
|
|
150
|
+
private def target_manager
|
151
|
+
@target_manager
|
152
|
+
end
|
153
|
+
|
108
154
|
# @return [Puppeteer::BrowserContext]
|
109
155
|
def create_incognito_browser_context
|
110
156
|
result = @connection.send_message('Target.createBrowserContext')
|
@@ -123,19 +169,16 @@ class Puppeteer::Browser
|
|
123
169
|
|
124
170
|
# @param context_id [String]
|
125
171
|
def dispose_context(context_id)
|
172
|
+
return unless context_id
|
126
173
|
@connection.send_message('Target.disposeBrowserContext', browserContextId: context_id)
|
127
174
|
@contexts.delete(context_id)
|
128
175
|
end
|
129
176
|
|
130
|
-
class
|
131
|
-
def initialize
|
132
|
-
super('Target should not exist before targetCreated')
|
133
|
-
end
|
134
|
-
end
|
177
|
+
class MissingBrowserContextError < StandardError ; end
|
135
178
|
|
136
|
-
# @param
|
137
|
-
|
138
|
-
|
179
|
+
# @param target_info [Puppeteer::Target::TargetInfo]
|
180
|
+
# @param session [CDPSession|nil]
|
181
|
+
def create_target(target_info, session)
|
139
182
|
browser_context_id = target_info.browser_context_id
|
140
183
|
context =
|
141
184
|
if browser_context_id && @contexts.has_key?(browser_context_id)
|
@@ -144,56 +187,39 @@ class Puppeteer::Browser
|
|
144
187
|
@default_context
|
145
188
|
end
|
146
189
|
|
147
|
-
|
148
|
-
raise
|
190
|
+
unless context
|
191
|
+
raise MissingBrowserContextError.new('Missing browser context')
|
149
192
|
end
|
150
193
|
|
151
|
-
|
152
|
-
|
153
|
-
target = Puppeteer::Target.new(
|
194
|
+
Puppeteer::Target.new(
|
154
195
|
target_info: target_info,
|
196
|
+
session: session,
|
155
197
|
browser_context: context,
|
198
|
+
target_manager: @target_manager,
|
156
199
|
session_factory: -> { @connection.create_session(target_info) },
|
157
200
|
ignore_https_errors: @ignore_https_errors,
|
158
201
|
default_viewport: @default_viewport,
|
159
202
|
is_page_target_callback: @is_page_target_callback,
|
160
203
|
)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
if await target.initialized_promise
|
204
|
+
end
|
205
|
+
|
206
|
+
private def handle_attached_to_target(target)
|
207
|
+
if target.initialized_promise.value!
|
166
208
|
emit_event(BrowserEmittedEvents::TargetCreated, target)
|
167
|
-
|
209
|
+
target.browser_context.emit_event(BrowserContextEmittedEvents::TargetCreated, target)
|
168
210
|
end
|
169
211
|
end
|
170
212
|
|
171
|
-
|
172
|
-
def handle_target_destroyed(event)
|
173
|
-
target_id = event['targetId']
|
174
|
-
target = @targets[target_id]
|
213
|
+
private def handle_detached_from_target(target)
|
175
214
|
target.ignore_initialize_callback_promise
|
176
|
-
@targets.delete(target_id)
|
177
|
-
if_present(@wait_for_creating_targets.delete(target_id)) do |promise|
|
178
|
-
promise.reject('target destroyed')
|
179
|
-
end
|
180
215
|
target.closed_callback
|
181
|
-
if
|
216
|
+
if target.initialized_promise.value!
|
182
217
|
emit_event(BrowserEmittedEvents::TargetDestroyed, target)
|
183
218
|
target.browser_context.emit_event(BrowserContextEmittedEvents::TargetDestroyed, target)
|
184
219
|
end
|
185
220
|
end
|
186
221
|
|
187
|
-
|
188
|
-
def initialize
|
189
|
-
super('target should exist before targetInfoChanged')
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# @param {!Protocol.Target.targetInfoChangedPayload} event
|
194
|
-
def handle_target_info_changed(event)
|
195
|
-
target_info = Puppeteer::Target::TargetInfo.new(event['targetInfo'])
|
196
|
-
target = @targets[target_info.target_id] or raise TargetNotExistError.new
|
222
|
+
private def handle_target_changed(target, target_info)
|
197
223
|
previous_url = target.url
|
198
224
|
was_initialized = target.initialized?
|
199
225
|
target.handle_target_info_changed(target_info)
|
@@ -203,6 +229,10 @@ class Puppeteer::Browser
|
|
203
229
|
end
|
204
230
|
end
|
205
231
|
|
232
|
+
private def handle_target_discovered(target_info)
|
233
|
+
emit_event('targetdiscovered', target_info)
|
234
|
+
end
|
235
|
+
|
206
236
|
# @return [String]
|
207
237
|
def ws_endpoint
|
208
238
|
@connection.url
|
@@ -212,44 +242,47 @@ class Puppeteer::Browser
|
|
212
242
|
@default_context.new_page
|
213
243
|
end
|
214
244
|
|
245
|
+
class MissingTargetError < StandardError ; end
|
246
|
+
class CreatePageError < StandardError ; end
|
247
|
+
|
215
248
|
# @param {?string} contextId
|
216
249
|
# @return {!Promise<!Puppeteer.Page>}
|
217
250
|
def create_page_in_context(context_id)
|
218
|
-
create_target_params = {
|
219
|
-
|
220
|
-
|
221
|
-
|
251
|
+
create_target_params = {
|
252
|
+
url: 'about:blank',
|
253
|
+
browserContextId: context_id,
|
254
|
+
}.compact
|
222
255
|
result = @connection.send_message('Target.createTarget', **create_target_params)
|
223
256
|
target_id = result['targetId']
|
224
|
-
target = @
|
257
|
+
target = @target_manager.available_targets[target_id]
|
225
258
|
unless target
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
259
|
+
raise MissingTargetError.new("Missing target for page (id = #{target_id})")
|
260
|
+
end
|
261
|
+
unless target.initialized_promise.value!
|
262
|
+
raise CreatePageError.new("Failed to create target for page (id = #{target_id})")
|
263
|
+
end
|
264
|
+
page = target.page
|
265
|
+
unless page
|
266
|
+
raise CreatePageError.new("Failed to create a page for context (id = #{context_id})")
|
234
267
|
end
|
235
|
-
|
236
|
-
await target.page
|
268
|
+
page
|
237
269
|
end
|
238
270
|
|
239
|
-
#
|
271
|
+
# All active targets inside the Browser. In case of multiple browser contexts, returns
|
272
|
+
# an array with all the targets in all browser contexts.
|
240
273
|
def targets
|
241
|
-
@
|
274
|
+
@target_manager.available_targets.values.select { |target| target.initialized? }
|
242
275
|
end
|
243
276
|
|
244
277
|
|
245
|
-
#
|
278
|
+
# The target associated with the browser.
|
246
279
|
def target
|
247
|
-
targets.find { |target| target.type == 'browser' }
|
280
|
+
targets.find { |target| target.type == 'browser' } or raise 'Browser target is not found'
|
248
281
|
end
|
249
282
|
|
250
283
|
# used only in Target#opener
|
251
284
|
private def find_target_by_id(target_id)
|
252
|
-
@
|
285
|
+
@target_manager.available_targets[target_id]
|
253
286
|
end
|
254
287
|
|
255
288
|
# @param predicate [Proc(Puppeteer::Target -> Boolean)]
|
@@ -293,12 +326,12 @@ class Puppeteer::Browser
|
|
293
326
|
|
294
327
|
# @return [String]
|
295
328
|
def version
|
296
|
-
|
329
|
+
Version.fetch(@connection).product
|
297
330
|
end
|
298
331
|
|
299
332
|
# @return [String]
|
300
333
|
def user_agent
|
301
|
-
|
334
|
+
Version.fetch(@connection).user_agent
|
302
335
|
end
|
303
336
|
|
304
337
|
def close
|
@@ -307,6 +340,7 @@ class Puppeteer::Browser
|
|
307
340
|
end
|
308
341
|
|
309
342
|
def disconnect
|
343
|
+
@target_manager.dispose
|
310
344
|
@connection.dispose
|
311
345
|
end
|
312
346
|
|
@@ -315,6 +349,10 @@ class Puppeteer::Browser
|
|
315
349
|
end
|
316
350
|
|
317
351
|
class Version
|
352
|
+
def self.fetch(connection)
|
353
|
+
new(connection.send_message('Browser.getVersion'))
|
354
|
+
end
|
355
|
+
|
318
356
|
def initialize(hash)
|
319
357
|
@protocol_version = hash['protocolVersion']
|
320
358
|
@product = hash['product']
|
@@ -325,8 +363,4 @@ class Puppeteer::Browser
|
|
325
363
|
|
326
364
|
attr_reader :protocol_version, :product, :revision, :user_agent, :js_version
|
327
365
|
end
|
328
|
-
|
329
|
-
private def get_version
|
330
|
-
Version.new(@connection.send_message('Browser.getVersion'))
|
331
|
-
end
|
332
366
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative './browser'
|
2
|
+
require_relative './launcher/browser_options'
|
3
|
+
|
4
|
+
class Puppeteer::BrowserConnector
|
5
|
+
def initialize(options)
|
6
|
+
@browser_options = Puppeteer::Launcher::BrowserOptions.new(options)
|
7
|
+
@browser_ws_endpoint = options[:browser_ws_endpoint]
|
8
|
+
@browser_url = options[:browser_url]
|
9
|
+
@transport = options[:transport]
|
10
|
+
end
|
11
|
+
|
12
|
+
# @return [Puppeteer::Browser]
|
13
|
+
def connect_to_browser
|
14
|
+
version = Puppeteer::Browser::Version.fetch(connection)
|
15
|
+
product = version.product.downcase.include?('firefox') ? 'firefox' : 'chrome'
|
16
|
+
|
17
|
+
result = connection.send_message('Target.getBrowserContexts')
|
18
|
+
browser_context_ids = result['browserContextIds']
|
19
|
+
|
20
|
+
Puppeteer::Browser.create(
|
21
|
+
product: product,
|
22
|
+
connection: connection,
|
23
|
+
context_ids: browser_context_ids,
|
24
|
+
ignore_https_errors: @browser_options.ignore_https_errors?,
|
25
|
+
default_viewport: @browser_options.default_viewport,
|
26
|
+
process: nil,
|
27
|
+
close_callback: -> { connection.send_message('Browser.close') },
|
28
|
+
target_filter_callback: @browser_options.target_filter,
|
29
|
+
is_page_target_callback: @browser_options.is_page_target,
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
private def connection
|
34
|
+
@connection ||=
|
35
|
+
if @browser_ws_endpoint && @browser_url.nil? && @transport.nil?
|
36
|
+
connect_with_browser_ws_endpoint(@browser_ws_endpoint)
|
37
|
+
elsif @browser_ws_endpoint.nil? && @browser_url && @transport.nil?
|
38
|
+
connect_with_browser_url(@browser_url)
|
39
|
+
elsif @browser_ws_endpoint.nil? && @browser_url.nil? && @transport
|
40
|
+
connect_with_transport(@transport)
|
41
|
+
else
|
42
|
+
raise ArgumentError.new("Exactly one of browserWSEndpoint, browserURL or transport must be passed to puppeteer.connect")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Puppeteer::Connection]
|
47
|
+
private def connect_with_browser_ws_endpoint(browser_ws_endpoint)
|
48
|
+
transport = Puppeteer::WebSocketTransport.create(browser_ws_endpoint)
|
49
|
+
Puppeteer::Connection.new(browser_ws_endpoint, transport, @browser_options.slow_mo)
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Puppeteer::Connection]
|
53
|
+
private def connect_with_browser_url(browser_url)
|
54
|
+
require 'net/http'
|
55
|
+
uri = URI(browser_url)
|
56
|
+
uri.path = '/json/version'
|
57
|
+
response_body = Net::HTTP.get(uri)
|
58
|
+
json = JSON.parse(response_body)
|
59
|
+
connection_url = json['webSocketDebuggerUrl']
|
60
|
+
connect_with_browser_ws_endpoint(connection_url)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @return [Puppeteer::Connection]
|
64
|
+
private def connect_with_transport(transport)
|
65
|
+
Puppeteer::Connection.new('', transport, @browser_options.slow_mo)
|
66
|
+
end
|
67
|
+
end
|