puppeteer-ruby 0.42.0 → 0.43.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 +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
|