ferrum 0.6 → 0.9
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 +296 -38
- data/lib/ferrum.rb +29 -4
- data/lib/ferrum/browser.rb +14 -8
- data/lib/ferrum/browser/client.rb +19 -10
- data/lib/ferrum/browser/command.rb +57 -0
- 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 +53 -107
- 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/cookies.rb +7 -0
- data/lib/ferrum/dialog.rb +2 -2
- data/lib/ferrum/frame.rb +23 -5
- data/lib/ferrum/frame/dom.rb +38 -41
- data/lib/ferrum/frame/runtime.rb +54 -33
- data/lib/ferrum/headers.rb +1 -1
- data/lib/ferrum/keyboard.rb +3 -3
- data/lib/ferrum/mouse.rb +14 -3
- data/lib/ferrum/network.rb +60 -16
- data/lib/ferrum/network/exchange.rb +24 -21
- data/lib/ferrum/network/intercepted_request.rb +12 -3
- data/lib/ferrum/network/response.rb +4 -0
- data/lib/ferrum/node.rb +59 -26
- data/lib/ferrum/page.rb +45 -17
- data/lib/ferrum/page/frames.rb +11 -19
- data/lib/ferrum/page/screenshot.rb +3 -3
- data/lib/ferrum/target.rb +10 -2
- data/lib/ferrum/version.rb +1 -1
- metadata +10 -5
data/lib/ferrum/browser.rb
CHANGED
@@ -4,6 +4,7 @@ require "base64"
|
|
4
4
|
require "forwardable"
|
5
5
|
require "ferrum/page"
|
6
6
|
require "ferrum/contexts"
|
7
|
+
require "ferrum/browser/xvfb"
|
7
8
|
require "ferrum/browser/process"
|
8
9
|
require "ferrum/browser/client"
|
9
10
|
|
@@ -16,18 +17,20 @@ module Ferrum
|
|
16
17
|
extend Forwardable
|
17
18
|
delegate %i[default_context] => :contexts
|
18
19
|
delegate %i[targets create_target create_page page pages windows] => :default_context
|
19
|
-
delegate %i[goto back forward refresh
|
20
|
-
at_css at_xpath css xpath current_url
|
20
|
+
delegate %i[goto back forward refresh reload stop wait_for_reload
|
21
|
+
at_css at_xpath css xpath current_url current_title url title
|
22
|
+
body doctype set_content
|
21
23
|
headers cookies network
|
22
24
|
mouse keyboard
|
23
25
|
screenshot pdf viewport_size
|
24
26
|
frames frame_by main_frame
|
25
27
|
evaluate evaluate_on evaluate_async execute
|
26
|
-
add_script_tag add_style_tag
|
28
|
+
add_script_tag add_style_tag bypass_csp
|
27
29
|
on] => :page
|
30
|
+
delegate %i[default_user_agent] => :process
|
28
31
|
|
29
32
|
attr_reader :client, :process, :contexts, :logger, :js_errors,
|
30
|
-
:slowmo, :base_url, :options, :window_size
|
33
|
+
:slowmo, :base_url, :options, :window_size, :ws_max_receive_size
|
31
34
|
attr_writer :timeout
|
32
35
|
|
33
36
|
def initialize(options = nil)
|
@@ -38,9 +41,10 @@ module Ferrum
|
|
38
41
|
@original_window_size = @window_size
|
39
42
|
|
40
43
|
@options = Hash(options.merge(window_size: @window_size))
|
41
|
-
@logger, @timeout
|
44
|
+
@logger, @timeout, @ws_max_receive_size =
|
45
|
+
@options.values_at(:logger, :timeout, :ws_max_receive_size)
|
42
46
|
@js_errors = @options.fetch(:js_errors, false)
|
43
|
-
@slowmo = @options[:slowmo].
|
47
|
+
@slowmo = @options[:slowmo].to_f
|
44
48
|
|
45
49
|
if @options.key?(:base_url)
|
46
50
|
self.base_url = @options[:base_url]
|
@@ -67,7 +71,9 @@ module Ferrum
|
|
67
71
|
end
|
68
72
|
|
69
73
|
def extensions
|
70
|
-
@extensions ||= Array(@options[:extensions]).map
|
74
|
+
@extensions ||= Array(@options[:extensions]).map do |ext|
|
75
|
+
(ext.is_a?(Hash) && ext[:source]) || File.read(ext)
|
76
|
+
end
|
71
77
|
end
|
72
78
|
|
73
79
|
def timeout
|
@@ -111,7 +117,7 @@ module Ferrum
|
|
111
117
|
def start
|
112
118
|
Ferrum.started
|
113
119
|
@process = Process.start(@options)
|
114
|
-
@client = Client.new(self, @process.ws_url
|
120
|
+
@client = Client.new(self, @process.ws_url)
|
115
121
|
@contexts = Contexts.new(self)
|
116
122
|
end
|
117
123
|
end
|
@@ -7,20 +7,25 @@ require "ferrum/browser/web_socket"
|
|
7
7
|
module Ferrum
|
8
8
|
class Browser
|
9
9
|
class Client
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
INTERRUPTIONS = %w[Fetch.requestPaused Fetch.authRequired].freeze
|
11
|
+
|
12
|
+
def initialize(browser, ws_url, id_starts_with: 0)
|
13
13
|
@browser = browser
|
14
|
-
@
|
15
|
-
@
|
16
|
-
@
|
14
|
+
@command_id = id_starts_with
|
15
|
+
@pendings = Concurrent::Hash.new
|
16
|
+
@ws = WebSocket.new(ws_url, @browser.ws_max_receive_size, @browser.logger)
|
17
|
+
@subscriber, @interruptor = Subscriber.build(2)
|
17
18
|
|
18
19
|
@thread = Thread.new do
|
19
20
|
Thread.current.abort_on_exception = true
|
20
|
-
|
21
|
+
if Thread.current.respond_to?(:report_on_exception=)
|
22
|
+
Thread.current.report_on_exception = true
|
23
|
+
end
|
21
24
|
|
22
25
|
while message = @ws.messages.pop
|
23
|
-
if
|
26
|
+
if INTERRUPTIONS.include?(message["method"])
|
27
|
+
@interruptor.async.call(message)
|
28
|
+
elsif message.key?("method")
|
24
29
|
@subscriber.async.call(message)
|
25
30
|
else
|
26
31
|
@pendings[message["id"]]&.set(message)
|
@@ -33,7 +38,6 @@ module Ferrum
|
|
33
38
|
pending = Concurrent::IVar.new
|
34
39
|
message = build_message(method, params)
|
35
40
|
@pendings[message[:id]] = pending
|
36
|
-
sleep(@slowmo) if @slowmo
|
37
41
|
@ws.send_message(message)
|
38
42
|
data = pending.value!(@browser.timeout)
|
39
43
|
@pendings.delete(message[:id])
|
@@ -46,7 +50,12 @@ module Ferrum
|
|
46
50
|
end
|
47
51
|
|
48
52
|
def on(event, &block)
|
49
|
-
|
53
|
+
case event
|
54
|
+
when *INTERRUPTIONS
|
55
|
+
@interruptor.on(event, &block)
|
56
|
+
else
|
57
|
+
@subscriber.on(event, &block)
|
58
|
+
end
|
50
59
|
end
|
51
60
|
|
52
61
|
def close
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ferrum
|
4
|
+
class Browser
|
5
|
+
class Command
|
6
|
+
NOT_FOUND = "Could not find an executable for the browser. Try to make " \
|
7
|
+
"it available on the PATH or set environment varible for " \
|
8
|
+
"example BROWSER_PATH=\"/usr/bin/chrome\"".freeze
|
9
|
+
|
10
|
+
# Currently only these browsers support CDP:
|
11
|
+
# https://github.com/cyrus-and/chrome-remote-interface#implementations
|
12
|
+
def self.build(options, user_data_dir)
|
13
|
+
defaults = case options[:browser_name]
|
14
|
+
when :firefox
|
15
|
+
Options::Firefox.options
|
16
|
+
when :chrome, :opera, :edge, nil
|
17
|
+
Options::Chrome.options
|
18
|
+
else
|
19
|
+
raise NotImplementedError, "not supported browser"
|
20
|
+
end
|
21
|
+
|
22
|
+
new(defaults, options, user_data_dir)
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :defaults, :path, :options
|
26
|
+
|
27
|
+
def initialize(defaults, options, user_data_dir)
|
28
|
+
@flags = {}
|
29
|
+
@defaults = defaults
|
30
|
+
@options, @user_data_dir = options, user_data_dir
|
31
|
+
@path = options[:browser_path] || ENV["BROWSER_PATH"] || defaults.detect_path
|
32
|
+
raise Cliver::Dependency::NotFound.new(NOT_FOUND) unless @path
|
33
|
+
merge_options
|
34
|
+
end
|
35
|
+
|
36
|
+
def xvfb?
|
37
|
+
!!options[:xvfb]
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_a
|
41
|
+
[path] + @flags.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def merge_options
|
47
|
+
@flags = defaults.merge_required(@flags, options, @user_data_dir)
|
48
|
+
|
49
|
+
unless options[:ignore_default_browser_options]
|
50
|
+
@flags = defaults.merge_default(@flags, options)
|
51
|
+
end
|
52
|
+
|
53
|
+
@flags.merge!(options.fetch(:browser_options, {}))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "singleton"
|
4
|
+
|
5
|
+
module Ferrum
|
6
|
+
class Browser
|
7
|
+
module Options
|
8
|
+
class Base
|
9
|
+
BROWSER_HOST = "127.0.0.1"
|
10
|
+
BROWSER_PORT = "0"
|
11
|
+
|
12
|
+
include Singleton
|
13
|
+
|
14
|
+
def self.options
|
15
|
+
instance
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
self.class::DEFAULT_OPTIONS
|
20
|
+
end
|
21
|
+
|
22
|
+
def except(*keys)
|
23
|
+
to_h.reject { |n, _| keys.include?(n) }
|
24
|
+
end
|
25
|
+
|
26
|
+
def detect_path
|
27
|
+
if Ferrum.mac?
|
28
|
+
self.class::MAC_BIN_PATH.find { |n| File.exist?(n) }
|
29
|
+
else
|
30
|
+
self.class::LINUX_BIN_PATH.find do |name|
|
31
|
+
path = Cliver.detect(name) and break(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def merge_required(flags, options, user_data_dir)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def merge_default(flags, options)
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ferrum
|
4
|
+
class Browser
|
5
|
+
module Options
|
6
|
+
class Chrome < Base
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
"headless" => nil,
|
9
|
+
"disable-gpu" => nil,
|
10
|
+
"hide-scrollbars" => nil,
|
11
|
+
"mute-audio" => nil,
|
12
|
+
"enable-automation" => nil,
|
13
|
+
"disable-web-security" => nil,
|
14
|
+
"disable-session-crashed-bubble" => nil,
|
15
|
+
"disable-breakpad" => nil,
|
16
|
+
"disable-sync" => nil,
|
17
|
+
"no-first-run" => nil,
|
18
|
+
"use-mock-keychain" => nil,
|
19
|
+
"keep-alive-for-test" => nil,
|
20
|
+
"disable-popup-blocking" => nil,
|
21
|
+
"disable-extensions" => nil,
|
22
|
+
"disable-hang-monitor" => nil,
|
23
|
+
"disable-features" => "site-per-process,TranslateUI",
|
24
|
+
"disable-translate" => nil,
|
25
|
+
"disable-background-networking" => nil,
|
26
|
+
"enable-features" => "NetworkService,NetworkServiceInProcess",
|
27
|
+
"disable-background-timer-throttling" => nil,
|
28
|
+
"disable-backgrounding-occluded-windows" => nil,
|
29
|
+
"disable-client-side-phishing-detection" => nil,
|
30
|
+
"disable-default-apps" => nil,
|
31
|
+
"disable-dev-shm-usage" => nil,
|
32
|
+
"disable-ipc-flooding-protection" => nil,
|
33
|
+
"disable-prompt-on-repost" => nil,
|
34
|
+
"disable-renderer-backgrounding" => nil,
|
35
|
+
"force-color-profile" => "srgb",
|
36
|
+
"metrics-recording-only" => nil,
|
37
|
+
"safebrowsing-disable-auto-update" => nil,
|
38
|
+
"password-store" => "basic",
|
39
|
+
# Note: --no-sandbox is not needed if you properly setup a user in the container.
|
40
|
+
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
41
|
+
# "no-sandbox" => nil,
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
MAC_BIN_PATH = [
|
45
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
46
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
47
|
+
].freeze
|
48
|
+
LINUX_BIN_PATH = %w[chromium google-chrome-unstable google-chrome-beta
|
49
|
+
google-chrome chrome chromium-browser
|
50
|
+
google-chrome-stable].freeze
|
51
|
+
|
52
|
+
def merge_required(flags, options, user_data_dir)
|
53
|
+
port = options.fetch(:port, BROWSER_PORT)
|
54
|
+
host = options.fetch(:host, BROWSER_HOST)
|
55
|
+
flags.merge("remote-debugging-port" => port,
|
56
|
+
"remote-debugging-address" => host,
|
57
|
+
# Doesn't work on MacOS, so we need to set it by CDP
|
58
|
+
"window-size" => options[:window_size].join(","),
|
59
|
+
"user-data-dir" => user_data_dir)
|
60
|
+
end
|
61
|
+
|
62
|
+
def merge_default(flags, options)
|
63
|
+
unless options.fetch(:headless, true)
|
64
|
+
defaults = except("headless", "disable-gpu")
|
65
|
+
end
|
66
|
+
|
67
|
+
defaults ||= DEFAULT_OPTIONS
|
68
|
+
defaults.merge(flags)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ferrum
|
4
|
+
class Browser
|
5
|
+
module Options
|
6
|
+
class Firefox < Base
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
"headless" => nil,
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
MAC_BIN_PATH = [
|
12
|
+
"/Applications/Firefox.app/Contents/MacOS/firefox-bin"
|
13
|
+
].freeze
|
14
|
+
LINUX_BIN_PATH = %w[firefox].freeze
|
15
|
+
|
16
|
+
def merge_required(flags, options, user_data_dir)
|
17
|
+
port = options.fetch(:port, BROWSER_PORT)
|
18
|
+
host = options.fetch(:host, BROWSER_HOST)
|
19
|
+
flags.merge("remote-debugger" => "#{host}:#{port}",
|
20
|
+
"profile" => user_data_dir)
|
21
|
+
end
|
22
|
+
|
23
|
+
def merge_default(flags, options)
|
24
|
+
unless options.fetch(:headless, true)
|
25
|
+
defaults = except("headless")
|
26
|
+
end
|
27
|
+
|
28
|
+
defaults ||= DEFAULT_OPTIONS
|
29
|
+
defaults.merge(flags)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -5,6 +5,11 @@ require "net/http"
|
|
5
5
|
require "json"
|
6
6
|
require "addressable"
|
7
7
|
require "tmpdir"
|
8
|
+
require "forwardable"
|
9
|
+
require "ferrum/browser/options/base"
|
10
|
+
require "ferrum/browser/options/chrome"
|
11
|
+
require "ferrum/browser/options/firefox"
|
12
|
+
require "ferrum/browser/command"
|
8
13
|
|
9
14
|
module Ferrum
|
10
15
|
class Browser
|
@@ -12,52 +17,14 @@ module Ferrum
|
|
12
17
|
KILL_TIMEOUT = 2
|
13
18
|
WAIT_KILLED = 0.05
|
14
19
|
PROCESS_TIMEOUT = ENV.fetch("FERRUM_PROCESS_TIMEOUT", 2).to_i
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
"enable-automation" => nil,
|
24
|
-
"disable-web-security" => nil,
|
25
|
-
"disable-session-crashed-bubble" => nil,
|
26
|
-
"disable-breakpad" => nil,
|
27
|
-
"disable-sync" => nil,
|
28
|
-
"no-first-run" => nil,
|
29
|
-
"use-mock-keychain" => nil,
|
30
|
-
"keep-alive-for-test" => nil,
|
31
|
-
"disable-popup-blocking" => nil,
|
32
|
-
"disable-extensions" => nil,
|
33
|
-
"disable-hang-monitor" => nil,
|
34
|
-
"disable-features" => "site-per-process,TranslateUI",
|
35
|
-
"disable-translate" => nil,
|
36
|
-
"disable-background-networking" => nil,
|
37
|
-
"enable-features" => "NetworkService,NetworkServiceInProcess",
|
38
|
-
"disable-background-timer-throttling" => nil,
|
39
|
-
"disable-backgrounding-occluded-windows" => nil,
|
40
|
-
"disable-client-side-phishing-detection" => nil,
|
41
|
-
"disable-default-apps" => nil,
|
42
|
-
"disable-dev-shm-usage" => nil,
|
43
|
-
"disable-ipc-flooding-protection" => nil,
|
44
|
-
"disable-prompt-on-repost" => nil,
|
45
|
-
"disable-renderer-backgrounding" => nil,
|
46
|
-
"force-color-profile" => "srgb",
|
47
|
-
"metrics-recording-only" => nil,
|
48
|
-
"safebrowsing-disable-auto-update" => nil,
|
49
|
-
"password-store" => "basic",
|
50
|
-
# Note: --no-sandbox is not needed if you properly setup a user in the container.
|
51
|
-
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
52
|
-
# "no-sandbox" => nil,
|
53
|
-
}.freeze
|
54
|
-
|
55
|
-
NOT_FOUND = "Could not find an executable for chrome. Try to make it " \
|
56
|
-
"available on the PATH or set environment varible for " \
|
57
|
-
"example BROWSER_PATH=\"/Applications/Chromium.app/Contents/MacOS/Chromium\""
|
58
|
-
|
59
|
-
|
60
|
-
attr_reader :host, :port, :ws_url, :pid, :path, :options, :cmd
|
20
|
+
|
21
|
+
attr_reader :host, :port, :ws_url, :pid, :command,
|
22
|
+
:default_user_agent, :browser_version, :protocol_version,
|
23
|
+
:v8_version, :webkit_version, :xvfb
|
24
|
+
|
25
|
+
|
26
|
+
extend Forwardable
|
27
|
+
delegate path: :command
|
61
28
|
|
62
29
|
def self.start(*args)
|
63
30
|
new(*args).tap(&:start)
|
@@ -85,65 +52,24 @@ module Ferrum
|
|
85
52
|
end
|
86
53
|
|
87
54
|
def self.directory_remover(path)
|
88
|
-
proc
|
89
|
-
begin
|
90
|
-
FileUtils.remove_entry(path)
|
91
|
-
rescue Errno::ENOENT
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
def self.detect_browser_path
|
97
|
-
if RUBY_PLATFORM.include?("darwin")
|
98
|
-
[
|
99
|
-
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
100
|
-
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
101
|
-
].find { |path| File.exist?(path) }
|
102
|
-
else
|
103
|
-
%w[chromium google-chrome-unstable google-chrome-beta google-chrome chrome chromium-browser google-chrome-stable].reduce(nil) do |path, exe|
|
104
|
-
path = Cliver.detect(exe)
|
105
|
-
break path if path
|
106
|
-
end
|
107
|
-
end
|
55
|
+
proc { FileUtils.remove_entry(path) rescue Errno::ENOENT }
|
108
56
|
end
|
109
57
|
|
110
58
|
def initialize(options)
|
111
|
-
@options = {}
|
112
|
-
|
113
|
-
@path = options[:browser_path] || BROWSER_PATH || self.class.detect_browser_path
|
114
|
-
|
115
59
|
if options[:url]
|
116
60
|
url = URI.join(options[:url].to_s, "/json/version")
|
117
61
|
response = JSON.parse(::Net::HTTP.get(url))
|
118
62
|
set_ws_url(response["webSocketDebuggerUrl"])
|
63
|
+
parse_browser_versions
|
119
64
|
return
|
120
65
|
end
|
121
66
|
|
122
|
-
|
123
|
-
@options.merge!("window-size" => options[:window_size].join(","))
|
124
|
-
|
125
|
-
port = options.fetch(:port, BROWSER_PORT)
|
126
|
-
@options.merge!("remote-debugging-port" => port)
|
127
|
-
|
128
|
-
host = options.fetch(:host, BROWSER_HOST)
|
129
|
-
@options.merge!("remote-debugging-address" => host)
|
130
|
-
|
131
|
-
@temp_user_data_dir = Dir.mktmpdir
|
132
|
-
ObjectSpace.define_finalizer(self, self.class.directory_remover(@temp_user_data_dir))
|
133
|
-
@options.merge!("user-data-dir" => @temp_user_data_dir)
|
134
|
-
|
135
|
-
@options = DEFAULT_OPTIONS.merge(@options)
|
136
|
-
|
137
|
-
unless options.fetch(:headless, true)
|
138
|
-
@options.delete("headless")
|
139
|
-
@options.delete("disable-gpu")
|
140
|
-
end
|
141
|
-
|
67
|
+
@logger = options[:logger]
|
142
68
|
@process_timeout = options.fetch(:process_timeout, PROCESS_TIMEOUT)
|
143
69
|
|
144
|
-
|
145
|
-
|
146
|
-
@
|
70
|
+
tmpdir = Dir.mktmpdir
|
71
|
+
ObjectSpace.define_finalizer(self, self.class.directory_remover(tmpdir))
|
72
|
+
@command = Command.build(options, tmpdir)
|
147
73
|
end
|
148
74
|
|
149
75
|
def start
|
@@ -156,21 +82,29 @@ module Ferrum
|
|
156
82
|
process_options[:pgroup] = true unless Ferrum.windows?
|
157
83
|
process_options[:out] = process_options[:err] = write_io
|
158
84
|
|
159
|
-
|
85
|
+
if @command.xvfb?
|
86
|
+
@xvfb = Xvfb.start(@command.options)
|
87
|
+
ObjectSpace.define_finalizer(self, self.class.process_killer(@xvfb.pid))
|
88
|
+
end
|
160
89
|
|
161
|
-
@
|
162
|
-
@pid = ::Process.spawn(*@cmd, process_options)
|
90
|
+
@pid = ::Process.spawn(Hash(@xvfb&.to_env), *@command.to_a, process_options)
|
163
91
|
ObjectSpace.define_finalizer(self, self.class.process_killer(@pid))
|
164
92
|
|
165
93
|
parse_ws_url(read_io, @process_timeout)
|
94
|
+
parse_browser_versions
|
166
95
|
ensure
|
167
96
|
close_io(read_io, write_io)
|
168
97
|
end
|
169
98
|
end
|
170
99
|
|
171
100
|
def stop
|
172
|
-
|
173
|
-
|
101
|
+
if @pid
|
102
|
+
kill(@pid)
|
103
|
+
kill(@xvfb.pid) if @xvfb&.pid
|
104
|
+
@pid = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
remove_user_data_dir if @user_data_dir
|
174
108
|
ObjectSpace.undefine_finalizer(self)
|
175
109
|
end
|
176
110
|
|
@@ -181,14 +115,13 @@ module Ferrum
|
|
181
115
|
|
182
116
|
private
|
183
117
|
|
184
|
-
def kill
|
185
|
-
self.class.process_killer(
|
186
|
-
@pid = nil
|
118
|
+
def kill(pid)
|
119
|
+
self.class.process_killer(pid).call
|
187
120
|
end
|
188
121
|
|
189
|
-
def
|
190
|
-
self.class.directory_remover(@
|
191
|
-
@
|
122
|
+
def remove_user_data_dir
|
123
|
+
self.class.directory_remover(@user_data_dir).call
|
124
|
+
@user_data_dir = nil
|
192
125
|
end
|
193
126
|
|
194
127
|
def parse_ws_url(read_io, timeout)
|
@@ -210,8 +143,8 @@ module Ferrum
|
|
210
143
|
end
|
211
144
|
|
212
145
|
unless ws_url
|
213
|
-
@logger.puts
|
214
|
-
raise
|
146
|
+
@logger.puts(output) if @logger
|
147
|
+
raise ProcessTimeoutError.new(timeout)
|
215
148
|
end
|
216
149
|
end
|
217
150
|
|
@@ -221,12 +154,25 @@ module Ferrum
|
|
221
154
|
@port = @ws_url.port
|
222
155
|
end
|
223
156
|
|
157
|
+
def parse_browser_versions
|
158
|
+
return unless ws_url.is_a?(Addressable::URI)
|
159
|
+
|
160
|
+
version_url = URI.parse(ws_url.merge(scheme: "http", path: "/json/version"))
|
161
|
+
response = JSON.parse(::Net::HTTP.get(version_url))
|
162
|
+
|
163
|
+
@v8_version = response["V8-Version"]
|
164
|
+
@browser_version = response["Browser"]
|
165
|
+
@webkit_version = response["WebKit-Version"]
|
166
|
+
@default_user_agent = response["User-Agent"]
|
167
|
+
@protocol_version = response["Protocol-Version"]
|
168
|
+
end
|
169
|
+
|
224
170
|
def close_io(*ios)
|
225
171
|
ios.each do |io|
|
226
172
|
begin
|
227
173
|
io.close unless io.closed?
|
228
174
|
rescue IOError
|
229
|
-
raise unless RUBY_ENGINE ==
|
175
|
+
raise unless RUBY_ENGINE == "jruby"
|
230
176
|
end
|
231
177
|
end
|
232
178
|
end
|