selenium-webdriver 4.45.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 +7 -0
- 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/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/common/client_config.rb +97 -0
- data/lib/selenium/webdriver/common/driver.rb +2 -2
- data/lib/selenium/webdriver/common/local_driver.rb +15 -4
- data/lib/selenium/webdriver/common/proxy.rb +1 -1
- 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/default.rb +56 -39
- data/lib/selenium/webdriver/safari/driver.rb +3 -3
- data/lib/selenium/webdriver/version.rb +1 -1
- metadata +25 -2
|
@@ -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
|
|
|
@@ -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
|
|
@@ -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
|
|
@@ -54,6 +55,16 @@ module Selenium
|
|
|
54
55
|
options.browser_version = nil if options.respond_to?(:binary) && options.binary
|
|
55
56
|
options.as_json
|
|
56
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
|
|
57
68
|
end
|
|
58
69
|
end
|
|
59
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,
|
|
@@ -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(*)
|
|
@@ -24,19 +24,41 @@ module Selenium
|
|
|
24
24
|
module Http
|
|
25
25
|
# @api private
|
|
26
26
|
class Default < Common
|
|
27
|
-
attr_writer :proxy
|
|
28
|
-
|
|
29
|
-
attr_accessor :open_timeout, :read_timeout
|
|
30
|
-
|
|
31
27
|
# Initializes object.
|
|
32
28
|
# Warning: Setting {#open_timeout} to non-nil values will cause a separate thread to spawn.
|
|
33
29
|
# Debuggers that freeze the process will not be able to evaluate any operations if that happens.
|
|
30
|
+
# @param [ClientConfig] client_config - Configuration used to build the HTTP client.
|
|
34
31
|
# @param [Numeric] open_timeout - Open timeout to apply to HTTP client.
|
|
35
32
|
# @param [Numeric] read_timeout - Read timeout (seconds) to apply to HTTP client.
|
|
36
|
-
def initialize(open_timeout: nil, read_timeout: nil)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
def initialize(client_config: nil, open_timeout: nil, read_timeout: nil)
|
|
34
|
+
if client_config && (open_timeout || read_timeout)
|
|
35
|
+
raise ArgumentError, 'Cannot use both :client_config and :open_timeout/:read_timeout'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
client_config ||= ClientConfig.new
|
|
39
|
+
client_config.open_timeout = open_timeout if open_timeout
|
|
40
|
+
client_config.read_timeout = read_timeout if read_timeout
|
|
41
|
+
super(client_config: client_config)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def open_timeout
|
|
45
|
+
client_config.open_timeout
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def read_timeout
|
|
49
|
+
client_config.read_timeout
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def open_timeout=(value)
|
|
53
|
+
client_config.open_timeout = value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def read_timeout=(value)
|
|
57
|
+
client_config.read_timeout = value
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def proxy=(value)
|
|
61
|
+
client_config.proxy = value
|
|
40
62
|
end
|
|
41
63
|
|
|
42
64
|
def close
|
|
@@ -94,16 +116,20 @@ module Selenium
|
|
|
94
116
|
end
|
|
95
117
|
|
|
96
118
|
if response.is_a? Net::HTTPRedirection
|
|
97
|
-
|
|
98
|
-
raise Error::WebDriverError, 'too many redirects' if redirects >= MAX_REDIRECTS
|
|
99
|
-
|
|
100
|
-
request(:get, URI.parse(response['Location']), DEFAULT_HEADERS.dup, nil, redirects + 1)
|
|
119
|
+
follow_redirect(response, redirects)
|
|
101
120
|
else
|
|
102
121
|
WebDriver.logger.debug(" <<< #{response.instance_variable_get(:@header).inspect}", id: :header)
|
|
103
122
|
create_response response.code, response.body, response.content_type
|
|
104
123
|
end
|
|
105
124
|
end
|
|
106
125
|
|
|
126
|
+
def follow_redirect(response, redirects)
|
|
127
|
+
WebDriver.logger.debug("Redirect to #{response['Location']}; times: #{redirects}")
|
|
128
|
+
raise Error::WebDriverError, 'too many redirects' if redirects >= client_config.max_redirects
|
|
129
|
+
|
|
130
|
+
request(:get, URI.parse(response['Location']), DEFAULT_HEADERS.dup, nil, redirects + 1)
|
|
131
|
+
end
|
|
132
|
+
|
|
107
133
|
def new_request_for(verb, url, headers, payload)
|
|
108
134
|
req = Net::HTTP.const_get(verb.to_s.capitalize).new(url.path, headers)
|
|
109
135
|
|
|
@@ -120,52 +146,43 @@ module Selenium
|
|
|
120
146
|
|
|
121
147
|
def new_http_client
|
|
122
148
|
if use_proxy?
|
|
123
|
-
url =
|
|
149
|
+
url = proxy.http
|
|
124
150
|
unless proxy.respond_to?(:http) && url
|
|
125
151
|
raise Error::WebDriverError,
|
|
126
|
-
"expected HTTP proxy, got #{
|
|
152
|
+
"expected HTTP proxy, got #{proxy.inspect}"
|
|
127
153
|
end
|
|
128
154
|
|
|
129
|
-
|
|
155
|
+
proxy_uri = URI.parse(url)
|
|
130
156
|
|
|
131
|
-
Net::HTTP.new(server_url.host, server_url.port,
|
|
157
|
+
Net::HTTP.new(server_url.host, server_url.port,
|
|
158
|
+
proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
|
|
132
159
|
else
|
|
133
160
|
Net::HTTP.new server_url.host, server_url.port
|
|
134
161
|
end
|
|
135
162
|
end
|
|
136
163
|
|
|
137
164
|
def proxy
|
|
138
|
-
|
|
139
|
-
proxy = ENV.fetch('http_proxy', nil) || ENV.fetch('HTTP_PROXY', nil)
|
|
140
|
-
no_proxy = ENV.fetch('no_proxy', nil) || ENV.fetch('NO_PROXY', nil)
|
|
141
|
-
|
|
142
|
-
if proxy
|
|
143
|
-
proxy = "http://#{proxy}" unless proxy.start_with?('http://')
|
|
144
|
-
Proxy.new(http: proxy, no_proxy: no_proxy)
|
|
145
|
-
end
|
|
146
|
-
end
|
|
165
|
+
client_config.proxy
|
|
147
166
|
end
|
|
148
167
|
|
|
149
168
|
def use_proxy?
|
|
150
169
|
return false if proxy.nil?
|
|
170
|
+
return true unless proxy.no_proxy
|
|
151
171
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
host == '*' ||
|
|
155
|
-
host == server_url.host || (
|
|
156
|
-
begin
|
|
157
|
-
IPAddr.new(host).include?(server_url.host)
|
|
158
|
-
rescue ArgumentError
|
|
159
|
-
false
|
|
160
|
-
end
|
|
161
|
-
)
|
|
162
|
-
end
|
|
172
|
+
!proxy_ignored?
|
|
173
|
+
end
|
|
163
174
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
175
|
+
def proxy_ignored?
|
|
176
|
+
proxy.no_proxy.split(',').map(&:strip).reject(&:empty?).any? do |host|
|
|
177
|
+
host == '*' || host == server_url.host || ip_match?(host)
|
|
167
178
|
end
|
|
168
179
|
end
|
|
180
|
+
|
|
181
|
+
def ip_match?(host)
|
|
182
|
+
IPAddr.new(host).include?(server_url.host)
|
|
183
|
+
rescue ArgumentError
|
|
184
|
+
false
|
|
185
|
+
end
|
|
169
186
|
end # Default
|
|
170
187
|
end # Http
|
|
171
188
|
end # Remote
|
|
@@ -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
|
|