selenium-webdriver 4.1.0 → 4.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +405 -1
  3. data/Gemfile +3 -0
  4. data/LICENSE +1 -1
  5. data/NOTICE +1 -1
  6. data/README.md +3 -3
  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 +38 -40
  11. data/lib/selenium/webdriver/atoms/findElements.js +27 -27
  12. data/lib/selenium/webdriver/atoms/getAttribute.js +6 -100
  13. data/lib/selenium/webdriver/atoms/isDisplayed.js +24 -96
  14. data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
  15. data/lib/selenium/webdriver/atoms.rb +5 -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_handler.rb +63 -0
  24. data/lib/selenium/webdriver/bidi/log_inspector.rb +147 -0
  25. data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
  26. data/lib/selenium/webdriver/bidi/session.rb +51 -0
  27. data/lib/selenium/webdriver/bidi/struct.rb +44 -0
  28. data/lib/selenium/webdriver/bidi.rb +66 -0
  29. data/lib/selenium/webdriver/chrome/driver.rb +9 -29
  30. data/lib/selenium/webdriver/chrome/features.rb +9 -67
  31. data/lib/selenium/webdriver/chrome/options.rb +3 -223
  32. data/lib/selenium/webdriver/chrome/profile.rb +3 -83
  33. data/lib/selenium/webdriver/chrome/service.rb +4 -19
  34. data/lib/selenium/webdriver/chrome.rb +0 -16
  35. data/lib/selenium/webdriver/chromium/driver.rb +61 -0
  36. data/lib/selenium/webdriver/chromium/features.rb +99 -0
  37. data/lib/selenium/webdriver/chromium/options.rb +243 -0
  38. data/lib/selenium/webdriver/chromium/profile.rb +113 -0
  39. data/lib/selenium/webdriver/chromium.rb +29 -0
  40. data/lib/selenium/webdriver/common/action_builder.rb +60 -22
  41. data/lib/selenium/webdriver/common/child_process.rb +130 -0
  42. data/lib/selenium/webdriver/common/driver.rb +46 -90
  43. data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
  44. data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
  45. data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
  46. data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
  47. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
  48. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +10 -5
  49. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -1
  50. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
  51. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -4
  52. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
  53. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
  54. data/lib/selenium/webdriver/common/driver_extensions/has_fedcm_dialog.rb +55 -0
  55. data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
  56. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
  57. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +2 -3
  58. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
  59. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -69
  60. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
  61. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -3
  62. data/lib/selenium/webdriver/common/driver_finder.rb +97 -0
  63. data/lib/selenium/webdriver/common/element.rb +8 -8
  64. data/lib/selenium/webdriver/common/error.rb +27 -4
  65. data/lib/selenium/webdriver/common/fedcm/account.rb +50 -0
  66. data/lib/selenium/webdriver/common/fedcm/dialog.rb +74 -0
  67. data/lib/selenium/webdriver/common/fedcm.rb +27 -0
  68. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
  69. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  70. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  71. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  72. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  73. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  74. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  75. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  76. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -71
  77. data/lib/selenium/webdriver/common/{driver_extensions/has_network_connection.rb → interactions/pointer_cancel.rb} +19 -11
  78. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  79. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  80. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  81. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  82. data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
  83. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  84. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  85. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +114 -0
  86. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  87. data/lib/selenium/webdriver/common/keys.rb +1 -0
  88. data/lib/selenium/webdriver/common/local_driver.rb +53 -0
  89. data/lib/selenium/webdriver/common/logger.rb +93 -28
  90. data/lib/selenium/webdriver/common/manager.rb +1 -28
  91. data/lib/selenium/webdriver/common/options.rb +19 -19
  92. data/lib/selenium/webdriver/common/platform.rb +12 -52
  93. data/lib/selenium/webdriver/common/port_prober.rb +1 -1
  94. data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
  95. data/lib/selenium/webdriver/common/proxy.rb +4 -4
  96. data/lib/selenium/webdriver/common/script.rb +45 -0
  97. data/lib/selenium/webdriver/common/search_context.rb +10 -8
  98. data/lib/selenium/webdriver/common/selenium_manager.rb +104 -0
  99. data/lib/selenium/webdriver/common/service.rb +21 -30
  100. data/lib/selenium/webdriver/common/service_manager.rb +8 -15
  101. data/lib/selenium/webdriver/common/shadow_root.rb +2 -3
  102. data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
  103. data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
  104. data/lib/selenium/webdriver/common/takes_screenshot.rb +6 -5
  105. data/lib/selenium/webdriver/common/target_locator.rb +2 -3
  106. data/lib/selenium/webdriver/common/timeouts.rb +2 -2
  107. data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
  108. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
  109. data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
  110. data/lib/selenium/webdriver/common/websocket_connection.rb +176 -0
  111. data/lib/selenium/webdriver/common/window.rb +6 -6
  112. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  113. data/lib/selenium/webdriver/common.rb +26 -5
  114. data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
  115. data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
  116. data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
  117. data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
  118. data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
  119. data/lib/selenium/webdriver/devtools/request.rb +1 -3
  120. data/lib/selenium/webdriver/devtools/response.rb +1 -3
  121. data/lib/selenium/webdriver/devtools.rb +17 -114
  122. data/lib/selenium/webdriver/edge/driver.rb +9 -3
  123. data/lib/selenium/webdriver/edge/features.rb +8 -4
  124. data/lib/selenium/webdriver/edge/options.rb +17 -5
  125. data/lib/selenium/webdriver/edge/profile.rb +2 -2
  126. data/lib/selenium/webdriver/edge/service.rb +8 -7
  127. data/lib/selenium/webdriver/edge.rb +0 -2
  128. data/lib/selenium/webdriver/firefox/driver.rb +9 -2
  129. data/lib/selenium/webdriver/firefox/features.rb +11 -7
  130. data/lib/selenium/webdriver/firefox/options.rb +10 -16
  131. data/lib/selenium/webdriver/firefox/profile.rb +21 -17
  132. data/lib/selenium/webdriver/firefox/profiles_ini.rb +1 -1
  133. data/lib/selenium/webdriver/firefox/service.rb +0 -18
  134. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  135. data/lib/selenium/webdriver/firefox.rb +1 -14
  136. data/lib/selenium/webdriver/ie/driver.rb +7 -1
  137. data/lib/selenium/webdriver/ie/features.rb +34 -0
  138. data/lib/selenium/webdriver/ie/options.rb +6 -4
  139. data/lib/selenium/webdriver/ie/service.rb +0 -22
  140. data/lib/selenium/webdriver/ie.rb +4 -17
  141. data/lib/selenium/webdriver/remote/bidi_bridge.rb +44 -0
  142. data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +22 -9
  143. data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
  144. data/lib/selenium/webdriver/remote/bridge.rb +147 -99
  145. data/lib/selenium/webdriver/remote/capabilities.rb +5 -55
  146. data/lib/selenium/webdriver/remote/driver.rb +35 -14
  147. data/lib/selenium/webdriver/remote/features.rb +75 -0
  148. data/lib/selenium/webdriver/remote/http/common.rb +24 -6
  149. data/lib/selenium/webdriver/remote/http/curb.rb +1 -3
  150. data/lib/selenium/webdriver/remote/http/default.rb +8 -14
  151. data/lib/selenium/webdriver/remote/response.rb +8 -34
  152. data/lib/selenium/webdriver/remote/server_error.rb +2 -2
  153. data/lib/selenium/webdriver/remote.rb +2 -1
  154. data/lib/selenium/webdriver/safari/driver.rb +7 -1
  155. data/lib/selenium/webdriver/safari/features.rb +5 -3
  156. data/lib/selenium/webdriver/safari/options.rb +5 -1
  157. data/lib/selenium/webdriver/safari/service.rb +10 -4
  158. data/lib/selenium/webdriver/safari.rb +1 -15
  159. data/lib/selenium/webdriver/support/color.rb +22 -22
  160. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  161. data/lib/selenium/webdriver/support/guards/guard.rb +14 -14
  162. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
  163. data/lib/selenium/webdriver/support/guards.rb +2 -2
  164. data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
  165. data/lib/selenium/webdriver/support/select.rb +3 -1
  166. data/lib/selenium/webdriver/version.rb +1 -1
  167. data/lib/selenium/webdriver.rb +7 -5
  168. data/selenium-webdriver.gemspec +20 -16
  169. metadata +135 -47
  170. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  171. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
  172. data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
@@ -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) }
@@ -49,6 +49,10 @@ module Selenium
49
49
  # @api public
50
50
 
51
51
  def screenshot_as(format, full_page: false)
52
+ if full_page && !respond_to?(:save_full_page_screenshot)
53
+ raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
54
+ end
55
+
52
56
  case format
53
57
  when :base64
54
58
  full_page ? full_screenshot : screenshot
@@ -57,10 +61,7 @@ module Selenium
57
61
  else
58
62
  raise Error::UnsupportedOperationError, "unsupported format: #{format.inspect}"
59
63
  end
60
- rescue NameError
61
- raise Error::UnsupportedOperationError, "Full Page Screenshots are not supported for #{inspect}"
62
64
  end
63
-
64
65
  end # TakesScreenshot
65
66
  end # WebDriver
66
67
  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.
@@ -0,0 +1,85 @@
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
+ #
21
+ # A credential stored in a virtual authenticator.
22
+ # @see https://w3c.github.io/webauthn/#credential-parameters
23
+ #
24
+
25
+ module Selenium
26
+ module WebDriver
27
+ class Credential
28
+ class << self
29
+ def resident(**opts)
30
+ Credential.new(resident_credential: true, **opts)
31
+ end
32
+
33
+ def non_resident(**opts)
34
+ Credential.new(resident_credential: false, **opts)
35
+ end
36
+
37
+ def encode(byte_array)
38
+ Base64.urlsafe_encode64(byte_array&.pack('C*'))
39
+ end
40
+
41
+ def decode(base64)
42
+ Base64.urlsafe_decode64(base64).unpack('C*')
43
+ end
44
+
45
+ def from_json(opts)
46
+ user_handle = opts['userHandle'] ? decode(opts['userHandle']) : nil
47
+ new(id: decode(opts['credentialId']),
48
+ resident_credential: opts['isResidentCredential'],
49
+ rp_id: opts['rpId'],
50
+ private_key: opts['privateKey'],
51
+ sign_count: opts['signCount'],
52
+ user_handle: user_handle)
53
+ end
54
+ end
55
+
56
+ attr_reader :id, :resident_credential, :rp_id, :user_handle, :private_key, :sign_count
57
+ alias resident_credential? resident_credential
58
+
59
+ def initialize(id:, resident_credential:, rp_id:, private_key:, **opts)
60
+ @id = id
61
+ @resident_credential = resident_credential
62
+ @rp_id = rp_id
63
+ @user_handle = opts.delete(:user_handle) { nil }
64
+ @private_key = private_key
65
+ @sign_count = opts.delete(:sign_count) { 0 }
66
+
67
+ raise ArgumentError, "Invalid arguments: #{opts.keys}" unless opts.empty?
68
+ end
69
+
70
+ #
71
+ # @api private
72
+ #
73
+
74
+ def as_json(*)
75
+ credential_data = {'credentialId' => Credential.encode(id),
76
+ 'isResidentCredential' => resident_credential?,
77
+ 'rpId' => rp_id,
78
+ 'privateKey' => Credential.encode(private_key),
79
+ 'signCount' => sign_count}
80
+ credential_data['userHandle'] = Credential.encode(user_handle) if user_handle
81
+ credential_data
82
+ end
83
+ end # Credential
84
+ end # WebDriver
85
+ end # Selenium
@@ -0,0 +1,72 @@
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 VirtualAuthenticator
23
+ attr_reader :options
24
+
25
+ #
26
+ # api private
27
+ # Use `Driver#add_virtual_authenticator`
28
+ #
29
+
30
+ def initialize(bridge, authenticator_id, options)
31
+ @id = authenticator_id
32
+ @bridge = bridge
33
+ @options = options
34
+ @valid = true
35
+ end
36
+
37
+ def add_credential(credential)
38
+ credential = credential.as_json
39
+ @bridge.add_credential credential, @id
40
+ end
41
+
42
+ def credentials
43
+ credential_data = @bridge.credentials @id
44
+ credential_data.map do |cred|
45
+ Credential.from_json(cred)
46
+ end
47
+ end
48
+
49
+ def remove_credential(credential_id)
50
+ credential_id = Credential.encode(credential_id) if credential_id.instance_of?(Array)
51
+ @bridge.remove_credential credential_id, @id
52
+ end
53
+
54
+ def remove_all_credentials
55
+ @bridge.remove_all_credentials @id
56
+ end
57
+
58
+ def user_verified=(verified)
59
+ @bridge.user_verified verified, @id
60
+ end
61
+
62
+ def remove!
63
+ @bridge.remove_virtual_authenticator(@id)
64
+ @valid = false
65
+ end
66
+
67
+ def valid?
68
+ @valid
69
+ end
70
+ end # VirtualAuthenticator
71
+ end # WebDriver
72
+ end # Selenium
@@ -0,0 +1,62 @@
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
+ #
21
+ # Options for the creation of virtual authenticators.
22
+ # @see http://w3c.github.io/webauthn/#sctn-automation
23
+ #
24
+
25
+ module Selenium
26
+ module WebDriver
27
+ class VirtualAuthenticatorOptions
28
+ PROTOCOL = {ctap2: 'ctap2', u2f: 'ctap1/u2f'}.freeze
29
+ TRANSPORT = {ble: 'ble', usb: 'usb', nfc: 'nfc', internal: 'internal'}.freeze
30
+
31
+ attr_accessor :protocol, :transport, :resident_key, :user_verification, :user_consenting, :user_verified
32
+ alias resident_key? resident_key
33
+ alias user_verification? user_verification
34
+ alias user_consenting? user_consenting
35
+ alias user_verified? user_verified
36
+
37
+ def initialize(**opts)
38
+ @protocol = opts.delete(:protocol) { :ctap2 }
39
+ @transport = opts.delete(:transport) { :usb }
40
+ @resident_key = opts.delete(:resident_key) { false }
41
+ @user_verification = opts.delete(:user_verification) { false }
42
+ @user_consenting = opts.delete(:user_consenting) { true }
43
+ @user_verified = opts.delete(:user_verified) { false }
44
+
45
+ raise ArgumentError, "Invalid arguments: #{opts.keys}" unless opts.empty?
46
+ end
47
+
48
+ #
49
+ # @api private
50
+ #
51
+
52
+ def as_json(*)
53
+ {'protocol' => PROTOCOL[protocol],
54
+ 'transport' => TRANSPORT[transport],
55
+ 'hasResidentKey' => resident_key?,
56
+ 'hasUserVerification' => user_verification?,
57
+ 'isUserConsenting' => user_consenting?,
58
+ 'isUserVerified' => user_verified?}
59
+ end
60
+ end # VirtualAuthenticatorOptions
61
+ end # WebDriver
62
+ end # Selenium
@@ -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
+ require 'websocket'
21
+
22
+ module Selenium
23
+ module WebDriver
24
+ class WebSocketConnection
25
+ CONNECTION_ERRORS = [
26
+ Errno::ECONNRESET, # connection is aborted (browser process was killed)
27
+ Errno::EPIPE # broken pipe (browser process was killed)
28
+ ].freeze
29
+
30
+ RESPONSE_WAIT_TIMEOUT = 30
31
+ RESPONSE_WAIT_INTERVAL = 0.1
32
+
33
+ MAX_LOG_MESSAGE_SIZE = 9999
34
+
35
+ def initialize(url:)
36
+ @callback_threads = ThreadGroup.new
37
+
38
+ @session_id = nil
39
+ @url = url
40
+
41
+ process_handshake
42
+ @socket_thread = attach_socket_listener
43
+ end
44
+
45
+ def close
46
+ @callback_threads.list.each(&:exit)
47
+ @socket_thread.exit
48
+ socket.close
49
+ end
50
+
51
+ def callbacks
52
+ @callbacks ||= Hash.new { |callbacks, event| callbacks[event] = [] }
53
+ end
54
+
55
+ def add_callback(event, &block)
56
+ callbacks[event] << block
57
+ block.object_id
58
+ end
59
+
60
+ def remove_callback(event, id)
61
+ return if callbacks[event].reject! { |callback| callback.object_id == id }
62
+
63
+ ids = callbacks[event]&.map(&:object_id)
64
+ raise Error::WebDriverError, "Callback with ID #{id} does not exist for event #{event}: #{ids}"
65
+ end
66
+
67
+ def send_cmd(**payload)
68
+ id = next_id
69
+ data = payload.merge(id: id)
70
+ WebDriver.logger.debug "WebSocket -> #{data}"[...MAX_LOG_MESSAGE_SIZE], id: :bidi
71
+ data = JSON.generate(data)
72
+ out_frame = WebSocket::Frame::Outgoing::Client.new(version: ws.version, data: data, type: 'text')
73
+ socket.write(out_frame.to_s)
74
+
75
+ wait.until { messages.delete(id) }
76
+ end
77
+
78
+ private
79
+
80
+ # We should be thread-safe to use the hash without synchronization
81
+ # because its keys are WebSocket message identifiers and they should be
82
+ # unique within a devtools session.
83
+ def messages
84
+ @messages ||= {}
85
+ end
86
+
87
+ def process_handshake
88
+ socket.print(ws.to_s)
89
+ ws << socket.readpartial(1024)
90
+ end
91
+
92
+ def attach_socket_listener
93
+ Thread.new do
94
+ Thread.current.abort_on_exception = true
95
+ Thread.current.report_on_exception = false
96
+
97
+ until socket.eof?
98
+ incoming_frame << socket.readpartial(1024)
99
+
100
+ while (frame = incoming_frame.next)
101
+ message = process_frame(frame)
102
+ next unless message['method']
103
+
104
+ params = message['params']
105
+ callbacks[message['method']].each do |callback|
106
+ @callback_threads.add(callback_thread(params, &callback))
107
+ end
108
+ end
109
+ end
110
+ rescue *CONNECTION_ERRORS
111
+ Thread.stop
112
+ end
113
+ end
114
+
115
+ def incoming_frame
116
+ @incoming_frame ||= WebSocket::Frame::Incoming::Client.new(version: ws.version)
117
+ end
118
+
119
+ def process_frame(frame)
120
+ message = frame.to_s
121
+
122
+ # Firefox will periodically fail on unparsable empty frame
123
+ return {} if message.empty?
124
+
125
+ message = JSON.parse(message)
126
+ messages[message['id']] = message
127
+ WebDriver.logger.debug "WebSocket <- #{message}"[...MAX_LOG_MESSAGE_SIZE], id: :bidi
128
+
129
+ message
130
+ end
131
+
132
+ def callback_thread(params)
133
+ Thread.new do
134
+ Thread.current.abort_on_exception = true
135
+
136
+ # We might end up blocked forever when we have an error in event.
137
+ # For example, if network interception event raises error,
138
+ # the browser will keep waiting for the request to be proceeded
139
+ # before returning back to the original thread. In this case,
140
+ # we should at least print the error.
141
+ Thread.current.report_on_exception = true
142
+
143
+ yield params
144
+ rescue Error::WebDriverError, *CONNECTION_ERRORS
145
+ Thread.stop
146
+ end
147
+ end
148
+
149
+ def wait
150
+ @wait ||= Wait.new(timeout: RESPONSE_WAIT_TIMEOUT, interval: RESPONSE_WAIT_INTERVAL)
151
+ end
152
+
153
+ def socket
154
+ @socket ||= if URI(@url).scheme == 'wss'
155
+ socket = TCPSocket.new(ws.host, ws.port)
156
+ socket = OpenSSL::SSL::SSLSocket.new(socket, OpenSSL::SSL::SSLContext.new)
157
+ socket.sync_close = true
158
+ socket.connect
159
+
160
+ socket
161
+ else
162
+ TCPSocket.new(ws.host, ws.port)
163
+ end
164
+ end
165
+
166
+ def ws
167
+ @ws ||= WebSocket::Handshake::Client.new(url: @url)
168
+ end
169
+
170
+ def next_id
171
+ @id ||= 0
172
+ @id += 1
173
+ end
174
+ end # BiDi
175
+ end # WebDriver
176
+ end # Selenium
@@ -36,8 +36,8 @@ module Selenium
36
36
 
37
37
  def size=(dimension)
38
38
  unless dimension.respond_to?(:width) && dimension.respond_to?(:height)
39
- raise ArgumentError, "expected #{dimension.inspect}:#{dimension.class}" \
40
- ' to respond to #width and #height'
39
+ raise ArgumentError, "expected #{dimension.inspect}:#{dimension.class} " \
40
+ 'to respond to #width and #height'
41
41
  end
42
42
 
43
43
  @bridge.resize_window dimension.width, dimension.height
@@ -61,8 +61,8 @@ module Selenium
61
61
 
62
62
  def position=(point)
63
63
  unless point.respond_to?(:x) && point.respond_to?(:y)
64
- raise ArgumentError, "expected #{point.inspect}:#{point.class}" \
65
- ' to respond to #x and #y'
64
+ raise ArgumentError, "expected #{point.inspect}:#{point.class} " \
65
+ 'to respond to #x and #y'
66
66
  end
67
67
 
68
68
  @bridge.reposition_window point.x, point.y
@@ -86,8 +86,8 @@ module Selenium
86
86
 
87
87
  def rect=(rectangle)
88
88
  unless %w[x y width height].all? { |val| rectangle.respond_to? val }
89
- raise ArgumentError, "expected #{rectangle.inspect}:#{rectangle.class}" \
90
- ' to respond to #x, #y, #width, and #height'
89
+ raise ArgumentError, "expected #{rectangle.inspect}:#{rectangle.class} " \
90
+ 'to respond to #x, #y, #width, and #height'
91
91
  end
92
92
 
93
93
  @bridge.set_window_rect(x: rectangle.x,
@@ -41,7 +41,7 @@ module Selenium
41
41
  to = File.join(destination, entry.name)
42
42
  dirname = File.dirname(to)
43
43
 
44
- FileUtils.mkdir_p dirname unless File.exist? dirname
44
+ FileUtils.mkdir_p dirname
45
45
  zip.extract(entry, to)
46
46
  end
47
47
  end
@@ -18,10 +18,13 @@
18
18
  # under the License.
19
19
 
20
20
  require 'selenium/webdriver/common/error'
21
+ require 'selenium/webdriver/common/local_driver'
22
+ require 'selenium/webdriver/common/driver_finder'
21
23
  require 'selenium/webdriver/common/platform'
22
24
  require 'selenium/webdriver/common/proxy'
23
25
  require 'selenium/webdriver/common/log_entry'
24
26
  require 'selenium/webdriver/common/file_reaper'
27
+ require 'selenium/webdriver/common/selenium_manager'
25
28
  require 'selenium/webdriver/common/service'
26
29
  require 'selenium/webdriver/common/service_manager'
27
30
  require 'selenium/webdriver/common/socket_lock'
@@ -38,25 +41,35 @@ require 'selenium/webdriver/common/logger'
38
41
  require 'selenium/webdriver/common/logs'
39
42
  require 'selenium/webdriver/common/manager'
40
43
  require 'selenium/webdriver/common/search_context'
44
+ require 'selenium/webdriver/common/interactions/interaction'
45
+ require 'selenium/webdriver/common/interactions/interactions'
46
+ require 'selenium/webdriver/common/interactions/pointer_event_properties'
47
+ require 'selenium/webdriver/common/interactions/pointer_cancel'
48
+ require 'selenium/webdriver/common/interactions/pointer_move'
49
+ require 'selenium/webdriver/common/interactions/pointer_press'
50
+ require 'selenium/webdriver/common/interactions/typing_interaction'
51
+ require 'selenium/webdriver/common/interactions/pause'
41
52
  require 'selenium/webdriver/common/interactions/key_actions'
42
53
  require 'selenium/webdriver/common/interactions/pointer_actions'
43
- require 'selenium/webdriver/common/interactions/interactions'
44
54
  require 'selenium/webdriver/common/interactions/input_device'
45
- require 'selenium/webdriver/common/interactions/interaction'
46
55
  require 'selenium/webdriver/common/interactions/none_input'
47
56
  require 'selenium/webdriver/common/interactions/key_input'
48
57
  require 'selenium/webdriver/common/interactions/pointer_input'
58
+ require 'selenium/webdriver/common/interactions/scroll'
59
+ require 'selenium/webdriver/common/interactions/wheel_input'
60
+ require 'selenium/webdriver/common/interactions/scroll_origin'
61
+ require 'selenium/webdriver/common/interactions/wheel_actions'
49
62
  require 'selenium/webdriver/common/action_builder'
63
+ require 'selenium/webdriver/common/virtual_authenticator/credential'
64
+ require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options'
65
+ require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator'
50
66
  require 'selenium/webdriver/common/html5/shared_web_storage'
51
67
  require 'selenium/webdriver/common/html5/local_storage'
52
68
  require 'selenium/webdriver/common/html5/session_storage'
53
69
  require 'selenium/webdriver/common/driver_extensions/has_web_storage'
54
70
  require 'selenium/webdriver/common/driver_extensions/downloads_files'
55
- require 'selenium/webdriver/common/driver_extensions/has_location'
56
71
  require 'selenium/webdriver/common/driver_extensions/has_session_id'
57
- require 'selenium/webdriver/common/driver_extensions/has_remote_status'
58
72
  require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
59
- require 'selenium/webdriver/common/driver_extensions/has_network_connection'
60
73
  require 'selenium/webdriver/common/driver_extensions/has_network_interception'
61
74
  require 'selenium/webdriver/common/driver_extensions/has_apple_permissions'
62
75
  require 'selenium/webdriver/common/driver_extensions/has_permissions'
@@ -66,7 +79,9 @@ 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'
84
+ require 'selenium/webdriver/common/driver_extensions/has_file_downloads'
70
85
  require 'selenium/webdriver/common/driver_extensions/has_authentication'
71
86
  require 'selenium/webdriver/common/driver_extensions/has_logs'
72
87
  require 'selenium/webdriver/common/driver_extensions/has_log_events'
@@ -74,6 +89,7 @@ require 'selenium/webdriver/common/driver_extensions/has_pinned_scripts'
74
89
  require 'selenium/webdriver/common/driver_extensions/has_cdp'
75
90
  require 'selenium/webdriver/common/driver_extensions/has_casting'
76
91
  require 'selenium/webdriver/common/driver_extensions/has_launching'
92
+ require 'selenium/webdriver/common/driver_extensions/has_fedcm_dialog'
77
93
  require 'selenium/webdriver/common/keys'
78
94
  require 'selenium/webdriver/common/profile_helper'
79
95
  require 'selenium/webdriver/common/options'
@@ -81,3 +97,8 @@ require 'selenium/webdriver/common/takes_screenshot'
81
97
  require 'selenium/webdriver/common/driver'
82
98
  require 'selenium/webdriver/common/element'
83
99
  require 'selenium/webdriver/common/shadow_root'
100
+ require 'selenium/webdriver/common/websocket_connection'
101
+ require 'selenium/webdriver/common/child_process'
102
+ require 'selenium/webdriver/common/script'
103
+ require 'selenium/webdriver/common/fedcm/account'
104
+ require 'selenium/webdriver/common/fedcm/dialog'
@@ -21,7 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
23
  class ConsoleEvent
24
-
25
24
  attr_accessor :type, :timestamp, :args
26
25
 
27
26
  def initialize(type:, timestamp:, args:)
@@ -31,7 +30,6 @@ module Selenium
31
30
  arg.key?('value') ? arg['value'] : arg
32
31
  end
33
32
  end
34
-
35
33
  end # ConsoleEvent
36
34
  end # DevTools
37
35
  end # WebDriver
@@ -21,7 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
23
  class ExceptionEvent
24
-
25
24
  attr_accessor :description, :timestamp, :stacktrace
26
25
 
27
26
  def initialize(description:, timestamp:, stacktrace:)
@@ -29,7 +28,6 @@ module Selenium
29
28
  @timestamp = Time.at(timestamp / 1000)
30
29
  @stacktrace = stacktrace
31
30
  end
32
-
33
31
  end # ExceptionEvent
34
32
  end # DevTools
35
33
  end # WebDriver
@@ -21,7 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
23
  class MutationEvent
24
-
25
24
  attr_accessor :element, :attribute_name, :current_value, :old_value
26
25
 
27
26
  def initialize(element:, attribute_name:, current_value:, old_value:)
@@ -30,7 +29,6 @@ module Selenium
30
29
  @current_value = current_value
31
30
  @old_value = old_value
32
31
  end
33
-
34
32
  end # MutationEvent
35
33
  end # DevTools
36
34
  end # WebDriver