selenium-webdriver 4.0.0.rc3 → 4.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +338 -0
  3. data/Gemfile +2 -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 +90 -79
  11. data/lib/selenium/webdriver/atoms/findElements.js +5 -6
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
  14. data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
  15. data/lib/selenium/webdriver/atoms.rb +2 -3
  16. data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
  17. data/lib/selenium/webdriver/bidi/browsing_context_info.rb +35 -0
  18. data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
  19. data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
  20. data/lib/selenium/webdriver/{common/driver_extensions/has_location.rb → bidi/log/filter_by.rb} +14 -11
  21. data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
  22. data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
  23. data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
  24. data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
  25. data/lib/selenium/webdriver/bidi/session.rb +51 -0
  26. data/lib/selenium/webdriver/bidi.rb +56 -0
  27. data/lib/selenium/webdriver/chrome/driver.rb +9 -29
  28. data/lib/selenium/webdriver/chrome/features.rb +6 -68
  29. data/lib/selenium/webdriver/chrome/options.rb +3 -223
  30. data/lib/selenium/webdriver/chrome/profile.rb +3 -83
  31. data/lib/selenium/webdriver/chrome/service.rb +4 -19
  32. data/lib/selenium/webdriver/chrome.rb +0 -16
  33. data/lib/selenium/webdriver/chromium/driver.rb +60 -0
  34. data/lib/selenium/webdriver/chromium/features.rb +103 -0
  35. data/lib/selenium/webdriver/chromium/options.rb +243 -0
  36. data/lib/selenium/webdriver/chromium/profile.rb +113 -0
  37. data/lib/selenium/webdriver/chromium.rb +29 -0
  38. data/lib/selenium/webdriver/common/action_builder.rb +62 -22
  39. data/lib/selenium/webdriver/common/child_process.rb +124 -0
  40. data/lib/selenium/webdriver/common/driver.rb +31 -81
  41. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
  42. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
  43. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
  44. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
  45. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
  46. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +10 -5
  47. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -1
  48. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
  49. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -4
  50. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
  51. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
  52. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
  53. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -2
  54. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +9 -2
  55. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -69
  56. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
  57. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -3
  58. data/lib/selenium/webdriver/common/driver_finder.rb +45 -0
  59. data/lib/selenium/webdriver/common/element.rb +8 -8
  60. data/lib/selenium/webdriver/common/error.rb +28 -5
  61. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
  62. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  63. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  64. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  65. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  66. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  67. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  68. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  69. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -70
  70. data/lib/selenium/webdriver/common/{driver_extensions/has_network_connection.rb → interactions/pointer_cancel.rb} +19 -11
  71. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  72. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  73. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  74. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  75. data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
  76. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  77. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  78. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  79. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  80. data/lib/selenium/webdriver/common/keys.rb +1 -0
  81. data/lib/selenium/webdriver/common/local_driver.rb +46 -0
  82. data/lib/selenium/webdriver/common/logger.rb +90 -25
  83. data/lib/selenium/webdriver/common/manager.rb +0 -27
  84. data/lib/selenium/webdriver/common/options.rb +14 -20
  85. data/lib/selenium/webdriver/common/platform.rb +5 -51
  86. data/lib/selenium/webdriver/common/port_prober.rb +1 -1
  87. data/lib/selenium/webdriver/common/profile_helper.rb +2 -8
  88. data/lib/selenium/webdriver/common/proxy.rb +2 -2
  89. data/lib/selenium/webdriver/common/search_context.rb +0 -6
  90. data/lib/selenium/webdriver/common/selenium_manager.rb +134 -0
  91. data/lib/selenium/webdriver/common/service.rb +21 -30
  92. data/lib/selenium/webdriver/common/service_manager.rb +8 -15
  93. data/lib/selenium/webdriver/common/shadow_root.rb +2 -3
  94. data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
  95. data/lib/selenium/webdriver/common/socket_poller.rb +31 -20
  96. data/lib/selenium/webdriver/common/takes_screenshot.rb +2 -3
  97. data/lib/selenium/webdriver/common/target_locator.rb +2 -3
  98. data/lib/selenium/webdriver/common/timeouts.rb +2 -2
  99. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
  100. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
  101. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  102. data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
  103. data/lib/selenium/webdriver/common/window.rb +6 -6
  104. data/lib/selenium/webdriver/common/zipper.rb +4 -10
  105. data/lib/selenium/webdriver/common.rb +21 -5
  106. data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
  107. data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
  108. data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
  109. data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
  110. data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
  111. data/lib/selenium/webdriver/devtools/request.rb +1 -3
  112. data/lib/selenium/webdriver/devtools/response.rb +1 -3
  113. data/lib/selenium/webdriver/devtools.rb +17 -103
  114. data/lib/selenium/webdriver/edge/driver.rb +9 -3
  115. data/lib/selenium/webdriver/edge/features.rb +4 -4
  116. data/lib/selenium/webdriver/edge/options.rb +17 -5
  117. data/lib/selenium/webdriver/edge/profile.rb +2 -2
  118. data/lib/selenium/webdriver/edge/service.rb +8 -7
  119. data/lib/selenium/webdriver/edge.rb +0 -2
  120. data/lib/selenium/webdriver/firefox/driver.rb +9 -2
  121. data/lib/selenium/webdriver/firefox/features.rb +6 -6
  122. data/lib/selenium/webdriver/firefox/options.rb +9 -14
  123. data/lib/selenium/webdriver/firefox/profile.rb +8 -12
  124. data/lib/selenium/webdriver/firefox/service.rb +0 -18
  125. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  126. data/lib/selenium/webdriver/firefox.rb +1 -14
  127. data/lib/selenium/webdriver/ie/driver.rb +7 -1
  128. data/lib/selenium/webdriver/ie/options.rb +2 -1
  129. data/lib/selenium/webdriver/ie/service.rb +0 -22
  130. data/lib/selenium/webdriver/ie.rb +0 -14
  131. data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +15 -8
  132. data/lib/selenium/webdriver/remote/bridge.rb +70 -37
  133. data/lib/selenium/webdriver/remote/capabilities.rb +3 -53
  134. data/lib/selenium/webdriver/remote/driver.rb +31 -14
  135. data/lib/selenium/webdriver/remote/http/common.rb +3 -3
  136. data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
  137. data/lib/selenium/webdriver/remote/http/default.rb +8 -14
  138. data/lib/selenium/webdriver/remote/response.rb +2 -3
  139. data/lib/selenium/webdriver/remote/server_error.rb +1 -1
  140. data/lib/selenium/webdriver/remote.rb +5 -5
  141. data/lib/selenium/webdriver/safari/driver.rb +7 -1
  142. data/lib/selenium/webdriver/safari/features.rb +0 -2
  143. data/lib/selenium/webdriver/safari/options.rb +5 -1
  144. data/lib/selenium/webdriver/safari/service.rb +10 -4
  145. data/lib/selenium/webdriver/safari.rb +1 -15
  146. data/lib/selenium/webdriver/support/color.rb +22 -22
  147. data/lib/selenium/webdriver/support/guards/guard.rb +0 -2
  148. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
  149. data/lib/selenium/webdriver/support/guards.rb +1 -1
  150. data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
  151. data/lib/selenium/webdriver/support/select.rb +4 -2
  152. data/lib/selenium/webdriver/version.rb +1 -1
  153. data/lib/selenium/webdriver.rb +7 -5
  154. data/selenium-webdriver.gemspec +15 -13
  155. metadata +79 -65
  156. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  157. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
  158. data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
@@ -62,22 +62,14 @@ module Selenium
62
62
  end
63
63
  end
64
64
 
65
- def bitsize
66
- @bitsize ||= if defined?(FFI::Platform::ADDRESS_SIZE)
67
- FFI::Platform::ADDRESS_SIZE
68
- elsif defined?(FFI)
69
- FFI.type_size(:pointer) == 4 ? 32 : 64
70
- elsif jruby?
71
- Integer(ENV_JAVA['sun.arch.data.model'])
72
- else
73
- 1.size == 4 ? 32 : 64
74
- end
75
- end
76
-
77
65
  def jruby?
78
66
  engine == :jruby
79
67
  end
80
68
 
69
+ def truffleruby?
70
+ engine == :truffleruby
71
+ end
72
+
81
73
  def ruby_version
82
74
  RUBY_VERSION
83
75
  end
@@ -104,8 +96,7 @@ module Selenium
104
96
  end
105
97
 
106
98
  def cygwin?
107
- RUBY_PLATFORM =~ /cygwin/
108
- !Regexp.last_match.nil?
99
+ RUBY_PLATFORM.include?('cygwin')
109
100
  end
110
101
 
111
102
  def null_device
@@ -155,43 +146,6 @@ module Selenium
155
146
  at_exit { yield if Process.pid == pid }
156
147
  end
157
148
 
158
- def find_binary(*binary_names)
159
- paths = ENV['PATH'].split(File::PATH_SEPARATOR)
160
-
161
- if windows?
162
- binary_names.map! { |n| "#{n}.exe" }
163
- binary_names.dup.each { |n| binary_names << n.gsub('exe', 'bat') }
164
- end
165
-
166
- binary_names.each do |binary_name|
167
- paths.each do |path|
168
- full_path = File.join(path, binary_name)
169
- full_path = unix_path(full_path) if windows?
170
- exe = Dir.glob(full_path).find { |f| File.executable?(f) }
171
- return exe if exe
172
- end
173
- end
174
-
175
- nil
176
- end
177
-
178
- def find_in_program_files(*binary_names)
179
- paths = [
180
- ENV['PROGRAMFILES'] || '\\Program Files',
181
- ENV['ProgramFiles(x86)'] || '\\Program Files (x86)',
182
- ENV['ProgramW6432'] || '\\Program Files'
183
- ]
184
-
185
- paths.each do |root|
186
- binary_names.each do |name|
187
- exe = File.join(root, name)
188
- return exe if File.executable?(exe)
189
- end
190
- end
191
-
192
- nil
193
- end
194
-
195
149
  def localhost
196
150
  info = Socket.getaddrinfo 'localhost', 80, Socket::AF_INET, Socket::SOCK_STREAM
197
151
 
@@ -34,7 +34,7 @@ module Selenium
34
34
  Platform.interfaces.each do |host|
35
35
  TCPServer.new(host, port).close
36
36
  rescue *IGNORED_ERRORS => e
37
- WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})")
37
+ WebDriver.logger.debug("port prober could not bind to #{host}:#{port} (#{e.message})", id: :driver_service)
38
38
  # ignored - some machines appear unable to bind to some of their interfaces
39
39
  end
40
40
 
@@ -40,7 +40,7 @@ module Selenium
40
40
  end
41
41
 
42
42
  def as_json(*)
43
- {"zip" => encoded}
43
+ {'zip' => encoded}
44
44
  end
45
45
 
46
46
  def to_json(*)
@@ -73,16 +73,10 @@ module Selenium
73
73
  def from_json(json)
74
74
  data = decoded(json)
75
75
 
76
- # can't use Tempfile here since it doesn't support File::BINARY mode on 1.8
77
- # can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
78
- tmp_dir = Dir.mktmpdir
79
- begin
80
- zip_path = File.join(tmp_dir, "webdriver-profile-duplicate-#{json.hash}.zip")
76
+ Tempfile.create do |zip_path|
81
77
  File.open(zip_path, 'wb') { |zip_file| zip_file << Base64.decode64(data) }
82
78
 
83
79
  new Zipper.unzip(zip_path)
84
- ensure
85
- FileUtils.rm_rf tmp_dir
86
80
  end
87
81
  end
88
82
  end # ClassMethods
@@ -74,7 +74,7 @@ module Selenium
74
74
  def ==(other)
75
75
  other.is_a?(self.class) && as_json == other.as_json
76
76
  end
77
- alias_method :eql?, :==
77
+ alias eql? ==
78
78
 
79
79
  def ftp=(value)
80
80
  self.type = :manual
@@ -152,7 +152,7 @@ module Selenium
152
152
  'socksUsername' => socks_username,
153
153
  'socksPassword' => socks_password,
154
154
  'socksVersion' => socks_version
155
- }.delete_if { |_k, v| v.nil? }
155
+ }.compact
156
156
 
157
157
  json_result if json_result.length > 1
158
158
  end
@@ -61,9 +61,6 @@ module Selenium
61
61
  raise ArgumentError, "cannot find element by #{how.inspect}" unless by
62
62
 
63
63
  bridge.find_element_by by, what, ref
64
- rescue Selenium::WebDriver::Error::TimeoutError
65
- # Implicit Wait times out in Edge
66
- raise Selenium::WebDriver::Error::NoSuchElementError
67
64
  end
68
65
 
69
66
  #
@@ -79,9 +76,6 @@ module Selenium
79
76
  raise ArgumentError, "cannot find elements by #{how.inspect}" unless by
80
77
 
81
78
  bridge.find_elements_by by, what, ref
82
- rescue Selenium::WebDriver::Error::TimeoutError
83
- # Implicit Wait times out in Edge
84
- []
85
79
  end
86
80
 
87
81
  private
@@ -0,0 +1,134 @@
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 'open3'
21
+
22
+ module Selenium
23
+ module WebDriver
24
+ #
25
+ # Wrapper for getting information from the Selenium Manager binaries.
26
+ # This implementation is still in beta, and may change.
27
+ # @api private
28
+ #
29
+ class SeleniumManager
30
+ class << self
31
+ attr_writer :bin_path
32
+
33
+ def bin_path
34
+ @bin_path ||= '../../../../../bin'
35
+ end
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
54
+ end
55
+
56
+ private
57
+
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
+ # @return [String] the path to the correct selenium manager
76
+ def binary
77
+ @binary ||= begin
78
+ location = ENV.fetch('SE_MANAGER_PATH', begin
79
+ directory = File.expand_path(bin_path, __FILE__)
80
+ if Platform.windows?
81
+ "#{directory}/windows/selenium-manager.exe"
82
+ elsif Platform.mac?
83
+ "#{directory}/macos/selenium-manager"
84
+ elsif Platform.linux?
85
+ "#{directory}/linux/selenium-manager"
86
+ end
87
+ end)
88
+
89
+ validate_location(location)
90
+ location
91
+ end
92
+ end
93
+
94
+ def validate_location(location)
95
+ begin
96
+ Platform.assert_file(location)
97
+ Platform.assert_executable(location)
98
+ rescue TypeError
99
+ raise Error::WebDriverError,
100
+ "Unable to locate or obtain Selenium Manager binary; #{location} is not a valid file object"
101
+ rescue Error::WebDriverError => e
102
+ raise Error::WebDriverError, "Selenium Manager binary located, but #{e.message}"
103
+ end
104
+
105
+ WebDriver.logger.debug("Selenium Manager binary found at #{location}", id: :selenium_manager)
106
+ end
107
+
108
+ def run(*command)
109
+ command += %w[--output json]
110
+ command << '--debug' if WebDriver.logger.debug?
111
+
112
+ WebDriver.logger.debug("Executing Process #{command}", id: :selenium_manager)
113
+
114
+ begin
115
+ stdout, stderr, status = Open3.capture3(*command)
116
+ rescue StandardError => e
117
+ raise Error::WebDriverError, "Unsuccessful command executed: #{command}; #{e.message}"
118
+ end
119
+
120
+ json_output = stdout.empty? ? {} : JSON.parse(stdout)
121
+ (json_output['logs'] || []).each do |log|
122
+ level = log['level'].casecmp('info').zero? ? 'debug' : log['level'].downcase
123
+ WebDriver.logger.send(level, log['message'], id: :selenium_manager)
124
+ end
125
+
126
+ result = json_output['result']
127
+ return result unless status.exitstatus.positive?
128
+
129
+ raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
130
+ end
131
+ end
132
+ end # SeleniumManager
133
+ end # WebDriver
134
+ end # Selenium
@@ -39,12 +39,13 @@ module Selenium
39
39
  def ie(**opts)
40
40
  IE::Service.new(**opts)
41
41
  end
42
- alias_method :internet_explorer, :ie
42
+ alias internet_explorer ie
43
43
 
44
44
  def edge(**opts)
45
45
  Edge::Service.new(**opts)
46
46
  end
47
- alias_method :microsoftedge, :edge
47
+ alias microsoftedge edge
48
+ alias msedge edge
48
49
 
49
50
  def safari(**opts)
50
51
  Safari::Service.new(**opts)
@@ -56,8 +57,8 @@ module Selenium
56
57
  end
57
58
  end
58
59
 
59
- attr_accessor :host
60
- attr_reader :executable_path, :port, :extra_args
60
+ attr_accessor :host, :executable_path, :port, :log, :args
61
+ alias extra_args args
61
62
 
62
63
  #
63
64
  # End users should use a class method for the desired driver, rather than using this directly.
@@ -65,47 +66,37 @@ module Selenium
65
66
  # @api private
66
67
  #
67
68
 
68
- def initialize(path: nil, port: nil, args: nil)
69
- path ||= self.class.driver_path
69
+ def initialize(path: nil, port: nil, log: nil, args: nil)
70
70
  port ||= self.class::DEFAULT_PORT
71
71
  args ||= []
72
72
 
73
- @executable_path = binary_path(path)
73
+ @executable_path = path
74
74
  @host = Platform.localhost
75
75
  @port = Integer(port)
76
-
77
- @extra_args = args.is_a?(Hash) ? extract_service_args(args) : args
76
+ @log = case log
77
+ when :stdout
78
+ $stdout
79
+ when :stderr
80
+ $stderr
81
+ else
82
+ log
83
+ end
84
+ @args = args
78
85
 
79
86
  raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
80
87
  end
81
88
 
82
89
  def launch
83
- sm = ServiceManager.new(self)
84
- sm.start
85
- sm
90
+ @executable_path ||= begin
91
+ default_options = WebDriver.const_get("#{self.class.name.split('::')[2]}::Options").new
92
+ DriverFinder.path(default_options, self.class)
93
+ end
94
+ ServiceManager.new(self).tap(&:start)
86
95
  end
87
96
 
88
97
  def shutdown_supported
89
98
  self.class::SHUTDOWN_SUPPORTED
90
99
  end
91
-
92
- protected
93
-
94
- def extract_service_args(driver_opts)
95
- driver_opts.key?(:args) ? driver_opts.delete(:args) : []
96
- end
97
-
98
- private
99
-
100
- def binary_path(path = nil)
101
- path = path.call if path.is_a?(Proc)
102
- path ||= Platform.find_binary(self.class::EXECUTABLE)
103
-
104
- raise Error::WebDriverError, self.class::MISSING_TEXT unless path
105
-
106
- Platform.assert_executable path
107
- path
108
- end
109
100
  end # Service
110
101
  end # WebDriver
111
102
  end # Selenium
@@ -40,7 +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.extra_args
43
+ @io = config.log
44
+ @extra_args = config.args
44
45
  @shutdown_supported = config.shutdown_supported
45
46
 
46
47
  raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
@@ -49,7 +50,7 @@ module Selenium
49
50
  def start
50
51
  raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
51
52
 
52
- Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
53
+ Platform.exit_hook { stop } # make sure we don't leave the server running
53
54
 
54
55
  socket_lock.locked do
55
56
  find_free_port
@@ -60,10 +61,11 @@ module Selenium
60
61
 
61
62
  def stop
62
63
  return unless @shutdown_supported
64
+ return if process_exited?
63
65
 
64
66
  stop_server
65
67
  @process.poll_for_exit STOP_TIMEOUT
66
- rescue ChildProcess::TimeoutError
68
+ rescue ChildProcess::TimeoutError, Errno::ECONNREFUSED
67
69
  nil # noop
68
70
  ensure
69
71
  stop_process
@@ -76,14 +78,10 @@ module Selenium
76
78
  private
77
79
 
78
80
  def build_process(*command)
79
- WebDriver.logger.debug("Executing Process #{command}")
81
+ WebDriver.logger.debug("Executing Process #{command}", id: :driver_service)
80
82
  @process = ChildProcess.build(*command)
81
- if WebDriver.logger.debug?
82
- @process.io.stdout = @process.io.stderr = WebDriver.logger.io
83
- elsif Platform.jruby?
84
- # Apparently we need to read the output of drivers on JRuby.
85
- @process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
86
- end
83
+ @io ||= WebDriver.logger.io if WebDriver.logger.debug?
84
+ @process.io = @io if @io
87
85
 
88
86
  @process
89
87
  end
@@ -103,8 +101,6 @@ module Selenium
103
101
 
104
102
  def start_process
105
103
  @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
106
- # NOTE: this is a bug only in Windows 7
107
- @process.leader = true unless Platform.windows?
108
104
  @process.start
109
105
  end
110
106
 
@@ -112,12 +108,9 @@ module Selenium
112
108
  return if process_exited?
113
109
 
114
110
  @process.stop STOP_TIMEOUT
115
- @process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
116
111
  end
117
112
 
118
113
  def stop_server
119
- return if process_exited?
120
-
121
114
  connect_to_server do |http|
122
115
  headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
123
116
  http.get('/shutdown', headers)
@@ -42,10 +42,10 @@ module Selenium
42
42
  def ==(other)
43
43
  other.is_a?(self.class) && ref == other.ref
44
44
  end
45
- alias_method :eql?, :==
45
+ alias eql? ==
46
46
 
47
47
  def hash
48
- @id.hash ^ @bridge.hash
48
+ [@id, @bridge].hash
49
49
  end
50
50
 
51
51
  #
@@ -81,7 +81,6 @@ module Selenium
81
81
  private
82
82
 
83
83
  attr_reader :bridge
84
-
85
84
  end # ShadowRoot
86
85
  end # WebDriver
87
86
  end # Selenium
@@ -26,6 +26,7 @@ module Selenium
26
26
  class SocketLock
27
27
  def initialize(port, timeout)
28
28
  @port = port
29
+ @server = nil
29
30
  @timeout = timeout
30
31
  end
31
32
 
@@ -66,11 +67,10 @@ module Selenium
66
67
 
67
68
  def can_lock?
68
69
  @server = TCPServer.new(Platform.localhost, @port)
69
- ChildProcess.close_on_exec @server
70
-
70
+ @server.close_on_exec = true
71
71
  true
72
72
  rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
73
- WebDriver.logger.debug("#{self}: #{e.message}")
73
+ WebDriver.logger.debug("#{self}: #{e.message}", id: :driver_service)
74
74
  false
75
75
  end
76
76
 
@@ -65,30 +65,41 @@ module Selenium
65
65
  arr << Errno::EALREADY if Platform.wsl?
66
66
  }.freeze
67
67
 
68
- def listening?
69
- addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)
70
- sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
71
- sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3])
72
-
73
- begin
74
- sock.connect_nonblock sockaddr
75
- rescue Errno::EINPROGRESS
76
- retry if socket_writable?(sock) && conn_completed?(sock)
77
- raise Errno::ECONNREFUSED
78
- rescue *CONNECTED_ERRORS
79
- # yay!
68
+ if Platform.jruby?
69
+ # we use a plain TCPSocket here since JRuby has issues closing socket
70
+ # see https://github.com/jruby/jruby/issues/5709
71
+ def listening?
72
+ TCPSocket.new(@host, @port).close
73
+ true
74
+ rescue *NOT_CONNECTED_ERRORS
75
+ false
76
+ end
77
+ else
78
+ def listening?
79
+ addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)
80
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
81
+ sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3])
82
+
83
+ begin
84
+ sock.connect_nonblock sockaddr
85
+ rescue Errno::EINPROGRESS
86
+ retry if socket_writable?(sock) && conn_completed?(sock)
87
+ raise Errno::ECONNREFUSED
88
+ rescue *CONNECTED_ERRORS
89
+ # yay!
90
+ end
91
+
92
+ sock.close
93
+ true
94
+ rescue *NOT_CONNECTED_ERRORS
95
+ sock&.close
96
+ WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}", id: :driver_service)
97
+ false
80
98
  end
81
-
82
- sock.close
83
- true
84
- rescue *NOT_CONNECTED_ERRORS
85
- sock&.close
86
- WebDriver.logger.debug("polling for socket on #{[@host, @port].inspect}")
87
- false
88
99
  end
89
100
 
90
101
  def socket_writable?(sock)
91
- IO.select(nil, [sock], nil, CONNECT_TIMEOUT)
102
+ sock.wait_writable(CONNECT_TIMEOUT)
92
103
  end
93
104
 
94
105
  def conn_completed?(sock)
@@ -32,8 +32,8 @@ module Selenium
32
32
  def save_screenshot(png_path, full_page: false)
33
33
  extension = File.extname(png_path).downcase
34
34
  if extension != '.png'
35
- WebDriver.logger.warn "name used for saved screenshot does not match file type. "\
36
- "It should end with .png extension",
35
+ WebDriver.logger.warn 'name used for saved screenshot does not match file type. ' \
36
+ 'It should end with .png extension',
37
37
  id: :screenshot
38
38
  end
39
39
  File.open(png_path, 'wb') { |f| f << screenshot_as(:png, full_page: full_page) }
@@ -60,7 +60,6 @@ module Selenium
60
60
  rescue NameError
61
61
  raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
62
62
  end
63
-
64
63
  end # TakesScreenshot
65
64
  end # WebDriver
66
65
  end # Selenium
@@ -51,9 +51,8 @@ module Selenium
51
51
  #
52
52
 
53
53
  def new_window(type = :window)
54
- unless %i[window tab].include?(type)
55
- raise ArgumentError, "Valid types are :tab and :window, received: #{type.inspect}"
56
- end
54
+ raise ArgumentError, "Valid types are :tab and :window, received: #{type.inspect}" unless %i[window
55
+ tab].include?(type)
57
56
 
58
57
  handle = @bridge.new_window(type)['handle']
59
58
 
@@ -48,7 +48,7 @@ module Selenium
48
48
  def script
49
49
  Float(@bridge.timeouts['script']) / 1000
50
50
  end
51
- alias_method :script_timeout, :script
51
+ alias script_timeout script
52
52
 
53
53
  #
54
54
  # Sets the amount of time to wait for an asynchronous script to finish
@@ -59,7 +59,7 @@ module Selenium
59
59
  def script=(seconds)
60
60
  @bridge.timeouts = {'script' => Integer(seconds * 1000)}
61
61
  end
62
- alias_method :script_timeout=, :script=
62
+ alias script_timeout= script=
63
63
 
64
64
  #
65
65
  # Gets the amount of time to wait for a page load to complete before throwing an error.