ferrum 0.17.1 → 0.18.0
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 +9 -1369
- data/lib/ferrum/accessibility/ax_node.rb +108 -0
- data/lib/ferrum/accessibility.rb +106 -0
- data/lib/ferrum/browser/binary.rb +41 -0
- data/lib/ferrum/browser/command.rb +20 -0
- data/lib/ferrum/browser/options/base.rb +69 -2
- data/lib/ferrum/browser/options/chrome.rb +88 -26
- data/lib/ferrum/browser/options/firefox.rb +32 -0
- data/lib/ferrum/browser/options.rb +43 -3
- data/lib/ferrum/browser/process.rb +56 -4
- data/lib/ferrum/browser/xvfb.rb +24 -0
- data/lib/ferrum/browser.rb +35 -9
- data/lib/ferrum/client/subscriber.rb +58 -0
- data/lib/ferrum/client/web_socket.rb +63 -12
- data/lib/ferrum/client.rb +206 -14
- data/lib/ferrum/context.rb +99 -4
- data/lib/ferrum/contexts.rb +136 -22
- data/lib/ferrum/cookies/cookie.rb +7 -1
- data/lib/ferrum/cookies.rb +5 -0
- data/lib/ferrum/dialog.rb +18 -2
- data/lib/ferrum/downloads.rb +52 -0
- data/lib/ferrum/errors.rb +62 -6
- data/lib/ferrum/frame/dom.rb +17 -0
- data/lib/ferrum/frame/runtime.rb +49 -8
- data/lib/ferrum/frame.rb +58 -1
- data/lib/ferrum/headers.rb +6 -0
- data/lib/ferrum/interceptable.rb +62 -0
- data/lib/ferrum/keyboard.rb +25 -0
- data/lib/ferrum/mouse.rb +6 -0
- data/lib/ferrum/network/auth_request.rb +81 -2
- data/lib/ferrum/network/error.rb +15 -0
- data/lib/ferrum/network/exchange.rb +10 -0
- data/lib/ferrum/network/intercepted_request.rb +94 -2
- data/lib/ferrum/network/request.rb +1 -1
- data/lib/ferrum/network/response.rb +16 -1
- data/lib/ferrum/network.rb +122 -10
- data/lib/ferrum/node.rb +305 -15
- data/lib/ferrum/page/animation.rb +7 -2
- data/lib/ferrum/page/frames.rb +69 -7
- data/lib/ferrum/page/screencast.rb +5 -0
- data/lib/ferrum/page/screenshot.rb +52 -20
- data/lib/ferrum/page/stream.rb +56 -0
- data/lib/ferrum/page/tracing.rb +6 -0
- data/lib/ferrum/page.rb +141 -46
- data/lib/ferrum/proxy.rb +52 -2
- data/lib/ferrum/rgba.rb +10 -0
- data/lib/ferrum/target.rb +124 -1
- data/lib/ferrum/utils/attempt.rb +20 -0
- data/lib/ferrum/utils/elapsed_time.rb +38 -0
- data/lib/ferrum/utils/event.rb +14 -0
- data/lib/ferrum/utils/platform.rb +22 -1
- data/lib/ferrum/utils/thread.rb +12 -0
- data/lib/ferrum/version.rb +1 -1
- data/lib/ferrum/worker.rb +125 -0
- data/lib/ferrum.rb +7 -0
- metadata +8 -21
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ferrum
|
|
4
|
+
class Accessibility
|
|
5
|
+
#
|
|
6
|
+
# Represents an [AXNode](https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/#type-AXNode)
|
|
7
|
+
# from the CDP Accessibility domain.
|
|
8
|
+
#
|
|
9
|
+
class AXNode
|
|
10
|
+
#
|
|
11
|
+
# @param [Hash{String => Object}] params
|
|
12
|
+
# The parsed CDP AXNode attributes.
|
|
13
|
+
#
|
|
14
|
+
def initialize(params)
|
|
15
|
+
@params = deep_freeze(params)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The AX node's role, e.g. `"button"`.
|
|
19
|
+
#
|
|
20
|
+
# @return [String, nil]
|
|
21
|
+
def role
|
|
22
|
+
@params.dig("role", "value")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# The AX node's accessible name.
|
|
26
|
+
#
|
|
27
|
+
# @return [String, nil]
|
|
28
|
+
def name
|
|
29
|
+
@params.dig("name", "value")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The AX node's accessible description.
|
|
33
|
+
#
|
|
34
|
+
# @return [String, nil]
|
|
35
|
+
def description
|
|
36
|
+
@params.dig("description", "value")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The AX node's current value.
|
|
40
|
+
#
|
|
41
|
+
# @return [String, Numeric, Boolean, nil] raw CDP AXValue.value; type varies by control
|
|
42
|
+
def value
|
|
43
|
+
@params.dig("value", "value")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# ARIA/computed properties flattened to `name => value`.
|
|
47
|
+
#
|
|
48
|
+
# @return [Hash{String => Object}]
|
|
49
|
+
def properties
|
|
50
|
+
@properties ||= Array(@params["properties"]).to_h do |property|
|
|
51
|
+
[property["name"], property.dig("value", "value")]
|
|
52
|
+
end.freeze
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Whether the node is ignored by the accessibility tree.
|
|
56
|
+
#
|
|
57
|
+
# @return [Boolean]
|
|
58
|
+
def ignored?
|
|
59
|
+
@params["ignored"] == true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# The reasons the node is ignored, if any.
|
|
63
|
+
#
|
|
64
|
+
# @return [Array, nil]
|
|
65
|
+
def ignored_reasons
|
|
66
|
+
@params["ignoredReasons"]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The AX node's id.
|
|
70
|
+
#
|
|
71
|
+
# @return [String, nil]
|
|
72
|
+
def node_id
|
|
73
|
+
@params["nodeId"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The id of the DOM node this AX node is backed by.
|
|
77
|
+
#
|
|
78
|
+
# @return [Integer, nil]
|
|
79
|
+
def backend_dom_node_id
|
|
80
|
+
@params["backendDOMNodeId"]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# The ids of this AX node's children.
|
|
84
|
+
#
|
|
85
|
+
# @return [Array, nil]
|
|
86
|
+
def child_ids
|
|
87
|
+
@params["childIds"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# The raw CDP AXNode hash.
|
|
91
|
+
#
|
|
92
|
+
# @return [Hash]
|
|
93
|
+
def to_h
|
|
94
|
+
@params
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def deep_freeze(object)
|
|
100
|
+
case object
|
|
101
|
+
when Hash then object.each { |key, value| deep_freeze(key.freeze) && deep_freeze(value) }
|
|
102
|
+
when Array then object.each { |value| deep_freeze(value) }
|
|
103
|
+
end
|
|
104
|
+
object.freeze
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ferrum/accessibility/ax_node"
|
|
4
|
+
|
|
5
|
+
module Ferrum
|
|
6
|
+
#
|
|
7
|
+
# Wraps the CDP [Accessibility](https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/)
|
|
8
|
+
# domain. The query commands work without `enable`; `enable`/`disable` are
|
|
9
|
+
# provided for completeness (live AX events).
|
|
10
|
+
#
|
|
11
|
+
# @note The node-scoped methods (`node_for`, `partial_tree`, `query` with a
|
|
12
|
+
# `node:`) issue the command against the node's owning page session. They
|
|
13
|
+
# support same-process (same-target) iframes; nodes living in an
|
|
14
|
+
# out-of-process iframe (OOPIF, separate CDP target) are not resolvable and
|
|
15
|
+
# will error or return an empty result.
|
|
16
|
+
#
|
|
17
|
+
class Accessibility
|
|
18
|
+
def initialize(page)
|
|
19
|
+
@page = page
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# The single non-ignored AXNode for a DOM node, or `nil`.
|
|
24
|
+
#
|
|
25
|
+
# @param [Ferrum::Node] node
|
|
26
|
+
# @return [AXNode, nil]
|
|
27
|
+
#
|
|
28
|
+
def node_for(node)
|
|
29
|
+
partial_tree(node: node).find { |ax_node| !ax_node.ignored? }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# The partial AX tree for a DOM node.
|
|
34
|
+
#
|
|
35
|
+
# @param [Ferrum::Node] node
|
|
36
|
+
# @param [Boolean] fetch_relatives
|
|
37
|
+
# @return [Array<AXNode>]
|
|
38
|
+
#
|
|
39
|
+
def partial_tree(node:, fetch_relatives: false)
|
|
40
|
+
nodes = node.page.command("Accessibility.getPartialAXTree",
|
|
41
|
+
nodeId: node.node_id,
|
|
42
|
+
fetchRelatives: fetch_relatives)["nodes"]
|
|
43
|
+
build(nodes)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
# The full AX tree for the page.
|
|
48
|
+
#
|
|
49
|
+
# @param [Integer, nil] depth
|
|
50
|
+
# @param [String, nil] frame_id
|
|
51
|
+
# @return [Array<AXNode>]
|
|
52
|
+
#
|
|
53
|
+
def snapshot(depth: nil, frame_id: nil)
|
|
54
|
+
params = { depth: depth, frameId: frame_id }.compact
|
|
55
|
+
build(@page.command("Accessibility.getFullAXTree", **params)["nodes"])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
#
|
|
59
|
+
# Query the AX tree by accessible name and/or role.
|
|
60
|
+
#
|
|
61
|
+
# @param [String, nil] name
|
|
62
|
+
# @param [String, nil] role
|
|
63
|
+
# @param [Ferrum::Node, nil] node Scope the query to this node's subtree.
|
|
64
|
+
# @return [Array<AXNode>]
|
|
65
|
+
#
|
|
66
|
+
def query(name: nil, role: nil, node: nil)
|
|
67
|
+
page = node ? node.page : @page
|
|
68
|
+
params = { accessibleName: name, role: role }.compact
|
|
69
|
+
params[:nodeId] = node ? node.node_id : page.document_node_id
|
|
70
|
+
build(page.command("Accessibility.queryAXTree", **params)["nodes"])
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# The root AXNode of the (optionally framed) document.
|
|
75
|
+
#
|
|
76
|
+
# @param [String, nil] frame_id
|
|
77
|
+
# @return [AXNode, nil]
|
|
78
|
+
#
|
|
79
|
+
def root(frame_id: nil)
|
|
80
|
+
params = { depth: 1, frameId: frame_id }.compact
|
|
81
|
+
build(@page.command("Accessibility.getFullAXTree", **params)["nodes"]).first
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Enables the Accessibility domain, activating live AX tree change events.
|
|
85
|
+
#
|
|
86
|
+
# @return [self]
|
|
87
|
+
def enable
|
|
88
|
+
@page.command("Accessibility.enable")
|
|
89
|
+
self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Disables the Accessibility domain.
|
|
93
|
+
#
|
|
94
|
+
# @return [self]
|
|
95
|
+
def disable
|
|
96
|
+
@page.command("Accessibility.disable")
|
|
97
|
+
self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def build(nodes)
|
|
103
|
+
Array(nodes).map { |node| AXNode.new(node) }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -2,23 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
module Ferrum
|
|
4
4
|
class Browser
|
|
5
|
+
#
|
|
6
|
+
# Locates an executable on the system `PATH`, mirroring what a shell's
|
|
7
|
+
# `which`/`where` would find. Used to resolve the browser (and Xvfb)
|
|
8
|
+
# binary when no explicit path is configured.
|
|
9
|
+
#
|
|
5
10
|
module Binary
|
|
6
11
|
module_function
|
|
7
12
|
|
|
13
|
+
#
|
|
14
|
+
# Finds the first executable path for the given command(s) on `PATH`.
|
|
15
|
+
#
|
|
16
|
+
# @param [String, Array<String>] commands
|
|
17
|
+
# Command name(s) to look up.
|
|
18
|
+
#
|
|
19
|
+
# @return [String, nil]
|
|
20
|
+
# Absolute path to the executable, or `nil` if none is found.
|
|
21
|
+
#
|
|
8
22
|
def find(commands)
|
|
9
23
|
enum(commands).first
|
|
10
24
|
end
|
|
11
25
|
|
|
26
|
+
#
|
|
27
|
+
# Finds all executable paths for the given command(s) on `PATH`.
|
|
28
|
+
#
|
|
29
|
+
# @param [String, Array<String>] commands
|
|
30
|
+
# Command name(s) to look up.
|
|
31
|
+
#
|
|
32
|
+
# @return [Array<String>]
|
|
33
|
+
# Absolute paths to matching executables.
|
|
34
|
+
#
|
|
12
35
|
def all(commands)
|
|
13
36
|
enum(commands).force
|
|
14
37
|
end
|
|
15
38
|
|
|
39
|
+
#
|
|
40
|
+
# Lazily enumerates executable paths for the given command(s) on `PATH`.
|
|
41
|
+
#
|
|
42
|
+
# @param [String, Array<String>] commands
|
|
43
|
+
# Command name(s) to look up.
|
|
44
|
+
#
|
|
45
|
+
# @return [Enumerator::Lazy]
|
|
46
|
+
# Lazy enumerator yielding matching executable paths.
|
|
47
|
+
#
|
|
16
48
|
def enum(commands)
|
|
17
49
|
paths, exts = prepare_paths
|
|
18
50
|
cmds = Array(commands).product(paths, exts)
|
|
19
51
|
lazy_find(cmds)
|
|
20
52
|
end
|
|
21
53
|
|
|
54
|
+
#
|
|
55
|
+
# Directories on `PATH` and the executable extensions to try against them.
|
|
56
|
+
#
|
|
57
|
+
# @return [Array(Array<String>, Array<String>)]
|
|
58
|
+
# The `PATH` directories, and the extensions from `PATHEXT` (plus `""`).
|
|
59
|
+
#
|
|
60
|
+
# @raise [EmptyPathError]
|
|
61
|
+
# If `PATH` is empty.
|
|
62
|
+
#
|
|
22
63
|
def prepare_paths
|
|
23
64
|
exts = (ENV.key?("PATHEXT") ? ENV.fetch("PATHEXT").split(";") : []) << ""
|
|
24
65
|
paths = ENV["PATH"].split(File::PATH_SEPARATOR)
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Ferrum
|
|
4
4
|
class Browser
|
|
5
|
+
#
|
|
6
|
+
# Builds the OS-level command used to spawn the browser process: resolves
|
|
7
|
+
# the executable path and merges the browser-specific required/default
|
|
8
|
+
# flags (see {Options::Base}) with any user-supplied ones into a single
|
|
9
|
+
# argument list.
|
|
10
|
+
#
|
|
5
11
|
class Command
|
|
6
12
|
NOT_FOUND = "Could not find an executable for the browser. Try to make " \
|
|
7
13
|
"it available on the PATH or set environment variable for " \
|
|
@@ -35,14 +41,28 @@ module Ferrum
|
|
|
35
41
|
merge_options
|
|
36
42
|
end
|
|
37
43
|
|
|
44
|
+
# Whether the browser should be launched under Xvfb.
|
|
45
|
+
#
|
|
46
|
+
# @return [Boolean]
|
|
38
47
|
def xvfb?
|
|
39
48
|
!!options.xvfb
|
|
40
49
|
end
|
|
41
50
|
|
|
51
|
+
#
|
|
52
|
+
# Command line arguments for spawning the browser process.
|
|
53
|
+
#
|
|
54
|
+
# @return [Array<String>]
|
|
55
|
+
# The browser path followed by its CLI flags.
|
|
56
|
+
#
|
|
42
57
|
def to_a
|
|
43
58
|
[path] + @flags.map { |k, v| v.nil? ? "--#{k}" : "--#{k}=#{v}" }
|
|
44
59
|
end
|
|
45
60
|
|
|
61
|
+
#
|
|
62
|
+
# String representation of the command used to spawn the browser process.
|
|
63
|
+
#
|
|
64
|
+
# @return [String]
|
|
65
|
+
#
|
|
46
66
|
def to_s
|
|
47
67
|
to_a.join(" \\ \n ")
|
|
48
68
|
end
|
|
@@ -6,14 +6,30 @@ require "open3"
|
|
|
6
6
|
module Ferrum
|
|
7
7
|
class Browser
|
|
8
8
|
class Options
|
|
9
|
+
#
|
|
10
|
+
# Abstract base for browser-specific default option builders. Subclasses
|
|
11
|
+
# (e.g. {Options::Chrome}, {Options::Firefox}) declare their own
|
|
12
|
+
# `DEFAULT_OPTIONS` and `PLATFORM_PATH` constants and implement
|
|
13
|
+
# {#merge_required}/{#merge_default} to produce the final CLI flags used
|
|
14
|
+
# by {Command}.
|
|
15
|
+
#
|
|
9
16
|
class Base
|
|
10
17
|
include Singleton
|
|
11
18
|
|
|
19
|
+
#
|
|
20
|
+
# Singleton instance holding the browser's default options.
|
|
21
|
+
#
|
|
22
|
+
# @return [Base]
|
|
23
|
+
#
|
|
12
24
|
def self.options
|
|
13
25
|
instance
|
|
14
26
|
end
|
|
15
27
|
|
|
28
|
+
# The installed browser's version string, obtained by running its
|
|
29
|
+
# binary with `--version`.
|
|
30
|
+
#
|
|
16
31
|
# @return [String, nil]
|
|
32
|
+
# The version output, or `nil` if the binary can't be found/run.
|
|
17
33
|
def self.version
|
|
18
34
|
out, = Open3.capture2(instance.detect_path, "--version")
|
|
19
35
|
out.strip
|
|
@@ -21,22 +37,73 @@ module Ferrum
|
|
|
21
37
|
nil
|
|
22
38
|
end
|
|
23
39
|
|
|
40
|
+
#
|
|
41
|
+
# Default CLI flags for the browser.
|
|
42
|
+
#
|
|
43
|
+
# @return [Hash{String => Object}]
|
|
44
|
+
#
|
|
24
45
|
def to_h
|
|
25
46
|
self.class::DEFAULT_OPTIONS
|
|
26
47
|
end
|
|
27
48
|
|
|
49
|
+
#
|
|
50
|
+
# Default CLI flags excluding the given keys.
|
|
51
|
+
#
|
|
52
|
+
# @param [Array<String>] keys
|
|
53
|
+
# Flag names to exclude.
|
|
54
|
+
#
|
|
55
|
+
# @return [Hash{String => Object}]
|
|
56
|
+
#
|
|
28
57
|
def except(*keys)
|
|
29
|
-
to_h.
|
|
58
|
+
to_h.except(*keys)
|
|
30
59
|
end
|
|
31
60
|
|
|
61
|
+
#
|
|
62
|
+
# Locates the browser executable on the system.
|
|
63
|
+
#
|
|
64
|
+
# @return [String, nil]
|
|
65
|
+
# Absolute path to the browser binary, or `nil` if not found.
|
|
66
|
+
#
|
|
32
67
|
def detect_path
|
|
33
|
-
Binary.find(self.class::PLATFORM_PATH[Utils::Platform.
|
|
68
|
+
Binary.find(self.class::PLATFORM_PATH[Utils::Platform.platform_name])
|
|
34
69
|
end
|
|
35
70
|
|
|
71
|
+
#
|
|
72
|
+
# Merges the CLI flags that are always required to drive the browser
|
|
73
|
+
# over CDP (e.g. remote debugging port, user data dir) into `flags`.
|
|
74
|
+
# Abstract; overridden by {Options::Chrome}/{Options::Firefox}.
|
|
75
|
+
#
|
|
76
|
+
# @param [Hash{String => Object}] flags
|
|
77
|
+
# The flags accumulated so far.
|
|
78
|
+
# @param [Options] options
|
|
79
|
+
# The user-supplied browser options.
|
|
80
|
+
# @param [String] user_data_dir
|
|
81
|
+
# Path to the browser's user data directory.
|
|
82
|
+
#
|
|
83
|
+
# @return [Hash{String => Object}]
|
|
84
|
+
#
|
|
85
|
+
# @raise [NotImplementedError]
|
|
86
|
+
# Always, unless overridden by a subclass.
|
|
87
|
+
#
|
|
36
88
|
def merge_required(flags, options, user_data_dir)
|
|
37
89
|
raise NotImplementedError
|
|
38
90
|
end
|
|
39
91
|
|
|
92
|
+
#
|
|
93
|
+
# Merges this browser's default CLI flags (see {#to_h}) into `flags`,
|
|
94
|
+
# unless `options.ignore_default_browser_options` is set. Abstract;
|
|
95
|
+
# overridden by {Options::Chrome}/{Options::Firefox}.
|
|
96
|
+
#
|
|
97
|
+
# @param [Hash{String => Object}] flags
|
|
98
|
+
# The flags accumulated so far.
|
|
99
|
+
# @param [Options] options
|
|
100
|
+
# The user-supplied browser options.
|
|
101
|
+
#
|
|
102
|
+
# @return [Hash{String => Object}]
|
|
103
|
+
#
|
|
104
|
+
# @raise [NotImplementedError]
|
|
105
|
+
# Always, unless overridden by a subclass.
|
|
106
|
+
#
|
|
40
107
|
def merge_default(flags, options)
|
|
41
108
|
raise NotImplementedError
|
|
42
109
|
end
|
|
@@ -3,46 +3,72 @@
|
|
|
3
3
|
module Ferrum
|
|
4
4
|
class Browser
|
|
5
5
|
class Options
|
|
6
|
+
#
|
|
7
|
+
# Chrome/Chromium-specific default flags and binary locations. Provides
|
|
8
|
+
# the CLI flags required to drive Chrome over CDP as well as the
|
|
9
|
+
# hardened defaults (disabling background networking, extensions,
|
|
10
|
+
# infobars, etc.) applied unless the user opts out.
|
|
11
|
+
#
|
|
6
12
|
class Chrome < Base
|
|
7
13
|
DEFAULT_OPTIONS = {
|
|
8
|
-
"
|
|
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-component-extensions-with-background-pages" => nil,
|
|
22
|
-
"disable-hang-monitor" => nil,
|
|
23
|
-
"disable-features" => "site-per-process,IsolateOrigins,TranslateUI",
|
|
24
|
-
"disable-translate" => nil,
|
|
14
|
+
"allow-pre-commit-input" => nil,
|
|
25
15
|
"disable-background-networking" => nil,
|
|
26
|
-
"enable-features" => "NetworkService,NetworkServiceInProcess",
|
|
27
16
|
"disable-background-timer-throttling" => nil,
|
|
28
17
|
"disable-backgrounding-occluded-windows" => nil,
|
|
18
|
+
"disable-blink-features" => "AutomationControlled",
|
|
19
|
+
"disable-breakpad" => nil,
|
|
29
20
|
"disable-client-side-phishing-detection" => nil,
|
|
21
|
+
"disable-component-extensions-with-background-pages" => nil,
|
|
22
|
+
"disable-component-update" => nil,
|
|
23
|
+
"disable-crash-reporter" => nil,
|
|
30
24
|
"disable-default-apps" => nil,
|
|
31
25
|
"disable-dev-shm-usage" => nil,
|
|
26
|
+
"disable-extensions" => nil,
|
|
27
|
+
"disable-features" => %w[
|
|
28
|
+
site-per-process
|
|
29
|
+
IsolateOrigins
|
|
30
|
+
TranslateUI
|
|
31
|
+
Translate
|
|
32
|
+
MacAppCodeSignClone
|
|
33
|
+
InterestFeedContentSuggestion
|
|
34
|
+
OptimizationHints
|
|
35
|
+
AcceptCHFrame
|
|
36
|
+
MediaRouter
|
|
37
|
+
].join(","),
|
|
38
|
+
"disable-field-trial-config" => nil,
|
|
39
|
+
"disable-hang-monitor" => nil,
|
|
40
|
+
"disable-infobars" => nil,
|
|
32
41
|
"disable-ipc-flooding-protection" => nil,
|
|
42
|
+
"disable-popup-blocking" => nil,
|
|
33
43
|
"disable-prompt-on-repost" => nil,
|
|
34
44
|
"disable-renderer-backgrounding" => nil,
|
|
45
|
+
"disable-search-engine-choice-screen" => nil,
|
|
46
|
+
"disable-session-crashed-bubble" => nil,
|
|
35
47
|
"disable-site-isolation-trials" => nil,
|
|
48
|
+
"disable-smooth-scrolling" => nil,
|
|
49
|
+
"disable-sync" => nil,
|
|
50
|
+
"disable-translate" => nil,
|
|
51
|
+
"disable-web-security" => nil,
|
|
52
|
+
"enable-automation" => nil,
|
|
53
|
+
"enable-features" => %w[
|
|
54
|
+
NetworkService
|
|
55
|
+
NetworkServiceInProcess
|
|
56
|
+
].join(","),
|
|
36
57
|
"force-color-profile" => "srgb",
|
|
58
|
+
"headless" => nil,
|
|
59
|
+
"hide-scrollbars" => nil,
|
|
60
|
+
"keep-alive-for-test" => nil,
|
|
37
61
|
"metrics-recording-only" => nil,
|
|
38
|
-
"
|
|
39
|
-
"
|
|
62
|
+
"mute-audio" => nil,
|
|
63
|
+
"no-crash-upload" => nil,
|
|
64
|
+
"no-crashpad" => nil,
|
|
65
|
+
"no-default-browser-check" => nil,
|
|
66
|
+
"no-first-run" => nil,
|
|
40
67
|
"no-startup-window" => nil,
|
|
68
|
+
"password-store" => "basic",
|
|
41
69
|
"remote-allow-origins" => "*",
|
|
42
|
-
"disable-
|
|
43
|
-
|
|
44
|
-
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
|
45
|
-
# "no-sandbox" => nil,
|
|
70
|
+
"safebrowsing-disable-auto-update" => nil,
|
|
71
|
+
"use-mock-keychain" => nil
|
|
46
72
|
}.freeze
|
|
47
73
|
|
|
48
74
|
MAC_BIN_PATH = [
|
|
@@ -61,6 +87,21 @@ module Ferrum
|
|
|
61
87
|
linux: LINUX_BIN_PATH
|
|
62
88
|
}.freeze
|
|
63
89
|
|
|
90
|
+
#
|
|
91
|
+
# Merges CLI flags required for Chrome to work with CDP.
|
|
92
|
+
#
|
|
93
|
+
# @param [Hash] flags
|
|
94
|
+
# Flags to merge required ones into.
|
|
95
|
+
#
|
|
96
|
+
# @param [Ferrum::Browser::Options] options
|
|
97
|
+
# Browser options.
|
|
98
|
+
#
|
|
99
|
+
# @param [String] user_data_dir
|
|
100
|
+
# Path to the browser's user data directory.
|
|
101
|
+
#
|
|
102
|
+
# @return [Hash]
|
|
103
|
+
# Merged flags.
|
|
104
|
+
#
|
|
64
105
|
def merge_required(flags, options, user_data_dir)
|
|
65
106
|
flags = flags.merge("remote-debugging-port" => options.port,
|
|
66
107
|
"remote-debugging-address" => options.host,
|
|
@@ -75,16 +116,37 @@ module Ferrum
|
|
|
75
116
|
flags
|
|
76
117
|
end
|
|
77
118
|
|
|
119
|
+
#
|
|
120
|
+
# Merges Chrome's default flags with the given ones, unless the browser
|
|
121
|
+
# is configured to ignore default browser options.
|
|
122
|
+
#
|
|
123
|
+
# @param [Hash] flags
|
|
124
|
+
# Flags that take precedence over the defaults.
|
|
125
|
+
#
|
|
126
|
+
# @param [Ferrum::Browser::Options] options
|
|
127
|
+
# Browser options.
|
|
128
|
+
#
|
|
129
|
+
# @return [Hash]
|
|
130
|
+
# Merged flags.
|
|
131
|
+
#
|
|
78
132
|
def merge_default(flags, options)
|
|
79
|
-
defaults = except("headless", "disable-gpu")
|
|
80
|
-
defaults ||= DEFAULT_OPTIONS
|
|
133
|
+
defaults = options.headless == false ? except("headless", "disable-gpu") : DEFAULT_OPTIONS.dup
|
|
81
134
|
defaults.delete("no-startup-window") if options.incognito == false
|
|
135
|
+
|
|
136
|
+
if options.dockerize || ENV["FERRUM_CHROME_DOCKERIZE"] == "true"
|
|
137
|
+
# NOTE: --no-sandbox is not needed if you properly set up a user in the container.
|
|
138
|
+
# https://github.com/ebidel/lighthouse-ci/blob/master/builder/Dockerfile#L35-L40
|
|
139
|
+
defaults = defaults.merge("no-sandbox" => nil, "disable-setuid-sandbox" => nil)
|
|
140
|
+
end
|
|
141
|
+
|
|
82
142
|
# On Windows, the --disable-gpu flag is a temporary workaround for a few bugs.
|
|
83
143
|
# See https://bugs.chromium.org/p/chromium/issues/detail?id=737678 for more information.
|
|
84
144
|
defaults = defaults.merge("disable-gpu" => nil) if Utils::Platform.windows?
|
|
145
|
+
|
|
85
146
|
# Use Metal on Apple Silicon
|
|
86
147
|
# https://github.com/google/angle#platform-support-via-backing-renderers
|
|
87
148
|
defaults = defaults.merge("use-angle" => "metal") if Utils::Platform.mac_arm?
|
|
149
|
+
|
|
88
150
|
defaults.merge(flags)
|
|
89
151
|
end
|
|
90
152
|
end
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
module Ferrum
|
|
4
4
|
class Browser
|
|
5
5
|
class Options
|
|
6
|
+
#
|
|
7
|
+
# Firefox-specific default flags and binary locations. Provides the CLI
|
|
8
|
+
# flags required to drive Firefox over CDP (remote debugger address and
|
|
9
|
+
# profile directory) along with its (minimal) set of default flags.
|
|
10
|
+
#
|
|
6
11
|
class Firefox < Base
|
|
7
12
|
DEFAULT_OPTIONS = {
|
|
8
13
|
"headless" => nil
|
|
@@ -22,10 +27,37 @@ module Ferrum
|
|
|
22
27
|
linux: LINUX_BIN_PATH
|
|
23
28
|
}.freeze
|
|
24
29
|
|
|
30
|
+
#
|
|
31
|
+
# Merges CLI flags required for Firefox to work with CDP.
|
|
32
|
+
#
|
|
33
|
+
# @param [Hash] flags
|
|
34
|
+
# Flags to merge required ones into.
|
|
35
|
+
#
|
|
36
|
+
# @param [Ferrum::Browser::Options] options
|
|
37
|
+
# Browser options.
|
|
38
|
+
#
|
|
39
|
+
# @param [String] user_data_dir
|
|
40
|
+
# Path to the browser's profile directory.
|
|
41
|
+
#
|
|
42
|
+
# @return [Hash]
|
|
43
|
+
# Merged flags.
|
|
44
|
+
#
|
|
25
45
|
def merge_required(flags, options, user_data_dir)
|
|
26
46
|
flags.merge("remote-debugger" => "#{options.host}:#{options.port}", "profile" => user_data_dir)
|
|
27
47
|
end
|
|
28
48
|
|
|
49
|
+
#
|
|
50
|
+
# Merges Firefox's default flags with the given ones.
|
|
51
|
+
#
|
|
52
|
+
# @param [Hash] flags
|
|
53
|
+
# Flags that take precedence over the defaults.
|
|
54
|
+
#
|
|
55
|
+
# @param [Ferrum::Browser::Options] options
|
|
56
|
+
# Browser options.
|
|
57
|
+
#
|
|
58
|
+
# @return [Hash]
|
|
59
|
+
# Merged flags.
|
|
60
|
+
#
|
|
29
61
|
def merge_default(flags, options)
|
|
30
62
|
defaults = except("headless") unless options.headless
|
|
31
63
|
|