ferrum 0.13 → 0.15
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 +288 -154
- data/lib/ferrum/browser/command.rb +8 -0
- data/lib/ferrum/browser/options/chrome.rb +17 -5
- data/lib/ferrum/browser/options.rb +38 -25
- data/lib/ferrum/browser/process.rb +44 -17
- data/lib/ferrum/browser.rb +34 -52
- data/lib/ferrum/client/subscriber.rb +76 -0
- data/lib/ferrum/{browser → client}/web_socket.rb +36 -22
- data/lib/ferrum/client.rb +169 -0
- data/lib/ferrum/context.rb +19 -15
- data/lib/ferrum/contexts.rb +46 -12
- data/lib/ferrum/cookies/cookie.rb +57 -0
- data/lib/ferrum/cookies.rb +40 -4
- data/lib/ferrum/downloads.rb +60 -0
- data/lib/ferrum/errors.rb +2 -1
- data/lib/ferrum/frame.rb +1 -0
- data/lib/ferrum/headers.rb +1 -1
- data/lib/ferrum/network/exchange.rb +29 -2
- data/lib/ferrum/network/intercepted_request.rb +8 -17
- data/lib/ferrum/network/request.rb +23 -39
- data/lib/ferrum/network/request_params.rb +57 -0
- data/lib/ferrum/network/response.rb +25 -5
- data/lib/ferrum/network.rb +43 -16
- data/lib/ferrum/node.rb +21 -1
- data/lib/ferrum/page/frames.rb +5 -5
- data/lib/ferrum/page/screenshot.rb +42 -24
- data/lib/ferrum/page.rb +183 -131
- 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 +14 -114
- data/lib/ferrum/browser/client.rb +0 -102
- data/lib/ferrum/browser/subscriber.rb +0 -36
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,61 +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
|
-
# https://cs.chromium.org/chromium/src/net/base/net_error_list.h
|
118
|
-
if
|
119
|
-
|
120
|
-
net::ERR_INTERNET_DISCONNECTED
|
121
|
-
net::ERR_CONNECTION_TIMED_OUT
|
122
|
-
net::ERR_FILE_NOT_FOUND].include?(response["errorText"])
|
123
|
-
raise StatusError, options[:url]
|
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})")
|
124
112
|
end
|
125
113
|
|
126
114
|
response["frameId"]
|
127
115
|
rescue TimeoutError
|
128
|
-
if @
|
116
|
+
if @options.pending_connection_errors
|
129
117
|
pendings = network.traffic.select(&:pending?).map(&:url).compact
|
130
118
|
raise PendingConnectionsError.new(options[:url], pendings) unless pendings.empty?
|
131
119
|
end
|
@@ -135,42 +123,76 @@ module Ferrum
|
|
135
123
|
|
136
124
|
def close
|
137
125
|
@headers.clear
|
138
|
-
|
139
|
-
|
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
|
+
)
|
140
158
|
end
|
141
159
|
|
142
160
|
def resize(width: nil, height: nil, fullscreen: false)
|
143
161
|
if fullscreen
|
144
162
|
width, height = document_size
|
145
|
-
|
163
|
+
self.window_bounds = { window_state: "fullscreen" }
|
146
164
|
else
|
147
|
-
|
148
|
-
|
165
|
+
self.window_bounds = { window_state: "normal" }
|
166
|
+
self.window_bounds = { width: width, height: height }
|
149
167
|
end
|
150
168
|
|
151
|
-
|
152
|
-
width: width,
|
153
|
-
height: height,
|
154
|
-
deviceScaleFactor: 1,
|
155
|
-
mobile: false,
|
156
|
-
fitWindow: false)
|
169
|
+
set_viewport(width: width, height: height)
|
157
170
|
end
|
158
171
|
|
159
172
|
#
|
160
|
-
#
|
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)
|
179
|
+
end
|
180
|
+
|
181
|
+
#
|
182
|
+
# The current position of the window.
|
161
183
|
#
|
162
184
|
# @return [(Integer, Integer)]
|
163
|
-
# The left, top coordinates of the
|
185
|
+
# The left, top coordinates of the window.
|
164
186
|
#
|
165
187
|
# @example
|
166
|
-
#
|
188
|
+
# page.position # => [10, 20]
|
167
189
|
#
|
168
190
|
def position
|
169
|
-
|
191
|
+
window_bounds.values_at("left", "top")
|
170
192
|
end
|
171
193
|
|
172
194
|
#
|
173
|
-
# Sets the position of the
|
195
|
+
# Sets the position of the window.
|
174
196
|
#
|
175
197
|
# @param [Hash{Symbol => Object}] options
|
176
198
|
#
|
@@ -181,20 +203,72 @@ module Ferrum
|
|
181
203
|
# The number of pixels from the top of the screen.
|
182
204
|
#
|
183
205
|
# @example
|
184
|
-
#
|
206
|
+
# page.position = { left: 10, top: 20 }
|
185
207
|
#
|
186
208
|
def position=(options)
|
187
|
-
|
188
|
-
|
189
|
-
|
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"]
|
190
264
|
end
|
191
265
|
|
192
266
|
#
|
193
267
|
# Reloads the current page.
|
194
268
|
#
|
195
269
|
# @example
|
196
|
-
#
|
197
|
-
#
|
270
|
+
# page.go_to("https://github.com/")
|
271
|
+
# page.refresh
|
198
272
|
#
|
199
273
|
def refresh
|
200
274
|
command("Page.reload", wait: timeout, slowmoable: true)
|
@@ -205,41 +279,41 @@ module Ferrum
|
|
205
279
|
# Stop all navigations and loading pending resources on the page.
|
206
280
|
#
|
207
281
|
# @example
|
208
|
-
#
|
209
|
-
#
|
282
|
+
# page.go_to("https://github.com/")
|
283
|
+
# page.stop
|
210
284
|
#
|
211
285
|
def stop
|
212
286
|
command("Page.stopLoading", slowmoable: true)
|
213
287
|
end
|
214
288
|
|
215
289
|
#
|
216
|
-
# Navigates to the previous URL in the
|
290
|
+
# Navigates to the previous URL in the history.
|
217
291
|
#
|
218
292
|
# @example
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
293
|
+
# page.go_to("https://github.com/")
|
294
|
+
# page.at_xpath("//a").click
|
295
|
+
# page.back
|
222
296
|
#
|
223
297
|
def back
|
224
298
|
history_navigate(delta: -1)
|
225
299
|
end
|
226
300
|
|
227
301
|
#
|
228
|
-
# Navigates to the next URL in the
|
302
|
+
# Navigates to the next URL in the history.
|
229
303
|
#
|
230
304
|
# @example
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
305
|
+
# page.go_to("https://github.com/")
|
306
|
+
# page.at_xpath("//a").click
|
307
|
+
# page.back
|
308
|
+
# page.forward
|
235
309
|
#
|
236
310
|
def forward
|
237
311
|
history_navigate(delta: 1)
|
238
312
|
end
|
239
313
|
|
240
|
-
def wait_for_reload(
|
314
|
+
def wait_for_reload(timeout = 1)
|
241
315
|
@event.reset if @event.set?
|
242
|
-
@event.wait(
|
316
|
+
@event.wait(timeout)
|
243
317
|
@event.set
|
244
318
|
end
|
245
319
|
|
@@ -251,29 +325,21 @@ module Ferrum
|
|
251
325
|
# @return [Boolean]
|
252
326
|
#
|
253
327
|
# @example
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
258
|
-
#
|
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
|
259
333
|
#
|
260
334
|
def bypass_csp(enabled: true)
|
261
335
|
command("Page.setBypassCSP", enabled: enabled)
|
262
336
|
enabled
|
263
337
|
end
|
264
338
|
|
265
|
-
def window_id
|
266
|
-
@browser.command("Browser.getWindowForTarget", targetId: @target_id)["windowId"]
|
267
|
-
end
|
268
|
-
|
269
|
-
def set_window_bounds(bounds = {})
|
270
|
-
@browser.command("Browser.setWindowBounds", windowId: window_id, bounds: bounds)
|
271
|
-
end
|
272
|
-
|
273
339
|
def command(method, wait: 0, slowmoable: false, **params)
|
274
340
|
iteration = @event.reset if wait.positive?
|
275
|
-
sleep(@
|
276
|
-
result =
|
341
|
+
sleep(@options.slowmo) if slowmoable && @options.slowmo.positive?
|
342
|
+
result = client.command(method, **params)
|
277
343
|
|
278
344
|
if wait.positive?
|
279
345
|
# Wait a bit after command and check if iteration has
|
@@ -291,30 +357,30 @@ module Ferrum
|
|
291
357
|
def on(name, &block)
|
292
358
|
case name
|
293
359
|
when :dialog
|
294
|
-
|
360
|
+
client.on("Page.javascriptDialogOpening") do |params, index, total|
|
295
361
|
dialog = Dialog.new(self, params)
|
296
362
|
block.call(dialog, index, total)
|
297
363
|
end
|
298
364
|
when :request
|
299
|
-
|
300
|
-
request = Network::InterceptedRequest.new(
|
365
|
+
client.on("Fetch.requestPaused") do |params, index, total|
|
366
|
+
request = Network::InterceptedRequest.new(client, params)
|
301
367
|
exchange = network.select(request.network_id).last
|
302
368
|
exchange ||= network.build_exchange(request.network_id)
|
303
369
|
exchange.intercepted_request = request
|
304
370
|
block.call(request, index, total)
|
305
371
|
end
|
306
372
|
when :auth
|
307
|
-
|
373
|
+
client.on("Fetch.authRequired") do |params, index, total|
|
308
374
|
request = Network::AuthRequest.new(self, params)
|
309
375
|
block.call(request, index, total)
|
310
376
|
end
|
311
377
|
else
|
312
|
-
|
378
|
+
client.on(name, &block)
|
313
379
|
end
|
314
380
|
end
|
315
381
|
|
316
382
|
def subscribed?(event)
|
317
|
-
|
383
|
+
client.subscribed?(event)
|
318
384
|
end
|
319
385
|
|
320
386
|
def use_proxy?
|
@@ -325,19 +391,24 @@ module Ferrum
|
|
325
391
|
use_proxy? && @proxy_user && @proxy_password
|
326
392
|
end
|
327
393
|
|
394
|
+
def document_node_id
|
395
|
+
command("DOM.getDocument", depth: 0).dig("root", "nodeId")
|
396
|
+
end
|
397
|
+
|
328
398
|
private
|
329
399
|
|
330
400
|
def subscribe
|
331
401
|
frames_subscribe
|
332
402
|
network.subscribe
|
403
|
+
downloads.subscribe
|
333
404
|
|
334
|
-
if @
|
405
|
+
if @options.logger
|
335
406
|
on("Runtime.consoleAPICalled") do |params|
|
336
|
-
params["args"].each { |r| @
|
407
|
+
params["args"].each { |r| @options.logger.puts(r["value"]) }
|
337
408
|
end
|
338
409
|
end
|
339
410
|
|
340
|
-
if @
|
411
|
+
if @options.js_errors
|
341
412
|
on("Runtime.exceptionThrown") do |params|
|
342
413
|
# FIXME: https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/
|
343
414
|
Thread.main.raise JavaScriptError.new(
|
@@ -350,7 +421,7 @@ module Ferrum
|
|
350
421
|
on(:dialog) do |dialog, _index, total|
|
351
422
|
if total == 1
|
352
423
|
warn "Dialog was shown but you didn't provide `on(:dialog)` callback, accepting it by default. " \
|
353
|
-
"Please take a look at https://github.com/rubycdp/ferrum#
|
424
|
+
"Please take a look at https://github.com/rubycdp/ferrum#dialogs"
|
354
425
|
dialog.accept
|
355
426
|
end
|
356
427
|
end
|
@@ -372,28 +443,17 @@ module Ferrum
|
|
372
443
|
end
|
373
444
|
end
|
374
445
|
|
375
|
-
if @
|
376
|
-
unless Pathname.new(@browser.options.save_path).absolute?
|
377
|
-
raise Error, "supply absolute path for `:save_path` option"
|
378
|
-
end
|
379
|
-
|
380
|
-
@browser.command("Browser.setDownloadBehavior",
|
381
|
-
browserContextId: context.id,
|
382
|
-
downloadPath: @browser.options.save_path,
|
383
|
-
behavior: "allow", eventsEnabled: true)
|
384
|
-
end
|
446
|
+
downloads.set_behavior(save_path: @options.save_path) if @options.save_path
|
385
447
|
|
386
|
-
@
|
448
|
+
@options.extensions.each do |extension|
|
387
449
|
command("Page.addScriptToEvaluateOnNewDocument", source: extension)
|
388
450
|
end
|
389
451
|
|
390
452
|
inject_extensions
|
391
453
|
|
392
|
-
width, height = @browser.window_size
|
393
|
-
resize(width: width, height: height)
|
394
|
-
|
395
454
|
response = command("Page.getNavigationHistory")
|
396
|
-
|
455
|
+
transition_type = response.dig("entries", 0, "transitionType")
|
456
|
+
return if transition_type == "auto_toplevel"
|
397
457
|
|
398
458
|
# If we create page by clicking links, submitting forms and so on it
|
399
459
|
# opens a new window for which `frameStoppedLoading` event never
|
@@ -404,7 +464,7 @@ module Ferrum
|
|
404
464
|
end
|
405
465
|
|
406
466
|
def inject_extensions
|
407
|
-
@
|
467
|
+
@options.extensions.each do |extension|
|
408
468
|
# https://github.com/GoogleChrome/puppeteer/issues/1443
|
409
469
|
# https://github.com/ChromeDevTools/devtools-protocol/issues/77
|
410
470
|
# https://github.com/cyrus-and/chrome-remote-interface/issues/319
|
@@ -434,26 +494,18 @@ module Ferrum
|
|
434
494
|
url = Addressable::URI.parse(url_or_path)
|
435
495
|
nil_or_relative = url.nil? || url.relative?
|
436
496
|
|
437
|
-
if nil_or_relative && !@
|
497
|
+
if nil_or_relative && !@options.base_url
|
438
498
|
raise "Set :base_url browser's option or use absolute url in `go_to`, you passed: #{url_or_path}"
|
439
499
|
end
|
440
500
|
|
441
|
-
(nil_or_relative ? @
|
442
|
-
end
|
443
|
-
|
444
|
-
def document_node_id
|
445
|
-
command("DOM.getDocument", depth: 0).dig("root", "nodeId")
|
446
|
-
end
|
447
|
-
|
448
|
-
def ws_url
|
449
|
-
"ws://#{@browser.process.host}:#{@browser.process.port}/devtools/page/#{@target_id}"
|
501
|
+
(nil_or_relative ? @options.base_url.join(url.to_s) : url).to_s
|
450
502
|
end
|
451
503
|
|
452
504
|
def proxy=(options)
|
453
|
-
@proxy_host = options&.[](:host) || @
|
454
|
-
@proxy_port = options&.[](:port) || @
|
455
|
-
@proxy_user = options&.[](:user) || @
|
456
|
-
@proxy_password = options&.[](:password) || @
|
505
|
+
@proxy_host = options&.[](:host) || @options.proxy&.[](:host)
|
506
|
+
@proxy_port = options&.[](:port) || @options.proxy&.[](:port)
|
507
|
+
@proxy_user = options&.[](:user) || @options.proxy&.[](:user)
|
508
|
+
@proxy_password = options&.[](:password) || @options.proxy&.[](:password)
|
457
509
|
end
|
458
510
|
end
|
459
511
|
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