selenium-webdriver 4.44.0 → 4.46.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/CHANGES +19 -0
- data/Gemfile +1 -4
- data/README.md +1 -1
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/webdriver/atoms/findElements.js +272 -68
- data/lib/selenium/webdriver/atoms/getAttribute.js +152 -17
- data/lib/selenium/webdriver/atoms/isDisplayed.js +385 -39
- data/lib/selenium/webdriver/bidi/protocol/bluetooth.rb +465 -0
- data/lib/selenium/webdriver/bidi/protocol/browser.rb +222 -0
- data/lib/selenium/webdriver/bidi/protocol/browsing_context.rb +694 -0
- data/lib/selenium/webdriver/bidi/protocol/domain.rb +40 -0
- data/lib/selenium/webdriver/bidi/protocol/emulation.rb +334 -0
- data/lib/selenium/webdriver/bidi/protocol/input.rb +281 -0
- data/lib/selenium/webdriver/bidi/protocol/log.rb +104 -0
- data/lib/selenium/webdriver/bidi/protocol/network.rb +637 -0
- data/lib/selenium/webdriver/bidi/protocol/permissions.rb +73 -0
- data/lib/selenium/webdriver/bidi/protocol/script.rb +874 -0
- data/lib/selenium/webdriver/bidi/protocol/session.rb +241 -0
- data/lib/selenium/webdriver/bidi/protocol/speculation.rb +56 -0
- data/lib/selenium/webdriver/bidi/protocol/storage.rb +157 -0
- data/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb +83 -0
- data/lib/selenium/webdriver/bidi/protocol/web_extension.rb +93 -0
- data/lib/selenium/webdriver/bidi/protocol.rb +39 -0
- data/lib/selenium/webdriver/bidi/serialization/record.rb +226 -0
- data/lib/selenium/webdriver/bidi/serialization/union.rb +114 -0
- data/lib/selenium/webdriver/bidi/serialization.rb +78 -0
- data/lib/selenium/webdriver/bidi/support/bidi_generate.rb +936 -0
- data/lib/selenium/webdriver/bidi/support/check_generated.rb +55 -0
- data/lib/selenium/webdriver/bidi/transport.rb +52 -0
- data/lib/selenium/webdriver/bidi.rb +3 -0
- data/lib/selenium/webdriver/chrome/driver.rb +3 -3
- data/lib/selenium/webdriver/chromium/options.rb +3 -1
- data/lib/selenium/webdriver/chromium/profile.rb +6 -0
- data/lib/selenium/webdriver/common/client_config.rb +97 -0
- data/lib/selenium/webdriver/common/driver.rb +2 -2
- data/lib/selenium/webdriver/common/driver_finder.rb +32 -23
- data/lib/selenium/webdriver/common/local_driver.rb +19 -12
- data/lib/selenium/webdriver/common/proxy.rb +1 -1
- data/lib/selenium/webdriver/common/service.rb +1 -11
- data/lib/selenium/webdriver/common.rb +1 -0
- data/lib/selenium/webdriver/edge/driver.rb +3 -3
- data/lib/selenium/webdriver/firefox/driver.rb +3 -3
- data/lib/selenium/webdriver/ie/driver.rb +3 -3
- data/lib/selenium/webdriver/remote/bidi_bridge.rb +6 -1
- data/lib/selenium/webdriver/remote/bridge.rb +8 -10
- data/lib/selenium/webdriver/remote/driver.rb +12 -4
- data/lib/selenium/webdriver/remote/http/common.rb +25 -12
- data/lib/selenium/webdriver/remote/http/curb.rb +6 -8
- data/lib/selenium/webdriver/remote/http/default.rb +56 -39
- data/lib/selenium/webdriver/safari/driver.rb +3 -3
- data/lib/selenium/webdriver/safari/options.rb +8 -4
- data/lib/selenium/webdriver/safari.rb +1 -6
- data/lib/selenium/webdriver/support/guards/guard.rb +10 -11
- data/lib/selenium/webdriver/support/guards.rb +2 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/selenium-webdriver.gemspec +1 -1
- metadata +27 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
require 'json'
|
|
21
|
+
require_relative 'bidi_generate'
|
|
22
|
+
|
|
23
|
+
module BiDiGenerate
|
|
24
|
+
# Verifies the checked-in protocol .rb match what the generator would produce from the
|
|
25
|
+
# current schema — catching a hand-edit or a forgotten regeneration. Re-renders each module
|
|
26
|
+
# in memory (no file writes) and compares. The .rbs are covered by Steep.
|
|
27
|
+
def self.check!(schema_rootpath)
|
|
28
|
+
modules = build_ir(Schema.new(JSON.parse(File.read(schema_path(schema_rootpath)))))
|
|
29
|
+
protocol_dir = File.expand_path('../protocol', __dir__)
|
|
30
|
+
template = File.join(__dir__, 'templates', 'module.rb.erb')
|
|
31
|
+
|
|
32
|
+
stale = modules.reject do |mod|
|
|
33
|
+
path = File.join(protocol_dir, "#{mod.filename}.rb")
|
|
34
|
+
File.exist?(path) && File.read(path) == render(mod, template)
|
|
35
|
+
end
|
|
36
|
+
return if stale.empty?
|
|
37
|
+
|
|
38
|
+
warn "Generated BiDi protocol code is stale or hand-edited: #{stale.map { |m| "#{m.filename}.rb" }.sort.join(', ')}"
|
|
39
|
+
warn 'Regenerate with: bazel run //rb/lib/selenium/webdriver:bidi-generate'
|
|
40
|
+
exit 1
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# $(rootpath) is relative to the runfiles root; __dir__ anchors us there so it resolves the
|
|
44
|
+
# same way locally and on RBE (an execpath would not). This file lives at
|
|
45
|
+
# rb/lib/selenium/webdriver/bidi/support, so expand six levels up to the root — File.expand_path
|
|
46
|
+
# is separator-agnostic, unlike stripping a "/"-spelled suffix (which would miss on Windows).
|
|
47
|
+
# The cwd-relative rootpath fallback matches how spec_support's `rlocation` resolves.
|
|
48
|
+
def self.schema_path(rootpath)
|
|
49
|
+
runfiles_root = File.expand_path('../../../../../..', __dir__)
|
|
50
|
+
[File.join(runfiles_root, rootpath), rootpath].find { |p| File.exist?(p) } ||
|
|
51
|
+
raise("BiDi schema not found (looked for #{rootpath})")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
BiDiGenerate.check!(ARGV.fetch(0)) if $PROGRAM_NAME == __FILE__
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
module Selenium
|
|
21
|
+
module WebDriver
|
|
22
|
+
class BiDi
|
|
23
|
+
# The seam between the generated Protocol layer and the websocket: serializes a
|
|
24
|
+
# command's params, sends it, and parses the reply into its declared type.
|
|
25
|
+
#
|
|
26
|
+
# @api private
|
|
27
|
+
class Transport
|
|
28
|
+
def initialize(connection)
|
|
29
|
+
@connection = connection
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def execute(cmd:, params: nil, result: nil)
|
|
33
|
+
reply = @connection.send_cmd(method: cmd, params: serialize(params))
|
|
34
|
+
raise Error::WebDriverError, error_message(reply) if reply['error']
|
|
35
|
+
|
|
36
|
+
value = reply['result']
|
|
37
|
+
result ? result.from_json(value) : value
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def serialize(params)
|
|
43
|
+
params&.as_json || {}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def error_message(reply)
|
|
47
|
+
"#{reply['error']}: #{reply['message']}\n#{reply['stacktrace']}"
|
|
48
|
+
end
|
|
49
|
+
end # Transport
|
|
50
|
+
end # BiDi
|
|
51
|
+
end # WebDriver
|
|
52
|
+
end # Selenium
|
|
@@ -30,9 +30,9 @@ module Selenium
|
|
|
30
30
|
class Driver < Chromium::Driver
|
|
31
31
|
include LocalDriver
|
|
32
32
|
|
|
33
|
-
def initialize(options: nil, service: nil, url: nil, **)
|
|
34
|
-
initialize_local_driver(options, service, url) do |caps,
|
|
35
|
-
super(caps: caps,
|
|
33
|
+
def initialize(options: nil, service: nil, url: nil, http_client: nil, client_config: nil, **)
|
|
34
|
+
initialize_local_driver(options, service, url, http_client, client_config) do |caps, client|
|
|
35
|
+
super(caps: caps, http_client: client, **)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -49,7 +49,9 @@ module Selenium
|
|
|
49
49
|
# options = Selenium::WebDriver::Chrome::Options.new(args: ['start-maximized', 'user-data-dir=/tmp/temp_profile'])
|
|
50
50
|
# driver = Selenium::WebDriver.for(:chrome, options: options)
|
|
51
51
|
#
|
|
52
|
-
# @param [Profile] profile An instance of a Chrome::Profile
|
|
52
|
+
# @param [Profile] profile (Deprecated) An instance of a Chrome::Profile class.
|
|
53
|
+
# Use {#add_argument} with `--user-data-dir=...`, {#add_preference}, and
|
|
54
|
+
# {#add_extension} instead.
|
|
53
55
|
# @param [Hash] opts the pre-defined options to create the Chrome::Options with
|
|
54
56
|
# @option opts [Array] encoded_extensions List of extensions that do not need to be Base64 encoded
|
|
55
57
|
# @option opts [Array<String>] args List of command-line arguments to use when starting Chrome
|
|
@@ -28,6 +28,12 @@ module Selenium
|
|
|
28
28
|
include ProfileHelper
|
|
29
29
|
|
|
30
30
|
def initialize(model = nil)
|
|
31
|
+
WebDriver.logger.deprecate(
|
|
32
|
+
'Chromium::Profile (including Chrome::Profile and Edge::Profile)',
|
|
33
|
+
"Options#add_argument('--user-data-dir=...'), Options#add_preference, and Options#add_extension",
|
|
34
|
+
id: :chromium_profile
|
|
35
|
+
)
|
|
36
|
+
|
|
31
37
|
@model = verify_model(model)
|
|
32
38
|
@extensions = []
|
|
33
39
|
@encoded_extensions = []
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Licensed to the Software Freedom Conservancy (SFC) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The SFC licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
|
|
20
|
+
require 'uri'
|
|
21
|
+
|
|
22
|
+
module Selenium
|
|
23
|
+
module WebDriver
|
|
24
|
+
#
|
|
25
|
+
# Configuration for HTTP clients.
|
|
26
|
+
#
|
|
27
|
+
|
|
28
|
+
class ClientConfig
|
|
29
|
+
class << self
|
|
30
|
+
attr_accessor :default_extra_headers
|
|
31
|
+
attr_writer :default_user_agent
|
|
32
|
+
|
|
33
|
+
def default_user_agent
|
|
34
|
+
@default_user_agent ||= "selenium/#{WebDriver::VERSION} (ruby #{Platform.os})"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
attr_accessor :open_timeout, :read_timeout, :max_redirects, :proxy
|
|
39
|
+
attr_writer :extra_headers, :user_agent
|
|
40
|
+
attr_reader :server_url
|
|
41
|
+
|
|
42
|
+
#
|
|
43
|
+
# @param [Numeric] open_timeout Seconds to wait for the connection to open.
|
|
44
|
+
# @param [Numeric] read_timeout Seconds to wait for a response.
|
|
45
|
+
# @param [Integer] max_redirects Maximum number of redirects to follow.
|
|
46
|
+
# @param [Proxy] proxy Proxy to use for the connection.
|
|
47
|
+
# @param [Hash] extra_headers Additional headers to send with each request.
|
|
48
|
+
# @param [String] user_agent Value to send as the User-Agent header.
|
|
49
|
+
# @param [String, URI] server_url URL of the server to connect to.
|
|
50
|
+
#
|
|
51
|
+
def initialize(open_timeout: 60,
|
|
52
|
+
read_timeout: 120,
|
|
53
|
+
max_redirects: 20,
|
|
54
|
+
proxy: nil,
|
|
55
|
+
extra_headers: nil,
|
|
56
|
+
user_agent: nil,
|
|
57
|
+
server_url: nil)
|
|
58
|
+
@open_timeout = open_timeout
|
|
59
|
+
@read_timeout = read_timeout
|
|
60
|
+
@max_redirects = max_redirects
|
|
61
|
+
@proxy = proxy || proxy_from_environment
|
|
62
|
+
@extra_headers = extra_headers
|
|
63
|
+
@user_agent = user_agent
|
|
64
|
+
self.server_url = server_url
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def user_agent
|
|
68
|
+
@user_agent || self.class.default_user_agent
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def extra_headers
|
|
72
|
+
@extra_headers || self.class.default_extra_headers
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def server_url=(url)
|
|
76
|
+
if url.nil?
|
|
77
|
+
@server_url = nil
|
|
78
|
+
else
|
|
79
|
+
url = url.to_s
|
|
80
|
+
url += '/' unless url.end_with?('/')
|
|
81
|
+
@server_url = URI.parse(url)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def proxy_from_environment
|
|
88
|
+
proxy = ENV.fetch('http_proxy', nil) || ENV.fetch('HTTP_PROXY', nil)
|
|
89
|
+
return unless proxy
|
|
90
|
+
|
|
91
|
+
no_proxy = ENV.fetch('no_proxy', nil) || ENV.fetch('NO_PROXY', nil)
|
|
92
|
+
proxy = "http://#{proxy}" unless proxy.match?(%r{\Ahttps?://})
|
|
93
|
+
Proxy.new(http: proxy, no_proxy: no_proxy)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -319,9 +319,9 @@ module Selenium
|
|
|
319
319
|
|
|
320
320
|
attr_reader :bridge
|
|
321
321
|
|
|
322
|
-
def create_bridge(caps:,
|
|
322
|
+
def create_bridge(caps:, http_client:)
|
|
323
323
|
klass = caps['webSocketUrl'] ? Remote::BiDiBridge : Remote::Bridge
|
|
324
|
-
klass.new(http_client: http_client
|
|
324
|
+
klass.new(http_client: http_client).tap do |bridge|
|
|
325
325
|
bridge.create_session(caps)
|
|
326
326
|
end
|
|
327
327
|
end
|
|
@@ -26,6 +26,8 @@ module Selenium
|
|
|
26
26
|
new(options, service_class.new).driver_path
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
# @param options [Options, nil] when nil driver parsed from Service::EXECUTABLE
|
|
30
|
+
# @param service [Service]
|
|
29
31
|
def initialize(options, service)
|
|
30
32
|
@options = options
|
|
31
33
|
@service = service
|
|
@@ -47,36 +49,43 @@ module Selenium
|
|
|
47
49
|
|
|
48
50
|
def paths
|
|
49
51
|
@paths ||= begin
|
|
50
|
-
path = @service.
|
|
51
|
-
path
|
|
52
|
-
exe = @service.class::EXECUTABLE
|
|
53
|
-
if path
|
|
54
|
-
WebDriver.logger.debug("Skipping Selenium Manager; path to #{exe} specified in service class: #{path}")
|
|
55
|
-
Platform.assert_executable(path)
|
|
56
|
-
{driver_path: path}
|
|
57
|
-
else
|
|
58
|
-
output = SeleniumManager.binary_paths(*to_args)
|
|
59
|
-
formatted = {driver_path: Platform.cygwin_path(output['driver_path'], only_cygwin: true),
|
|
60
|
-
browser_path: Platform.cygwin_path(output['browser_path'], only_cygwin: true)}
|
|
61
|
-
Platform.assert_executable(formatted[:driver_path])
|
|
62
|
-
|
|
63
|
-
browser_path = formatted[:browser_path]
|
|
64
|
-
Platform.assert_executable(browser_path)
|
|
65
|
-
if @options.respond_to?(:binary) && @options.binary.nil?
|
|
66
|
-
@options.binary = browser_path
|
|
67
|
-
@options.browser_version = nil
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
formatted
|
|
71
|
-
end
|
|
52
|
+
path = @service.executable_path || env_path || class_path
|
|
53
|
+
path ? paths_from_service(path) : paths_from_manager
|
|
72
54
|
rescue StandardError => e
|
|
73
55
|
WebDriver.logger.error("Exception occurred: #{e.message}")
|
|
74
56
|
WebDriver.logger.error("Backtrace:\n\t#{e.backtrace&.join("\n\t")}")
|
|
75
|
-
raise Error::NoSuchDriverError, "Unable to obtain #{
|
|
57
|
+
raise Error::NoSuchDriverError, "Unable to obtain #{@service.class::EXECUTABLE}"
|
|
76
58
|
end
|
|
77
59
|
end
|
|
78
60
|
|
|
61
|
+
def env_path
|
|
62
|
+
ENV.fetch(@service.class::DRIVER_PATH_ENV_KEY, nil)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def class_path
|
|
66
|
+
path = @service.class.driver_path
|
|
67
|
+
path.is_a?(Proc) ? path.call : path
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def paths_from_service(path)
|
|
71
|
+
exe = @service.class::EXECUTABLE
|
|
72
|
+
WebDriver.logger.debug("Skipping Selenium Manager; path to #{exe} specified in service class: #{path}")
|
|
73
|
+
Platform.assert_executable(path)
|
|
74
|
+
{driver_path: path}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def paths_from_manager
|
|
78
|
+
output = SeleniumManager.binary_paths(*to_args)
|
|
79
|
+
formatted = {driver_path: Platform.cygwin_path(output['driver_path'], only_cygwin: true),
|
|
80
|
+
browser_path: Platform.cygwin_path(output['browser_path'], only_cygwin: true)}
|
|
81
|
+
Platform.assert_executable(formatted[:driver_path])
|
|
82
|
+
Platform.assert_executable(formatted[:browser_path])
|
|
83
|
+
formatted
|
|
84
|
+
end
|
|
85
|
+
|
|
79
86
|
def to_args
|
|
87
|
+
return ['--driver', @service.class::EXECUTABLE] unless @options
|
|
88
|
+
|
|
80
89
|
args = ['--browser', @options.browser_name]
|
|
81
90
|
if @options.browser_version
|
|
82
91
|
args << '--browser-version'
|
|
@@ -20,15 +20,16 @@
|
|
|
20
20
|
module Selenium
|
|
21
21
|
module WebDriver
|
|
22
22
|
module LocalDriver
|
|
23
|
-
def initialize_local_driver(options, service, url)
|
|
24
|
-
|
|
23
|
+
def initialize_local_driver(options, service, url, http_client, client_config)
|
|
24
|
+
assert_local_arguments(url, http_client, client_config)
|
|
25
25
|
|
|
26
26
|
service ||= Service.send(browser)
|
|
27
27
|
caps = process_options(options, service)
|
|
28
|
-
|
|
28
|
+
http_client ||= Remote::Http::Default.new(client_config: client_config)
|
|
29
|
+
http_client.server_url = service_url(service)
|
|
29
30
|
|
|
30
31
|
begin
|
|
31
|
-
yield(caps,
|
|
32
|
+
yield(caps, http_client) if block_given?
|
|
32
33
|
rescue Selenium::WebDriver::Error::WebDriverError
|
|
33
34
|
@service_manager&.stop
|
|
34
35
|
raise
|
|
@@ -48,16 +49,22 @@ module Selenium
|
|
|
48
49
|
raise ArgumentError, ":options must be an instance of #{default_options.class}"
|
|
49
50
|
end
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
options.browser_version = nil
|
|
56
|
-
end
|
|
57
|
-
finder.driver_path
|
|
58
|
-
end
|
|
52
|
+
finder = WebDriver::DriverFinder.new(options, service)
|
|
53
|
+
options.binary = finder.browser_path if options.respond_to?(:binary) && finder.browser_path?
|
|
54
|
+
service.executable_path = finder.driver_path
|
|
55
|
+
options.browser_version = nil if options.respond_to?(:binary) && options.binary
|
|
59
56
|
options.as_json
|
|
60
57
|
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
def assert_local_arguments(url, http_client, client_config)
|
|
62
|
+
if url || client_config&.server_url
|
|
63
|
+
raise ArgumentError, "Can't set the server URL for #{self.class}; the service provides it"
|
|
64
|
+
elsif http_client && client_config
|
|
65
|
+
raise ArgumentError, 'Cannot use both :http_client and :client_config'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
61
68
|
end
|
|
62
69
|
end
|
|
63
70
|
end
|
|
@@ -145,7 +145,7 @@ module Selenium
|
|
|
145
145
|
'proxyType' => TYPES[type].downcase,
|
|
146
146
|
'ftpProxy' => ftp,
|
|
147
147
|
'httpProxy' => http,
|
|
148
|
-
'noProxy' => no_proxy.is_a?(String) ? no_proxy.split(',
|
|
148
|
+
'noProxy' => no_proxy.is_a?(String) ? no_proxy.split(',').map(&:strip).reject(&:empty?) : no_proxy,
|
|
149
149
|
'proxyAutoconfigUrl' => pac,
|
|
150
150
|
'sslProxy' => ssl,
|
|
151
151
|
'autodetect' => auto_detect,
|
|
@@ -69,7 +69,6 @@ module Selenium
|
|
|
69
69
|
def initialize(path: nil, port: nil, log: nil, args: nil)
|
|
70
70
|
port ||= self.class::DEFAULT_PORT
|
|
71
71
|
args ||= []
|
|
72
|
-
path ||= env_path
|
|
73
72
|
|
|
74
73
|
@executable_path = path
|
|
75
74
|
@host = Platform.localhost
|
|
@@ -88,7 +87,7 @@ module Selenium
|
|
|
88
87
|
end
|
|
89
88
|
|
|
90
89
|
def launch
|
|
91
|
-
@executable_path ||=
|
|
90
|
+
@executable_path ||= DriverFinder.new(nil, self).driver_path
|
|
92
91
|
ServiceManager.new(self).tap(&:start)
|
|
93
92
|
end
|
|
94
93
|
|
|
@@ -96,15 +95,6 @@ module Selenium
|
|
|
96
95
|
self.class::SHUTDOWN_SUPPORTED
|
|
97
96
|
end
|
|
98
97
|
|
|
99
|
-
def find_driver_path
|
|
100
|
-
default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
|
|
101
|
-
DriverFinder.new(default_options, self).driver_path
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def env_path
|
|
105
|
-
ENV.fetch(self.class::DRIVER_PATH_ENV_KEY, nil)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
98
|
private
|
|
109
99
|
|
|
110
100
|
def warn_driver_log_override
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
# under the License.
|
|
19
19
|
|
|
20
20
|
require 'selenium/webdriver/common/error'
|
|
21
|
+
require 'selenium/webdriver/common/client_config'
|
|
21
22
|
require 'selenium/webdriver/common/local_driver'
|
|
22
23
|
require 'selenium/webdriver/common/driver_finder'
|
|
23
24
|
require 'selenium/webdriver/common/platform'
|
|
@@ -30,9 +30,9 @@ module Selenium
|
|
|
30
30
|
class Driver < Chromium::Driver
|
|
31
31
|
include LocalDriver
|
|
32
32
|
|
|
33
|
-
def initialize(options: nil, service: nil, url: nil, **)
|
|
34
|
-
initialize_local_driver(options, service, url) do |caps,
|
|
35
|
-
super(caps: caps,
|
|
33
|
+
def initialize(options: nil, service: nil, url: nil, http_client: nil, client_config: nil, **)
|
|
34
|
+
initialize_local_driver(options, service, url, http_client, client_config) do |caps, client|
|
|
35
|
+
super(caps: caps, http_client: client, **)
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
@@ -36,9 +36,9 @@ module Selenium
|
|
|
36
36
|
|
|
37
37
|
include LocalDriver
|
|
38
38
|
|
|
39
|
-
def initialize(options: nil, service: nil, url: nil, **)
|
|
40
|
-
initialize_local_driver(options, service, url) do |caps,
|
|
41
|
-
super(caps: caps,
|
|
39
|
+
def initialize(options: nil, service: nil, url: nil, http_client: nil, client_config: nil, **)
|
|
40
|
+
initialize_local_driver(options, service, url, http_client, client_config) do |caps, client|
|
|
41
|
+
super(caps: caps, http_client: client, **)
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -31,9 +31,9 @@ module Selenium
|
|
|
31
31
|
|
|
32
32
|
include LocalDriver
|
|
33
33
|
|
|
34
|
-
def initialize(options: nil, service: nil, url: nil, **)
|
|
35
|
-
initialize_local_driver(options, service, url) do |caps,
|
|
36
|
-
super(caps: caps,
|
|
34
|
+
def initialize(options: nil, service: nil, url: nil, http_client: nil, client_config: nil, **)
|
|
35
|
+
initialize_local_driver(options, service, url, http_client, client_config) do |caps, client|
|
|
36
|
+
super(caps: caps, http_client: client, **)
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
@@ -17,16 +17,21 @@
|
|
|
17
17
|
# specific language governing permissions and limitations
|
|
18
18
|
# under the License.
|
|
19
19
|
|
|
20
|
+
require 'selenium/webdriver/bidi'
|
|
21
|
+
require 'selenium/webdriver/bidi/transport'
|
|
22
|
+
|
|
20
23
|
module Selenium
|
|
21
24
|
module WebDriver
|
|
22
25
|
module Remote
|
|
23
26
|
class BiDiBridge < Bridge
|
|
24
|
-
attr_reader :bidi
|
|
27
|
+
attr_reader :bidi, :transport
|
|
25
28
|
|
|
26
29
|
def create_session(capabilities)
|
|
27
30
|
super
|
|
28
31
|
socket_url = @capabilities[:web_socket_url]
|
|
29
32
|
@bidi = Selenium::WebDriver::BiDi.new(url: socket_url)
|
|
33
|
+
# Share the BiDi object's socket until the bridge owns the connection directly.
|
|
34
|
+
@transport = BiDi::Transport.new(@bidi.ws)
|
|
30
35
|
end
|
|
31
36
|
|
|
32
37
|
def get(url)
|
|
@@ -51,20 +51,13 @@ module Selenium
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
#
|
|
54
|
-
#
|
|
55
|
-
# @param [String, URI] url url for the remote server
|
|
56
|
-
# @param [Object] http_client an HTTP client instance that implements the same protocol as Http::Default
|
|
54
|
+
# @param [Object] http_client a configured HTTP client implementing the same protocol as Http::Default
|
|
57
55
|
# @api private
|
|
58
56
|
#
|
|
59
57
|
|
|
60
|
-
def initialize(
|
|
61
|
-
|
|
62
|
-
uri.path += '/' unless uri.path.end_with?('/')
|
|
63
|
-
|
|
64
|
-
@http = http_client || Http::Default.new
|
|
65
|
-
@http.server_url = uri
|
|
58
|
+
def initialize(http_client:)
|
|
59
|
+
@http = http_client
|
|
66
60
|
@file_detector = nil
|
|
67
|
-
|
|
68
61
|
@locator_converter = self.class.locator_converter
|
|
69
62
|
end
|
|
70
63
|
|
|
@@ -605,6 +598,11 @@ module Selenium
|
|
|
605
598
|
raise(WebDriver::Error::WebDriverError, msg)
|
|
606
599
|
end
|
|
607
600
|
|
|
601
|
+
def transport
|
|
602
|
+
msg = 'BiDi must be enabled by setting #web_socket_url to true in options class'
|
|
603
|
+
raise(WebDriver::Error::WebDriverError, msg)
|
|
604
|
+
end
|
|
605
|
+
|
|
608
606
|
def command_list
|
|
609
607
|
COMMANDS
|
|
610
608
|
end
|
|
@@ -31,12 +31,14 @@ module Selenium
|
|
|
31
31
|
include DriverExtensions::HasFileDownloads
|
|
32
32
|
include DriverExtensions::HasSessionEvents
|
|
33
33
|
|
|
34
|
-
def initialize(capabilities: nil, options: nil, service: nil, url: nil,
|
|
35
|
-
|
|
34
|
+
def initialize(capabilities: nil, options: nil, service: nil, url: nil, http_client: nil, client_config: nil,
|
|
35
|
+
**)
|
|
36
|
+
assert_arguments(service, url, http_client, client_config)
|
|
36
37
|
|
|
37
|
-
url ||= "http://#{Platform.localhost}:4444/wd/hub"
|
|
38
38
|
caps = process_options(options, capabilities)
|
|
39
|
-
|
|
39
|
+
http_client ||= Remote::Http::Default.new(client_config: client_config)
|
|
40
|
+
http_client.server_url = url || client_config&.server_url || "http://#{Platform.localhost}:4444/wd/hub"
|
|
41
|
+
super(caps: caps, http_client: http_client, **)
|
|
40
42
|
@bridge.file_detector = ->((filename, *)) { File.exist?(filename) && filename.to_s }
|
|
41
43
|
command_list = @bridge.command_list
|
|
42
44
|
@bridge.extend(WebDriver::Remote::Features)
|
|
@@ -45,6 +47,12 @@ module Selenium
|
|
|
45
47
|
|
|
46
48
|
private
|
|
47
49
|
|
|
50
|
+
def assert_arguments(service, url, http_client, client_config)
|
|
51
|
+
raise ArgumentError, "Can not set :service object on #{self.class}" if service
|
|
52
|
+
raise ArgumentError, 'Cannot use both :http_client and :client_config' if http_client && client_config
|
|
53
|
+
raise ArgumentError, 'Cannot use both :url and a ClientConfig#server_url' if url && client_config&.server_url
|
|
54
|
+
end
|
|
55
|
+
|
|
48
56
|
def devtools_url
|
|
49
57
|
capabilities['se:cdp']
|
|
50
58
|
end
|
|
@@ -31,15 +31,32 @@ module Selenium
|
|
|
31
31
|
BINARY_ENCODINGS = [Encoding::BINARY, Encoding::ASCII_8BIT].freeze
|
|
32
32
|
|
|
33
33
|
class << self
|
|
34
|
-
attr_accessor :extra_headers
|
|
35
|
-
attr_writer :user_agent
|
|
36
|
-
|
|
37
34
|
def user_agent
|
|
38
|
-
|
|
35
|
+
ClientConfig.default_user_agent
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def user_agent=(value)
|
|
39
|
+
ClientConfig.default_user_agent = value
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def extra_headers
|
|
43
|
+
ClientConfig.default_extra_headers
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def extra_headers=(value)
|
|
47
|
+
ClientConfig.default_extra_headers = value
|
|
39
48
|
end
|
|
40
49
|
end
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
attr_reader :client_config
|
|
52
|
+
|
|
53
|
+
def initialize(client_config: nil)
|
|
54
|
+
@client_config = client_config || ClientConfig.new
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def server_url=(url)
|
|
58
|
+
client_config.server_url = url
|
|
59
|
+
end
|
|
43
60
|
|
|
44
61
|
def quit_errors
|
|
45
62
|
[IOError]
|
|
@@ -76,17 +93,13 @@ module Selenium
|
|
|
76
93
|
def common_headers
|
|
77
94
|
@common_headers ||= begin
|
|
78
95
|
headers = DEFAULT_HEADERS.dup
|
|
79
|
-
headers['User-Agent'] =
|
|
80
|
-
headers
|
|
81
|
-
|
|
82
|
-
headers
|
|
96
|
+
headers['User-Agent'] = client_config.user_agent
|
|
97
|
+
headers.merge(client_config.extra_headers || {})
|
|
83
98
|
end
|
|
84
99
|
end
|
|
85
100
|
|
|
86
101
|
def server_url
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
raise Error::WebDriverError, 'server_url not set'
|
|
102
|
+
client_config.server_url || raise(Error::WebDriverError, 'server_url not set')
|
|
90
103
|
end
|
|
91
104
|
|
|
92
105
|
def request(*)
|