selenium-webdriver 4.1.0 → 4.28.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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES +446 -1
  3. data/Gemfile +4 -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 +41 -43
  11. data/lib/selenium/webdriver/atoms/findElements.js +52 -50
  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 +100 -0
  17. data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
  18. data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
  19. data/lib/selenium/webdriver/{common/driver_extensions/has_location.rb → bidi/log/filter_by.rb} +14 -11
  20. data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
  21. data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
  22. data/lib/selenium/webdriver/bidi/log_handler.rb +65 -0
  23. data/lib/selenium/webdriver/bidi/log_inspector.rb +147 -0
  24. data/lib/selenium/webdriver/bidi/network.rb +82 -0
  25. data/lib/selenium/webdriver/bidi/session.rb +51 -0
  26. data/lib/selenium/webdriver/bidi/struct.rb +42 -0
  27. data/lib/selenium/webdriver/bidi.rb +67 -0
  28. data/lib/selenium/webdriver/chrome/driver.rb +9 -29
  29. data/lib/selenium/webdriver/chrome/features.rb +9 -67
  30. data/lib/selenium/webdriver/chrome/options.rb +3 -223
  31. data/lib/selenium/webdriver/chrome/profile.rb +3 -83
  32. data/lib/selenium/webdriver/chrome/service.rb +5 -19
  33. data/lib/selenium/webdriver/chrome.rb +0 -16
  34. data/lib/selenium/webdriver/chromium/driver.rb +61 -0
  35. data/lib/selenium/webdriver/chromium/features.rb +99 -0
  36. data/lib/selenium/webdriver/chromium/options.rb +243 -0
  37. data/lib/selenium/webdriver/chromium/profile.rb +113 -0
  38. data/lib/selenium/webdriver/chromium.rb +29 -0
  39. data/lib/selenium/webdriver/common/action_builder.rb +60 -22
  40. data/lib/selenium/webdriver/common/child_process.rb +136 -0
  41. data/lib/selenium/webdriver/common/driver.rb +54 -89
  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 +0 -1
  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 +0 -2
  46. data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
  47. data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +10 -5
  48. data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -1
  49. data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
  50. data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -4
  51. data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
  52. data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
  53. data/lib/selenium/webdriver/common/driver_extensions/has_fedcm_dialog.rb +55 -0
  54. data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
  55. data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
  56. data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +9 -3
  57. data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
  58. data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +8 -68
  59. data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
  60. data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -3
  61. data/lib/selenium/webdriver/common/driver_finder.rb +97 -0
  62. data/lib/selenium/webdriver/common/element.rb +8 -8
  63. data/lib/selenium/webdriver/common/error.rb +29 -4
  64. data/lib/selenium/webdriver/common/fedcm/account.rb +49 -0
  65. data/lib/selenium/webdriver/common/fedcm/dialog.rb +74 -0
  66. data/lib/selenium/webdriver/common/fedcm.rb +27 -0
  67. data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
  68. data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
  69. data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
  70. data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
  71. data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
  72. data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
  73. data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
  74. data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
  75. data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -71
  76. data/lib/selenium/webdriver/common/{driver_extensions/has_network_connection.rb → interactions/pointer_cancel.rb} +19 -11
  77. data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
  78. data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
  79. data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
  80. data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
  81. data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
  82. data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
  83. data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
  84. data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +114 -0
  85. data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
  86. data/lib/selenium/webdriver/common/keys.rb +1 -0
  87. data/lib/selenium/webdriver/common/local_driver.rb +53 -0
  88. data/lib/selenium/webdriver/common/logger.rb +93 -28
  89. data/lib/selenium/webdriver/common/manager.rb +2 -29
  90. data/lib/selenium/webdriver/common/network.rb +64 -0
  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 +24 -26
  100. data/lib/selenium/webdriver/common/service_manager.rb +9 -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 +5 -5
  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/wait.rb +1 -1
  111. data/lib/selenium/webdriver/common/websocket_connection.rb +176 -0
  112. data/lib/selenium/webdriver/common/window.rb +6 -6
  113. data/lib/selenium/webdriver/common/zipper.rb +1 -1
  114. data/lib/selenium/webdriver/common.rb +27 -5
  115. data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
  116. data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
  117. data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
  118. data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
  119. data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
  120. data/lib/selenium/webdriver/devtools/request.rb +1 -3
  121. data/lib/selenium/webdriver/devtools/response.rb +1 -3
  122. data/lib/selenium/webdriver/devtools.rb +17 -114
  123. data/lib/selenium/webdriver/edge/driver.rb +9 -3
  124. data/lib/selenium/webdriver/edge/features.rb +8 -4
  125. data/lib/selenium/webdriver/edge/options.rb +17 -5
  126. data/lib/selenium/webdriver/edge/profile.rb +2 -2
  127. data/lib/selenium/webdriver/edge/service.rb +8 -7
  128. data/lib/selenium/webdriver/edge.rb +0 -2
  129. data/lib/selenium/webdriver/firefox/driver.rb +9 -2
  130. data/lib/selenium/webdriver/firefox/features.rb +11 -7
  131. data/lib/selenium/webdriver/firefox/options.rb +10 -16
  132. data/lib/selenium/webdriver/firefox/profile.rb +21 -17
  133. data/lib/selenium/webdriver/firefox/profiles_ini.rb +1 -1
  134. data/lib/selenium/webdriver/firefox/service.rb +1 -18
  135. data/lib/selenium/webdriver/firefox/util.rb +46 -0
  136. data/lib/selenium/webdriver/firefox.rb +1 -14
  137. data/lib/selenium/webdriver/ie/driver.rb +7 -1
  138. data/lib/selenium/webdriver/ie/features.rb +34 -0
  139. data/lib/selenium/webdriver/ie/options.rb +6 -4
  140. data/lib/selenium/webdriver/ie/service.rb +1 -22
  141. data/lib/selenium/webdriver/ie.rb +4 -17
  142. data/lib/selenium/webdriver/remote/bidi_bridge.rb +66 -0
  143. data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +22 -9
  144. data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
  145. data/lib/selenium/webdriver/remote/bridge.rb +158 -100
  146. data/lib/selenium/webdriver/remote/capabilities.rb +5 -55
  147. data/lib/selenium/webdriver/remote/driver.rb +35 -14
  148. data/lib/selenium/webdriver/remote/features.rb +75 -0
  149. data/lib/selenium/webdriver/remote/http/common.rb +26 -6
  150. data/lib/selenium/webdriver/remote/http/curb.rb +10 -6
  151. data/lib/selenium/webdriver/remote/http/default.rb +8 -14
  152. data/lib/selenium/webdriver/remote/response.rb +14 -22
  153. data/lib/selenium/webdriver/remote/server_error.rb +2 -2
  154. data/lib/selenium/webdriver/remote.rb +3 -2
  155. data/lib/selenium/webdriver/safari/driver.rb +7 -1
  156. data/lib/selenium/webdriver/safari/features.rb +5 -3
  157. data/lib/selenium/webdriver/safari/options.rb +5 -1
  158. data/lib/selenium/webdriver/safari/service.rb +10 -4
  159. data/lib/selenium/webdriver/safari.rb +1 -15
  160. data/lib/selenium/webdriver/support/color.rb +22 -22
  161. data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
  162. data/lib/selenium/webdriver/support/guards/guard.rb +14 -14
  163. data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
  164. data/lib/selenium/webdriver/support/guards.rb +4 -4
  165. data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
  166. data/lib/selenium/webdriver/support/select.rb +3 -1
  167. data/lib/selenium/webdriver/version.rb +1 -1
  168. data/lib/selenium/webdriver.rb +7 -5
  169. data/selenium-webdriver.gemspec +22 -16
  170. metadata +137 -47
  171. data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
  172. data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
  173. data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
@@ -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,9 @@ 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'
105
+ require 'selenium/webdriver/common/network'
@@ -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
@@ -0,0 +1,173 @@
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
+ # Wraps the network request/response interception, providing
25
+ # thread-safety guarantees and handling special cases such as browser
26
+ # canceling requests midst interception.
27
+ #
28
+ # You should not be using this class directly, use Driver#intercept instead.
29
+ # @api private
30
+ #
31
+
32
+ class NetworkInterceptor
33
+ # CDP fails to get body on certain responses (301) and raises:
34
+ # "Can only get response body on requests captured after headers received."
35
+ CANNOT_GET_BODY_ON_REDIRECT_ERROR_CODE = '-32000'
36
+
37
+ # CDP fails to operate with intercepted requests.
38
+ # Typical reason is browser cancelling intercepted requests/responses.
39
+ INVALID_INTERCEPTION_ID_ERROR_CODE = '-32602'
40
+
41
+ def initialize(devtools)
42
+ @devtools = devtools
43
+ @lock = Mutex.new
44
+ end
45
+
46
+ def intercept(&block)
47
+ devtools.network.on(:loading_failed) { |params| track_cancelled_request(params) }
48
+ devtools.fetch.on(:request_paused) { |params| request_paused(params, &block) }
49
+
50
+ devtools.network.set_cache_disabled(cache_disabled: true)
51
+ devtools.network.enable
52
+ devtools.fetch.enable(patterns: [{requestStage: 'Request'}, {requestStage: 'Response'}])
53
+ end
54
+
55
+ private
56
+
57
+ attr_accessor :devtools, :lock
58
+
59
+ # We should be thread-safe to use the hash without synchronization
60
+ # because its keys are interception job identifiers and they should be
61
+ # unique within a devtools session.
62
+ def pending_response_requests
63
+ @pending_response_requests ||= {}
64
+ end
65
+
66
+ # Ensure usage of cancelled_requests is thread-safe via synchronization!
67
+ def cancelled_requests
68
+ @cancelled_requests ||= []
69
+ end
70
+
71
+ def track_cancelled_request(data)
72
+ return unless data['canceled']
73
+
74
+ lock.synchronize { cancelled_requests << data['requestId'] }
75
+ end
76
+
77
+ def request_paused(data, &block)
78
+ id = data['requestId']
79
+ network_id = data['networkId']
80
+
81
+ with_cancellable_request(network_id) do
82
+ if response?(data)
83
+ block = pending_response_requests.delete(id)
84
+ intercept_response(id, data, &block)
85
+ else
86
+ intercept_request(id, data, &block)
87
+ end
88
+ end
89
+ end
90
+
91
+ # The presence of any of these fields indicate we're at the response stage.
92
+ # @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused
93
+ def response?(params)
94
+ params.key?('responseStatusCode') || params.key?('responseErrorReason')
95
+ end
96
+
97
+ def intercept_request(id, params, &block)
98
+ original = DevTools::Request.from(id, params)
99
+ mutable = DevTools::Request.from(id, params)
100
+
101
+ block.call(mutable) do |&continue|
102
+ pending_response_requests[id] = continue
103
+
104
+ if original == mutable
105
+ continue_request(original.id)
106
+ else
107
+ mutate_request(mutable)
108
+ end
109
+ end
110
+ end
111
+
112
+ def intercept_response(id, params)
113
+ return continue_response(id) unless block_given?
114
+
115
+ body = fetch_response_body(id)
116
+ original = DevTools::Response.from(id, body, params)
117
+ mutable = DevTools::Response.from(id, body, params)
118
+ yield mutable
119
+
120
+ if original == mutable
121
+ continue_response(id)
122
+ else
123
+ mutate_response(mutable)
124
+ end
125
+ end
126
+
127
+ def continue_request(id)
128
+ devtools.fetch.continue_request(request_id: id)
129
+ end
130
+ alias continue_response continue_request
131
+
132
+ def mutate_request(request)
133
+ devtools.fetch.continue_request(
134
+ request_id: request.id,
135
+ url: request.url,
136
+ method: request.method,
137
+ post_data: (Base64.strict_encode64(request.post_data) if request.post_data),
138
+ headers: request.headers.map do |k, v|
139
+ {name: k, value: v}
140
+ end
141
+ )
142
+ end
143
+
144
+ def mutate_response(response)
145
+ devtools.fetch.fulfill_request(
146
+ request_id: response.id,
147
+ body: (Base64.strict_encode64(response.body) if response.body),
148
+ response_code: response.code,
149
+ response_headers: response.headers.map do |k, v|
150
+ {name: k, value: v}
151
+ end
152
+ )
153
+ end
154
+
155
+ def fetch_response_body(id)
156
+ devtools.fetch.get_response_body(request_id: id).dig('result', 'body')
157
+ rescue Error::WebDriverError => e
158
+ raise unless e.message.start_with?(CANNOT_GET_BODY_ON_REDIRECT_ERROR_CODE)
159
+ end
160
+
161
+ def with_cancellable_request(network_id)
162
+ yield
163
+ rescue Error::WebDriverError => e
164
+ raise if e.message.start_with?(INVALID_INTERCEPTION_ID_ERROR_CODE) && !cancelled?(network_id)
165
+ end
166
+
167
+ def cancelled?(network_id)
168
+ lock.synchronize { !!cancelled_requests.delete(network_id) }
169
+ end
170
+ end # NetworkInterceptor
171
+ end # DevTools
172
+ end # WebDriver
173
+ end # Selenium
@@ -21,7 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
23
  class PinnedScript
24
-
25
24
  attr_accessor :key, :devtools_identifier, :script
26
25
 
27
26
  def initialize(script)
@@ -52,7 +51,6 @@ module Selenium
52
51
  def remove
53
52
  "__webdriver_#{key} = undefined"
54
53
  end
55
-
56
54
  end # PinnedScript
57
55
  end # DevTools
58
56
  end # WebDriver
@@ -21,7 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
23
  class Request
24
-
25
24
  attr_accessor :url, :method, :headers, :post_data
26
25
  attr_reader :id
27
26
 
@@ -35,7 +34,7 @@ module Selenium
35
34
  id: id,
36
35
  url: params.dig('request', 'url'),
37
36
  method: params.dig('request', 'method'),
38
- headers: params.dig('request', 'headers'),
37
+ headers: params.dig('request', 'headers').dup,
39
38
  post_data: params.dig('request', 'postData')
40
39
  )
41
40
  end
@@ -60,7 +59,6 @@ module Selenium
60
59
  def inspect
61
60
  %(#<#{self.class.name} @id="#{id}" @method="#{method}" @url="#{url}")
62
61
  end
63
-
64
62
  end # Request
65
63
  end # DevTools
66
64
  end # WebDriver
@@ -21,7 +21,6 @@ module Selenium
21
21
  module WebDriver
22
22
  class DevTools
23
23
  class Response
24
-
25
24
  attr_accessor :code, :body, :headers
26
25
  attr_reader :id
27
26
 
@@ -35,7 +34,7 @@ module Selenium
35
34
  id: id,
36
35
  code: params['responseStatusCode'],
37
36
  body: (Base64.strict_decode64(encoded_body) if encoded_body),
38
- headers: params['responseHeaders'].each_with_object({}) do |header, hash|
37
+ headers: params.fetch('responseHeaders', []).each_with_object({}) do |header, hash|
39
38
  hash[header['name']] = header['value']
40
39
  end
41
40
  )
@@ -59,7 +58,6 @@ module Selenium
59
58
  def inspect
60
59
  %(#<#{self.class.name} @id="#{id}" @code="#{code}")
61
60
  end
62
-
63
61
  end # Response
64
62
  end # DevTools
65
63
  end # WebDriver
@@ -20,59 +20,49 @@
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
72
52
  end
73
53
 
74
54
  def method_missing(method, *_args)
75
- desired_class = "Selenium::DevTools::V#{Selenium::DevTools.version}::#{method.capitalize}"
55
+ namespace = "Selenium::DevTools::V#{Selenium::DevTools.version}"
56
+ methods_to_classes = "#{namespace}::METHODS_TO_CLASSES"
57
+
58
+ desired_class = if Object.const_defined?(methods_to_classes)
59
+ # selenium-devtools 0.113 and newer
60
+ "#{namespace}::#{Object.const_get(methods_to_classes)[method]}"
61
+ else
62
+ # selenium-devtools 0.112 and older
63
+ "#{namespace}::#{method.capitalize}"
64
+ end
65
+
76
66
  return unless Object.const_defined?(desired_class)
77
67
 
78
68
  self.class.class_eval do
@@ -91,32 +81,6 @@ module Selenium
91
81
 
92
82
  private
93
83
 
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
84
  def start_session
121
85
  targets = target.get_targets.dig('result', 'targetInfos')
122
86
  page_target = targets.find { |target| target['type'] == 'page' }
@@ -124,70 +88,9 @@ module Selenium
124
88
  @session_id = session.dig('result', 'sessionId')
125
89
  end
126
90
 
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
91
  def error_message(error)
188
92
  [error['code'], error['message'], error['data']].join(': ')
189
93
  end
190
-
191
94
  end # DevTools
192
95
  end # WebDriver
193
96
  end # Selenium
@@ -17,18 +17,24 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- require 'selenium/webdriver/chrome/driver'
20
+ require 'selenium/webdriver/chromium/driver'
21
21
 
22
22
  module Selenium
23
23
  module WebDriver
24
24
  module Edge
25
-
26
25
  #
27
26
  # Driver implementation for Microsoft Edge.
28
27
  # @api private
29
28
  #
30
29
 
31
- class Driver < Selenium::WebDriver::Chrome::Driver
30
+ class Driver < Chromium::Driver
31
+ include LocalDriver
32
+
33
+ def initialize(options: nil, service: nil, url: nil, **opts)
34
+ caps, url = initialize_local_driver(options, service, url)
35
+ super(caps: caps, url: url, **opts)
36
+ end
37
+
32
38
  def browser
33
39
  :edge
34
40
  end
@@ -17,26 +17,30 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- require 'selenium/webdriver/chrome/features'
20
+ require 'selenium/webdriver/chromium/features'
21
21
 
22
22
  module Selenium
23
23
  module WebDriver
24
24
  module Edge
25
25
  module Features
26
-
27
- include WebDriver::Chrome::Features
26
+ include WebDriver::Chromium::Features
28
27
 
29
28
  EDGE_COMMANDS = {
30
29
  get_cast_sinks: [:get, 'session/:session_id/ms/cast/get_sinks'],
31
30
  set_cast_sink_to_use: [:post, 'session/:session_id/ms/cast/set_sink_to_use'],
32
31
  start_cast_tab_mirroring: [:post, 'session/:session_id/ms/cast/start_tab_mirroring'],
32
+ start_cast_desktop_mirroring: [:post, 'session/:session_id/ms/cast/start_desktop_mirroring'],
33
33
  get_cast_issue_message: [:get, 'session/:session_id/ms/cast/get_issue_message'],
34
34
  stop_casting: [:post, 'session/:session_id/ms/cast/stop_casting'],
35
35
  send_command: [:post, 'session/:session_id/ms/cdp/execute']
36
36
  }.freeze
37
37
 
38
+ def command_list
39
+ EDGE_COMMANDS.merge(CHROMIUM_COMMANDS).merge(self.class::COMMANDS)
40
+ end
41
+
38
42
  def commands(command)
39
- EDGE_COMMANDS[command] || Chrome::Features::CHROME_COMMANDS[command] || self.class::COMMANDS[command]
43
+ command_list[command]
40
44
  end
41
45
  end # Bridge
42
46
  end # Edge
@@ -17,23 +17,35 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- require 'selenium/webdriver/chrome/options'
20
+ require 'selenium/webdriver/chromium/options'
21
21
 
22
22
  module Selenium
23
23
  module WebDriver
24
24
  module Edge
25
- class Options < Selenium::WebDriver::Chrome::Options
25
+ class Options < Chromium::Options
26
26
  KEY = 'ms:edgeOptions'
27
27
  BROWSER = 'MicrosoftEdge'
28
28
 
29
- protected
29
+ #
30
+ # Changes the browser name enable webview2
31
+ # see: https://learn.microsoft.com/en-us/microsoft-edge/webview2/how-to/webdriver
32
+ # Automation of WebView2 apps with Microsoft Edge WebDriver
33
+ #
34
+ # @example Enable webview2
35
+ # options = Selenium::WebDriver::Edge::Options.new
36
+ # options.webview2!
37
+ #
30
38
 
31
- def enable_logging(browser_options)
32
- browser_options['ms:loggingPrefs'] = @logging_prefs
39
+ def webview2!
40
+ @options[:browser_name] = 'webview2'
33
41
  end
34
42
 
35
43
  private
36
44
 
45
+ def enable_logging(browser_options)
46
+ browser_options['ms:loggingPrefs'] = @logging_prefs
47
+ end
48
+
37
49
  def binary_path
38
50
  Edge.path
39
51
  end
@@ -17,7 +17,7 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
 
20
- require 'selenium/webdriver/chrome/profile'
20
+ require 'selenium/webdriver/chromium/profile'
21
21
 
22
22
  module Selenium
23
23
  module WebDriver
@@ -26,7 +26,7 @@ module Selenium
26
26
  # @private
27
27
  #
28
28
 
29
- class Profile < Selenium::WebDriver::Chrome::Profile
29
+ class Profile < Chromium::Profile
30
30
  end # Profile
31
31
  end # Edge
32
32
  end # WebDriver