selenium-webdriver 4.12.0 → 4.23.0

Sign up to get free protection for your applications and to get access to all the features.
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