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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +7 -0
  3. data/bin/linux/selenium-manager +0 -0
  4. data/bin/macos/selenium-manager +0 -0
  5. data/bin/windows/selenium-manager.exe +0 -0
  6. data/lib/selenium/webdriver/bidi/protocol/bluetooth.rb +465 -0
  7. data/lib/selenium/webdriver/bidi/protocol/browser.rb +222 -0
  8. data/lib/selenium/webdriver/bidi/protocol/browsing_context.rb +694 -0
  9. data/lib/selenium/webdriver/bidi/protocol/domain.rb +40 -0
  10. data/lib/selenium/webdriver/bidi/protocol/emulation.rb +334 -0
  11. data/lib/selenium/webdriver/bidi/protocol/input.rb +281 -0
  12. data/lib/selenium/webdriver/bidi/protocol/log.rb +104 -0
  13. data/lib/selenium/webdriver/bidi/protocol/network.rb +637 -0
  14. data/lib/selenium/webdriver/bidi/protocol/permissions.rb +73 -0
  15. data/lib/selenium/webdriver/bidi/protocol/script.rb +874 -0
  16. data/lib/selenium/webdriver/bidi/protocol/session.rb +241 -0
  17. data/lib/selenium/webdriver/bidi/protocol/speculation.rb +56 -0
  18. data/lib/selenium/webdriver/bidi/protocol/storage.rb +157 -0
  19. data/lib/selenium/webdriver/bidi/protocol/user_agent_client_hints.rb +83 -0
  20. data/lib/selenium/webdriver/bidi/protocol/web_extension.rb +93 -0
  21. data/lib/selenium/webdriver/bidi/protocol.rb +39 -0
  22. data/lib/selenium/webdriver/bidi/serialization/record.rb +226 -0
  23. data/lib/selenium/webdriver/bidi/serialization/union.rb +114 -0
  24. data/lib/selenium/webdriver/bidi/serialization.rb +78 -0
  25. data/lib/selenium/webdriver/bidi/support/bidi_generate.rb +936 -0
  26. data/lib/selenium/webdriver/bidi/support/check_generated.rb +55 -0
  27. data/lib/selenium/webdriver/bidi/transport.rb +52 -0
  28. data/lib/selenium/webdriver/bidi.rb +3 -0
  29. data/lib/selenium/webdriver/chrome/driver.rb +3 -3
  30. data/lib/selenium/webdriver/common/client_config.rb +97 -0
  31. data/lib/selenium/webdriver/common/driver.rb +2 -2
  32. data/lib/selenium/webdriver/common/local_driver.rb +15 -4
  33. data/lib/selenium/webdriver/common/proxy.rb +1 -1
  34. data/lib/selenium/webdriver/common.rb +1 -0
  35. data/lib/selenium/webdriver/edge/driver.rb +3 -3
  36. data/lib/selenium/webdriver/firefox/driver.rb +3 -3
  37. data/lib/selenium/webdriver/ie/driver.rb +3 -3
  38. data/lib/selenium/webdriver/remote/bidi_bridge.rb +6 -1
  39. data/lib/selenium/webdriver/remote/bridge.rb +8 -10
  40. data/lib/selenium/webdriver/remote/driver.rb +12 -4
  41. data/lib/selenium/webdriver/remote/http/common.rb +25 -12
  42. data/lib/selenium/webdriver/remote/http/default.rb +56 -39
  43. data/lib/selenium/webdriver/safari/driver.rb +3 -3
  44. data/lib/selenium/webdriver/version.rb +1 -1
  45. 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
@@ -35,6 +35,9 @@ module Selenium
35
35
  @ws = WebSocketConnection.new(url: url)
36
36
  end
37
37
 
38
+ # @api private
39
+ attr_reader :ws
40
+
38
41
  def close
39
42
  @ws.close
40
43
  end
@@ -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, driver_url|
35
- super(caps: caps, url: driver_url, **)
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:, url:, http_client: nil)
322
+ def create_bridge(caps:, http_client:)
323
323
  klass = caps['webSocketUrl'] ? Remote::BiDiBridge : Remote::Bridge
324
- klass.new(http_client: http_client, url: url).tap do |bridge|
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
- raise ArgumentError, "Can't initialize #{self.class} with :url" if url
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
- url = service_url(service)
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, url) if block_given?
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(', ') : no_proxy,
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, driver_url|
35
- super(caps: caps, url: driver_url, **)
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, driver_url|
41
- super(caps: caps, url: driver_url, **)
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, driver_url|
36
- super(caps: caps, url: driver_url, **)
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
- # Initializes the bridge with the given server URL
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(url:, http_client: nil)
61
- uri = url.is_a?(URI) ? url : URI.parse(url)
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
- raise ArgumentError, "Can not set :service object on #{self.class}" if service
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
- super(caps: caps, url: url, **)
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
- @user_agent ||= "selenium/#{WebDriver::VERSION} (ruby #{Platform.os})"
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
- attr_writer :server_url
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'] = Common.user_agent
80
- headers = headers.merge(Common.extra_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
- return @server_url if @server_url
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
- @open_timeout = open_timeout
38
- @read_timeout = read_timeout
39
- super()
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
- WebDriver.logger.debug("Redirect to #{response['Location']}; times: #{redirects}")
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 = @proxy.http
149
+ url = proxy.http
124
150
  unless proxy.respond_to?(:http) && url
125
151
  raise Error::WebDriverError,
126
- "expected HTTP proxy, got #{@proxy.inspect}"
152
+ "expected HTTP proxy, got #{proxy.inspect}"
127
153
  end
128
154
 
129
- proxy = URI.parse(url)
155
+ proxy_uri = URI.parse(url)
130
156
 
131
- Net::HTTP.new(server_url.host, server_url.port, proxy.host, proxy.port, proxy.user, proxy.password)
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
- @proxy ||= begin
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
- if proxy.no_proxy
153
- ignored = proxy.no_proxy.split(',').any? do |host|
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
- !ignored
165
- else
166
- true
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, driver_url|
36
- super(caps: caps, url: driver_url, **)
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
 
@@ -19,6 +19,6 @@
19
19
 
20
20
  module Selenium
21
21
  module WebDriver
22
- VERSION = '4.45.0'
22
+ VERSION = '4.46.0'
23
23
  end # WebDriver
24
24
  end # Selenium