selenium-webdriver 4.1.0 → 4.3.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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +70 -1
  3. data/LICENSE +1 -1
  4. data/NOTICE +1 -1
  5. data/lib/selenium/server.rb +14 -9
  6. data/lib/selenium/webdriver/bidi/session.rb +38 -0
  7. data/lib/selenium/webdriver/bidi.rb +55 -0
  8. data/lib/selenium/webdriver/chrome/features.rb +5 -0
  9. data/lib/selenium/webdriver/chrome/options.rb +19 -19
  10. data/lib/selenium/webdriver/chrome.rb +0 -14
  11. data/lib/selenium/webdriver/common/action_builder.rb +108 -21
  12. data/lib/selenium/webdriver/common/driver.rb +13 -55
  13. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +12 -5
  14. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -0
  15. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -2
  16. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
  17. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -1
  18. data/lib/selenium/webdriver/common/element.rb +1 -1
  19. data/lib/selenium/webdriver/common/error.rb +1 -1
  20. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  21. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  22. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  23. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  24. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  25. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  26. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  27. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +56 -66
  28. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  29. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  30. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  31. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  32. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  33. data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
  34. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  35. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  36. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  37. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  38. data/lib/selenium/webdriver/common/keys.rb +1 -0
  39. data/lib/selenium/webdriver/common/manager.rb +0 -27
  40. data/lib/selenium/webdriver/common/options.rb +2 -9
  41. data/lib/selenium/webdriver/common/platform.rb +4 -4
  42. data/lib/selenium/webdriver/common/search_context.rb +0 -6
  43. data/lib/selenium/webdriver/common/service_manager.rb +2 -3
  44. data/lib/selenium/webdriver/common/shadow_root.rb +1 -1
  45. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  46. data/lib/selenium/webdriver/common/websocket_connection.rb +149 -0
  47. data/lib/selenium/webdriver/common.rb +14 -3
  48. data/lib/selenium/webdriver/devtools/request.rb +1 -1
  49. data/lib/selenium/webdriver/devtools/response.rb +1 -1
  50. data/lib/selenium/webdriver/devtools.rb +5 -112
  51. data/lib/selenium/webdriver/edge/features.rb +1 -0
  52. data/lib/selenium/webdriver/firefox/driver.rb +1 -0
  53. data/lib/selenium/webdriver/firefox/features.rb +2 -5
  54. data/lib/selenium/webdriver/firefox/options.rb +3 -1
  55. data/lib/selenium/webdriver/firefox/profile.rb +1 -5
  56. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  57. data/lib/selenium/webdriver/firefox.rb +1 -14
  58. data/lib/selenium/webdriver/ie.rb +0 -14
  59. data/lib/selenium/webdriver/remote/bridge.rb +21 -19
  60. data/lib/selenium/webdriver/remote/commands.rb +0 -5
  61. data/lib/selenium/webdriver/remote/driver.rb +0 -1
  62. data/lib/selenium/webdriver/remote/http/default.rb +6 -12
  63. data/lib/selenium/webdriver/remote/response.rb +2 -2
  64. data/lib/selenium/webdriver/safari.rb +0 -14
  65. data/lib/selenium/webdriver/support/cdp_client_generator.rb +4 -4
  66. data/lib/selenium/webdriver/support/color.rb +7 -7
  67. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -1
  68. data/lib/selenium/webdriver/support/guards.rb +1 -1
  69. data/lib/selenium/webdriver/version.rb +1 -1
  70. data/lib/selenium/webdriver.rb +1 -0
  71. data/selenium-webdriver.gemspec +7 -4
  72. metadata +56 -8
  73. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
@@ -104,33 +104,6 @@ module Selenium
104
104
  @timeouts ||= Timeouts.new(@bridge)
105
105
  end
106
106
 
107
- def logs
108
- WebDriver.logger.deprecate('Manager#logs', 'Chrome::Driver#logs')
109
- @logs ||= Logs.new(@bridge)
110
- end
111
-
112
- #
113
- # @param type [Symbol] Supports two values: :tab and :window.
114
- # @return [String] The value of the window handle
115
- #
116
- def new_window(type = :tab)
117
- WebDriver.logger.deprecate('Manager#new_window', 'TargetLocator#new_window', id: :new_window) do
118
- 'e.g., `driver.switch_to.new_window(:tab)`'
119
- end
120
- case type
121
- when :tab, :window
122
- result = @bridge.new_window(type)
123
- unless result.key?('handle')
124
- raise UnknownError, "the driver did not return a handle. " \
125
- "The returned result: #{result.inspect}"
126
- end
127
- result['handle']
128
- else
129
- raise ArgumentError, "invalid argument for type. Got: '#{type.inspect}'. " \
130
- "Try :tab or :window"
131
- end
132
- end
133
-
134
107
  def window
135
108
  @window ||= Window.new(@bridge)
136
109
  end
@@ -66,17 +66,10 @@ module Selenium
66
66
 
67
67
  attr_accessor :options
68
68
 
69
- def initialize(options: nil, **opts)
69
+ def initialize(**opts)
70
70
  self.class.set_capabilities
71
71
 
72
- @options = if options
73
- WebDriver.logger.deprecate(":options as keyword for initializing #{self.class}",
74
- "custom values directly in #new constructor",
75
- id: :options_options)
76
- opts.merge(options)
77
- else
78
- opts
79
- end
72
+ @options = opts
80
73
  @options[:browser_name] = self.class::BROWSER
81
74
  end
82
75
 
@@ -104,7 +104,7 @@ module Selenium
104
104
  end
105
105
 
106
106
  def cygwin?
107
- RUBY_PLATFORM =~ /cygwin/
107
+ RUBY_PLATFORM.include?('cygwin')
108
108
  !Regexp.last_match.nil?
109
109
  end
110
110
 
@@ -177,9 +177,9 @@ module Selenium
177
177
 
178
178
  def find_in_program_files(*binary_names)
179
179
  paths = [
180
- ENV['PROGRAMFILES'] || '\\Program Files',
181
- ENV['ProgramFiles(x86)'] || '\\Program Files (x86)',
182
- ENV['ProgramW6432'] || '\\Program Files'
180
+ ENV.fetch('PROGRAMFILES', '\\Program Files'),
181
+ ENV.fetch('ProgramFiles(x86)', '\\Program Files (x86)'),
182
+ ENV.fetch('ProgramW6432', '\\Program Files')
183
183
  ]
184
184
 
185
185
  paths.each do |root|
@@ -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
@@ -49,7 +49,7 @@ module Selenium
49
49
  def start
50
50
  raise "already started: #{uri.inspect} #{@executable_path.inspect}" if process_running?
51
51
 
52
- Platform.exit_hook(&method(:stop)) # make sure we don't leave the server running
52
+ Platform.exit_hook { stop } # make sure we don't leave the server running
53
53
 
54
54
  socket_lock.locked do
55
55
  find_free_port
@@ -60,6 +60,7 @@ module Selenium
60
60
 
61
61
  def stop
62
62
  return unless @shutdown_supported
63
+ return if process_exited?
63
64
 
64
65
  stop_server
65
66
  @process.poll_for_exit STOP_TIMEOUT
@@ -116,8 +117,6 @@ module Selenium
116
117
  end
117
118
 
118
119
  def stop_server
119
- return if process_exited?
120
-
121
120
  connect_to_server do |http|
122
121
  headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
123
122
  http.get('/shutdown', headers)
@@ -45,7 +45,7 @@ module Selenium
45
45
  alias_method :eql?, :==
46
46
 
47
47
  def hash
48
- @id.hash ^ @bridge.hash
48
+ [@id, @bridge].hash
49
49
  end
50
50
 
51
51
  #
@@ -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,149 @@
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 'websocket'
21
+
22
+ module Selenium
23
+ module WebDriver
24
+ class WebSocketConnection
25
+ RESPONSE_WAIT_TIMEOUT = 30
26
+ RESPONSE_WAIT_INTERVAL = 0.1
27
+
28
+ def initialize(url:)
29
+ @callback_threads = ThreadGroup.new
30
+
31
+ @messages = []
32
+ @session_id = nil
33
+ @url = url
34
+
35
+ process_handshake
36
+ @socket_thread = attach_socket_listener
37
+ end
38
+
39
+ def close
40
+ @callback_threads.list.each(&:exit)
41
+ @socket_thread.exit
42
+ socket.close
43
+ end
44
+
45
+ def callbacks
46
+ @callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] }
47
+ end
48
+
49
+ def send_cmd(**payload)
50
+ id = next_id
51
+ data = payload.merge(id: id)
52
+ data = JSON.generate(data)
53
+ WebDriver.logger.debug "WebSocket -> #{data}"
54
+
55
+ out_frame = WebSocket::Frame::Outgoing::Client.new(version: ws.version, data: data, type: 'text')
56
+ socket.write(out_frame.to_s)
57
+
58
+ wait.until { @messages.find { |m| m['id'] == id } }
59
+ end
60
+
61
+ private
62
+
63
+ def process_handshake
64
+ socket.print(ws.to_s)
65
+ ws << socket.readpartial(1024)
66
+ end
67
+
68
+ def attach_socket_listener
69
+ Thread.new do
70
+ Thread.current.abort_on_exception = true
71
+ Thread.current.report_on_exception = false
72
+
73
+ until socket.eof?
74
+ incoming_frame << socket.readpartial(1024)
75
+
76
+ while (frame = incoming_frame.next)
77
+ message = process_frame(frame)
78
+ next unless message['method']
79
+
80
+ params = message['params']
81
+ callbacks[message['method']].each do |callback|
82
+ @callback_threads.add(callback_thread(params, &callback))
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ def incoming_frame
90
+ @incoming_frame ||= WebSocket::Frame::Incoming::Client.new(version: ws.version)
91
+ end
92
+
93
+ def process_frame(frame)
94
+ message = frame.to_s
95
+
96
+ # Firefox will periodically fail on unparsable empty frame
97
+ return {} if message.empty?
98
+
99
+ message = JSON.parse(message)
100
+ @messages << message
101
+ WebDriver.logger.debug "WebSocket <- #{message}"
102
+
103
+ message
104
+ end
105
+
106
+ def callback_thread(params)
107
+ Thread.new do
108
+ Thread.current.abort_on_exception = true
109
+
110
+ # We might end up blocked forever when we have an error in event.
111
+ # For example, if network interception event raises error,
112
+ # the browser will keep waiting for the request to be proceeded
113
+ # before returning back to the original thread. In this case,
114
+ # we should at least print the error.
115
+ Thread.current.report_on_exception = true
116
+
117
+ yield params
118
+ end
119
+ end
120
+
121
+ def wait
122
+ @wait ||= Wait.new(timeout: RESPONSE_WAIT_TIMEOUT, interval: RESPONSE_WAIT_INTERVAL)
123
+ end
124
+
125
+ def socket
126
+ @socket ||= if URI(@url).scheme == 'wss'
127
+ socket = TCPSocket.new(ws.host, ws.port)
128
+ socket = OpenSSL::SSL::SSLSocket.new(socket, OpenSSL::SSL::SSLContext.new)
129
+ socket.sync_close = true
130
+ socket.connect
131
+
132
+ socket
133
+ else
134
+ TCPSocket.new(ws.host, ws.port)
135
+ end
136
+ end
137
+
138
+ def ws
139
+ @ws ||= WebSocket::Handshake::Client.new(url: @url)
140
+ end
141
+
142
+ def next_id
143
+ @id ||= 0
144
+ @id += 1
145
+ end
146
+
147
+ end # BiDi
148
+ end # WebDriver
149
+ end # Selenium
@@ -38,14 +38,24 @@ require 'selenium/webdriver/common/logger'
38
38
  require 'selenium/webdriver/common/logs'
39
39
  require 'selenium/webdriver/common/manager'
40
40
  require 'selenium/webdriver/common/search_context'
41
+ require 'selenium/webdriver/common/interactions/interaction'
42
+ require 'selenium/webdriver/common/interactions/interactions'
43
+ require 'selenium/webdriver/common/interactions/pointer_event_properties'
44
+ require 'selenium/webdriver/common/interactions/pointer_cancel'
45
+ require 'selenium/webdriver/common/interactions/pointer_move'
46
+ require 'selenium/webdriver/common/interactions/pointer_press'
47
+ require 'selenium/webdriver/common/interactions/typing_interaction'
48
+ require 'selenium/webdriver/common/interactions/pause'
41
49
  require 'selenium/webdriver/common/interactions/key_actions'
42
50
  require 'selenium/webdriver/common/interactions/pointer_actions'
43
- require 'selenium/webdriver/common/interactions/interactions'
44
51
  require 'selenium/webdriver/common/interactions/input_device'
45
- require 'selenium/webdriver/common/interactions/interaction'
46
52
  require 'selenium/webdriver/common/interactions/none_input'
47
53
  require 'selenium/webdriver/common/interactions/key_input'
48
54
  require 'selenium/webdriver/common/interactions/pointer_input'
55
+ require 'selenium/webdriver/common/interactions/scroll'
56
+ require 'selenium/webdriver/common/interactions/wheel_input'
57
+ require 'selenium/webdriver/common/interactions/scroll_origin'
58
+ require 'selenium/webdriver/common/interactions/wheel_actions'
49
59
  require 'selenium/webdriver/common/action_builder'
50
60
  require 'selenium/webdriver/common/html5/shared_web_storage'
51
61
  require 'selenium/webdriver/common/html5/local_storage'
@@ -54,7 +64,6 @@ require 'selenium/webdriver/common/driver_extensions/has_web_storage'
54
64
  require 'selenium/webdriver/common/driver_extensions/downloads_files'
55
65
  require 'selenium/webdriver/common/driver_extensions/has_location'
56
66
  require 'selenium/webdriver/common/driver_extensions/has_session_id'
57
- require 'selenium/webdriver/common/driver_extensions/has_remote_status'
58
67
  require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
59
68
  require 'selenium/webdriver/common/driver_extensions/has_network_connection'
60
69
  require 'selenium/webdriver/common/driver_extensions/has_network_interception'
@@ -66,6 +75,7 @@ require 'selenium/webdriver/common/driver_extensions/prints_page'
66
75
  require 'selenium/webdriver/common/driver_extensions/uploads_files'
67
76
  require 'selenium/webdriver/common/driver_extensions/full_page_screenshot'
68
77
  require 'selenium/webdriver/common/driver_extensions/has_addons'
78
+ require 'selenium/webdriver/common/driver_extensions/has_bidi'
69
79
  require 'selenium/webdriver/common/driver_extensions/has_devtools'
70
80
  require 'selenium/webdriver/common/driver_extensions/has_authentication'
71
81
  require 'selenium/webdriver/common/driver_extensions/has_logs'
@@ -81,3 +91,4 @@ require 'selenium/webdriver/common/takes_screenshot'
81
91
  require 'selenium/webdriver/common/driver'
82
92
  require 'selenium/webdriver/common/element'
83
93
  require 'selenium/webdriver/common/shadow_root'
94
+ require 'selenium/webdriver/common/websocket_connection'
@@ -35,7 +35,7 @@ module Selenium
35
35
  id: id,
36
36
  url: params.dig('request', 'url'),
37
37
  method: params.dig('request', 'method'),
38
- headers: params.dig('request', 'headers'),
38
+ headers: params.dig('request', 'headers').dup,
39
39
  post_data: params.dig('request', 'postData')
40
40
  )
41
41
  end
@@ -35,7 +35,7 @@ module Selenium
35
35
  id: id,
36
36
  code: params['responseStatusCode'],
37
37
  body: (Base64.strict_decode64(encoded_body) if encoded_body),
38
- headers: params['responseHeaders'].each_with_object({}) do |header, hash|
38
+ headers: params.fetch('responseHeaders', []).each_with_object({}) do |header, hash|
39
39
  hash[header['name']] = header['value']
40
40
  end
41
41
  )
@@ -20,9 +20,6 @@
20
20
  module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
- RESPONSE_WAIT_TIMEOUT = 30
24
- RESPONSE_WAIT_INTERVAL = 0.1
25
-
26
23
  autoload :ConsoleEvent, 'selenium/webdriver/devtools/console_event'
27
24
  autoload :ExceptionEvent, 'selenium/webdriver/devtools/exception_event'
28
25
  autoload :MutationEvent, 'selenium/webdriver/devtools/mutation_event'
@@ -31,41 +28,23 @@ module Selenium
31
28
  autoload :Response, 'selenium/webdriver/devtools/response'
32
29
 
33
30
  def initialize(url:)
34
- @callback_threads = ThreadGroup.new
35
-
36
- @messages = []
31
+ @ws = WebSocketConnection.new(url: url)
37
32
  @session_id = nil
38
- @url = url
39
-
40
- process_handshake
41
- @socket_thread = attach_socket_listener
42
33
  start_session
43
34
  end
44
35
 
45
36
  def close
46
- @callback_threads.list.each(&:exit)
47
- @socket_thread.exit
48
- socket.close
37
+ @ws.close
49
38
  end
50
39
 
51
40
  def callbacks
52
- @callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] }
41
+ @ws.callbacks
53
42
  end
54
43
 
55
44
  def send_cmd(method, **params)
56
- id = next_id
57
- data = {id: id, method: method, params: params.reject { |_, v| v.nil? }}
45
+ data = {method: method, params: params.compact}
58
46
  data[:sessionId] = @session_id if @session_id
59
- data = JSON.generate(data)
60
- WebDriver.logger.debug "DevTools -> #{data}"
61
-
62
- out_frame = WebSocket::Frame::Outgoing::Client.new(version: ws.version, data: data, type: 'text')
63
- socket.write(out_frame.to_s)
64
-
65
- message = wait.until do
66
- @messages.find { |m| m['id'] == id }
67
- end
68
-
47
+ message = @ws.send_cmd(**data)
69
48
  raise Error::WebDriverError, error_message(message['error']) if message['error']
70
49
 
71
50
  message
@@ -91,32 +70,6 @@ module Selenium
91
70
 
92
71
  private
93
72
 
94
- def process_handshake
95
- socket.print(ws.to_s)
96
- ws << socket.readpartial(1024)
97
- end
98
-
99
- def attach_socket_listener
100
- Thread.new do
101
- Thread.current.abort_on_exception = true
102
- Thread.current.report_on_exception = false
103
-
104
- until socket.eof?
105
- incoming_frame << socket.readpartial(1024)
106
-
107
- while (frame = incoming_frame.next)
108
- message = process_frame(frame)
109
- next unless message['method']
110
-
111
- params = message['params']
112
- callbacks[message['method']].each do |callback|
113
- @callback_threads.add(callback_thread(params, &callback))
114
- end
115
- end
116
- end
117
- end
118
- end
119
-
120
73
  def start_session
121
74
  targets = target.get_targets.dig('result', 'targetInfos')
122
75
  page_target = targets.find { |target| target['type'] == 'page' }
@@ -124,66 +77,6 @@ module Selenium
124
77
  @session_id = session.dig('result', 'sessionId')
125
78
  end
126
79
 
127
- def incoming_frame
128
- @incoming_frame ||= WebSocket::Frame::Incoming::Client.new(version: ws.version)
129
- end
130
-
131
- def process_frame(frame)
132
- message = frame.to_s
133
-
134
- # Firefox will periodically fail on unparsable empty frame
135
- return {} if message.empty?
136
-
137
- message = JSON.parse(message)
138
- @messages << message
139
- WebDriver.logger.debug "DevTools <- #{message}"
140
-
141
- message
142
- end
143
-
144
- def callback_thread(params)
145
- Thread.new do
146
- Thread.current.abort_on_exception = true
147
-
148
- # We might end up blocked forever when we have an error in event.
149
- # For example, if network interception event raises error,
150
- # the browser will keep waiting for the request to be proceeded
151
- # before returning back to the original thread. In this case,
152
- # we should at least print the error.
153
- Thread.current.report_on_exception = true
154
-
155
- yield params
156
- end
157
- end
158
-
159
- def wait
160
- @wait ||= Wait.new(timeout: RESPONSE_WAIT_TIMEOUT, interval: RESPONSE_WAIT_INTERVAL)
161
- end
162
-
163
- def socket
164
- @socket ||= begin
165
- if URI(@url).scheme == 'wss'
166
- socket = TCPSocket.new(ws.host, ws.port)
167
- socket = OpenSSL::SSL::SSLSocket.new(socket, OpenSSL::SSL::SSLContext.new)
168
- socket.sync_close = true
169
- socket.connect
170
-
171
- socket
172
- else
173
- TCPSocket.new(ws.host, ws.port)
174
- end
175
- end
176
- end
177
-
178
- def ws
179
- @ws ||= WebSocket::Handshake::Client.new(url: @url)
180
- end
181
-
182
- def next_id
183
- @id ||= 0
184
- @id += 1
185
- end
186
-
187
80
  def error_message(error)
188
81
  [error['code'], error['message'], error['data']].join(': ')
189
82
  end
@@ -30,6 +30,7 @@ module Selenium
30
30
  get_cast_sinks: [:get, 'session/:session_id/ms/cast/get_sinks'],
31
31
  set_cast_sink_to_use: [:post, 'session/:session_id/ms/cast/set_sink_to_use'],
32
32
  start_cast_tab_mirroring: [:post, 'session/:session_id/ms/cast/start_tab_mirroring'],
33
+ start_cast_desktop_mirroring: [:post, 'session/:session_id/ms/cast/start_desktop_mirroring'],
33
34
  get_cast_issue_message: [:get, 'session/:session_id/ms/cast/get_issue_message'],
34
35
  stop_casting: [:post, 'session/:session_id/ms/cast/stop_casting'],
35
36
  send_command: [:post, 'session/:session_id/ms/cdp/execute']
@@ -30,6 +30,7 @@ module Selenium
30
30
  EXTENSIONS = [DriverExtensions::HasAddons,
31
31
  DriverExtensions::FullPageScreenshot,
32
32
  DriverExtensions::HasContext,
33
+ DriverExtensions::HasBiDi,
33
34
  DriverExtensions::HasDevTools,
34
35
  DriverExtensions::HasLogEvents,
35
36
  DriverExtensions::HasNetworkInterception,
@@ -35,12 +35,9 @@ module Selenium
35
35
  end
36
36
 
37
37
  def install_addon(path, temporary)
38
- if @file_detector
39
- local_file = @file_detector.call(path)
40
- path = upload(local_file) if local_file
41
- end
38
+ addon = File.open(path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
42
39
 
43
- payload = {path: path}
40
+ payload = {addon: addon}
44
41
  payload[:temporary] = temporary unless temporary.nil?
45
42
  execute :install_addon, {}, payload
46
43
  end
@@ -25,11 +25,12 @@ module Selenium
25
25
 
26
26
  KEY = 'moz:firefoxOptions'
27
27
 
28
- # see: https://firefox-source-docs.mozilla.org/testing/geckodriver/Capabilities.html
28
+ # see: https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
29
29
  CAPABILITIES = {binary: 'binary',
30
30
  args: 'args',
31
31
  log: 'log',
32
32
  prefs: 'prefs',
33
+ env: 'env',
33
34
  android_package: 'androidPackage',
34
35
  android_activity: 'androidActivity',
35
36
  android_device_serial: 'androidDeviceSerial',
@@ -62,6 +63,7 @@ module Selenium
62
63
 
63
64
  @options[:args] ||= []
64
65
  @options[:prefs] ||= {}
66
+ @options[:env] ||= {}
65
67
  @options[:log] ||= {level: log_level} if log_level
66
68
 
67
69
  process_profile(@options.delete(:profile))
@@ -96,7 +96,7 @@ module Selenium
96
96
  raise TypeError, "expected one of #{VALID_PREFERENCE_TYPES.inspect}, got #{value.inspect}:#{value.class}"
97
97
  end
98
98
 
99
- if value.is_a?(String) && stringified?(value)
99
+ if value.is_a?(String) && Util.stringified?(value)
100
100
  raise ArgumentError, "preference values must be plain strings: #{key.inspect} => #{value.inspect}"
101
101
  end
102
102
 
@@ -221,10 +221,6 @@ module Selenium
221
221
  end
222
222
  end
223
223
  end
224
-
225
- def stringified?(str)
226
- /^".*"$/.match?(str)
227
- end
228
224
  end # Profile
229
225
  end # Firefox
230
226
  end # WebDriver
@@ -0,0 +1,46 @@
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
+ module Firefox
23
+ # @api private
24
+ module Util
25
+ module_function
26
+
27
+ def app_data_path
28
+ case Platform.os
29
+ when :windows
30
+ "#{ENV.fetch('APPDATA')}\\Mozilla\\Firefox"
31
+ when :macosx
32
+ "#{Platform.home}/Library/Application Support/Firefox"
33
+ when :unix, :linux
34
+ "#{Platform.home}/.mozilla/firefox"
35
+ else
36
+ raise "Unknown os: #{Platform.os}"
37
+ end
38
+ end
39
+
40
+ def stringified?(str)
41
+ str =~ /^".*"$/
42
+ end
43
+ end # Util
44
+ end # Firefox
45
+ end # WebDriver
46
+ end # Selenium
@@ -24,6 +24,7 @@ require 'rexml/document'
24
24
  module Selenium
25
25
  module WebDriver
26
26
  module Firefox
27
+ autoload :Util, 'selenium/webdriver/firefox/util'
27
28
  autoload :Extension, 'selenium/webdriver/firefox/extension'
28
29
  autoload :ProfilesIni, 'selenium/webdriver/firefox/profiles_ini'
29
30
  autoload :Profile, 'selenium/webdriver/firefox/profile'
@@ -41,20 +42,6 @@ module Selenium
41
42
  # until WebDriver Bidi is available.
42
43
  DEVTOOLS_VERSION = 85
43
44
 
44
- def self.driver_path=(path)
45
- WebDriver.logger.deprecate 'Selenium::WebDriver::Firefox#driver_path=',
46
- 'Selenium::WebDriver::Firefox::Service#driver_path=',
47
- id: :driver_path
48
- Selenium::WebDriver::Firefox::Service.driver_path = path
49
- end
50
-
51
- def self.driver_path
52
- WebDriver.logger.deprecate 'Selenium::WebDriver::Firefox#driver_path',
53
- 'Selenium::WebDriver::Firefox::Service#driver_path',
54
- id: :driver_path
55
- Selenium::WebDriver::Firefox::Service.driver_path
56
- end
57
-
58
45
  def self.path=(path)
59
46
  Platform.assert_executable path
60
47
  @path = path