ferrum 0.9 → 0.10
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 +110 -74
- data/lib/ferrum.rb +17 -8
- data/lib/ferrum/browser.rb +6 -5
- data/lib/ferrum/browser/client.rb +4 -0
- data/lib/ferrum/browser/command.rb +1 -1
- data/lib/ferrum/browser/process.rb +5 -3
- data/lib/ferrum/browser/subscriber.rb +4 -0
- data/lib/ferrum/context.rb +3 -3
- data/lib/ferrum/frame.rb +1 -0
- data/lib/ferrum/frame/dom.rb +30 -30
- data/lib/ferrum/frame/runtime.rb +54 -71
- data/lib/ferrum/network.rb +23 -6
- data/lib/ferrum/network/error.rb +8 -15
- data/lib/ferrum/node.rb +11 -0
- data/lib/ferrum/page.rb +23 -11
- data/lib/ferrum/page/frames.rb +5 -2
- data/lib/ferrum/page/screenshot.rb +62 -10
- data/lib/ferrum/rbga.rb +38 -0
- data/lib/ferrum/version.rb +1 -1
- metadata +6 -5
data/lib/ferrum/network/error.rb
CHANGED
@@ -3,24 +3,17 @@
|
|
3
3
|
module Ferrum
|
4
4
|
class Network
|
5
5
|
class Error
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def id
|
11
|
-
@data["networkRequestId"]
|
12
|
-
end
|
13
|
-
|
14
|
-
def url
|
15
|
-
@data["url"]
|
16
|
-
end
|
6
|
+
attr_writer :canceled
|
7
|
+
attr_reader :time, :timestamp
|
8
|
+
attr_accessor :id, :url, :type, :error_text, :monotonic_time, :description
|
17
9
|
|
18
|
-
def
|
19
|
-
@
|
10
|
+
def canceled?
|
11
|
+
@canceled
|
20
12
|
end
|
21
13
|
|
22
|
-
def
|
23
|
-
@
|
14
|
+
def timestamp=(value)
|
15
|
+
@timestamp = value
|
16
|
+
@time = Time.strptime((value / 1000).to_s, "%s")
|
24
17
|
end
|
25
18
|
end
|
26
19
|
end
|
data/lib/ferrum/node.rb
CHANGED
@@ -141,10 +141,21 @@ module Ferrum
|
|
141
141
|
end.map { |q| to_points(q) }.first
|
142
142
|
|
143
143
|
get_position(points, x, y, position)
|
144
|
+
rescue Ferrum::BrowserError => e
|
145
|
+
return raise unless e.message&.include?("Could not compute content quads")
|
146
|
+
|
147
|
+
find_position_via_js
|
144
148
|
end
|
145
149
|
|
146
150
|
private
|
147
151
|
|
152
|
+
def find_position_via_js
|
153
|
+
[
|
154
|
+
evaluate("this.getBoundingClientRect().left + window.pageXOffset + (this.offsetWidth / 2)"), # x
|
155
|
+
evaluate("this.getBoundingClientRect().top + window.pageYOffset + (this.offsetHeight / 2)") # y
|
156
|
+
]
|
157
|
+
end
|
158
|
+
|
148
159
|
def get_content_quads
|
149
160
|
quads = page.command("DOM.getContentQuads", nodeId: node_id)["quads"]
|
150
161
|
raise "Node is either not visible or not an HTMLElement" if quads.size == 0
|
data/lib/ferrum/page.rb
CHANGED
@@ -32,7 +32,7 @@ module Ferrum
|
|
32
32
|
extend Forwardable
|
33
33
|
delegate %i[at_css at_xpath css xpath
|
34
34
|
current_url current_title url title body doctype set_content
|
35
|
-
execution_id evaluate evaluate_on evaluate_async execute
|
35
|
+
execution_id evaluate evaluate_on evaluate_async execute evaluate_func
|
36
36
|
add_script_tag add_style_tag] => :main_frame
|
37
37
|
|
38
38
|
include Frames, Screenshot
|
@@ -65,7 +65,7 @@ module Ferrum
|
|
65
65
|
@browser.timeout
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
68
|
+
def go_to(url = nil)
|
69
69
|
options = { url: combine_url!(url) }
|
70
70
|
options.merge!(referrer: referrer) if referrer
|
71
71
|
response = command("Page.navigate", wait: GOTO_WAIT, **options)
|
@@ -78,9 +78,12 @@ module Ferrum
|
|
78
78
|
end
|
79
79
|
response["frameId"]
|
80
80
|
rescue TimeoutError
|
81
|
-
|
82
|
-
|
81
|
+
if @browser.pending_connection_errors
|
82
|
+
pendings = network.traffic.select(&:pending?).map { |e| e.request.url }
|
83
|
+
raise PendingConnectionsError.new(options[:url], pendings) unless pendings.empty?
|
84
|
+
end
|
83
85
|
end
|
86
|
+
alias goto go_to
|
84
87
|
|
85
88
|
def close
|
86
89
|
@headers.clear
|
@@ -89,15 +92,12 @@ module Ferrum
|
|
89
92
|
end
|
90
93
|
|
91
94
|
def resize(width: nil, height: nil, fullscreen: false)
|
92
|
-
result = @browser.command("Browser.getWindowForTarget", targetId: @target_id)
|
93
|
-
@window_id, @bounds = result.values_at("windowId", "bounds")
|
94
|
-
|
95
95
|
if fullscreen
|
96
96
|
width, height = document_size
|
97
|
-
|
97
|
+
set_window_bounds(windowState: "fullscreen")
|
98
98
|
else
|
99
|
-
|
100
|
-
|
99
|
+
set_window_bounds(windowState: "normal")
|
100
|
+
set_window_bounds(width: width, height: height)
|
101
101
|
end
|
102
102
|
|
103
103
|
command("Emulation.setDeviceMetricsOverride", slowmoable: true,
|
@@ -137,6 +137,14 @@ module Ferrum
|
|
137
137
|
enabled
|
138
138
|
end
|
139
139
|
|
140
|
+
def window_id
|
141
|
+
@browser.command("Browser.getWindowForTarget", targetId: @target_id)["windowId"]
|
142
|
+
end
|
143
|
+
|
144
|
+
def set_window_bounds(bounds = {})
|
145
|
+
@browser.command("Browser.setWindowBounds", windowId: window_id, bounds: bounds)
|
146
|
+
end
|
147
|
+
|
140
148
|
def command(method, wait: 0, slowmoable: false, **params)
|
141
149
|
iteration = @event.reset if wait > 0
|
142
150
|
sleep(@browser.slowmo) if slowmoable && @browser.slowmo > 0
|
@@ -179,6 +187,10 @@ module Ferrum
|
|
179
187
|
end
|
180
188
|
end
|
181
189
|
|
190
|
+
def subscribed?(event)
|
191
|
+
@client.subscribed?(event)
|
192
|
+
end
|
193
|
+
|
182
194
|
private
|
183
195
|
|
184
196
|
def subscribe
|
@@ -261,7 +273,7 @@ module Ferrum
|
|
261
273
|
nil_or_relative = url.nil? || url.relative?
|
262
274
|
|
263
275
|
if nil_or_relative && !@browser.base_url
|
264
|
-
raise "Set :base_url browser's option or use absolute url in `
|
276
|
+
raise "Set :base_url browser's option or use absolute url in `go_to`, you passed: #{url_or_path}"
|
265
277
|
end
|
266
278
|
|
267
279
|
(nil_or_relative ? @browser.base_url.join(url.to_s) : url).to_s
|
data/lib/ferrum/page/frames.rb
CHANGED
@@ -75,8 +75,11 @@ module Ferrum
|
|
75
75
|
frame_id = params.dig("context", "auxData", "frameId")
|
76
76
|
|
77
77
|
unless @main_frame.id
|
78
|
-
|
79
|
-
|
78
|
+
root_frame = command("Page.getFrameTree").dig("frameTree", "frame", "id")
|
79
|
+
if frame_id == root_frame
|
80
|
+
@main_frame.id = frame_id
|
81
|
+
@frames[frame_id] = @main_frame
|
82
|
+
end
|
80
83
|
end
|
81
84
|
|
82
85
|
frame = @frames[frame_id] || Frame.new(frame_id, self)
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "ferrum/rbga"
|
4
|
+
|
3
5
|
module Ferrum
|
4
6
|
class Page
|
5
7
|
module Screenshot
|
@@ -24,19 +26,33 @@ module Ferrum
|
|
24
26
|
A6: { width: 4.13, height: 5.83 },
|
25
27
|
}.freeze
|
26
28
|
|
29
|
+
STREAM_CHUNK = 128 * 1024
|
30
|
+
|
27
31
|
def screenshot(**opts)
|
28
32
|
path, encoding = common_options(**opts)
|
29
33
|
options = screenshot_options(path, **opts)
|
30
|
-
data = capture_screenshot(options, opts[:full])
|
34
|
+
data = capture_screenshot(options, opts[:full], opts[:background_color])
|
31
35
|
return data if encoding == :base64
|
32
|
-
|
36
|
+
|
37
|
+
bin = Base64.decode64(data)
|
38
|
+
save_file(path, bin)
|
33
39
|
end
|
34
40
|
|
35
41
|
def pdf(**opts)
|
36
42
|
path, encoding = common_options(**opts)
|
37
|
-
options = pdf_options(**opts)
|
38
|
-
|
39
|
-
|
43
|
+
options = pdf_options(**opts).merge(transferMode: "ReturnAsStream")
|
44
|
+
handle = command("Page.printToPDF", **options).fetch("stream")
|
45
|
+
|
46
|
+
if path
|
47
|
+
stream_to_file(handle, path: path)
|
48
|
+
else
|
49
|
+
stream_to_memory(handle, encoding: encoding)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def mhtml(path: nil)
|
54
|
+
data = command("Page.captureSnapshot", format: :mhtml).fetch("data")
|
55
|
+
return data if path.nil?
|
40
56
|
save_file(path, data)
|
41
57
|
end
|
42
58
|
|
@@ -56,9 +72,31 @@ module Ferrum
|
|
56
72
|
private
|
57
73
|
|
58
74
|
def save_file(path, data)
|
59
|
-
|
60
|
-
|
61
|
-
|
75
|
+
return data unless path
|
76
|
+
File.open(path.to_s, "wb") { |f| f.write(data) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def stream_to_file(handle, path:)
|
80
|
+
File.open(path, "wb") { |f| stream_to(handle, f) }
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
def stream_to_memory(handle, encoding:)
|
85
|
+
data = String.new("") # Mutable string has << and compatible to File
|
86
|
+
stream_to(handle, data)
|
87
|
+
encoding == :base64 ? Base64.encode64(data) : data
|
88
|
+
end
|
89
|
+
|
90
|
+
def stream_to(handle, output)
|
91
|
+
loop do
|
92
|
+
result = command("IO.read", handle: handle, size: STREAM_CHUNK)
|
93
|
+
|
94
|
+
data_chunk = result["data"]
|
95
|
+
data_chunk = Base64.decode64(data_chunk) if result["base64Encoded"]
|
96
|
+
output << data_chunk
|
97
|
+
|
98
|
+
break if result["eof"]
|
99
|
+
end
|
62
100
|
end
|
63
101
|
|
64
102
|
def common_options(encoding: :base64, path: nil, **_)
|
@@ -134,9 +172,11 @@ module Ferrum
|
|
134
172
|
option.to_s.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.to_sym
|
135
173
|
end
|
136
174
|
|
137
|
-
def capture_screenshot(options, full)
|
175
|
+
def capture_screenshot(options, full, background_color)
|
138
176
|
maybe_resize_fullscreen(full) do
|
139
|
-
|
177
|
+
with_background_color(background_color) do
|
178
|
+
command("Page.captureScreenshot", **options)
|
179
|
+
end
|
140
180
|
end.fetch("data")
|
141
181
|
end
|
142
182
|
|
@@ -150,6 +190,18 @@ module Ferrum
|
|
150
190
|
ensure
|
151
191
|
resize(width: width, height: height) if full
|
152
192
|
end
|
193
|
+
|
194
|
+
def with_background_color(color)
|
195
|
+
if color
|
196
|
+
raise ArgumentError, "Accept Ferrum::RGBA class only" unless color.is_a?(RGBA)
|
197
|
+
|
198
|
+
command("Emulation.setDefaultBackgroundColorOverride", color: color.to_h)
|
199
|
+
end
|
200
|
+
|
201
|
+
yield
|
202
|
+
ensure
|
203
|
+
command("Emulation.setDefaultBackgroundColorOverride") if color
|
204
|
+
end
|
153
205
|
end
|
154
206
|
end
|
155
207
|
end
|
data/lib/ferrum/rbga.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Ferrum
|
2
|
+
class RGBA
|
3
|
+
def initialize(red, green, blue, alpha)
|
4
|
+
self.red = red
|
5
|
+
self.green = green
|
6
|
+
self.blue = blue
|
7
|
+
self.alpha = alpha
|
8
|
+
|
9
|
+
validate
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h
|
13
|
+
{ r: red, g: green, b: blue, a: alpha }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_accessor :red, :green, :blue, :alpha
|
19
|
+
|
20
|
+
def validate
|
21
|
+
[red, green, blue].each(&method(:validate_color))
|
22
|
+
validate_alpha
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate_color(value)
|
26
|
+
return if value && value.is_a?(Integer) && Range.new(0, 255).include?(value)
|
27
|
+
|
28
|
+
raise ArgumentError, "Wrong value of #{value} should be Integer from 0 to 255"
|
29
|
+
end
|
30
|
+
|
31
|
+
def validate_alpha
|
32
|
+
return if alpha && alpha.is_a?(Float) && Range.new(0.0, 1.0).include?(alpha)
|
33
|
+
|
34
|
+
raise ArgumentError,
|
35
|
+
"Wrong alpha value #{alpha} should be Float between 0.0 (fully transparent) and 1.0 (fully opaque)"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/ferrum/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ferrum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.10'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Vorotilin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-driver
|
@@ -78,14 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
81
|
+
version: '13.0'
|
82
82
|
type: :development
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: '
|
88
|
+
version: '13.0'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: rspec
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -212,6 +212,7 @@ files:
|
|
212
212
|
- lib/ferrum/page.rb
|
213
213
|
- lib/ferrum/page/frames.rb
|
214
214
|
- lib/ferrum/page/screenshot.rb
|
215
|
+
- lib/ferrum/rbga.rb
|
215
216
|
- lib/ferrum/target.rb
|
216
217
|
- lib/ferrum/version.rb
|
217
218
|
homepage: https://github.com/route/ferrum
|
@@ -233,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
233
234
|
- !ruby/object:Gem::Version
|
234
235
|
version: '0'
|
235
236
|
requirements: []
|
236
|
-
rubygems_version: 3.1.
|
237
|
+
rubygems_version: 3.1.4
|
237
238
|
signing_key:
|
238
239
|
specification_version: 4
|
239
240
|
summary: Ruby headless Chrome driver
|