selenium-webdriver 4.12.0 → 4.23.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +135 -1
  3. data/Gemfile +1 -0
  4. data/LICENSE +1 -1
  5. data/NOTICE +1 -1
  6. data/README.md +2 -2
  7. data/bin/linux/selenium-manager +0 -0
  8. data/bin/macos/selenium-manager +0 -0
  9. data/bin/windows/selenium-manager.exe +0 -0
  10. data/lib/selenium/server.rb +2 -1
  11. data/lib/selenium/webdriver/atoms/findElements.js +28 -27
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +6 -100
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +24 -96
  14. data/lib/selenium/webdriver/atoms.rb +6 -3
  15. data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +1 -1
  16. data/lib/selenium/webdriver/bidi/log_handler.rb +63 -0
  17. data/lib/selenium/webdriver/bidi/log_inspector.rb +5 -1
  18. data/lib/selenium/webdriver/bidi/session.rb +7 -7
  19. data/lib/selenium/webdriver/bidi/struct.rb +44 -0
  20. data/lib/selenium/webdriver/bidi.rb +10 -0
  21. data/lib/selenium/webdriver/chrome/features.rb +5 -1
  22. data/lib/selenium/webdriver/chrome/service.rb +7 -0
  23. data/lib/selenium/webdriver/chromium/driver.rb +1 -1
  24. data/lib/selenium/webdriver/chromium/features.rb +0 -4
  25. data/lib/selenium/webdriver/common/action_builder.rb +0 -4
  26. data/lib/selenium/webdriver/common/child_process.rb +8 -2
  27. data/lib/selenium/webdriver/common/driver.rb +23 -17
  28. data/lib/selenium/webdriver/common/driver_extensions/has_bidi.rb +1 -1
  29. data/lib/selenium/webdriver/common/driver_extensions/has_fedcm_dialog.rb +55 -0
  30. data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
  31. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
  32. data/lib/selenium/webdriver/common/driver_finder.rb +66 -14
  33. data/lib/selenium/webdriver/common/error.rb +22 -22
  34. data/lib/selenium/webdriver/common/fedcm/account.rb +50 -0
  35. data/lib/selenium/webdriver/common/fedcm/dialog.rb +74 -0
  36. data/lib/selenium/webdriver/common/fedcm.rb +27 -0
  37. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +0 -1
  38. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +1 -1
  39. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +2 -1
  40. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +1 -1
  41. data/lib/selenium/webdriver/common/local_driver.rb +8 -1
  42. data/lib/selenium/webdriver/common/logger.rb +7 -7
  43. data/lib/selenium/webdriver/common/manager.rb +1 -1
  44. data/lib/selenium/webdriver/common/options.rb +6 -2
  45. data/lib/selenium/webdriver/common/platform.rb +7 -1
  46. data/lib/selenium/webdriver/common/proxy.rb +2 -2
  47. data/lib/selenium/webdriver/common/script.rb +45 -0
  48. data/lib/selenium/webdriver/common/search_context.rb +10 -2
  49. data/lib/selenium/webdriver/common/selenium_manager.rb +34 -59
  50. data/lib/selenium/webdriver/common/service.rb +4 -0
  51. data/lib/selenium/webdriver/common/service_manager.rb +1 -1
  52. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  53. data/lib/selenium/webdriver/common/takes_screenshot.rb +4 -2
  54. data/lib/selenium/webdriver/common/websocket_connection.rb +12 -0
  55. data/lib/selenium/webdriver/common.rb +5 -2
  56. data/lib/selenium/webdriver/devtools/network_interceptor.rb +1 -1
  57. data/lib/selenium/webdriver/edge/features.rb +5 -1
  58. data/lib/selenium/webdriver/edge/service.rb +7 -0
  59. data/lib/selenium/webdriver/firefox/features.rb +5 -1
  60. data/lib/selenium/webdriver/firefox/options.rb +3 -0
  61. data/lib/selenium/webdriver/firefox/profile.rb +14 -6
  62. data/lib/selenium/webdriver/firefox/profiles_ini.rb +1 -1
  63. data/lib/selenium/webdriver/{common/driver_extensions/has_location.rb → ie/features.rb} +8 -10
  64. data/lib/selenium/webdriver/ie/options.rb +3 -2
  65. data/lib/selenium/webdriver/ie.rb +4 -3
  66. data/lib/selenium/webdriver/{common/driver_extensions/has_network_connection.rb → remote/bidi_bridge.rb} +18 -11
  67. data/lib/selenium/webdriver/remote/bridge/commands.rb +13 -7
  68. data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
  69. data/lib/selenium/webdriver/remote/bridge.rb +87 -69
  70. data/lib/selenium/webdriver/remote/capabilities.rb +2 -2
  71. data/lib/selenium/webdriver/remote/driver.rb +4 -0
  72. data/lib/selenium/webdriver/remote/features.rb +75 -0
  73. data/lib/selenium/webdriver/remote/http/common.rb +21 -3
  74. data/lib/selenium/webdriver/remote/response.rb +8 -33
  75. data/lib/selenium/webdriver/remote/server_error.rb +1 -1
  76. data/lib/selenium/webdriver/remote.rb +2 -0
  77. data/lib/selenium/webdriver/safari/features.rb +5 -1
  78. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  79. data/lib/selenium/webdriver/support/guards/guard.rb +14 -12
  80. data/lib/selenium/webdriver/support/guards.rb +1 -1
  81. data/lib/selenium/webdriver/version.rb +1 -1
  82. data/lib/selenium/webdriver.rb +1 -1
  83. data/selenium-webdriver.gemspec +9 -7
  84. metadata +84 -6
@@ -103,7 +103,7 @@ module Selenium
103
103
  #
104
104
  # @param [Array, Symbol] ids
105
105
  #
106
- def allow(ids)
106
+ def allow(*ids)
107
107
  @allowed += Array(ids).flatten
108
108
  end
109
109
 
@@ -112,7 +112,7 @@ module Selenium
112
112
  # Overrides default #debug to skip ignored messages by provided id
113
113
  #
114
114
  # @param [String] message
115
- # @param [Symbol, Array<Sybmol>] id
115
+ # @param [Symbol, Array<Symbol>] id
116
116
  # @yield see #deprecate
117
117
  #
118
118
  def debug(message, id: [], &block)
@@ -123,7 +123,7 @@ module Selenium
123
123
  # Used to supply information of general interest
124
124
  #
125
125
  # @param [String] message
126
- # @param [Symbol, Array<Sybmol>] id
126
+ # @param [Symbol, Array<Symbol>] id
127
127
  # @yield see #deprecate
128
128
  #
129
129
  def info(message, id: [], &block)
@@ -134,7 +134,7 @@ module Selenium
134
134
  # Used to supply information that suggests an error occurred
135
135
  #
136
136
  # @param [String] message
137
- # @param [Symbol, Array<Sybmol>] id
137
+ # @param [Symbol, Array<Symbol>] id
138
138
  # @yield see #deprecate
139
139
  #
140
140
  def error(message, id: [], &block)
@@ -145,7 +145,7 @@ module Selenium
145
145
  # Used to supply information that suggests action be taken by user
146
146
  #
147
147
  # @param [String] message
148
- # @param [Symbol, Array<Sybmol>] id
148
+ # @param [Symbol, Array<Symbol>] id
149
149
  # @yield see #deprecate
150
150
  #
151
151
  def warn(message, id: [], &block)
@@ -181,11 +181,11 @@ module Selenium
181
181
  private
182
182
 
183
183
  def create_logger(name, level:)
184
- logger = ::Logger.new($stdout)
184
+ logger = ::Logger.new($stderr)
185
185
  logger.progname = name
186
186
  logger.level = level
187
187
  logger.formatter = proc do |severity, time, progname, msg|
188
- "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n"
188
+ "#{time.strftime('%F %T')} #{severity} #{progname} #{msg}\n".force_encoding('UTF-8')
189
189
  end
190
190
 
191
191
  logger
@@ -56,7 +56,7 @@ module Selenium
56
56
  opts[:httpOnly] = http_only if http_only
57
57
 
58
58
  obj = opts.delete(:expires)
59
- opts[:expiry] = seconds_from(obj).to_i if obj
59
+ opts[:expiry] = seconds_from(obj).to_int if obj
60
60
 
61
61
  @bridge.add_cookie opts
62
62
  end
@@ -24,6 +24,8 @@ module Selenium
24
24
  set_window_rect timeouts unhandled_prompt_behavior strict_file_interactability
25
25
  web_socket_url].freeze
26
26
 
27
+ GRID_OPTIONS = %i[enable_downloads].freeze
28
+
27
29
  class << self
28
30
  attr_reader :driver_path
29
31
 
@@ -57,7 +59,7 @@ module Selenium
57
59
  @options[key]
58
60
  end
59
61
 
60
- define_method "#{key}=" do |value|
62
+ define_method :"#{key}=" do |value|
61
63
  @options[key] = value
62
64
  end
63
65
  end
@@ -104,6 +106,8 @@ module Selenium
104
106
  def as_json(*)
105
107
  options = @options.dup
106
108
 
109
+ downloads = options.delete(:enable_downloads)
110
+ options['se:downloadsEnabled'] = downloads unless downloads.nil?
107
111
  w3c_options = process_w3c_options(options)
108
112
 
109
113
  browser_options = self.class::CAPABILITIES.each_with_object({}) do |(capability_alias, capability_name), hash|
@@ -173,7 +177,7 @@ module Selenium
173
177
  end
174
178
 
175
179
  def camel_case(str)
176
- str.gsub(/_([a-z])/) { Regexp.last_match(1).upcase }
180
+ str.gsub(/_([a-z])/) { Regexp.last_match(1)&.upcase }
177
181
  end
178
182
  end # Options
179
183
  end # WebDriver
@@ -86,6 +86,10 @@ module Selenium
86
86
  os == :linux
87
87
  end
88
88
 
89
+ def unix?
90
+ os == :unix
91
+ end
92
+
89
93
  def wsl?
90
94
  return false unless linux?
91
95
 
@@ -107,7 +111,9 @@ module Selenium
107
111
  windows? && !cygwin? ? %("#{str}") : str
108
112
  end
109
113
 
110
- def cygwin_path(path, **opts)
114
+ def cygwin_path(path, only_cygwin: false, **opts)
115
+ return path if only_cygwin && !cygwin?
116
+
111
117
  flags = []
112
118
  opts.each { |k, v| flags << "--#{k}" if v }
113
119
 
@@ -49,7 +49,7 @@ module Selenium
49
49
  proxy = new
50
50
 
51
51
  ALLOWED.each do |k, v|
52
- proxy.send("#{k}=", data[v]) if data.key?(v)
52
+ proxy.send(:"#{k}=", data[v]) if data.key?(v)
53
53
  end
54
54
 
55
55
  proxy
@@ -60,7 +60,7 @@ module Selenium
60
60
 
61
61
  opts.each do |k, v|
62
62
  if ALLOWED.key?(k)
63
- send("#{k}=", v)
63
+ send(:"#{k}=", v)
64
64
  else
65
65
  not_allowed << k
66
66
  end
@@ -0,0 +1,45 @@
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 Script
23
+ def initialize(bridge)
24
+ @log_handler = BiDi::LogHandler.new(bridge.bidi)
25
+ end
26
+
27
+ # @return [int] id of the handler
28
+ def add_console_message_handler(&block)
29
+ @log_handler.add_message_handler('console', &block)
30
+ end
31
+
32
+ # @return [int] id of the handler
33
+ def add_javascript_error_handler(&block)
34
+ @log_handler.add_message_handler('javascript', &block)
35
+ end
36
+
37
+ # @param [int] id of the handler previously added
38
+ def remove_console_message_handler(id)
39
+ @log_handler.remove_message_handler(id)
40
+ end
41
+
42
+ alias remove_javascript_error_handler remove_console_message_handler
43
+ end # Script
44
+ end # WebDriver
45
+ end # Selenium
@@ -35,6 +35,14 @@ module Selenium
35
35
  xpath: 'xpath'
36
36
  }.freeze
37
37
 
38
+ class << self
39
+ attr_accessor :extra_finders
40
+
41
+ def finders
42
+ FINDERS.merge(extra_finders || {})
43
+ end
44
+ end
45
+
38
46
  #
39
47
  # Find the first element matching the given arguments
40
48
  #
@@ -57,7 +65,7 @@ module Selenium
57
65
  def find_element(*args)
58
66
  how, what = extract_args(args)
59
67
 
60
- by = FINDERS[how.to_sym]
68
+ by = SearchContext.finders[how.to_sym]
61
69
  raise ArgumentError, "cannot find element by #{how.inspect}" unless by
62
70
 
63
71
  bridge.find_element_by by, what, ref
@@ -72,7 +80,7 @@ module Selenium
72
80
  def find_elements(*args)
73
81
  how, what = extract_args(args)
74
82
 
75
- by = FINDERS[how.to_sym]
83
+ by = SearchContext.finders[how.to_sym]
76
84
  raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
77
85
 
78
86
  bridge.find_elements_by by, what, ref
@@ -34,76 +34,33 @@ module Selenium
34
34
  @bin_path ||= '../../../../../bin'
35
35
  end
36
36
 
37
- # @param [Options] options browser options.
38
- # @return [String] the path to the correct driver.
39
- def driver_path(options)
40
- command = generate_command(binary, options)
41
-
42
- output = run(*command)
43
-
44
- browser_path = output['browser_path']
45
- driver_path = output['driver_path']
46
- Platform.assert_executable driver_path
47
-
48
- if options.respond_to? :binary
49
- options.binary = browser_path
50
- options.browser_version = nil
51
- end
52
-
53
- driver_path
37
+ # @param [Array] arguments what gets sent to to Selenium Manager binary.
38
+ # @return [Hash] paths to the requested assets.
39
+ def binary_paths(*arguments)
40
+ arguments += %w[--language-binding ruby]
41
+ arguments += %w[--output json]
42
+ arguments << '--debug' if WebDriver.logger.debug?
43
+
44
+ run(binary, *arguments)
54
45
  end
55
46
 
56
47
  private
57
48
 
58
- def generate_command(binary, options)
59
- command = [binary, '--browser', options.browser_name]
60
- if options.browser_version
61
- command << '--browser-version'
62
- command << options.browser_version
63
- end
64
- if options.respond_to?(:binary) && !options.binary.nil?
65
- command << '--browser-path'
66
- command << options.binary.gsub('\\', '\\\\\\')
67
- end
68
- if options.proxy
69
- command << '--proxy'
70
- (command << options.proxy.ssl) || options.proxy.http
71
- end
72
- command
73
- end
74
-
75
49
  # @return [String] the path to the correct selenium manager
76
50
  def binary
77
51
  @binary ||= begin
78
- path = File.expand_path(bin_path, __FILE__)
79
- path << if Platform.windows?
80
- '/windows/selenium-manager.exe'
81
- elsif Platform.mac?
82
- '/macos/selenium-manager'
83
- elsif Platform.linux?
84
- '/linux/selenium-manager'
85
- end
86
- location = File.expand_path(path, __FILE__)
87
-
88
- begin
89
- Platform.assert_file(location)
90
- Platform.assert_executable(location)
91
- rescue TypeError
92
- raise Error::WebDriverError,
93
- "Unable to locate or obtain Selenium Manager binary; #{location} is not a valid file object"
94
- rescue Error::WebDriverError => e
95
- raise Error::WebDriverError, "Selenium Manager binary located, but #{e.message}"
52
+ if (location = ENV.fetch('SE_MANAGER_PATH', nil))
53
+ WebDriver.logger.debug("Selenium Manager set by ENV['SE_MANAGER_PATH']: #{location}")
96
54
  end
55
+ location ||= platform_location
97
56
 
57
+ Platform.assert_executable(location)
98
58
  WebDriver.logger.debug("Selenium Manager binary found at #{location}", id: :selenium_manager)
99
59
  location
100
60
  end
101
61
  end
102
62
 
103
63
  def run(*command)
104
- command += %w[--output json]
105
- command << '--debug' if WebDriver.logger.debug?
106
-
107
64
  WebDriver.logger.debug("Executing Process #{command}", id: :selenium_manager)
108
65
 
109
66
  begin
@@ -112,16 +69,34 @@ module Selenium
112
69
  raise Error::WebDriverError, "Unsuccessful command executed: #{command}; #{e.message}"
113
70
  end
114
71
 
115
- json_output = stdout.empty? ? {} : JSON.parse(stdout)
116
- (json_output['logs'] || []).each do |log|
72
+ json_output = stdout.empty? ? {'logs' => [], 'result' => {}} : JSON.parse(stdout)
73
+ json_output['logs'].each do |log|
117
74
  level = log['level'].casecmp('info').zero? ? 'debug' : log['level'].downcase
118
75
  WebDriver.logger.send(level, log['message'], id: :selenium_manager)
119
76
  end
120
77
 
121
78
  result = json_output['result']
122
- return result unless status.exitstatus.positive?
79
+ return result unless status.exitstatus.positive? || result.nil?
123
80
 
124
- raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
81
+ raise Error::WebDriverError,
82
+ "Unsuccessful command executed: #{command} - Code #{status.exitstatus}\n#{result}\n#{stderr}"
83
+ end
84
+
85
+ def platform_location
86
+ directory = File.expand_path(bin_path, __FILE__)
87
+ if Platform.windows?
88
+ "#{directory}/windows/selenium-manager.exe"
89
+ elsif Platform.mac?
90
+ "#{directory}/macos/selenium-manager"
91
+ elsif Platform.linux?
92
+ "#{directory}/linux/selenium-manager"
93
+ elsif Platform.unix?
94
+ WebDriver.logger.warn('Selenium Manager binary may not be compatible with Unix',
95
+ id: %i[selenium_manager unix_binary])
96
+ "#{directory}/linux/selenium-manager"
97
+ else
98
+ raise Error::WebDriverError, "unsupported platform: #{Platform.os}"
99
+ end
125
100
  end
126
101
  end
127
102
  end # SeleniumManager
@@ -87,6 +87,10 @@ module Selenium
87
87
  end
88
88
 
89
89
  def launch
90
+ @executable_path ||= begin
91
+ default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
92
+ DriverFinder.new(default_options, self).driver_path
93
+ end
90
94
  ServiceManager.new(self).tap(&:start)
91
95
  end
92
96
 
@@ -40,8 +40,8 @@ module Selenium
40
40
  @executable_path = config.executable_path
41
41
  @host = Platform.localhost
42
42
  @port = config.port
43
- @extra_args = config.args
44
43
  @io = config.log
44
+ @extra_args = config.args
45
45
  @shutdown_supported = config.shutdown_supported
46
46
 
47
47
  raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
@@ -78,7 +78,7 @@ module Selenium
78
78
  def listening?
79
79
  addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)
80
80
  sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
81
- sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3])
81
+ sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3].to_s)
82
82
 
83
83
  begin
84
84
  sock.connect_nonblock sockaddr
@@ -49,6 +49,10 @@ module Selenium
49
49
  # @api public
50
50
 
51
51
  def screenshot_as(format, full_page: false)
52
+ if full_page && !respond_to?(:save_full_page_screenshot)
53
+ raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
54
+ end
55
+
52
56
  case format
53
57
  when :base64
54
58
  full_page ? full_screenshot : screenshot
@@ -57,8 +61,6 @@ module Selenium
57
61
  else
58
62
  raise Error::UnsupportedOperationError, "unsupported format: #{format.inspect}"
59
63
  end
60
- rescue NameError
61
- raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
62
64
  end
63
65
  end # TakesScreenshot
64
66
  end # WebDriver
@@ -52,6 +52,18 @@ module Selenium
52
52
  @callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] }
53
53
  end
54
54
 
55
+ def add_callback(event, &block)
56
+ callbacks[event] << block
57
+ block.object_id
58
+ end
59
+
60
+ def remove_callback(event, id)
61
+ return if callbacks[event].reject! { |callback| callback.object_id == id }
62
+
63
+ ids = callbacks[event]&.map(&:object_id)
64
+ raise Error::WebDriverError, "Callback with ID #{id} does not exist for event #{event}: #{ids}"
65
+ end
66
+
55
67
  def send_cmd(**payload)
56
68
  id = next_id
57
69
  data = payload.merge(id: id)
@@ -68,10 +68,8 @@ require 'selenium/webdriver/common/html5/local_storage'
68
68
  require 'selenium/webdriver/common/html5/session_storage'
69
69
  require 'selenium/webdriver/common/driver_extensions/has_web_storage'
70
70
  require 'selenium/webdriver/common/driver_extensions/downloads_files'
71
- require 'selenium/webdriver/common/driver_extensions/has_location'
72
71
  require 'selenium/webdriver/common/driver_extensions/has_session_id'
73
72
  require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
74
- require 'selenium/webdriver/common/driver_extensions/has_network_connection'
75
73
  require 'selenium/webdriver/common/driver_extensions/has_network_interception'
76
74
  require 'selenium/webdriver/common/driver_extensions/has_apple_permissions'
77
75
  require 'selenium/webdriver/common/driver_extensions/has_permissions'
@@ -83,6 +81,7 @@ require 'selenium/webdriver/common/driver_extensions/full_page_screenshot'
83
81
  require 'selenium/webdriver/common/driver_extensions/has_addons'
84
82
  require 'selenium/webdriver/common/driver_extensions/has_bidi'
85
83
  require 'selenium/webdriver/common/driver_extensions/has_devtools'
84
+ require 'selenium/webdriver/common/driver_extensions/has_file_downloads'
86
85
  require 'selenium/webdriver/common/driver_extensions/has_authentication'
87
86
  require 'selenium/webdriver/common/driver_extensions/has_logs'
88
87
  require 'selenium/webdriver/common/driver_extensions/has_log_events'
@@ -90,6 +89,7 @@ require 'selenium/webdriver/common/driver_extensions/has_pinned_scripts'
90
89
  require 'selenium/webdriver/common/driver_extensions/has_cdp'
91
90
  require 'selenium/webdriver/common/driver_extensions/has_casting'
92
91
  require 'selenium/webdriver/common/driver_extensions/has_launching'
92
+ require 'selenium/webdriver/common/driver_extensions/has_fedcm_dialog'
93
93
  require 'selenium/webdriver/common/keys'
94
94
  require 'selenium/webdriver/common/profile_helper'
95
95
  require 'selenium/webdriver/common/options'
@@ -99,3 +99,6 @@ require 'selenium/webdriver/common/element'
99
99
  require 'selenium/webdriver/common/shadow_root'
100
100
  require 'selenium/webdriver/common/websocket_connection'
101
101
  require 'selenium/webdriver/common/child_process'
102
+ require 'selenium/webdriver/common/script'
103
+ require 'selenium/webdriver/common/fedcm/account'
104
+ require 'selenium/webdriver/common/fedcm/dialog'
@@ -98,7 +98,7 @@ module Selenium
98
98
  original = DevTools::Request.from(id, params)
99
99
  mutable = DevTools::Request.from(id, params)
100
100
 
101
- block.call(mutable) do |&continue| # rubocop:disable Performance/RedundantBlockCall
101
+ block.call(mutable) do |&continue|
102
102
  pending_response_requests[id] = continue
103
103
 
104
104
  if original == mutable
@@ -35,8 +35,12 @@ module Selenium
35
35
  send_command: [:post, 'session/:session_id/ms/cdp/execute']
36
36
  }.freeze
37
37
 
38
+ def command_list
39
+ EDGE_COMMANDS.merge(CHROMIUM_COMMANDS).merge(self.class::COMMANDS)
40
+ end
41
+
38
42
  def commands(command)
39
- EDGE_COMMANDS[command] || CHROMIUM_COMMANDS[command] || self.class::COMMANDS[command]
43
+ command_list[command]
40
44
  end
41
45
  end # Bridge
42
46
  end # Edge
@@ -24,6 +24,13 @@ module Selenium
24
24
  DEFAULT_PORT = 9515
25
25
  EXECUTABLE = 'msedgedriver'
26
26
  SHUTDOWN_SUPPORTED = true
27
+
28
+ def log
29
+ return @log unless @log.is_a? String
30
+
31
+ @args << "--log-path=#{@log}"
32
+ @log = nil
33
+ end
27
34
  end # Service
28
35
  end # Edge
29
36
  end # WebDriver
@@ -29,8 +29,12 @@ module Selenium
29
29
  full_page_screenshot: [:get, 'session/:session_id/moz/screenshot/full']
30
30
  }.freeze
31
31
 
32
+ def command_list
33
+ FIREFOX_COMMANDS.merge(self.class::COMMANDS)
34
+ end
35
+
32
36
  def commands(command)
33
- FIREFOX_COMMANDS[command] || self.class::COMMANDS[command]
37
+ command_list[command]
34
38
  end
35
39
 
36
40
  def install_addon(path, temporary)
@@ -64,6 +64,9 @@ module Selenium
64
64
 
65
65
  @options[:args] ||= []
66
66
  @options[:prefs] ||= {}
67
+ # Firefox 129 onwards the CDP protocol will not be enabled by default. Setting this preference will enable it.
68
+ # https://fxdx.dev/deprecating-cdp-support-in-firefox-embracing-the-future-with-webdriver-bidi/.
69
+ @options[:prefs]['remote.active-protocols'] = 3
67
70
  @options[:env] ||= {}
68
71
  @options[:log] ||= {level: log_level} if log_level
69
72
 
@@ -24,6 +24,10 @@ module Selenium
24
24
  include ProfileHelper
25
25
 
26
26
  VALID_PREFERENCE_TYPES = [TrueClass, FalseClass, Integer, Float, String].freeze
27
+ WEBDRIVER_PREFS = {
28
+ port: 'webdriver_firefox_port',
29
+ log_file: 'webdriver.log.file'
30
+ }.freeze
27
31
 
28
32
  DEFAULT_PREFERENCES = {
29
33
  'browser.newtabpage.enabled' => false,
@@ -33,8 +37,10 @@ module Selenium
33
37
  'security.csp.enable' => false
34
38
  }.freeze
35
39
 
36
- attr_reader :name, :log_file
37
- attr_writer :secure_ssl, :load_no_focus_lib
40
+ LOCK_FILES = %w[.parentlock parent.lock lock].freeze
41
+
42
+ attr_reader :name, :log_file
43
+ attr_writer :secure_ssl, :load_no_focus_lib
38
44
 
39
45
  class << self
40
46
  def ini
@@ -176,7 +182,7 @@ module Selenium
176
182
  end
177
183
 
178
184
  def delete_lock_files(directory)
179
- %w[.parentlock parent.lock].each do |name|
185
+ LOCK_FILES.each do |name|
180
186
  FileUtils.rm_f File.join(directory, name)
181
187
  end
182
188
  end
@@ -204,8 +210,8 @@ module Selenium
204
210
  File.read(path).split("\n").each do |line|
205
211
  next unless line =~ /user_pref\("([^"]+)"\s*,\s*(.+?)\);/
206
212
 
207
- key = Regexp.last_match(1).strip
208
- value = Regexp.last_match(2).strip
213
+ key = Regexp.last_match(1)&.strip
214
+ value = Regexp.last_match(2)&.strip
209
215
 
210
216
  # wrap the value in an array to make it a valid JSON string.
211
217
  prefs[key] = JSON.parse("[#{value}]").first
@@ -221,7 +227,9 @@ module Selenium
221
227
  end
222
228
  end
223
229
  end
224
- end # Profile
230
+ end
231
+
232
+ # Profile
225
233
  end # Firefox
226
234
  end # WebDriver
227
235
  end # Selenium
@@ -52,7 +52,7 @@ module Selenium
52
52
  when /^\[Profile/
53
53
  name, path = nil if path_for(name, is_relative, path)
54
54
  when /^Name=(.+)$/
55
- name = Regexp.last_match(1).strip
55
+ name = Regexp.last_match(1)&.strip
56
56
  when /^IsRelative=(.+)$/
57
57
  is_relative = Regexp.last_match(1).strip == '1'
58
58
  when /^Path=(.+)$/
@@ -17,20 +17,18 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- # TODO: Deprecated; Delete after 4.0 release
21
20
  module Selenium
22
21
  module WebDriver
23
- module DriverExtensions
24
- module HasLocation
25
- def location
26
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support getting location'
22
+ module IE
23
+ module Features
24
+ def command_list
25
+ self.class::COMMANDS
27
26
  end
28
27
 
29
- def location=(*)
30
- raise Error::UnsupportedOperationError, 'The W3C standard does not currently support setting location'
28
+ def commands(command)
29
+ command_list[command]
31
30
  end
32
- alias set_location location
33
- end # HasLocation
34
- end # DriverExtensions
31
+ end # Bridge
32
+ end # Ie
35
33
  end # WebDriver
36
34
  end # Selenium
@@ -42,7 +42,8 @@ module Selenium
42
42
  use_legacy_file_upload_dialog_handling: 'ie.useLegacyFileUploadDialogHandling',
43
43
  attach_to_edge_chrome: 'ie.edgechromium',
44
44
  edge_executable_path: 'ie.edgepath',
45
- ignore_process_match: 'ie.ignoreprocessmatch'
45
+ ignore_process_match: 'ie.ignoreprocessmatch',
46
+ silent: 'silent'
46
47
  }.freeze
47
48
  BROWSER = 'internet explorer'
48
49
 
@@ -81,7 +82,7 @@ module Selenium
81
82
 
82
83
  def initialize(**opts)
83
84
  @args = (opts.delete(:args) || []).to_set
84
- super(**opts)
85
+ super
85
86
 
86
87
  @options[:native_events] = true if @options[:native_events].nil?
87
88
  end
@@ -20,9 +20,10 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  module IE
23
- autoload :Driver, 'selenium/webdriver/ie/driver'
24
- autoload :Options, 'selenium/webdriver/ie/options'
25
- autoload :Service, 'selenium/webdriver/ie/service'
23
+ autoload :Features, 'selenium/webdriver/ie/features'
24
+ autoload :Driver, 'selenium/webdriver/ie/driver'
25
+ autoload :Options, 'selenium/webdriver/ie/options'
26
+ autoload :Service, 'selenium/webdriver/ie/service'
26
27
  end # IE
27
28
  end # WebDriver
28
29
  end # Selenium