selenium-webdriver 3.142.7 → 4.9.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 (186) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES +583 -5
  3. data/Gemfile +3 -1
  4. data/LICENSE +1 -1
  5. data/NOTICE +2 -0
  6. data/README.md +4 -5
  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 +93 -78
  11. data/lib/selenium/webdriver/atoms/findElements.js +122 -0
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +100 -7
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +76 -78
  14. data/lib/selenium/webdriver/atoms/mutationListener.js +55 -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/bidi/log/filter_by.rb +40 -0
  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/{common/keyboard.rb → bidi.rb} +21 -35
  27. data/lib/selenium/webdriver/chrome/driver.rb +9 -86
  28. data/lib/selenium/webdriver/chrome/features.rb +44 -0
  29. data/lib/selenium/webdriver/chrome/options.rb +9 -158
  30. data/lib/selenium/webdriver/chrome/profile.rb +3 -80
  31. data/lib/selenium/webdriver/chrome/service.rb +6 -33
  32. data/lib/selenium/webdriver/chrome.rb +5 -18
  33. data/lib/selenium/webdriver/chromium/driver.rb +61 -0
  34. data/lib/selenium/webdriver/{chrome/bridge.rb → chromium/features.rb} +51 -16
  35. data/lib/selenium/webdriver/chromium/options.rb +261 -0
  36. data/lib/selenium/webdriver/chromium/profile.rb +113 -0
  37. data/lib/selenium/webdriver/chromium/service.rb +42 -0
  38. data/lib/selenium/webdriver/chromium.rb +32 -0
  39. data/lib/selenium/webdriver/common/action_builder.rb +135 -237
  40. data/lib/selenium/webdriver/common/child_process.rb +124 -0
  41. data/lib/selenium/webdriver/common/driver.rb +94 -43
  42. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
  43. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +42 -0
  44. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
  45. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +49 -0
  46. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +87 -0
  47. data/lib/selenium/webdriver/common/driver_extensions/{has_touch_screen.rb → has_bidi.rb} +9 -9
  48. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +86 -0
  49. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +36 -0
  50. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +42 -0
  51. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
  52. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +41 -0
  53. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +36 -0
  54. data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +5 -9
  55. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +143 -0
  56. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_logs.rb} +4 -4
  57. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +16 -1
  58. data/lib/selenium/webdriver/common/driver_extensions/has_network_connection.rb +6 -27
  59. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +69 -0
  60. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +11 -13
  61. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +75 -0
  62. data/lib/selenium/webdriver/common/driver_extensions/{rotatable.rb → prints_page.rb} +18 -20
  63. data/lib/selenium/webdriver/common/driver_finder.rb +43 -0
  64. data/lib/selenium/webdriver/common/element.rb +89 -29
  65. data/lib/selenium/webdriver/common/error.rb +32 -198
  66. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
  67. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  68. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -22
  69. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  70. data/lib/selenium/webdriver/common/interactions/key_actions.rb +10 -6
  71. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  72. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  73. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  74. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +71 -82
  75. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  76. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  77. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  78. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  79. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  80. data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
  81. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  82. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  83. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  84. data/lib/selenium/webdriver/common/{w3c_manager.rb → interactions/wheel_input.rb} +14 -17
  85. data/lib/selenium/webdriver/common/keys.rb +1 -0
  86. data/lib/selenium/webdriver/common/local_driver.rb +55 -0
  87. data/lib/selenium/webdriver/common/log_entry.rb +2 -2
  88. data/lib/selenium/webdriver/common/logger.rb +58 -15
  89. data/lib/selenium/webdriver/common/manager.rb +11 -38
  90. data/lib/selenium/webdriver/common/options.rb +169 -23
  91. data/lib/selenium/webdriver/common/platform.rb +14 -6
  92. data/lib/selenium/webdriver/common/port_prober.rb +4 -6
  93. data/lib/selenium/webdriver/common/profile_helper.rb +11 -9
  94. data/lib/selenium/webdriver/common/proxy.rb +7 -4
  95. data/lib/selenium/webdriver/common/search_context.rb +7 -9
  96. data/lib/selenium/webdriver/common/selenium_manager.rb +108 -0
  97. data/lib/selenium/webdriver/common/service.rb +17 -136
  98. data/lib/selenium/webdriver/common/service_manager.rb +142 -0
  99. data/lib/selenium/webdriver/common/shadow_root.rb +86 -0
  100. data/lib/selenium/webdriver/common/socket_lock.rb +4 -4
  101. data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
  102. data/lib/selenium/webdriver/common/takes_screenshot.rb +65 -0
  103. data/lib/selenium/webdriver/common/target_locator.rb +31 -4
  104. data/lib/selenium/webdriver/common/timeouts.rb +31 -4
  105. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
  106. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
  107. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  108. data/lib/selenium/webdriver/common/wait.rb +1 -1
  109. data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
  110. data/lib/selenium/webdriver/common/window.rb +6 -10
  111. data/lib/selenium/webdriver/common/zipper.rb +4 -10
  112. data/lib/selenium/webdriver/common.rb +42 -18
  113. data/lib/selenium/webdriver/devtools/console_event.rb +36 -0
  114. data/lib/selenium/webdriver/devtools/exception_event.rb +34 -0
  115. data/lib/selenium/webdriver/devtools/mutation_event.rb +35 -0
  116. data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
  117. data/lib/selenium/webdriver/devtools/pinned_script.rb +57 -0
  118. data/lib/selenium/webdriver/devtools/request.rb +65 -0
  119. data/lib/selenium/webdriver/devtools/response.rb +64 -0
  120. data/lib/selenium/webdriver/devtools.rb +96 -0
  121. data/lib/selenium/webdriver/edge/driver.rb +11 -27
  122. data/lib/selenium/webdriver/edge/features.rb +44 -0
  123. data/lib/selenium/webdriver/edge/options.rb +10 -49
  124. data/lib/selenium/webdriver/edge/profile.rb +33 -0
  125. data/lib/selenium/webdriver/edge/service.rb +7 -27
  126. data/lib/selenium/webdriver/edge.rb +11 -14
  127. data/lib/selenium/webdriver/firefox/driver.rb +38 -19
  128. data/lib/selenium/webdriver/firefox/extension.rb +8 -0
  129. data/lib/selenium/webdriver/firefox/features.rb +66 -0
  130. data/lib/selenium/webdriver/firefox/options.rb +77 -50
  131. data/lib/selenium/webdriver/firefox/profile.rb +16 -70
  132. data/lib/selenium/webdriver/firefox/service.rb +3 -13
  133. data/lib/selenium/webdriver/firefox/util.rb +1 -1
  134. data/lib/selenium/webdriver/firefox.rb +17 -28
  135. data/lib/selenium/webdriver/ie/driver.rb +5 -45
  136. data/lib/selenium/webdriver/ie/options.rb +15 -46
  137. data/lib/selenium/webdriver/ie/service.rb +11 -19
  138. data/lib/selenium/webdriver/ie.rb +3 -16
  139. data/lib/selenium/webdriver/remote/bridge/commands.rb +170 -0
  140. data/lib/selenium/webdriver/remote/bridge.rb +590 -86
  141. data/lib/selenium/webdriver/remote/capabilities.rb +182 -124
  142. data/lib/selenium/webdriver/remote/driver.rb +30 -15
  143. data/lib/selenium/webdriver/remote/http/common.rb +0 -5
  144. data/lib/selenium/webdriver/remote/http/curb.rb +0 -2
  145. data/lib/selenium/webdriver/remote/http/default.rb +23 -31
  146. data/lib/selenium/webdriver/remote/response.rb +17 -49
  147. data/lib/selenium/webdriver/remote.rb +14 -12
  148. data/lib/selenium/webdriver/safari/driver.rb +7 -29
  149. data/lib/selenium/webdriver/safari/{bridge.rb → features.rb} +3 -5
  150. data/lib/selenium/webdriver/safari/options.rb +12 -27
  151. data/lib/selenium/webdriver/safari/service.rb +3 -11
  152. data/lib/selenium/webdriver/safari.rb +14 -20
  153. data/lib/selenium/webdriver/support/block_event_listener.rb +1 -1
  154. data/lib/selenium/webdriver/support/color.rb +22 -22
  155. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  156. data/lib/selenium/webdriver/support/guards/guard.rb +87 -0
  157. data/lib/selenium/webdriver/{firefox/marionette/bridge.rb → support/guards/guard_condition.rb} +21 -20
  158. data/lib/selenium/webdriver/support/guards.rb +95 -0
  159. data/lib/selenium/webdriver/support/relative_locator.rb +50 -0
  160. data/lib/selenium/webdriver/support/select.rb +6 -4
  161. data/lib/selenium/webdriver/support.rb +1 -0
  162. data/lib/selenium/webdriver/version.rb +1 -1
  163. data/lib/selenium/webdriver.rb +17 -17
  164. data/selenium-webdriver.gemspec +36 -18
  165. metadata +164 -94
  166. data/lib/selenium/webdriver/common/bridge_helper.rb +0 -82
  167. data/lib/selenium/webdriver/common/driver_extensions/takes_screenshot.rb +0 -64
  168. data/lib/selenium/webdriver/common/mouse.rb +0 -89
  169. data/lib/selenium/webdriver/common/touch_action_builder.rb +0 -78
  170. data/lib/selenium/webdriver/common/touch_screen.rb +0 -123
  171. data/lib/selenium/webdriver/common/w3c_action_builder.rb +0 -212
  172. data/lib/selenium/webdriver/edge/bridge.rb +0 -76
  173. data/lib/selenium/webdriver/firefox/binary.rb +0 -187
  174. data/lib/selenium/webdriver/firefox/extension/prefs.json +0 -69
  175. data/lib/selenium/webdriver/firefox/extension/webdriver.xpi +0 -0
  176. data/lib/selenium/webdriver/firefox/launcher.rb +0 -111
  177. data/lib/selenium/webdriver/firefox/legacy/driver.rb +0 -83
  178. data/lib/selenium/webdriver/firefox/marionette/driver.rb +0 -90
  179. data/lib/selenium/webdriver/firefox/native/linux/amd64/x_ignore_nofocus.so +0 -0
  180. data/lib/selenium/webdriver/firefox/native/linux/x86/x_ignore_nofocus.so +0 -0
  181. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -60
  182. data/lib/selenium/webdriver/remote/oss/bridge.rb +0 -594
  183. data/lib/selenium/webdriver/remote/oss/commands.rb +0 -223
  184. data/lib/selenium/webdriver/remote/w3c/bridge.rb +0 -605
  185. data/lib/selenium/webdriver/remote/w3c/capabilities.rb +0 -310
  186. data/lib/selenium/webdriver/remote/w3c/commands.rb +0 -157
@@ -0,0 +1,108 @@
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
+ BIN_PATH = '../../../../../bin'
31
+
32
+ class << self
33
+ # @param [Options] options browser options.
34
+ # @return [String] the path to the correct driver.
35
+ def driver_path(options)
36
+ message = 'applicable driver not found; attempting to install with Selenium Manager'
37
+ WebDriver.logger.warn(message)
38
+
39
+ unless options.is_a?(Options)
40
+ raise ArgumentError, "SeleniumManager requires a WebDriver::Options instance, not #{options.inspect}"
41
+ end
42
+
43
+ command = [binary, '--browser', options.browser_name, '--output', 'json']
44
+ if options.browser_version
45
+ command << '--browser-version'
46
+ command << options.browser_version
47
+ end
48
+ if options.respond_to?(:binary) && !options.binary.nil?
49
+ command << '--browser-path'
50
+ command << options.binary.gsub('\\', '\\\\\\')
51
+ end
52
+ command << '--debug' if WebDriver.logger.debug?
53
+
54
+ location = run(*command)
55
+ WebDriver.logger.debug("Driver found at #{location}")
56
+ Platform.assert_executable location
57
+
58
+ location
59
+ end
60
+
61
+ private
62
+
63
+ # @return [String] the path to the correct selenium manager
64
+ def binary
65
+ @binary ||= begin
66
+ path = File.expand_path(BIN_PATH, __FILE__)
67
+ path << if Platform.windows?
68
+ '/windows/selenium-manager.exe'
69
+ elsif Platform.mac?
70
+ '/macos/selenium-manager'
71
+ elsif Platform.linux?
72
+ '/linux/selenium-manager'
73
+ end
74
+ location = File.expand_path(path, __FILE__)
75
+ unless location.is_a?(String) && File.exist?(location) && File.executable?(location)
76
+ raise Error::WebDriverError, 'Unable to obtain Selenium Manager'
77
+ end
78
+
79
+ WebDriver.logger.debug("Selenium Manager found at #{location}")
80
+ location
81
+ end
82
+ end
83
+
84
+ def run(*command)
85
+ WebDriver.logger.debug("Executing Process #{command}")
86
+
87
+ begin
88
+ stdout, stderr, status = Open3.capture3(*command)
89
+ json_output = stdout.empty? ? nil : JSON.parse(stdout)
90
+ result = json_output&.dig('result', 'message')
91
+ rescue StandardError => e
92
+ raise Error::WebDriverError, "Unsuccessful command executed: #{command}", e.message
93
+ end
94
+
95
+ if status.exitstatus.positive?
96
+ raise Error::WebDriverError, "Unsuccessful command executed: #{command}\n#{result}#{stderr}"
97
+ end
98
+
99
+ json_output['logs'].each do |log|
100
+ WebDriver.logger.send(log['level'].downcase, log['message'])
101
+ end
102
+
103
+ result
104
+ end
105
+ end
106
+ end # SeleniumManager
107
+ end # WebDriver
108
+ end # Selenium
@@ -21,51 +21,31 @@ module Selenium
21
21
  module WebDriver
22
22
  #
23
23
  # Base class implementing default behavior of service object,
24
- # responsible for starting and stopping driver implementations.
24
+ # responsible for storing a service manager configuration.
25
25
  #
26
26
 
27
27
  class Service
28
- START_TIMEOUT = 20
29
- SOCKET_LOCK_TIMEOUT = 45
30
- STOP_TIMEOUT = 20
31
-
32
- @default_port = nil
33
- @driver_path = nil
34
- @executable = nil
35
- @missing_text = nil
36
-
37
28
  class << self
38
- attr_reader :default_port, :driver_path, :executable, :missing_text, :shutdown_supported
29
+ attr_reader :driver_path
39
30
 
40
31
  def chrome(**opts)
41
32
  Chrome::Service.new(**opts)
42
33
  end
43
34
 
44
35
  def firefox(**opts)
45
- binary_path = Firefox::Binary.path
46
- args = opts.delete(:args)
47
- case args
48
- when Hash
49
- args[:binary] ||= binary_path
50
- opts[:args] = args
51
- when Array
52
- opts[:args] = ["--binary=#{binary_path}"]
53
- opts[:args] += args
54
- else
55
- opts[:args] = ["--binary=#{binary_path}"]
56
- end
57
-
58
36
  Firefox::Service.new(**opts)
59
37
  end
60
38
 
61
39
  def ie(**opts)
62
40
  IE::Service.new(**opts)
63
41
  end
64
- alias_method :internet_explorer, :ie
42
+ alias internet_explorer ie
65
43
 
66
44
  def edge(**opts)
67
45
  Edge::Service.new(**opts)
68
46
  end
47
+ alias microsoftedge edge
48
+ alias msedge edge
69
49
 
70
50
  def safari(**opts)
71
51
  Safari::Service.new(**opts)
@@ -77,8 +57,8 @@ module Selenium
77
57
  end
78
58
  end
79
59
 
80
- attr_accessor :host
81
- attr_reader :executable_path
60
+ attr_accessor :host, :executable_path, :port, :args
61
+ alias extra_args args
82
62
 
83
63
  #
84
64
  # End users should use a class method for the desired driver, rather than using this directly.
@@ -87,133 +67,34 @@ module Selenium
87
67
  #
88
68
 
89
69
  def initialize(path: nil, port: nil, args: nil)
90
- path ||= self.class.driver_path
91
- port ||= self.class.default_port
70
+ port ||= self.class::DEFAULT_PORT
92
71
  args ||= []
93
72
 
94
- @executable_path = binary_path(path)
73
+ @executable_path = path
95
74
  @host = Platform.localhost
96
75
  @port = Integer(port)
97
76
 
98
- @extra_args = args.is_a?(Hash) ? extract_service_args(args) : args
77
+ @args = args.is_a?(Hash) ? extract_service_args(args) : args
99
78
 
100
79
  raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
101
80
  end
102
81
 
103
- def start
104
- raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
105
-
106
- Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
107
-
108
- socket_lock.locked do
109
- find_free_port
110
- start_process
111
- connect_until_stable
112
- end
113
- end
114
-
115
- def stop
116
- return unless self.class.shutdown_supported
117
-
118
- stop_server
119
- @process.poll_for_exit STOP_TIMEOUT
120
- rescue ChildProcess::TimeoutError
121
- nil # noop
122
- ensure
123
- stop_process
124
- end
125
-
126
- def uri
127
- @uri ||= URI.parse("http://#{@host}:#{@port}")
128
- end
129
-
130
- private
131
-
132
- def binary_path(path = nil)
133
- path = path.call if path.is_a?(Proc)
134
- path ||= Platform.find_binary(self.class.executable)
135
-
136
- raise Error::WebDriverError, self.class.missing_text unless path
137
-
138
- Platform.assert_executable path
139
- path
140
- end
141
-
142
- def build_process(*command)
143
- WebDriver.logger.debug("Executing Process #{command}")
144
- @process = ChildProcess.build(*command)
145
- if WebDriver.logger.debug?
146
- @process.io.stdout = @process.io.stderr = WebDriver.logger.io
147
- elsif Platform.jruby?
148
- # Apparently we need to read the output of drivers on JRuby.
149
- @process.io.stdout = @process.io.stderr = File.new(Platform.null_device, 'w')
150
- end
151
-
152
- @process
153
- end
154
-
155
- def connect_to_server
156
- Net::HTTP.start(@host, @port) do |http|
157
- http.open_timeout = STOP_TIMEOUT / 2
158
- http.read_timeout = STOP_TIMEOUT / 2
159
-
160
- yield http
161
- end
162
- end
163
-
164
- def find_free_port
165
- @port = PortProber.above(@port)
166
- end
167
-
168
- def start_process
169
- @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
170
- # Note: this is a bug only in Windows 7
171
- @process.leader = true unless Platform.windows?
172
- @process.start
173
- end
174
-
175
- def stop_process
176
- return if process_exited?
177
-
178
- @process.stop STOP_TIMEOUT
179
- @process.io.stdout.close if Platform.jruby? && !WebDriver.logger.debug?
180
- end
181
-
182
- def stop_server
183
- return if process_exited?
184
-
185
- connect_to_server { |http| http.get('/shutdown') }
186
- end
187
-
188
- def process_running?
189
- defined?(@process) && @process&.alive?
82
+ def launch
83
+ ServiceManager.new(self).tap(&:start)
190
84
  end
191
85
 
192
- def process_exited?
193
- @process.nil? || @process.exited?
194
- end
195
-
196
- def connect_until_stable
197
- socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
198
- return if socket_poller.connected?
199
-
200
- raise Error::WebDriverError, cannot_connect_error_text
201
- end
202
-
203
- def cannot_connect_error_text
204
- "unable to connect to #{self.class.executable} #{@host}:#{@port}"
205
- end
206
-
207
- def socket_lock
208
- @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
86
+ def shutdown_supported
87
+ self.class::SHUTDOWN_SUPPORTED
209
88
  end
210
89
 
211
90
  protected
212
91
 
213
92
  def extract_service_args(driver_opts)
93
+ WebDriver.logger.deprecate('initializing Service class with :args using Hash',
94
+ ':args parameter with an Array of String values',
95
+ id: :driver_opts)
214
96
  driver_opts.key?(:args) ? driver_opts.delete(:args) : []
215
97
  end
216
-
217
98
  end # Service
218
99
  end # WebDriver
219
100
  end # Selenium
@@ -0,0 +1,142 @@
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
+ #
23
+ # Base class implementing default behavior of service_manager object,
24
+ # responsible for starting and stopping driver implementations.
25
+ #
26
+ # @api private
27
+ #
28
+ class ServiceManager
29
+ START_TIMEOUT = 20
30
+ SOCKET_LOCK_TIMEOUT = 45
31
+ STOP_TIMEOUT = 20
32
+
33
+ #
34
+ # End users should use a class method for the desired driver, rather than using this directly.
35
+ #
36
+ # @api private
37
+ #
38
+
39
+ def initialize(config)
40
+ @executable_path = config.executable_path
41
+ @host = Platform.localhost
42
+ @port = config.port
43
+ @extra_args = config.args
44
+ @shutdown_supported = config.shutdown_supported
45
+
46
+ raise Error::WebDriverError, "invalid port: #{@port}" if @port < 1
47
+ end
48
+
49
+ def start
50
+ raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
51
+
52
+ Platform.exit_hook { stop } # make sure we don't leave the server running
53
+
54
+ socket_lock.locked do
55
+ find_free_port
56
+ start_process
57
+ connect_until_stable
58
+ end
59
+ end
60
+
61
+ def stop
62
+ return unless @shutdown_supported
63
+ return if process_exited?
64
+
65
+ stop_server
66
+ @process.poll_for_exit STOP_TIMEOUT
67
+ rescue ChildProcess::TimeoutError, Errno::ECONNREFUSED
68
+ nil # noop
69
+ ensure
70
+ stop_process
71
+ end
72
+
73
+ def uri
74
+ @uri ||= URI.parse("http://#{@host}:#{@port}")
75
+ end
76
+
77
+ private
78
+
79
+ def build_process(*command)
80
+ WebDriver.logger.debug("Executing Process #{command}")
81
+ @process = ChildProcess.build(*command)
82
+ @process.io = WebDriver.logger.io if WebDriver.logger.debug?
83
+
84
+ @process
85
+ end
86
+
87
+ def connect_to_server
88
+ Net::HTTP.start(@host, @port) do |http|
89
+ http.open_timeout = STOP_TIMEOUT / 2
90
+ http.read_timeout = STOP_TIMEOUT / 2
91
+
92
+ yield http
93
+ end
94
+ end
95
+
96
+ def find_free_port
97
+ @port = PortProber.above(@port)
98
+ end
99
+
100
+ def start_process
101
+ @process = build_process(@executable_path, "--port=#{@port}", *@extra_args)
102
+ @process.start
103
+ end
104
+
105
+ def stop_process
106
+ return if process_exited?
107
+
108
+ @process.stop STOP_TIMEOUT
109
+ end
110
+
111
+ def stop_server
112
+ connect_to_server do |http|
113
+ headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
114
+ http.get('/shutdown', headers)
115
+ end
116
+ end
117
+
118
+ def process_running?
119
+ defined?(@process) && @process&.alive?
120
+ end
121
+
122
+ def process_exited?
123
+ @process.nil? || @process.exited?
124
+ end
125
+
126
+ def connect_until_stable
127
+ socket_poller = SocketPoller.new @host, @port, START_TIMEOUT
128
+ return if socket_poller.connected?
129
+
130
+ raise Error::WebDriverError, cannot_connect_error_text
131
+ end
132
+
133
+ def cannot_connect_error_text
134
+ "unable to connect to #{@executable_path} #{@host}:#{@port}"
135
+ end
136
+
137
+ def socket_lock
138
+ @socket_lock ||= SocketLock.new(@port - 1, SOCKET_LOCK_TIMEOUT)
139
+ end
140
+ end # Service
141
+ end # WebDriver
142
+ end # Selenium
@@ -0,0 +1,86 @@
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 ShadowRoot
23
+ ROOT_KEY = 'shadow-6066-11e4-a52e-4f735466cecf'
24
+
25
+ include SearchContext
26
+
27
+ #
28
+ # Creates a new shadow root
29
+ #
30
+ # @api private
31
+ #
32
+
33
+ def initialize(bridge, id)
34
+ @bridge = bridge
35
+ @id = id
36
+ end
37
+
38
+ def inspect
39
+ format '#<%<class>s:0x%<hash>x id=%<id>s>', class: self.class, hash: hash * 2, id: @id.inspect
40
+ end
41
+
42
+ def ==(other)
43
+ other.is_a?(self.class) && ref == other.ref
44
+ end
45
+ alias eql? ==
46
+
47
+ def hash
48
+ [@id, @bridge].hash
49
+ end
50
+
51
+ #
52
+ # @api private
53
+ # @see SearchContext
54
+ #
55
+
56
+ def ref
57
+ [:shadow_root, @id]
58
+ end
59
+
60
+ #
61
+ # Convert to a ShadowRoot JSON Object for transmission over the wire.
62
+ # @see https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#basic-terms-and-concepts
63
+ #
64
+ # @api private
65
+ #
66
+
67
+ def to_json(*)
68
+ JSON.generate as_json
69
+ end
70
+
71
+ #
72
+ # For Rails 3 - http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/
73
+ #
74
+ # @api private
75
+ #
76
+
77
+ def as_json(*)
78
+ {ROOT_KEY => @id}
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :bridge
84
+ end # ShadowRoot
85
+ end # WebDriver
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
- rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => ex
73
- WebDriver.logger.debug("#{self}: #{ex.message}")
72
+ rescue SocketError, Errno::EADDRINUSE, Errno::EBADF => e
73
+ WebDriver.logger.debug("#{self}: #{e.message}")
74
74
  false
75
75
  end
76
76
 
@@ -66,8 +66,8 @@ module Selenium
66
66
  }.freeze
67
67
 
68
68
  if Platform.jruby?
69
- # we use a plain TCPSocket here since JRuby has issues select()ing on a connecting socket
70
- # see http://jira.codehaus.org/browse/JRUBY-5165
69
+ # we use a plain TCPSocket here since JRuby has issues closing socket
70
+ # see https://github.com/jruby/jruby/issues/5709
71
71
  def listening?
72
72
  TCPSocket.new(@host, @port).close
73
73
  true
@@ -99,7 +99,7 @@ module Selenium
99
99
  end
100
100
 
101
101
  def socket_writable?(sock)
102
- IO.select(nil, [sock], nil, CONNECT_TIMEOUT)
102
+ sock.wait_writable(CONNECT_TIMEOUT)
103
103
  end
104
104
 
105
105
  def conn_completed?(sock)
@@ -0,0 +1,65 @@
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
+ #
23
+ # @api private
24
+ #
25
+ module TakesScreenshot
26
+ #
27
+ # Save a PNG screenshot of the viewport to the given path
28
+ #
29
+ # @api public
30
+ #
31
+
32
+ def save_screenshot(png_path, full_page: false)
33
+ extension = File.extname(png_path).downcase
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',
37
+ id: :screenshot
38
+ end
39
+ File.open(png_path, 'wb') { |f| f << screenshot_as(:png, full_page: full_page) }
40
+ end
41
+
42
+ #
43
+ # Return a PNG screenshot in the given format as a string
44
+ #
45
+ # @param [:base64, :png] format
46
+ # @param [Boolean] full_page allows taking full page screenshots if supported
47
+ # @return String screenshot
48
+ #
49
+ # @api public
50
+
51
+ def screenshot_as(format, full_page: false)
52
+ case format
53
+ when :base64
54
+ full_page ? full_screenshot : screenshot
55
+ when :png
56
+ screenshot_as(:base64, full_page: full_page).unpack1('m')
57
+ else
58
+ raise Error::UnsupportedOperationError, "unsupported format: #{format.inspect}"
59
+ end
60
+ rescue NameError
61
+ raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
62
+ end
63
+ end # TakesScreenshot
64
+ end # WebDriver
65
+ end # Selenium