ferrum 0.14 → 0.16
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/LICENSE +1 -1
- data/README.md +309 -158
- data/lib/ferrum/browser/command.rb +4 -0
- data/lib/ferrum/browser/options/chrome.rb +8 -3
- data/lib/ferrum/browser/options.rb +38 -25
- data/lib/ferrum/browser/process.rb +42 -17
- data/lib/ferrum/browser.rb +38 -50
- data/lib/ferrum/client/subscriber.rb +76 -0
- data/lib/ferrum/client/web_socket.rb +126 -0
- data/lib/ferrum/client.rb +171 -0
- data/lib/ferrum/context.rb +19 -15
- data/lib/ferrum/contexts.rb +46 -12
- data/lib/ferrum/cookies.rb +28 -1
- data/lib/ferrum/downloads.rb +60 -0
- data/lib/ferrum/errors.rb +10 -3
- data/lib/ferrum/headers.rb +1 -1
- data/lib/ferrum/network/exchange.rb +10 -1
- data/lib/ferrum/network/intercepted_request.rb +5 -5
- data/lib/ferrum/network/request.rb +9 -0
- data/lib/ferrum/network.rb +36 -24
- data/lib/ferrum/node.rb +11 -0
- data/lib/ferrum/page/frames.rb +7 -9
- data/lib/ferrum/page/screenshot.rb +54 -28
- data/lib/ferrum/page.rb +192 -118
- data/lib/ferrum/proxy.rb +1 -1
- data/lib/ferrum/target.rb +25 -5
- data/lib/ferrum/utils/elapsed_time.rb +0 -2
- data/lib/ferrum/utils/event.rb +19 -0
- data/lib/ferrum/utils/platform.rb +4 -0
- data/lib/ferrum/utils/thread.rb +18 -0
- data/lib/ferrum/version.rb +1 -1
- data/lib/ferrum.rb +3 -0
- metadata +28 -17
- data/lib/ferrum/browser/client.rb +0 -103
- data/lib/ferrum/browser/subscriber.rb +0 -36
- data/lib/ferrum/browser/web_socket.rb +0 -91
data/lib/ferrum/page.rb
CHANGED
@@ -1,50 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "forwardable"
|
4
|
+
require "pathname"
|
4
5
|
require "ferrum/mouse"
|
5
6
|
require "ferrum/keyboard"
|
6
7
|
require "ferrum/headers"
|
7
8
|
require "ferrum/cookies"
|
8
9
|
require "ferrum/dialog"
|
9
10
|
require "ferrum/network"
|
11
|
+
require "ferrum/downloads"
|
10
12
|
require "ferrum/page/frames"
|
11
13
|
require "ferrum/page/screenshot"
|
12
14
|
require "ferrum/page/animation"
|
13
15
|
require "ferrum/page/tracing"
|
14
16
|
require "ferrum/page/stream"
|
15
|
-
require "ferrum/browser/client"
|
16
17
|
|
17
18
|
module Ferrum
|
18
19
|
class Page
|
19
20
|
GOTO_WAIT = ENV.fetch("FERRUM_GOTO_WAIT", 0.1).to_f
|
20
21
|
|
21
|
-
class Event < Concurrent::Event
|
22
|
-
def iteration
|
23
|
-
synchronize { @iteration }
|
24
|
-
end
|
25
|
-
|
26
|
-
def reset
|
27
|
-
synchronize do
|
28
|
-
@iteration += 1
|
29
|
-
@set = false if @set
|
30
|
-
@iteration
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
22
|
extend Forwardable
|
36
23
|
delegate %i[at_css at_xpath css xpath
|
37
24
|
current_url current_title url title body doctype content=
|
38
25
|
execution_id execution_id! evaluate evaluate_on evaluate_async execute evaluate_func
|
39
26
|
add_script_tag add_style_tag] => :main_frame
|
27
|
+
delegate %i[base_url default_user_agent timeout timeout=] => :@options
|
40
28
|
|
41
29
|
include Animation
|
42
30
|
include Screenshot
|
43
31
|
include Frames
|
44
32
|
include Stream
|
45
33
|
|
46
|
-
attr_accessor :referrer
|
47
|
-
attr_reader :
|
34
|
+
attr_accessor :referrer
|
35
|
+
attr_reader :context_id, :target_id, :event, :tracing
|
36
|
+
|
37
|
+
# Client connection.
|
38
|
+
#
|
39
|
+
# @return [Client]
|
40
|
+
attr_reader :client
|
48
41
|
|
49
42
|
# Mouse object.
|
50
43
|
#
|
@@ -71,55 +64,56 @@ module Ferrum
|
|
71
64
|
# @return [Cookies]
|
72
65
|
attr_reader :cookies
|
73
66
|
|
74
|
-
|
67
|
+
# Downloads object.
|
68
|
+
#
|
69
|
+
# @return [Downloads]
|
70
|
+
attr_reader :downloads
|
71
|
+
|
72
|
+
def initialize(client, context_id:, target_id:, proxy: nil)
|
73
|
+
@client = client
|
74
|
+
@context_id = context_id
|
75
|
+
@target_id = target_id
|
76
|
+
@options = client.options
|
77
|
+
|
75
78
|
@frames = Concurrent::Map.new
|
76
79
|
@main_frame = Frame.new(nil, self)
|
77
|
-
@
|
78
|
-
@target_id = target_id
|
79
|
-
@timeout = @browser.timeout
|
80
|
-
@event = Event.new.tap(&:set)
|
80
|
+
@event = Utils::Event.new.tap(&:set)
|
81
81
|
self.proxy = proxy
|
82
82
|
|
83
|
-
@client = Browser::Client.new(ws_url, self,
|
84
|
-
logger: @browser.options.logger,
|
85
|
-
ws_max_receive_size: @browser.options.ws_max_receive_size,
|
86
|
-
id_starts_with: 1000)
|
87
|
-
|
88
83
|
@mouse = Mouse.new(self)
|
89
84
|
@keyboard = Keyboard.new(self)
|
90
85
|
@headers = Headers.new(self)
|
91
86
|
@cookies = Cookies.new(self)
|
92
87
|
@network = Network.new(self)
|
93
88
|
@tracing = Tracing.new(self)
|
89
|
+
@downloads = Downloads.new(self)
|
94
90
|
|
95
91
|
subscribe
|
96
92
|
prepare_page
|
97
93
|
end
|
98
94
|
|
99
|
-
def context
|
100
|
-
@browser.contexts.find_by(target_id: target_id)
|
101
|
-
end
|
102
|
-
|
103
95
|
#
|
104
96
|
# Navigates the page to a URL.
|
105
97
|
#
|
106
98
|
# @param [String, nil] url
|
107
99
|
# The URL to navigate to. The url should include scheme unless you set
|
108
|
-
# `{Browser#base_url = url}` when configuring
|
100
|
+
# `{Browser#base_url = url}` when configuring.
|
109
101
|
#
|
110
102
|
# @example
|
111
|
-
#
|
103
|
+
# page.go_to("https://github.com/")
|
112
104
|
#
|
113
105
|
def go_to(url = nil)
|
114
106
|
options = { url: combine_url!(url) }
|
115
107
|
options.merge!(referrer: referrer) if referrer
|
116
108
|
response = command("Page.navigate", wait: GOTO_WAIT, **options)
|
117
|
-
error_text = response["errorText"]
|
118
|
-
|
109
|
+
error_text = response["errorText"] # https://cs.chromium.org/chromium/src/net/base/net_error_list.h
|
110
|
+
if error_text && error_text != "net::ERR_ABORTED" # Request aborted due to user action or download
|
111
|
+
raise StatusError.new(options[:url], "Request to #{options[:url]} failed (#{error_text})")
|
112
|
+
end
|
119
113
|
|
120
114
|
response["frameId"]
|
121
115
|
rescue TimeoutError
|
122
|
-
if @
|
116
|
+
if @options.pending_connection_errors
|
123
117
|
pendings = network.traffic.select(&:pending?).map(&:url).compact
|
124
118
|
raise PendingConnectionsError.new(options[:url], pendings) unless pendings.empty?
|
125
119
|
end
|
@@ -129,41 +123,76 @@ module Ferrum
|
|
129
123
|
|
130
124
|
def close
|
131
125
|
@headers.clear
|
132
|
-
|
133
|
-
|
126
|
+
client.command("Target.closeTarget", async: true, targetId: @target_id)
|
127
|
+
close_connection
|
128
|
+
|
129
|
+
true
|
130
|
+
end
|
131
|
+
|
132
|
+
def close_connection
|
133
|
+
client&.close
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# Overrides device screen dimensions and emulates viewport according to parameters
|
138
|
+
#
|
139
|
+
# Read more [here](https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride).
|
140
|
+
#
|
141
|
+
# @param [Integer] width width value in pixels. 0 disables the override
|
142
|
+
#
|
143
|
+
# @param [Integer] height height value in pixels. 0 disables the override
|
144
|
+
#
|
145
|
+
# @param [Float] scale_factor device scale factor value. 0 disables the override
|
146
|
+
#
|
147
|
+
# @param [Boolean] mobile whether to emulate mobile device
|
148
|
+
#
|
149
|
+
def set_viewport(width:, height:, scale_factor: 0, mobile: false)
|
150
|
+
command(
|
151
|
+
"Emulation.setDeviceMetricsOverride",
|
152
|
+
slowmoable: true,
|
153
|
+
width: width,
|
154
|
+
height: height,
|
155
|
+
deviceScaleFactor: scale_factor,
|
156
|
+
mobile: mobile
|
157
|
+
)
|
134
158
|
end
|
135
159
|
|
136
160
|
def resize(width: nil, height: nil, fullscreen: false)
|
137
161
|
if fullscreen
|
138
162
|
width, height = document_size
|
139
|
-
|
163
|
+
self.window_bounds = { window_state: "fullscreen" }
|
140
164
|
else
|
141
|
-
|
142
|
-
|
165
|
+
self.window_bounds = { window_state: "normal" }
|
166
|
+
self.window_bounds = { width: width, height: height }
|
143
167
|
end
|
144
168
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
169
|
+
set_viewport(width: width, height: height)
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Disables JavaScript execution from the HTML source for the page.
|
174
|
+
#
|
175
|
+
# This doesn't prevent users evaluate JavaScript with Ferrum.
|
176
|
+
#
|
177
|
+
def disable_javascript
|
178
|
+
command("Emulation.setScriptExecutionDisabled", value: true)
|
150
179
|
end
|
151
180
|
|
152
181
|
#
|
153
|
-
# The current position of the
|
182
|
+
# The current position of the window.
|
154
183
|
#
|
155
184
|
# @return [(Integer, Integer)]
|
156
|
-
# The left, top coordinates of the
|
185
|
+
# The left, top coordinates of the window.
|
157
186
|
#
|
158
187
|
# @example
|
159
|
-
#
|
188
|
+
# page.position # => [10, 20]
|
160
189
|
#
|
161
190
|
def position
|
162
|
-
|
191
|
+
window_bounds.values_at("left", "top")
|
163
192
|
end
|
164
193
|
|
165
194
|
#
|
166
|
-
# Sets the position of the
|
195
|
+
# Sets the position of the window.
|
167
196
|
#
|
168
197
|
# @param [Hash{Symbol => Object}] options
|
169
198
|
#
|
@@ -174,20 +203,72 @@ module Ferrum
|
|
174
203
|
# The number of pixels from the top of the screen.
|
175
204
|
#
|
176
205
|
# @example
|
177
|
-
#
|
206
|
+
# page.position = { left: 10, top: 20 }
|
178
207
|
#
|
179
208
|
def position=(options)
|
180
|
-
|
181
|
-
|
182
|
-
|
209
|
+
self.window_bounds = { left: options[:left], top: options[:top] }
|
210
|
+
end
|
211
|
+
|
212
|
+
# Sets the position of the window.
|
213
|
+
#
|
214
|
+
# @param [Hash{Symbol => Object}] bounds
|
215
|
+
#
|
216
|
+
# @option options [Integer] :left
|
217
|
+
# The number of pixels from the left-hand side of the screen.
|
218
|
+
#
|
219
|
+
# @option options [Integer] :top
|
220
|
+
# The number of pixels from the top of the screen.
|
221
|
+
#
|
222
|
+
# @option options [Integer] :width
|
223
|
+
# The window width in pixels.
|
224
|
+
#
|
225
|
+
# @option options [Integer] :height
|
226
|
+
# The window height in pixels.
|
227
|
+
#
|
228
|
+
# @option options [String] :window_state
|
229
|
+
# The window state. Default to normal. Allowed Values: normal, minimized, maximized, fullscreen
|
230
|
+
#
|
231
|
+
# @example
|
232
|
+
# page.window_bounds = { left: 10, top: 20, width: 1024, height: 768, window_state: "normal" }
|
233
|
+
#
|
234
|
+
def window_bounds=(bounds)
|
235
|
+
options = bounds.dup
|
236
|
+
window_state = options.delete(:window_state)
|
237
|
+
bounds = { windowState: window_state, **options }.compact
|
238
|
+
|
239
|
+
client.command("Browser.setWindowBounds", windowId: window_id, bounds: bounds)
|
240
|
+
end
|
241
|
+
|
242
|
+
#
|
243
|
+
# Current window bounds.
|
244
|
+
#
|
245
|
+
# @return [Hash{String => (Integer, String)}]
|
246
|
+
#
|
247
|
+
# @example
|
248
|
+
# page.window_bounds # => { "left": 0, "top": 1286, "width": 10, "height": 10, "windowState": "normal" }
|
249
|
+
#
|
250
|
+
def window_bounds
|
251
|
+
client.command("Browser.getWindowBounds", windowId: window_id).fetch("bounds")
|
252
|
+
end
|
253
|
+
|
254
|
+
#
|
255
|
+
# Current window id.
|
256
|
+
#
|
257
|
+
# @return [Integer]
|
258
|
+
#
|
259
|
+
# @example
|
260
|
+
# page.window_id # => 1
|
261
|
+
#
|
262
|
+
def window_id
|
263
|
+
client.command("Browser.getWindowForTarget", targetId: target_id)["windowId"]
|
183
264
|
end
|
184
265
|
|
185
266
|
#
|
186
267
|
# Reloads the current page.
|
187
268
|
#
|
188
269
|
# @example
|
189
|
-
#
|
190
|
-
#
|
270
|
+
# page.go_to("https://github.com/")
|
271
|
+
# page.refresh
|
191
272
|
#
|
192
273
|
def refresh
|
193
274
|
command("Page.reload", wait: timeout, slowmoable: true)
|
@@ -198,41 +279,41 @@ module Ferrum
|
|
198
279
|
# Stop all navigations and loading pending resources on the page.
|
199
280
|
#
|
200
281
|
# @example
|
201
|
-
#
|
202
|
-
#
|
282
|
+
# page.go_to("https://github.com/")
|
283
|
+
# page.stop
|
203
284
|
#
|
204
285
|
def stop
|
205
286
|
command("Page.stopLoading", slowmoable: true)
|
206
287
|
end
|
207
288
|
|
208
289
|
#
|
209
|
-
# Navigates to the previous URL in the
|
290
|
+
# Navigates to the previous URL in the history.
|
210
291
|
#
|
211
292
|
# @example
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
293
|
+
# page.go_to("https://github.com/")
|
294
|
+
# page.at_xpath("//a").click
|
295
|
+
# page.back
|
215
296
|
#
|
216
297
|
def back
|
217
298
|
history_navigate(delta: -1)
|
218
299
|
end
|
219
300
|
|
220
301
|
#
|
221
|
-
# Navigates to the next URL in the
|
302
|
+
# Navigates to the next URL in the history.
|
222
303
|
#
|
223
304
|
# @example
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
305
|
+
# page.go_to("https://github.com/")
|
306
|
+
# page.at_xpath("//a").click
|
307
|
+
# page.back
|
308
|
+
# page.forward
|
228
309
|
#
|
229
310
|
def forward
|
230
311
|
history_navigate(delta: 1)
|
231
312
|
end
|
232
313
|
|
233
|
-
def wait_for_reload(
|
314
|
+
def wait_for_reload(timeout = 1)
|
234
315
|
@event.reset if @event.set?
|
235
|
-
@event.wait(
|
316
|
+
@event.wait(timeout)
|
236
317
|
@event.set
|
237
318
|
end
|
238
319
|
|
@@ -244,29 +325,35 @@ module Ferrum
|
|
244
325
|
# @return [Boolean]
|
245
326
|
#
|
246
327
|
# @example
|
247
|
-
#
|
248
|
-
#
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
328
|
+
# page.bypass_csp # => true
|
329
|
+
# page.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
|
330
|
+
# page.refresh
|
331
|
+
# page.add_script_tag(content: "window.__injected = 42")
|
332
|
+
# page.evaluate("window.__injected") # => 42
|
252
333
|
#
|
253
334
|
def bypass_csp(enabled: true)
|
254
335
|
command("Page.setBypassCSP", enabled: enabled)
|
255
336
|
enabled
|
256
337
|
end
|
257
338
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
339
|
+
#
|
340
|
+
# Activates (focuses) the target for the given page.
|
341
|
+
# When you have multiple tabs you work with, and you need to switch a given one.
|
342
|
+
#
|
343
|
+
# @return [Boolean]
|
344
|
+
#
|
345
|
+
# @example
|
346
|
+
# page.activate # => true
|
347
|
+
#
|
348
|
+
def activate
|
349
|
+
command("Target.activateTarget", targetId: target_id)
|
350
|
+
true
|
264
351
|
end
|
265
352
|
|
266
353
|
def command(method, wait: 0, slowmoable: false, **params)
|
267
354
|
iteration = @event.reset if wait.positive?
|
268
|
-
sleep(@
|
269
|
-
result =
|
355
|
+
sleep(@options.slowmo) if slowmoable && @options.slowmo.positive?
|
356
|
+
result = client.command(method, **params)
|
270
357
|
|
271
358
|
if wait.positive?
|
272
359
|
# Wait a bit after command and check if iteration has
|
@@ -284,30 +371,30 @@ module Ferrum
|
|
284
371
|
def on(name, &block)
|
285
372
|
case name
|
286
373
|
when :dialog
|
287
|
-
|
374
|
+
client.on("Page.javascriptDialogOpening") do |params, index, total|
|
288
375
|
dialog = Dialog.new(self, params)
|
289
376
|
block.call(dialog, index, total)
|
290
377
|
end
|
291
378
|
when :request
|
292
|
-
|
293
|
-
request = Network::InterceptedRequest.new(
|
379
|
+
client.on("Fetch.requestPaused") do |params, index, total|
|
380
|
+
request = Network::InterceptedRequest.new(client, params)
|
294
381
|
exchange = network.select(request.network_id).last
|
295
382
|
exchange ||= network.build_exchange(request.network_id)
|
296
383
|
exchange.intercepted_request = request
|
297
384
|
block.call(request, index, total)
|
298
385
|
end
|
299
386
|
when :auth
|
300
|
-
|
387
|
+
client.on("Fetch.authRequired") do |params, index, total|
|
301
388
|
request = Network::AuthRequest.new(self, params)
|
302
389
|
block.call(request, index, total)
|
303
390
|
end
|
304
391
|
else
|
305
|
-
|
392
|
+
client.on(name, &block)
|
306
393
|
end
|
307
394
|
end
|
308
395
|
|
309
396
|
def subscribed?(event)
|
310
|
-
|
397
|
+
client.subscribed?(event)
|
311
398
|
end
|
312
399
|
|
313
400
|
def use_proxy?
|
@@ -318,7 +405,9 @@ module Ferrum
|
|
318
405
|
use_proxy? && @proxy_user && @proxy_password
|
319
406
|
end
|
320
407
|
|
321
|
-
def document_node_id
|
408
|
+
def document_node_id(async: false)
|
409
|
+
return client.command("DOM.getDocument", async: true, depth: 0) if async
|
410
|
+
|
322
411
|
command("DOM.getDocument", depth: 0).dig("root", "nodeId")
|
323
412
|
end
|
324
413
|
|
@@ -327,14 +416,15 @@ module Ferrum
|
|
327
416
|
def subscribe
|
328
417
|
frames_subscribe
|
329
418
|
network.subscribe
|
419
|
+
downloads.subscribe
|
330
420
|
|
331
|
-
if @
|
421
|
+
if @options.logger
|
332
422
|
on("Runtime.consoleAPICalled") do |params|
|
333
|
-
params["args"].each { |r| @
|
423
|
+
params["args"].each { |r| @options.logger.puts(r["value"]) }
|
334
424
|
end
|
335
425
|
end
|
336
426
|
|
337
|
-
if @
|
427
|
+
if @options.js_errors
|
338
428
|
on("Runtime.exceptionThrown") do |params|
|
339
429
|
# FIXME: https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/
|
340
430
|
Thread.main.raise JavaScriptError.new(
|
@@ -369,26 +459,14 @@ module Ferrum
|
|
369
459
|
end
|
370
460
|
end
|
371
461
|
|
372
|
-
if @
|
373
|
-
unless Pathname.new(@browser.options.save_path).absolute?
|
374
|
-
raise Error, "supply absolute path for `:save_path` option"
|
375
|
-
end
|
376
|
-
|
377
|
-
@browser.command("Browser.setDownloadBehavior",
|
378
|
-
browserContextId: context.id,
|
379
|
-
downloadPath: @browser.options.save_path,
|
380
|
-
behavior: "allow", eventsEnabled: true)
|
381
|
-
end
|
462
|
+
downloads.set_behavior(save_path: @options.save_path) if @options.save_path
|
382
463
|
|
383
|
-
@
|
464
|
+
@options.extensions.each do |extension|
|
384
465
|
command("Page.addScriptToEvaluateOnNewDocument", source: extension)
|
385
466
|
end
|
386
467
|
|
387
468
|
inject_extensions
|
388
469
|
|
389
|
-
width, height = @browser.window_size
|
390
|
-
resize(width: width, height: height)
|
391
|
-
|
392
470
|
response = command("Page.getNavigationHistory")
|
393
471
|
transition_type = response.dig("entries", 0, "transitionType")
|
394
472
|
return if transition_type == "auto_toplevel"
|
@@ -402,7 +480,7 @@ module Ferrum
|
|
402
480
|
end
|
403
481
|
|
404
482
|
def inject_extensions
|
405
|
-
@
|
483
|
+
@options.extensions.each do |extension|
|
406
484
|
# https://github.com/GoogleChrome/puppeteer/issues/1443
|
407
485
|
# https://github.com/ChromeDevTools/devtools-protocol/issues/77
|
408
486
|
# https://github.com/cyrus-and/chrome-remote-interface/issues/319
|
@@ -432,22 +510,18 @@ module Ferrum
|
|
432
510
|
url = Addressable::URI.parse(url_or_path)
|
433
511
|
nil_or_relative = url.nil? || url.relative?
|
434
512
|
|
435
|
-
if nil_or_relative && !@
|
513
|
+
if nil_or_relative && !@options.base_url
|
436
514
|
raise "Set :base_url browser's option or use absolute url in `go_to`, you passed: #{url_or_path}"
|
437
515
|
end
|
438
516
|
|
439
|
-
(nil_or_relative ? @
|
440
|
-
end
|
441
|
-
|
442
|
-
def ws_url
|
443
|
-
"ws://#{@browser.process.host}:#{@browser.process.port}/devtools/page/#{@target_id}"
|
517
|
+
(nil_or_relative ? @options.base_url.join(url.to_s) : url).to_s
|
444
518
|
end
|
445
519
|
|
446
520
|
def proxy=(options)
|
447
|
-
@proxy_host = options&.[](:host) || @
|
448
|
-
@proxy_port = options&.[](:port) || @
|
449
|
-
@proxy_user = options&.[](:user) || @
|
450
|
-
@proxy_password = options&.[](:password) || @
|
521
|
+
@proxy_host = options&.[](:host) || @options.proxy&.[](:host)
|
522
|
+
@proxy_port = options&.[](:port) || @options.proxy&.[](:port)
|
523
|
+
@proxy_user = options&.[](:user) || @options.proxy&.[](:user)
|
524
|
+
@proxy_password = options&.[](:password) || @options.proxy&.[](:password)
|
451
525
|
end
|
452
526
|
end
|
453
527
|
end
|
data/lib/ferrum/proxy.rb
CHANGED
data/lib/ferrum/target.rb
CHANGED
@@ -8,17 +8,21 @@ module Ferrum
|
|
8
8
|
# where we enhance page class and build page ourselves.
|
9
9
|
attr_writer :page
|
10
10
|
|
11
|
-
|
11
|
+
attr_reader :session_id, :options
|
12
|
+
|
13
|
+
def initialize(browser_client, session_id = nil, params = nil)
|
12
14
|
@page = nil
|
13
|
-
@
|
15
|
+
@session_id = session_id
|
14
16
|
@params = params
|
17
|
+
@browser_client = browser_client
|
18
|
+
@options = browser_client.options
|
15
19
|
end
|
16
20
|
|
17
21
|
def update(params)
|
18
|
-
@params
|
22
|
+
@params.merge!(params)
|
19
23
|
end
|
20
24
|
|
21
|
-
def
|
25
|
+
def connected?
|
22
26
|
!!@page
|
23
27
|
end
|
24
28
|
|
@@ -26,9 +30,13 @@ module Ferrum
|
|
26
30
|
@page ||= build_page
|
27
31
|
end
|
28
32
|
|
33
|
+
def client
|
34
|
+
@client ||= build_client
|
35
|
+
end
|
36
|
+
|
29
37
|
def build_page(**options)
|
30
38
|
maybe_sleep_if_new_window
|
31
|
-
Page.new(
|
39
|
+
Page.new(client, context_id: context_id, target_id: id, **options)
|
32
40
|
end
|
33
41
|
|
34
42
|
def id
|
@@ -63,5 +71,17 @@ module Ferrum
|
|
63
71
|
# Dirty hack because new window doesn't have events at all
|
64
72
|
sleep(NEW_WINDOW_WAIT) if window?
|
65
73
|
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def build_client
|
78
|
+
return @browser_client.session(session_id) if options.flatten
|
79
|
+
|
80
|
+
Client.new(ws_url, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
def ws_url
|
84
|
+
@browser_client.ws_url.merge(path: "/devtools/page/#{id}")
|
85
|
+
end
|
66
86
|
end
|
67
87
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ferrum
|
4
|
+
module Utils
|
5
|
+
class Event < Concurrent::Event
|
6
|
+
def iteration
|
7
|
+
synchronize { @iteration }
|
8
|
+
end
|
9
|
+
|
10
|
+
def reset
|
11
|
+
synchronize do
|
12
|
+
@iteration += 1
|
13
|
+
@set = false if @set
|
14
|
+
@iteration
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ferrum
|
4
|
+
module Utils
|
5
|
+
module Thread
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def spawn(abort_on_exception: true)
|
9
|
+
::Thread.new(abort_on_exception) do |whether_abort_on_exception|
|
10
|
+
::Thread.current.abort_on_exception = whether_abort_on_exception
|
11
|
+
::Thread.current.report_on_exception = true if ::Thread.current.respond_to?(:report_on_exception=)
|
12
|
+
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/ferrum/version.rb
CHANGED