ferrum 0.12 → 0.13
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/README.md +15 -20
- data/lib/ferrum/browser/client.rb +4 -4
- data/lib/ferrum/browser/command.rb +5 -6
- data/lib/ferrum/browser/options/base.rb +1 -4
- data/lib/ferrum/browser/options/chrome.rb +14 -9
- data/lib/ferrum/browser/options/firefox.rb +3 -6
- data/lib/ferrum/browser/options.rb +84 -0
- data/lib/ferrum/browser/process.rb +5 -6
- data/lib/ferrum/browser/version_info.rb +71 -0
- data/lib/ferrum/browser/xvfb.rb +1 -1
- data/lib/ferrum/browser.rb +176 -62
- data/lib/ferrum/context.rb +3 -2
- data/lib/ferrum/contexts.rb +2 -2
- data/lib/ferrum/cookies/cookie.rb +126 -0
- data/lib/ferrum/cookies.rb +86 -49
- data/lib/ferrum/dialog.rb +30 -0
- data/lib/ferrum/frame/dom.rb +177 -0
- data/lib/ferrum/frame/runtime.rb +41 -61
- data/lib/ferrum/frame.rb +90 -3
- data/lib/ferrum/headers.rb +28 -0
- data/lib/ferrum/keyboard.rb +45 -2
- data/lib/ferrum/mouse.rb +84 -0
- data/lib/ferrum/network/exchange.rb +86 -5
- data/lib/ferrum/network/request.rb +64 -0
- data/lib/ferrum/network/response.rb +83 -1
- data/lib/ferrum/network.rb +160 -0
- data/lib/ferrum/page/animation.rb +16 -0
- data/lib/ferrum/page/frames.rb +66 -11
- data/lib/ferrum/page/screenshot.rb +91 -0
- data/lib/ferrum/page/tracing.rb +26 -0
- data/lib/ferrum/page.rb +151 -32
- data/lib/ferrum/proxy.rb +91 -2
- data/lib/ferrum/target.rb +6 -4
- data/lib/ferrum/version.rb +1 -1
- metadata +5 -2
@@ -3,10 +3,26 @@
|
|
3
3
|
module Ferrum
|
4
4
|
class Page
|
5
5
|
module Animation
|
6
|
+
#
|
7
|
+
# Returns playback rate for CSS animations, defaults to `1`.
|
8
|
+
#
|
9
|
+
# @return [Integer]
|
10
|
+
#
|
6
11
|
def playback_rate
|
7
12
|
command("Animation.getPlaybackRate")["playbackRate"]
|
8
13
|
end
|
9
14
|
|
15
|
+
#
|
16
|
+
# Sets playback rate of CSS animations.
|
17
|
+
#
|
18
|
+
# @param [Integer] value
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# browser = Ferrum::Browser.new
|
22
|
+
# browser.playback_rate = 2000
|
23
|
+
# browser.go_to("https://google.com")
|
24
|
+
# browser.playback_rate # => 2000
|
25
|
+
#
|
10
26
|
def playback_rate=(value)
|
11
27
|
command("Animation.setPlaybackRate", playbackRate: value)
|
12
28
|
end
|
data/lib/ferrum/page/frames.rb
CHANGED
@@ -5,12 +5,54 @@ require "ferrum/frame"
|
|
5
5
|
module Ferrum
|
6
6
|
class Page
|
7
7
|
module Frames
|
8
|
+
# The page's main frame, the top of the tree and the parent of all frames.
|
9
|
+
#
|
10
|
+
# @return [Frame]
|
8
11
|
attr_reader :main_frame
|
9
12
|
|
13
|
+
#
|
14
|
+
# Returns all the frames current page have.
|
15
|
+
#
|
16
|
+
# @return [Array<Frame>]
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# browser.go_to("https://www.w3schools.com/tags/tag_frame.asp")
|
20
|
+
# browser.frames # =>
|
21
|
+
# # [
|
22
|
+
# # #<Ferrum::Frame
|
23
|
+
# # @id="C6D104CE454A025FBCF22B98DE612B12"
|
24
|
+
# # @parent_id=nil @name=nil @state=:stopped_loading @execution_id=1>,
|
25
|
+
# # #<Ferrum::Frame
|
26
|
+
# # @id="C09C4E4404314AAEAE85928EAC109A93"
|
27
|
+
# # @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=2>,
|
28
|
+
# # #<Ferrum::Frame
|
29
|
+
# # @id="2E9C7F476ED09D87A42F2FEE3C6FBC3C"
|
30
|
+
# # @parent_id="C6D104CE454A025FBCF22B98DE612B12" @state=:stopped_loading @execution_id=3>,
|
31
|
+
# # ...
|
32
|
+
# # ]
|
33
|
+
#
|
10
34
|
def frames
|
11
35
|
@frames.values
|
12
36
|
end
|
13
37
|
|
38
|
+
#
|
39
|
+
# Find frame by given options.
|
40
|
+
#
|
41
|
+
# @param [String] id
|
42
|
+
# Unique frame's id that browser provides.
|
43
|
+
#
|
44
|
+
# @param [String] name
|
45
|
+
# Frame's name if there's one.
|
46
|
+
#
|
47
|
+
# @param [String] execution_id
|
48
|
+
# Frame's context execution id.
|
49
|
+
#
|
50
|
+
# @return [Frame, nil]
|
51
|
+
# The matching frame.
|
52
|
+
#
|
53
|
+
# @example
|
54
|
+
# browser.frame_by(id: "C6D104CE454A025FBCF22B98DE612B12")
|
55
|
+
#
|
14
56
|
def frame_by(id: nil, name: nil, execution_id: nil)
|
15
57
|
if id
|
16
58
|
@frames[id]
|
@@ -25,6 +67,7 @@ module Ferrum
|
|
25
67
|
|
26
68
|
def frames_subscribe
|
27
69
|
subscribe_frame_attached
|
70
|
+
subscribe_frame_detached
|
28
71
|
subscribe_frame_started_loading
|
29
72
|
subscribe_frame_navigated
|
30
73
|
subscribe_frame_stopped_loading
|
@@ -43,14 +86,26 @@ module Ferrum
|
|
43
86
|
def subscribe_frame_attached
|
44
87
|
on("Page.frameAttached") do |params|
|
45
88
|
parent_frame_id, frame_id = params.values_at("parentFrameId", "frameId")
|
46
|
-
@frames
|
89
|
+
@frames.put_if_absent(frame_id, Frame.new(frame_id, self, parent_frame_id))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def subscribe_frame_detached
|
94
|
+
on("Page.frameDetached") do |params|
|
95
|
+
frame = @frames[params["frameId"]]
|
96
|
+
|
97
|
+
if frame&.main?
|
98
|
+
frame.execution_id = nil
|
99
|
+
else
|
100
|
+
@frames.delete(params["frameId"])
|
101
|
+
end
|
47
102
|
end
|
48
103
|
end
|
49
104
|
|
50
105
|
def subscribe_frame_started_loading
|
51
106
|
on("Page.frameStartedLoading") do |params|
|
52
107
|
frame = @frames[params["frameId"]]
|
53
|
-
frame.state = :started_loading
|
108
|
+
frame.state = :started_loading if frame
|
54
109
|
@event.reset
|
55
110
|
end
|
56
111
|
end
|
@@ -59,8 +114,11 @@ module Ferrum
|
|
59
114
|
on("Page.frameNavigated") do |params|
|
60
115
|
frame_id, name = params["frame"]&.values_at("id", "name")
|
61
116
|
frame = @frames[frame_id]
|
62
|
-
|
63
|
-
frame
|
117
|
+
|
118
|
+
if frame
|
119
|
+
frame.state = :navigated
|
120
|
+
frame.name = name
|
121
|
+
end
|
64
122
|
end
|
65
123
|
end
|
66
124
|
|
@@ -107,14 +165,12 @@ module Ferrum
|
|
107
165
|
root_frame = command("Page.getFrameTree").dig("frameTree", "frame", "id")
|
108
166
|
if frame_id == root_frame
|
109
167
|
@main_frame.id = frame_id
|
110
|
-
@frames
|
168
|
+
@frames.put_if_absent(frame_id, @main_frame)
|
111
169
|
end
|
112
170
|
end
|
113
171
|
|
114
|
-
frame = @frames
|
172
|
+
frame = @frames.fetch_or_store(frame_id, Frame.new(frame_id, self))
|
115
173
|
frame.execution_id = context_id
|
116
|
-
|
117
|
-
@frames[frame_id] ||= frame
|
118
174
|
end
|
119
175
|
end
|
120
176
|
|
@@ -128,13 +184,12 @@ module Ferrum
|
|
128
184
|
|
129
185
|
def subscribe_execution_contexts_cleared
|
130
186
|
on("Runtime.executionContextsCleared") do
|
131
|
-
@frames.
|
132
|
-
@main_frame.execution_id = nil
|
187
|
+
@frames.each_value { |f| f.execution_id = nil }
|
133
188
|
end
|
134
189
|
end
|
135
190
|
|
136
191
|
def idling?
|
137
|
-
@frames.all? { |
|
192
|
+
@frames.values.all? { |f| f.state == :stopped_loading }
|
138
193
|
end
|
139
194
|
end
|
140
195
|
end
|
@@ -26,6 +26,51 @@ module Ferrum
|
|
26
26
|
A6: { width: 4.13, height: 5.83 }
|
27
27
|
}.freeze
|
28
28
|
|
29
|
+
#
|
30
|
+
# Saves screenshot on a disk or returns it as base64.
|
31
|
+
#
|
32
|
+
# @param [Hash{Symbol => Object}] opts
|
33
|
+
#
|
34
|
+
# @option opts [String] :path
|
35
|
+
# The path to save a screenshot on the disk. `:encoding` will be set to
|
36
|
+
# `:binary` automatically.
|
37
|
+
#
|
38
|
+
# @option opts [:base64, :binary] :encoding
|
39
|
+
# The encoding the image should be returned in.
|
40
|
+
#
|
41
|
+
# @option opts ["jpeg", "png"] :format
|
42
|
+
# The format the image should be returned in.
|
43
|
+
#
|
44
|
+
# @option opts [Integer] :quality
|
45
|
+
# The image quality. **Note:** 0-100 works for jpeg only.
|
46
|
+
#
|
47
|
+
# @option opts [Boolean] :full
|
48
|
+
# Whether you need full page screenshot or a viewport.
|
49
|
+
#
|
50
|
+
# @option opts [String] :selector
|
51
|
+
# CSS selector for the given element.
|
52
|
+
#
|
53
|
+
# @option opts [Float] :scale
|
54
|
+
# Zoom in/out.
|
55
|
+
#
|
56
|
+
# @option opts [Ferrum::RGBA] :background_color
|
57
|
+
# Sets the background color.
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# browser.go_to("https://google.com/")
|
61
|
+
#
|
62
|
+
# @example Save on the disk in PNG:
|
63
|
+
# browser.screenshot(path: "google.png") # => 134660
|
64
|
+
#
|
65
|
+
# @example Save on the disk in JPG:
|
66
|
+
# browser.screenshot(path: "google.jpg") # => 30902
|
67
|
+
#
|
68
|
+
# @example Save to Base64 the whole page not only viewport and reduce quality:
|
69
|
+
# browser.screenshot(full: true, quality: 60) # "iVBORw0KGgoAAAANS...
|
70
|
+
#
|
71
|
+
# @example Save with specific background color:
|
72
|
+
# browser.screenshot(background_color: Ferrum::RGBA.new(0, 0, 0, 0.0))
|
73
|
+
#
|
29
74
|
def screenshot(**opts)
|
30
75
|
path, encoding = common_options(**opts)
|
31
76
|
options = screenshot_options(path, **opts)
|
@@ -36,6 +81,42 @@ module Ferrum
|
|
36
81
|
save_file(path, bin)
|
37
82
|
end
|
38
83
|
|
84
|
+
#
|
85
|
+
# Saves PDF on a disk or returns it as Base64.
|
86
|
+
#
|
87
|
+
# @param [Hash{Symbol => Object}] opts
|
88
|
+
#
|
89
|
+
# @option opts [String] :path
|
90
|
+
# The path to save a screenshot on the disk. `:encoding` will be set to
|
91
|
+
# `:binary` automatically.
|
92
|
+
#
|
93
|
+
# @option opts [:base64, :binary] :encoding
|
94
|
+
# The encoding the image should be returned in.
|
95
|
+
#
|
96
|
+
# @option opts [Boolean] :landscape (false)
|
97
|
+
# Page orientation.
|
98
|
+
#
|
99
|
+
# @option opts [Float] :scale
|
100
|
+
# Zoom in/out.
|
101
|
+
#
|
102
|
+
# @option opts [:letter, :legal, :tabloid, :ledger, :A0, :A1, :A2, :A3, :A4, :A5, :A6] :format
|
103
|
+
# The standard paper size.
|
104
|
+
#
|
105
|
+
# @option opts [Float] :paper_width
|
106
|
+
# Sets the paper's width.
|
107
|
+
#
|
108
|
+
# @option opts [Float] :paper_height
|
109
|
+
# Sets the paper's height.
|
110
|
+
#
|
111
|
+
# @note
|
112
|
+
# See other [native options](https://chromedevtools.github.io/devtools-protocol/tot/Page#method-printToPDF) you
|
113
|
+
# can pass.
|
114
|
+
#
|
115
|
+
# @example
|
116
|
+
# browser.go_to("https://google.com/")
|
117
|
+
# # Save to disk as a PDF
|
118
|
+
# browser.pdf(path: "google.pdf", paper_width: 1.0, paper_height: 1.0) # => true
|
119
|
+
#
|
39
120
|
def pdf(**opts)
|
40
121
|
path, encoding = common_options(**opts)
|
41
122
|
options = pdf_options(**opts).merge(transferMode: "ReturnAsStream")
|
@@ -43,6 +124,16 @@ module Ferrum
|
|
43
124
|
stream_to(path: path, encoding: encoding, handle: handle)
|
44
125
|
end
|
45
126
|
|
127
|
+
#
|
128
|
+
# Saves MHTML on a disk or returns it as a string.
|
129
|
+
#
|
130
|
+
# @param [String, nil] path
|
131
|
+
# The path to save a file on the disk.
|
132
|
+
#
|
133
|
+
# @example
|
134
|
+
# browser.go_to("https://google.com/")
|
135
|
+
# browser.mhtml(path: "google.mhtml") # => 87742
|
136
|
+
#
|
46
137
|
def mhtml(path: nil)
|
47
138
|
data = command("Page.captureSnapshot", format: :mhtml).fetch("data")
|
48
139
|
return data if path.nil?
|
data/lib/ferrum/page/tracing.rb
CHANGED
@@ -19,6 +19,32 @@ module Ferrum
|
|
19
19
|
@subscribed_tracing_complete = false
|
20
20
|
end
|
21
21
|
|
22
|
+
#
|
23
|
+
# Accepts block, records trace and by default returns trace data from `Tracing.tracingComplete` event as output.
|
24
|
+
#
|
25
|
+
# @param [String, nil] path
|
26
|
+
# Save data on the disk.
|
27
|
+
#
|
28
|
+
# @param [:binary, :base64] encoding
|
29
|
+
# Encode output as Base64 or plain text.
|
30
|
+
#
|
31
|
+
# @param [Float, nil] timeout
|
32
|
+
# Wait until file streaming finishes in the specified time or raise
|
33
|
+
# error.
|
34
|
+
#
|
35
|
+
# @param [Boolean] screenshots
|
36
|
+
# capture screenshots in the trace.
|
37
|
+
#
|
38
|
+
# @param [Hash{String => Object}] trace_config
|
39
|
+
# config for [trace](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#type-TraceConfig),
|
40
|
+
# for categories see [getCategories](https://chromedevtools.github.io/devtools-protocol/tot/Tracing/#method-getCategories),
|
41
|
+
# only one trace config can be active at a time per browser.
|
42
|
+
#
|
43
|
+
# @return [String, true]
|
44
|
+
# The trace data from the `Tracing.tracingComplete` event.
|
45
|
+
# When `path` is specified returns `true` and stores trace data into
|
46
|
+
# file.
|
47
|
+
#
|
22
48
|
def record(path: nil, encoding: :binary, timeout: nil, trace_config: nil, screenshots: false)
|
23
49
|
@path = path
|
24
50
|
@encoding = encoding
|
data/lib/ferrum/page.rb
CHANGED
@@ -35,7 +35,7 @@ module Ferrum
|
|
35
35
|
extend Forwardable
|
36
36
|
delegate %i[at_css at_xpath css xpath
|
37
37
|
current_url current_title url title body doctype content=
|
38
|
-
execution_id evaluate evaluate_on evaluate_async execute evaluate_func
|
38
|
+
execution_id execution_id! evaluate evaluate_on evaluate_async execute evaluate_func
|
39
39
|
add_script_tag add_style_tag] => :main_frame
|
40
40
|
|
41
41
|
include Animation
|
@@ -43,23 +43,47 @@ module Ferrum
|
|
43
43
|
include Frames
|
44
44
|
include Stream
|
45
45
|
|
46
|
-
attr_accessor :referrer
|
47
|
-
attr_reader :target_id, :browser,
|
48
|
-
:headers, :cookies, :network,
|
49
|
-
:mouse, :keyboard, :event,
|
50
|
-
:tracing
|
46
|
+
attr_accessor :referrer, :timeout
|
47
|
+
attr_reader :target_id, :browser, :event, :tracing
|
51
48
|
|
52
|
-
|
53
|
-
|
49
|
+
# Mouse object.
|
50
|
+
#
|
51
|
+
# @return [Mouse]
|
52
|
+
attr_reader :mouse
|
53
|
+
|
54
|
+
# Keyboard object.
|
55
|
+
#
|
56
|
+
# @return [Keyboard]
|
57
|
+
attr_reader :keyboard
|
58
|
+
|
59
|
+
# Network object.
|
60
|
+
#
|
61
|
+
# @return [Network]
|
62
|
+
attr_reader :network
|
63
|
+
|
64
|
+
# Headers object.
|
65
|
+
#
|
66
|
+
# @return [Headers]
|
67
|
+
attr_reader :headers
|
68
|
+
|
69
|
+
# Cookie store.
|
70
|
+
#
|
71
|
+
# @return [Cookies]
|
72
|
+
attr_reader :cookies
|
73
|
+
|
74
|
+
def initialize(target_id, browser, proxy: nil)
|
75
|
+
@frames = Concurrent::Map.new
|
54
76
|
@main_frame = Frame.new(nil, self)
|
55
77
|
@browser = browser
|
56
78
|
@target_id = target_id
|
79
|
+
@timeout = @browser.timeout
|
57
80
|
@event = Event.new.tap(&:set)
|
81
|
+
self.proxy = proxy
|
58
82
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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)
|
63
87
|
|
64
88
|
@mouse = Mouse.new(self)
|
65
89
|
@keyboard = Keyboard.new(self)
|
@@ -72,14 +96,20 @@ module Ferrum
|
|
72
96
|
prepare_page
|
73
97
|
end
|
74
98
|
|
75
|
-
def timeout
|
76
|
-
@browser.timeout
|
77
|
-
end
|
78
|
-
|
79
99
|
def context
|
80
100
|
@browser.contexts.find_by(target_id: target_id)
|
81
101
|
end
|
82
102
|
|
103
|
+
#
|
104
|
+
# Navigates the page to a URL.
|
105
|
+
#
|
106
|
+
# @param [String, nil] url
|
107
|
+
# The URL to navigate to. The url should include scheme unless you set
|
108
|
+
# `{Browser#base_url = url}` when configuring driver.
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# browser.go_to("https://github.com/")
|
112
|
+
#
|
83
113
|
def go_to(url = nil)
|
84
114
|
options = { url: combine_url!(url) }
|
85
115
|
options.merge!(referrer: referrer) if referrer
|
@@ -88,14 +118,15 @@ module Ferrum
|
|
88
118
|
if %w[net::ERR_NAME_NOT_RESOLVED
|
89
119
|
net::ERR_NAME_RESOLUTION_FAILED
|
90
120
|
net::ERR_INTERNET_DISCONNECTED
|
91
|
-
net::ERR_CONNECTION_TIMED_OUT
|
121
|
+
net::ERR_CONNECTION_TIMED_OUT
|
122
|
+
net::ERR_FILE_NOT_FOUND].include?(response["errorText"])
|
92
123
|
raise StatusError, options[:url]
|
93
124
|
end
|
94
125
|
|
95
126
|
response["frameId"]
|
96
127
|
rescue TimeoutError
|
97
|
-
if @browser.pending_connection_errors
|
98
|
-
pendings = network.traffic.select(&:pending?).map
|
128
|
+
if @browser.options.pending_connection_errors
|
129
|
+
pendings = network.traffic.select(&:pending?).map(&:url).compact
|
99
130
|
raise PendingConnectionsError.new(options[:url], pendings) unless pendings.empty?
|
100
131
|
end
|
101
132
|
end
|
@@ -125,29 +156,83 @@ module Ferrum
|
|
125
156
|
fitWindow: false)
|
126
157
|
end
|
127
158
|
|
159
|
+
#
|
160
|
+
# The current position of the browser window.
|
161
|
+
#
|
162
|
+
# @return [(Integer, Integer)]
|
163
|
+
# The left, top coordinates of the browser window.
|
164
|
+
#
|
165
|
+
# @example
|
166
|
+
# browser.position # => [10, 20]
|
167
|
+
#
|
128
168
|
def position
|
129
169
|
@browser.command("Browser.getWindowBounds", windowId: window_id).fetch("bounds").values_at("left", "top")
|
130
170
|
end
|
131
171
|
|
172
|
+
#
|
173
|
+
# Sets the position of the browser window.
|
174
|
+
#
|
175
|
+
# @param [Hash{Symbol => Object}] options
|
176
|
+
#
|
177
|
+
# @option options [Integer] :left
|
178
|
+
# The number of pixels from the left-hand side of the screen.
|
179
|
+
#
|
180
|
+
# @option options [Integer] :top
|
181
|
+
# The number of pixels from the top of the screen.
|
182
|
+
#
|
183
|
+
# @example
|
184
|
+
# browser.position = { left: 10, top: 20 }
|
185
|
+
#
|
132
186
|
def position=(options)
|
133
187
|
@browser.command("Browser.setWindowBounds",
|
134
188
|
windowId: window_id,
|
135
189
|
bounds: { left: options[:left], top: options[:top] })
|
136
190
|
end
|
137
191
|
|
192
|
+
#
|
193
|
+
# Reloads the current page.
|
194
|
+
#
|
195
|
+
# @example
|
196
|
+
# browser.go_to("https://github.com/")
|
197
|
+
# browser.refresh
|
198
|
+
#
|
138
199
|
def refresh
|
139
200
|
command("Page.reload", wait: timeout, slowmoable: true)
|
140
201
|
end
|
141
202
|
alias reload refresh
|
142
203
|
|
204
|
+
#
|
205
|
+
# Stop all navigations and loading pending resources on the page.
|
206
|
+
#
|
207
|
+
# @example
|
208
|
+
# browser.go_to("https://github.com/")
|
209
|
+
# browser.stop
|
210
|
+
#
|
143
211
|
def stop
|
144
212
|
command("Page.stopLoading", slowmoable: true)
|
145
213
|
end
|
146
214
|
|
215
|
+
#
|
216
|
+
# Navigates to the previous URL in the browser's history.
|
217
|
+
#
|
218
|
+
# @example
|
219
|
+
# browser.go_to("https://github.com/")
|
220
|
+
# browser.at_xpath("//a").click
|
221
|
+
# browser.back
|
222
|
+
#
|
147
223
|
def back
|
148
224
|
history_navigate(delta: -1)
|
149
225
|
end
|
150
226
|
|
227
|
+
#
|
228
|
+
# Navigates to the next URL in the browser's history.
|
229
|
+
#
|
230
|
+
# @example
|
231
|
+
# browser.go_to("https://github.com/")
|
232
|
+
# browser.at_xpath("//a").click
|
233
|
+
# browser.back
|
234
|
+
# browser.forward
|
235
|
+
#
|
151
236
|
def forward
|
152
237
|
history_navigate(delta: 1)
|
153
238
|
end
|
@@ -158,6 +243,20 @@ module Ferrum
|
|
158
243
|
@event.set
|
159
244
|
end
|
160
245
|
|
246
|
+
#
|
247
|
+
# Enables/disables CSP bypass.
|
248
|
+
#
|
249
|
+
# @param [Boolean] enabled
|
250
|
+
#
|
251
|
+
# @return [Boolean]
|
252
|
+
#
|
253
|
+
# @example
|
254
|
+
# browser.bypass_csp # => true
|
255
|
+
# browser.go_to("https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md")
|
256
|
+
# browser.refresh
|
257
|
+
# browser.add_script_tag(content: "window.__injected = 42")
|
258
|
+
# browser.evaluate("window.__injected") # => 42
|
259
|
+
#
|
161
260
|
def bypass_csp(enabled: true)
|
162
261
|
command("Page.setBypassCSP", enabled: enabled)
|
163
262
|
enabled
|
@@ -173,16 +272,16 @@ module Ferrum
|
|
173
272
|
|
174
273
|
def command(method, wait: 0, slowmoable: false, **params)
|
175
274
|
iteration = @event.reset if wait.positive?
|
176
|
-
sleep(@browser.slowmo) if slowmoable && @browser.slowmo.positive?
|
275
|
+
sleep(@browser.options.slowmo) if slowmoable && @browser.options.slowmo.positive?
|
177
276
|
result = @client.command(method, params)
|
178
277
|
|
179
278
|
if wait.positive?
|
180
|
-
@event.wait(wait)
|
181
279
|
# Wait a bit after command and check if iteration has
|
182
280
|
# changed which means there was some network event for
|
183
281
|
# the main frame and it started to load new content.
|
282
|
+
@event.wait(wait)
|
184
283
|
if iteration != @event.iteration
|
185
|
-
set = @event.wait(
|
284
|
+
set = @event.wait(timeout)
|
186
285
|
raise TimeoutError unless set
|
187
286
|
end
|
188
287
|
end
|
@@ -218,19 +317,27 @@ module Ferrum
|
|
218
317
|
@client.subscribed?(event)
|
219
318
|
end
|
220
319
|
|
320
|
+
def use_proxy?
|
321
|
+
@proxy_host && @proxy_port
|
322
|
+
end
|
323
|
+
|
324
|
+
def use_authorized_proxy?
|
325
|
+
use_proxy? && @proxy_user && @proxy_password
|
326
|
+
end
|
327
|
+
|
221
328
|
private
|
222
329
|
|
223
330
|
def subscribe
|
224
331
|
frames_subscribe
|
225
332
|
network.subscribe
|
226
333
|
|
227
|
-
if @browser.logger
|
334
|
+
if @browser.options.logger
|
228
335
|
on("Runtime.consoleAPICalled") do |params|
|
229
|
-
params["args"].each { |r| @browser.logger.puts(r["value"]) }
|
336
|
+
params["args"].each { |r| @browser.options.logger.puts(r["value"]) }
|
230
337
|
end
|
231
338
|
end
|
232
339
|
|
233
|
-
if @browser.js_errors
|
340
|
+
if @browser.options.js_errors
|
234
341
|
on("Runtime.exceptionThrown") do |params|
|
235
342
|
# FIXME: https://jvns.ca/blog/2015/11/27/why-rubys-timeout-is-dangerous-and-thread-dot-raise-is-terrifying/
|
236
343
|
Thread.main.raise JavaScriptError.new(
|
@@ -257,21 +364,22 @@ module Ferrum
|
|
257
364
|
command("Log.enable")
|
258
365
|
command("Network.enable")
|
259
366
|
|
260
|
-
if
|
261
|
-
|
262
|
-
|
367
|
+
if use_authorized_proxy?
|
368
|
+
network.authorize(user: @proxy_user,
|
369
|
+
password: @proxy_password,
|
370
|
+
type: :proxy) do |request, _index, _total|
|
263
371
|
request.continue
|
264
372
|
end
|
265
373
|
end
|
266
374
|
|
267
|
-
if @browser.options
|
268
|
-
unless Pathname.new(@browser.options
|
375
|
+
if @browser.options.save_path
|
376
|
+
unless Pathname.new(@browser.options.save_path).absolute?
|
269
377
|
raise Error, "supply absolute path for `:save_path` option"
|
270
378
|
end
|
271
379
|
|
272
380
|
@browser.command("Browser.setDownloadBehavior",
|
273
381
|
browserContextId: context.id,
|
274
|
-
downloadPath: browser.options
|
382
|
+
downloadPath: @browser.options.save_path,
|
275
383
|
behavior: "allow", eventsEnabled: true)
|
276
384
|
end
|
277
385
|
|
@@ -303,7 +411,7 @@ module Ferrum
|
|
303
411
|
# We also evaluate script just in case because
|
304
412
|
# `Page.addScriptToEvaluateOnNewDocument` doesn't work in popups.
|
305
413
|
command("Runtime.evaluate", expression: extension,
|
306
|
-
|
414
|
+
executionContextId: execution_id!,
|
307
415
|
returnByValue: true)
|
308
416
|
end
|
309
417
|
end
|
@@ -336,5 +444,16 @@ module Ferrum
|
|
336
444
|
def document_node_id
|
337
445
|
command("DOM.getDocument", depth: 0).dig("root", "nodeId")
|
338
446
|
end
|
447
|
+
|
448
|
+
def ws_url
|
449
|
+
"ws://#{@browser.process.host}:#{@browser.process.port}/devtools/page/#{@target_id}"
|
450
|
+
end
|
451
|
+
|
452
|
+
def proxy=(options)
|
453
|
+
@proxy_host = options&.[](:host) || @browser.options.proxy&.[](:host)
|
454
|
+
@proxy_port = options&.[](:port) || @browser.options.proxy&.[](:port)
|
455
|
+
@proxy_user = options&.[](:user) || @browser.options.proxy&.[](:user)
|
456
|
+
@proxy_password = options&.[](:password) || @browser.options.proxy&.[](:password)
|
457
|
+
end
|
339
458
|
end
|
340
459
|
end
|