selenium-webdriver 4.1.0 → 4.7.1

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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +141 -1
  3. data/LICENSE +1 -1
  4. data/NOTICE +1 -1
  5. data/bin/linux/selenium-manager +0 -0
  6. data/bin/macos/selenium-manager +0 -0
  7. data/bin/windows/selenium-manager.exe +0 -0
  8. data/lib/selenium/server.rb +34 -26
  9. data/lib/selenium/webdriver/atoms/findElements.js +0 -0
  10. data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
  11. data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
  12. data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
  13. data/lib/selenium/webdriver/bidi/session.rb +38 -0
  14. data/lib/selenium/webdriver/bidi.rb +55 -0
  15. data/lib/selenium/webdriver/chrome/driver.rb +1 -0
  16. data/lib/selenium/webdriver/chrome/features.rb +5 -0
  17. data/lib/selenium/webdriver/chrome/options.rb +33 -19
  18. data/lib/selenium/webdriver/chrome/service.rb +1 -1
  19. data/lib/selenium/webdriver/chrome.rb +0 -14
  20. data/lib/selenium/webdriver/common/action_builder.rb +108 -21
  21. data/lib/selenium/webdriver/common/child_process.rb +126 -0
  22. data/lib/selenium/webdriver/common/driver.rb +22 -55
  23. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +12 -5
  24. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -0
  25. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -2
  26. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -1
  27. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -67
  28. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -1
  29. data/lib/selenium/webdriver/common/element.rb +2 -2
  30. data/lib/selenium/webdriver/common/error.rb +1 -1
  31. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  32. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  33. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  34. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  35. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  36. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  37. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  38. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -70
  39. data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
  40. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  41. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  42. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  43. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  44. data/lib/selenium/webdriver/common/interactions/scroll.rb +57 -0
  45. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  46. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  47. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
  48. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  49. data/lib/selenium/webdriver/common/keys.rb +1 -0
  50. data/lib/selenium/webdriver/common/manager.rb +0 -27
  51. data/lib/selenium/webdriver/common/options.rb +2 -9
  52. data/lib/selenium/webdriver/common/platform.rb +8 -5
  53. data/lib/selenium/webdriver/common/search_context.rb +0 -6
  54. data/lib/selenium/webdriver/common/selenium_manager.rb +91 -0
  55. data/lib/selenium/webdriver/common/service.rb +7 -3
  56. data/lib/selenium/webdriver/common/service_manager.rb +3 -12
  57. data/lib/selenium/webdriver/common/shadow_root.rb +1 -1
  58. data/lib/selenium/webdriver/common/socket_lock.rb +1 -2
  59. data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
  60. data/lib/selenium/webdriver/common/takes_screenshot.rb +1 -1
  61. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +83 -0
  62. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +73 -0
  63. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  64. data/lib/selenium/webdriver/common/websocket_connection.rb +165 -0
  65. data/lib/selenium/webdriver/common/window.rb +6 -6
  66. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  67. data/lib/selenium/webdriver/common.rb +19 -3
  68. data/lib/selenium/webdriver/devtools/network_interceptor.rb +176 -0
  69. data/lib/selenium/webdriver/devtools/request.rb +1 -1
  70. data/lib/selenium/webdriver/devtools/response.rb +1 -1
  71. data/lib/selenium/webdriver/devtools.rb +6 -112
  72. data/lib/selenium/webdriver/edge/features.rb +1 -0
  73. data/lib/selenium/webdriver/firefox/driver.rb +1 -0
  74. data/lib/selenium/webdriver/firefox/features.rb +6 -5
  75. data/lib/selenium/webdriver/firefox/options.rb +5 -2
  76. data/lib/selenium/webdriver/firefox/profile.rb +1 -5
  77. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  78. data/lib/selenium/webdriver/firefox.rb +1 -14
  79. data/lib/selenium/webdriver/ie.rb +0 -14
  80. data/lib/selenium/webdriver/remote/bridge.rb +54 -19
  81. data/lib/selenium/webdriver/remote/commands.rb +15 -6
  82. data/lib/selenium/webdriver/remote/driver.rb +0 -1
  83. data/lib/selenium/webdriver/remote/http/default.rb +6 -12
  84. data/lib/selenium/webdriver/remote/response.rb +2 -2
  85. data/lib/selenium/webdriver/safari/options.rb +1 -1
  86. data/lib/selenium/webdriver/safari.rb +0 -14
  87. data/lib/selenium/webdriver/support/cdp_client_generator.rb +4 -4
  88. data/lib/selenium/webdriver/support/color.rb +7 -7
  89. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -1
  90. data/lib/selenium/webdriver/support/guards.rb +1 -1
  91. data/lib/selenium/webdriver/support/nightly_version_generator.rb +60 -0
  92. data/lib/selenium/webdriver/support/select.rb +3 -1
  93. data/lib/selenium/webdriver/version.rb +1 -1
  94. data/lib/selenium/webdriver.rb +1 -1
  95. data/selenium-webdriver.gemspec +14 -10
  96. metadata +66 -42
  97. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  98. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
@@ -22,6 +22,7 @@ require 'selenium/webdriver/common/platform'
22
22
  require 'selenium/webdriver/common/proxy'
23
23
  require 'selenium/webdriver/common/log_entry'
24
24
  require 'selenium/webdriver/common/file_reaper'
25
+ require 'selenium/webdriver/common/selenium_manager'
25
26
  require 'selenium/webdriver/common/service'
26
27
  require 'selenium/webdriver/common/service_manager'
27
28
  require 'selenium/webdriver/common/socket_lock'
@@ -38,15 +39,28 @@ require 'selenium/webdriver/common/logger'
38
39
  require 'selenium/webdriver/common/logs'
39
40
  require 'selenium/webdriver/common/manager'
40
41
  require 'selenium/webdriver/common/search_context'
42
+ require 'selenium/webdriver/common/interactions/interaction'
43
+ require 'selenium/webdriver/common/interactions/interactions'
44
+ require 'selenium/webdriver/common/interactions/pointer_event_properties'
45
+ require 'selenium/webdriver/common/interactions/pointer_cancel'
46
+ require 'selenium/webdriver/common/interactions/pointer_move'
47
+ require 'selenium/webdriver/common/interactions/pointer_press'
48
+ require 'selenium/webdriver/common/interactions/typing_interaction'
49
+ require 'selenium/webdriver/common/interactions/pause'
41
50
  require 'selenium/webdriver/common/interactions/key_actions'
42
51
  require 'selenium/webdriver/common/interactions/pointer_actions'
43
- require 'selenium/webdriver/common/interactions/interactions'
44
52
  require 'selenium/webdriver/common/interactions/input_device'
45
- require 'selenium/webdriver/common/interactions/interaction'
46
53
  require 'selenium/webdriver/common/interactions/none_input'
47
54
  require 'selenium/webdriver/common/interactions/key_input'
48
55
  require 'selenium/webdriver/common/interactions/pointer_input'
56
+ require 'selenium/webdriver/common/interactions/scroll'
57
+ require 'selenium/webdriver/common/interactions/wheel_input'
58
+ require 'selenium/webdriver/common/interactions/scroll_origin'
59
+ require 'selenium/webdriver/common/interactions/wheel_actions'
49
60
  require 'selenium/webdriver/common/action_builder'
61
+ require 'selenium/webdriver/common/virtual_authenticator/credential'
62
+ require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options'
63
+ require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator'
50
64
  require 'selenium/webdriver/common/html5/shared_web_storage'
51
65
  require 'selenium/webdriver/common/html5/local_storage'
52
66
  require 'selenium/webdriver/common/html5/session_storage'
@@ -54,7 +68,6 @@ require 'selenium/webdriver/common/driver_extensions/has_web_storage'
54
68
  require 'selenium/webdriver/common/driver_extensions/downloads_files'
55
69
  require 'selenium/webdriver/common/driver_extensions/has_location'
56
70
  require 'selenium/webdriver/common/driver_extensions/has_session_id'
57
- require 'selenium/webdriver/common/driver_extensions/has_remote_status'
58
71
  require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
59
72
  require 'selenium/webdriver/common/driver_extensions/has_network_connection'
60
73
  require 'selenium/webdriver/common/driver_extensions/has_network_interception'
@@ -66,6 +79,7 @@ require 'selenium/webdriver/common/driver_extensions/prints_page'
66
79
  require 'selenium/webdriver/common/driver_extensions/uploads_files'
67
80
  require 'selenium/webdriver/common/driver_extensions/full_page_screenshot'
68
81
  require 'selenium/webdriver/common/driver_extensions/has_addons'
82
+ require 'selenium/webdriver/common/driver_extensions/has_bidi'
69
83
  require 'selenium/webdriver/common/driver_extensions/has_devtools'
70
84
  require 'selenium/webdriver/common/driver_extensions/has_authentication'
71
85
  require 'selenium/webdriver/common/driver_extensions/has_logs'
@@ -81,3 +95,5 @@ require 'selenium/webdriver/common/takes_screenshot'
81
95
  require 'selenium/webdriver/common/driver'
82
96
  require 'selenium/webdriver/common/element'
83
97
  require 'selenium/webdriver/common/shadow_root'
98
+ require 'selenium/webdriver/common/websocket_connection'
99
+ require 'selenium/webdriver/common/child_process'
@@ -0,0 +1,176 @@
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 DevTools
23
+
24
+ #
25
+ # Wraps the network request/response interception, providing
26
+ # thread-safety guarantees and handling special cases such as browser
27
+ # canceling requests midst interception.
28
+ #
29
+ # You should not be using this class directly, use Driver#intercept instead.
30
+ # @api private
31
+ #
32
+
33
+ class NetworkInterceptor
34
+
35
+ # CDP fails to get body on certain responses (301) and raises:
36
+ # "Can only get response body on requests captured after headers received."
37
+ CANNOT_GET_BODY_ON_REDIRECT_ERROR_CODE = "-32000"
38
+
39
+ # CDP fails to operate with intercepted requests.
40
+ # Typical reason is browser cancelling intercepted requests/responses.
41
+ INVALID_INTERCEPTION_ID_ERROR_CODE = "-32602"
42
+
43
+ def initialize(devtools)
44
+ @devtools = devtools
45
+ @lock = Mutex.new
46
+ end
47
+
48
+ def intercept(&block)
49
+ devtools.network.on(:loading_failed) { |params| track_cancelled_request(params) }
50
+ devtools.fetch.on(:request_paused) { |params| request_paused(params, &block) }
51
+
52
+ devtools.network.set_cache_disabled(cache_disabled: true)
53
+ devtools.network.enable
54
+ devtools.fetch.enable(patterns: [{requestStage: 'Request'}, {requestStage: 'Response'}])
55
+ end
56
+
57
+ private
58
+
59
+ attr_accessor :devtools, :lock
60
+
61
+ # We should be thread-safe to use the hash without synchronization
62
+ # because its keys are interception job identifiers and they should be
63
+ # unique within a devtools session.
64
+ def pending_response_requests
65
+ @pending_response_requests ||= {}
66
+ end
67
+
68
+ # Ensure usage of cancelled_requests is thread-safe via synchronization!
69
+ def cancelled_requests
70
+ @cancelled_requests ||= []
71
+ end
72
+
73
+ def track_cancelled_request(data)
74
+ return unless data['canceled']
75
+
76
+ lock.synchronize { cancelled_requests << data['requestId'] }
77
+ end
78
+
79
+ def request_paused(data, &block)
80
+ id = data['requestId']
81
+ network_id = data['networkId']
82
+
83
+ with_cancellable_request(network_id) do
84
+ if response?(data)
85
+ block = pending_response_requests.delete(id)
86
+ intercept_response(id, data, &block)
87
+ else
88
+ intercept_request(id, data, &block)
89
+ end
90
+ end
91
+ end
92
+
93
+ # The presence of any of these fields indicate we're at the response stage.
94
+ # @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused
95
+ def response?(params)
96
+ params.key?('responseStatusCode') || params.key?('responseErrorReason')
97
+ end
98
+
99
+ def intercept_request(id, params, &block)
100
+ original = DevTools::Request.from(id, params)
101
+ mutable = DevTools::Request.from(id, params)
102
+
103
+ block.call(mutable) do |&continue| # rubocop:disable Performance/RedundantBlockCall
104
+ pending_response_requests[id] = continue
105
+
106
+ if original == mutable
107
+ continue_request(original.id)
108
+ else
109
+ mutate_request(mutable)
110
+ end
111
+ end
112
+ end
113
+
114
+ def intercept_response(id, params)
115
+ return continue_response(id) unless block_given?
116
+
117
+ body = fetch_response_body(id)
118
+ original = DevTools::Response.from(id, body, params)
119
+ mutable = DevTools::Response.from(id, body, params)
120
+ yield mutable
121
+
122
+ if original == mutable
123
+ continue_response(id)
124
+ else
125
+ mutate_response(mutable)
126
+ end
127
+ end
128
+
129
+ def continue_request(id)
130
+ devtools.fetch.continue_request(request_id: id)
131
+ end
132
+ alias_method :continue_response, :continue_request
133
+
134
+ def mutate_request(request)
135
+ devtools.fetch.continue_request(
136
+ request_id: request.id,
137
+ url: request.url,
138
+ method: request.method,
139
+ post_data: request.post_data,
140
+ headers: request.headers.map do |k, v|
141
+ {name: k, value: v}
142
+ end
143
+ )
144
+ end
145
+
146
+ def mutate_response(response)
147
+ devtools.fetch.fulfill_request(
148
+ request_id: response.id,
149
+ body: (Base64.strict_encode64(response.body) if response.body),
150
+ response_code: response.code,
151
+ response_headers: response.headers.map do |k, v|
152
+ {name: k, value: v}
153
+ end
154
+ )
155
+ end
156
+
157
+ def fetch_response_body(id)
158
+ devtools.fetch.get_response_body(request_id: id).dig('result', 'body')
159
+ rescue Error::WebDriverError => e
160
+ raise unless e.message.start_with?(CANNOT_GET_BODY_ON_REDIRECT_ERROR_CODE)
161
+ end
162
+
163
+ def with_cancellable_request(network_id)
164
+ yield
165
+ rescue Error::WebDriverError => e
166
+ raise if e.message.start_with?(INVALID_INTERCEPTION_ID_ERROR_CODE) && !cancelled?(network_id)
167
+ end
168
+
169
+ def cancelled?(network_id)
170
+ lock.synchronize { !!cancelled_requests.delete(network_id) }
171
+ end
172
+
173
+ end # NetworkInterceptor
174
+ end # DevTools
175
+ end # WebDriver
176
+ end # Selenium
@@ -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,52 +20,32 @@
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'
26
+ autoload :NetworkInterceptor, 'selenium/webdriver/devtools/network_interceptor'
29
27
  autoload :PinnedScript, 'selenium/webdriver/devtools/pinned_script'
30
28
  autoload :Request, 'selenium/webdriver/devtools/request'
31
29
  autoload :Response, 'selenium/webdriver/devtools/response'
32
30
 
33
31
  def initialize(url:)
34
- @callback_threads = ThreadGroup.new
35
-
36
- @messages = []
32
+ @ws = WebSocketConnection.new(url: url)
37
33
  @session_id = nil
38
- @url = url
39
-
40
- process_handshake
41
- @socket_thread = attach_socket_listener
42
34
  start_session
43
35
  end
44
36
 
45
37
  def close
46
- @callback_threads.list.each(&:exit)
47
- @socket_thread.exit
48
- socket.close
38
+ @ws.close
49
39
  end
50
40
 
51
41
  def callbacks
52
- @callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] }
42
+ @ws.callbacks
53
43
  end
54
44
 
55
45
  def send_cmd(method, **params)
56
- id = next_id
57
- data = {id: id, method: method, params: params.reject { |_, v| v.nil? }}
46
+ data = {method: method, params: params.compact}
58
47
  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
-
48
+ message = @ws.send_cmd(**data)
69
49
  raise Error::WebDriverError, error_message(message['error']) if message['error']
70
50
 
71
51
  message
@@ -91,32 +71,6 @@ module Selenium
91
71
 
92
72
  private
93
73
 
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
74
  def start_session
121
75
  targets = target.get_targets.dig('result', 'targetInfos')
122
76
  page_target = targets.find { |target| target['type'] == 'page' }
@@ -124,66 +78,6 @@ module Selenium
124
78
  @session_id = session.dig('result', 'sessionId')
125
79
  end
126
80
 
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
81
  def error_message(error)
188
82
  [error['code'], error['message'], error['data']].join(': ')
189
83
  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,13 @@ 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 = if File.directory?(path)
39
+ Zipper.zip(path)
40
+ else
41
+ File.open(path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
42
+ end
42
43
 
43
- payload = {path: path}
44
+ payload = {addon: addon}
44
45
  payload[:temporary] = temporary unless temporary.nil?
45
46
  execute :install_addon, {}, payload
46
47
  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',
@@ -56,12 +57,14 @@ module Selenium
56
57
  #
57
58
 
58
59
  def initialize(log_level: nil, **opts)
59
- @debugger_address = opts.delete(:debugger_address)
60
+ @debugger_address = opts.delete(:debugger_address) { true }
61
+ opts[:accept_insecure_certs] = true unless opts.key?(:accept_insecure_certs)
60
62
 
61
63
  super(**opts)
62
64
 
63
65
  @options[:args] ||= []
64
66
  @options[:prefs] ||= {}
67
+ @options[:env] ||= {}
65
68
  @options[:log] ||= {level: log_level} if log_level
66
69
 
67
70
  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
@@ -23,20 +23,6 @@ module Selenium
23
23
  autoload :Driver, 'selenium/webdriver/ie/driver'
24
24
  autoload :Options, 'selenium/webdriver/ie/options'
25
25
  autoload :Service, 'selenium/webdriver/ie/service'
26
-
27
- def self.driver_path=(path)
28
- WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path=',
29
- 'Selenium::WebDriver::IE::Service#driver_path=',
30
- id: :driver_path
31
- Selenium::WebDriver::IE::Service.driver_path = path
32
- end
33
-
34
- def self.driver_path
35
- WebDriver.logger.deprecate 'Selenium::WebDriver::IE#driver_path',
36
- 'Selenium::WebDriver::IE::Service#driver_path',
37
- id: :driver_path
38
- Selenium::WebDriver::IE::Service.driver_path
39
- end
40
26
  end # IE
41
27
  end # WebDriver
42
28
  end # Selenium