ferrum 0.7 → 0.10.2
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 +325 -86
- data/lib/ferrum.rb +35 -7
- data/lib/ferrum/browser.rb +14 -10
- data/lib/ferrum/browser/client.rb +8 -6
- data/lib/ferrum/browser/command.rb +26 -25
- data/lib/ferrum/browser/options/base.rb +46 -0
- data/lib/ferrum/browser/options/chrome.rb +73 -0
- data/lib/ferrum/browser/options/firefox.rb +34 -0
- data/lib/ferrum/browser/process.rb +24 -12
- data/lib/ferrum/browser/subscriber.rb +5 -1
- data/lib/ferrum/browser/web_socket.rb +23 -4
- data/lib/ferrum/browser/xvfb.rb +37 -0
- data/lib/ferrum/context.rb +3 -3
- data/lib/ferrum/cookies.rb +7 -0
- data/lib/ferrum/dialog.rb +2 -2
- data/lib/ferrum/frame.rb +20 -3
- data/lib/ferrum/frame/dom.rb +34 -37
- data/lib/ferrum/frame/runtime.rb +90 -84
- data/lib/ferrum/keyboard.rb +3 -3
- data/lib/ferrum/mouse.rb +14 -3
- data/lib/ferrum/network.rb +23 -6
- data/lib/ferrum/network/error.rb +8 -15
- data/lib/ferrum/network/intercepted_request.rb +1 -1
- data/lib/ferrum/node.rb +70 -26
- data/lib/ferrum/page.rb +45 -17
- data/lib/ferrum/page/frames.rb +17 -3
- data/lib/ferrum/page/screenshot.rb +64 -12
- data/lib/ferrum/rbga.rb +38 -0
- data/lib/ferrum/version.rb +1 -1
- metadata +12 -9
- data/lib/ferrum/browser/chrome.rb +0 -76
- data/lib/ferrum/browser/firefox.rb +0 -34
data/lib/ferrum/page/frames.rb
CHANGED
@@ -71,23 +71,37 @@ module Ferrum
|
|
71
71
|
end
|
72
72
|
|
73
73
|
on("Runtime.executionContextCreated") do |params|
|
74
|
+
setting_up_main_frame = false
|
74
75
|
context_id = params.dig("context", "id")
|
75
76
|
frame_id = params.dig("context", "auxData", "frameId")
|
77
|
+
|
78
|
+
unless @main_frame.id
|
79
|
+
root_frame = command("Page.getFrameTree").dig("frameTree", "frame", "id")
|
80
|
+
if frame_id == root_frame
|
81
|
+
setting_up_main_frame = true
|
82
|
+
@main_frame.id = frame_id
|
83
|
+
@frames[frame_id] = @main_frame
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
76
87
|
frame = @frames[frame_id] || Frame.new(frame_id, self)
|
77
|
-
frame.
|
88
|
+
frame.set_execution_id(context_id)
|
89
|
+
|
90
|
+
# Set event because `execution_id` might raise NoExecutionContextError
|
91
|
+
@event.set if setting_up_main_frame
|
78
92
|
|
79
|
-
@main_frame ||= frame
|
80
93
|
@frames[frame_id] ||= frame
|
81
94
|
end
|
82
95
|
|
83
96
|
on("Runtime.executionContextDestroyed") do |params|
|
84
97
|
execution_id = params["executionContextId"]
|
85
98
|
frame = frames.find { |f| f.execution_id?(execution_id) }
|
86
|
-
frame.
|
99
|
+
frame.reset_execution_id
|
87
100
|
end
|
88
101
|
|
89
102
|
on("Runtime.executionContextsCleared") do
|
90
103
|
@frames.delete_if { |_, f| !f.main? }
|
104
|
+
@main_frame.reset_execution_id
|
91
105
|
end
|
92
106
|
end
|
93
107
|
|
@@ -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
|
|
@@ -48,17 +64,39 @@ module Ferrum
|
|
48
64
|
|
49
65
|
def document_size
|
50
66
|
evaluate <<~JS
|
51
|
-
[document.documentElement.
|
52
|
-
document.documentElement.
|
67
|
+
[document.documentElement.scrollWidth,
|
68
|
+
document.documentElement.scrollHeight]
|
53
69
|
JS
|
54
70
|
end
|
55
71
|
|
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:
|
4
|
+
version: 0.10.2
|
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-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: websocket-driver
|
@@ -64,28 +64,28 @@ dependencies:
|
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '2.
|
67
|
+
version: '2.5'
|
68
68
|
type: :runtime
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: '2.
|
74
|
+
version: '2.5'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
76
|
name: rake
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
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
|
@@ -181,13 +181,15 @@ files:
|
|
181
181
|
- README.md
|
182
182
|
- lib/ferrum.rb
|
183
183
|
- lib/ferrum/browser.rb
|
184
|
-
- lib/ferrum/browser/chrome.rb
|
185
184
|
- lib/ferrum/browser/client.rb
|
186
185
|
- lib/ferrum/browser/command.rb
|
187
|
-
- lib/ferrum/browser/
|
186
|
+
- lib/ferrum/browser/options/base.rb
|
187
|
+
- lib/ferrum/browser/options/chrome.rb
|
188
|
+
- lib/ferrum/browser/options/firefox.rb
|
188
189
|
- lib/ferrum/browser/process.rb
|
189
190
|
- lib/ferrum/browser/subscriber.rb
|
190
191
|
- lib/ferrum/browser/web_socket.rb
|
192
|
+
- lib/ferrum/browser/xvfb.rb
|
191
193
|
- lib/ferrum/context.rb
|
192
194
|
- lib/ferrum/contexts.rb
|
193
195
|
- lib/ferrum/cookies.rb
|
@@ -210,6 +212,7 @@ files:
|
|
210
212
|
- lib/ferrum/page.rb
|
211
213
|
- lib/ferrum/page/frames.rb
|
212
214
|
- lib/ferrum/page/screenshot.rb
|
215
|
+
- lib/ferrum/rbga.rb
|
213
216
|
- lib/ferrum/target.rb
|
214
217
|
- lib/ferrum/version.rb
|
215
218
|
homepage: https://github.com/route/ferrum
|
@@ -231,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
234
|
- !ruby/object:Gem::Version
|
232
235
|
version: '0'
|
233
236
|
requirements: []
|
234
|
-
rubygems_version: 3.
|
237
|
+
rubygems_version: 3.1.4
|
235
238
|
signing_key:
|
236
239
|
specification_version: 4
|
237
240
|
summary: Ruby headless Chrome driver
|
@@ -1,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Ferrum
|
4
|
-
class Browser
|
5
|
-
class Chrome < Command
|
6
|
-
DEFAULT_OPTIONS = {
|
7
|
-
"headless" => nil,
|
8
|
-
"disable-gpu" => nil,
|
9
|
-
"hide-scrollbars" => nil,
|
10
|
-
"mute-audio" => nil,
|
11
|
-
"enable-automation" => nil,
|
12
|
-
"disable-web-security" => nil,
|
13
|
-
"disable-session-crashed-bubble" => nil,
|
14
|
-
"disable-breakpad" => nil,
|
15
|
-
"disable-sync" => nil,
|
16
|
-
"no-first-run" => nil,
|
17
|
-
"use-mock-keychain" => nil,
|
18
|
-
"keep-alive-for-test" => nil,
|
19
|
-
"disable-popup-blocking" => nil,
|
20
|
-
"disable-extensions" => nil,
|
21
|
-
"disable-hang-monitor" => nil,
|
22
|
-
"disable-features" => "site-per-process,TranslateUI",
|
23
|
-
"disable-translate" => nil,
|
24
|
-
"disable-background-networking" => nil,
|
25
|
-
"enable-features" => "NetworkService,NetworkServiceInProcess",
|
26
|
-
"disable-background-timer-throttling" => nil,
|
27
|
-
"disable-backgrounding-occluded-windows" => nil,
|
28
|
-
"disable-client-side-phishing-detection" => nil,
|
29
|
-
"disable-default-apps" => nil,
|
30
|
-
"disable-dev-shm-usage" => nil,
|
31
|
-
"disable-ipc-flooding-protection" => nil,
|
32
|
-
"disable-prompt-on-repost" => nil,
|
33
|
-
"disable-renderer-backgrounding" => nil,
|
34
|
-
"force-color-profile" => "srgb",
|
35
|
-
"metrics-recording-only" => nil,
|
36
|
-
"safebrowsing-disable-auto-update" => nil,
|
37
|
-
"password-store" => "basic",
|
38
|
-
# Note: --no-sandbox is not needed if you properly setup a user in the container.
|
39
|
-
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
40
|
-
# "no-sandbox" => nil,
|
41
|
-
}.freeze
|
42
|
-
|
43
|
-
MAC_BIN_PATH = [
|
44
|
-
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
45
|
-
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
46
|
-
].freeze
|
47
|
-
LINUX_BIN_PATH = %w[chromium google-chrome-unstable google-chrome-beta
|
48
|
-
google-chrome chrome chromium-browser
|
49
|
-
google-chrome-stable].freeze
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def combine_flags
|
54
|
-
# Doesn't work on MacOS, so we need to set it by CDP as well
|
55
|
-
@flags.merge!("window-size" => options[:window_size].join(","))
|
56
|
-
|
57
|
-
port = options.fetch(:port, BROWSER_PORT)
|
58
|
-
@flags.merge!("remote-debugging-port" => port)
|
59
|
-
|
60
|
-
host = options.fetch(:host, BROWSER_HOST)
|
61
|
-
@flags.merge!("remote-debugging-address" => host)
|
62
|
-
|
63
|
-
@flags.merge!("user-data-dir" => @user_data_dir)
|
64
|
-
|
65
|
-
@flags = DEFAULT_OPTIONS.merge(@flags)
|
66
|
-
|
67
|
-
unless options.fetch(:headless, true)
|
68
|
-
@flags.delete("headless")
|
69
|
-
@flags.delete("disable-gpu")
|
70
|
-
end
|
71
|
-
|
72
|
-
@flags.merge!(options.fetch(:browser_options, {}))
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Ferrum
|
4
|
-
class Browser
|
5
|
-
class Firefox < Command
|
6
|
-
DEFAULT_OPTIONS = {
|
7
|
-
"headless" => nil,
|
8
|
-
}.freeze
|
9
|
-
|
10
|
-
MAC_BIN_PATH = [
|
11
|
-
"/Applications/Firefox.app/Contents/MacOS/firefox-bin"
|
12
|
-
].freeze
|
13
|
-
LINUX_BIN_PATH = %w[firefox].freeze
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def combine_flags
|
18
|
-
port = options.fetch(:port, BROWSER_PORT)
|
19
|
-
host = options.fetch(:host, BROWSER_HOST)
|
20
|
-
@flags.merge!("remote-debugger" => "#{host}:#{port}")
|
21
|
-
|
22
|
-
@flags.merge!("profile" => @user_data_dir)
|
23
|
-
|
24
|
-
@flags = DEFAULT_OPTIONS.merge(@flags)
|
25
|
-
|
26
|
-
unless options.fetch(:headless, true)
|
27
|
-
@flags.delete("headless")
|
28
|
-
end
|
29
|
-
|
30
|
-
@flags.merge!(options.fetch(:browser_options, {}))
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|