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
@@ -0,0 +1,243 @@
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 Chromium
23
+ class Options < WebDriver::Options
24
+ attr_accessor :profile, :logging_prefs
25
+
26
+ # see: http://chromedriver.chromium.org/capabilities
27
+ CAPABILITIES = {args: 'args',
28
+ binary: 'binary',
29
+ local_state: 'localState',
30
+ prefs: 'prefs',
31
+ detach: 'detach',
32
+ debugger_address: 'debuggerAddress',
33
+ exclude_switches: 'excludeSwitches',
34
+ minidump_path: 'minidumpPath',
35
+ emulation: 'mobileEmulation',
36
+ perf_logging_prefs: 'perfLoggingPrefs',
37
+ window_types: 'windowTypes',
38
+ android_package: 'androidPackage',
39
+ android_activity: 'androidActivity',
40
+ android_device_serial: 'androidDeviceSerial',
41
+ android_use_running_app: 'androidUseRunningApp'}.freeze
42
+
43
+ # NOTE: special handling of 'extensions' to validate when set instead of when used
44
+ attr_reader :extensions
45
+
46
+ # Create a new Options instance.
47
+ #
48
+ # @example
49
+ # options = Selenium::WebDriver::Chrome::Options.new(args: ['start-maximized', 'user-data-dir=/tmp/temp_profile'])
50
+ # driver = Selenium::WebDriver.for(:chrome, options: options)
51
+ #
52
+ # @param [Profile] profile An instance of a Chrome::Profile Class
53
+ # @param [Hash] opts the pre-defined options to create the Chrome::Options with
54
+ # @option opts [Array] encoded_extensions List of extensions that do not need to be Base64 encoded
55
+ # @option opts [Array<String>] args List of command-line arguments to use when starting Chrome
56
+ # @option opts [String] binary Path to the Chrome executable to use
57
+ # @option opts [Hash] prefs A hash with each entry consisting of the name of the preference and its value
58
+ # @option opts [Array<String>] extensions A list of paths to (.crx) Chrome extensions to install on startup
59
+ # @option opts [Hash] options A hash for raw options
60
+ # @option opts [Hash] emulation A hash for raw emulation options
61
+ # @option opts [Hash] local_state A hash for the Local State file in the user data folder
62
+ # @option opts [Boolean] detach whether browser is closed when the driver is sent the quit command
63
+ # @option opts [String] debugger_address address of a Chrome debugger server to connect to
64
+ # @option opts [Array<String>] exclude_switches command line switches to exclude
65
+ # @option opts [String] minidump_path Directory to store Chrome minidumps (linux only)
66
+ # @option opts [Hash] perf_logging_prefs A hash for performance logging preferences
67
+ # @option opts [Array<String>] window_types A list of window types to appear in the list of window handles
68
+ #
69
+
70
+ def initialize(profile: nil, **opts)
71
+ super(**opts)
72
+
73
+ @profile = profile
74
+
75
+ @options = {args: [],
76
+ prefs: {},
77
+ emulation: {},
78
+ extensions: [],
79
+ local_state: {},
80
+ exclude_switches: [],
81
+ perf_logging_prefs: {},
82
+ window_types: []}.merge(@options)
83
+
84
+ @logging_prefs = options.delete(:logging_prefs) || {}
85
+ @encoded_extensions = @options.delete(:encoded_extensions) || []
86
+ @extensions = []
87
+ @options.delete(:extensions).each { |ext| validate_extension(ext) }
88
+ end
89
+
90
+ #
91
+ # Add an extension by local path.
92
+ #
93
+ # @example
94
+ # options = Selenium::WebDriver::Chrome::Options.new
95
+ # options.add_extension('/path/to/extension.crx')
96
+ #
97
+ # @param [String] path The local path to the .crx file
98
+ #
99
+
100
+ def add_extension(path)
101
+ validate_extension(path)
102
+ end
103
+
104
+ #
105
+ # Add an extension by local path.
106
+ #
107
+ # @example
108
+ # extensions = ['/path/to/extension.crx', '/path/to/other.crx']
109
+ # options = Selenium::WebDriver::Chrome::Options.new
110
+ # options.extensions = extensions
111
+ #
112
+ # @param [Array<String>] extensions A list of paths to (.crx) Chrome extensions to install on startup
113
+ #
114
+
115
+ def extensions=(extensions)
116
+ extensions.each { |ext| validate_extension(ext) }
117
+ end
118
+
119
+ #
120
+ # Add an extension by Base64-encoded string.
121
+ #
122
+ # @example
123
+ # options = Selenium::WebDriver::Chrome::Options.new
124
+ # options.add_encoded_extension(encoded_string)
125
+ #
126
+ # @param [String] encoded The Base64-encoded string of the .crx file
127
+ #
128
+
129
+ def add_encoded_extension(encoded)
130
+ @encoded_extensions << encoded
131
+ end
132
+
133
+ #
134
+ # Add a command-line argument to use when starting Chrome.
135
+ #
136
+ # @example Start Chrome maximized
137
+ # options = Selenium::WebDriver::Chrome::Options.new
138
+ # options.add_argument('start-maximized')
139
+ #
140
+ # @param [String] arg The command-line argument to add
141
+ #
142
+
143
+ def add_argument(arg)
144
+ @options[:args] << arg
145
+ end
146
+
147
+ #
148
+ # Add a preference that is only applied to the user profile in use.
149
+ #
150
+ # @example Set the default homepage
151
+ # options = Selenium::WebDriver::Chrome::Options.new
152
+ # options.add_preference('homepage', 'http://www.seleniumhq.com/')
153
+ #
154
+ # @param [String] name Key of the preference
155
+ # @param [Boolean, String, Integer] value Value of the preference
156
+ #
157
+
158
+ def add_preference(name, value)
159
+ @options[:prefs][name] = value
160
+ end
161
+
162
+ #
163
+ # Add emulation device information
164
+ #
165
+ # see: http://chromedriver.chromium.org/mobile-emulation
166
+ #
167
+ # @example Start Chrome in mobile emulation mode by device name
168
+ # options = Selenium::WebDriver::Chrome::Options.new
169
+ # options.add_emulation(device_name: 'iPhone 6')
170
+ #
171
+ # @example Start Chrome in mobile emulation mode by device metrics
172
+ # options = Selenium::WebDriver::Chrome::Options.new
173
+ # options.add_emulation(device_metrics: {width: 400, height: 800, pixelRatio: 1, touch: true})
174
+ #
175
+ # @param [Hash] opts the pre-defined options for adding mobile emulation values
176
+ # @option opts [String] :device_name A valid device name from the Chrome DevTools Emulation panel
177
+ # @option opts [Hash] :device_metrics Hash containing width, height, pixelRatio, touch
178
+ # @option opts [String] :user_agent Full user agent
179
+ #
180
+
181
+ def add_emulation(**opts)
182
+ @options[:emulation] = opts
183
+ end
184
+
185
+ #
186
+ # Enables mobile browser use on Android.
187
+ #
188
+ # @see https://chromedriver.chromium.org/getting-started/getting-started---android
189
+ #
190
+ # @param [String] package The package name of the Chrome or WebView app.
191
+ # @param [String] serial_number The device serial number on which to launch the Chrome or WebView app.
192
+ # @param [String] use_running_app When true uses an already-running Chrome or WebView app,
193
+ # instead of launching the app with a clear data directory.
194
+ # @param [String] activity Name of the Activity hosting the WebView (Not available on Chrome Apps).
195
+ #
196
+
197
+ def enable_android(package: 'com.android.chrome', serial_number: nil, use_running_app: nil, activity: nil)
198
+ @options[:android_package] = package
199
+ @options[:android_activity] = activity unless activity.nil?
200
+ @options[:android_device_serial] = serial_number unless serial_number.nil?
201
+ @options[:android_use_running_app] = use_running_app unless use_running_app.nil?
202
+ end
203
+
204
+ protected
205
+
206
+ def process_browser_options(browser_options)
207
+ enable_logging(browser_options) unless @logging_prefs.empty?
208
+
209
+ options = browser_options[self.class::KEY]
210
+ options['binary'] ||= binary_path if binary_path
211
+
212
+ if @profile
213
+ options['args'] ||= []
214
+ options['args'] << "--user-data-dir=#{@profile.directory}"
215
+ end
216
+
217
+ return if (@encoded_extensions + @extensions).empty?
218
+
219
+ options['extensions'] = @encoded_extensions + @extensions.map { |ext| encode_extension(ext) }
220
+ end
221
+
222
+ def binary_path
223
+ Chrome.path
224
+ end
225
+
226
+ def encode_extension(path)
227
+ File.open(path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
228
+ end
229
+
230
+ def validate_extension(path)
231
+ raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.file?(path)
232
+ raise Error::WebDriverError, "file was not an extension #{path.inspect}" unless File.extname(path) == '.crx'
233
+
234
+ @extensions << path
235
+ end
236
+
237
+ def camelize?(key)
238
+ !%w[localState prefs].include?(key)
239
+ end
240
+ end # Options
241
+ end # Chromium
242
+ end # WebDriver
243
+ end # Selenium
@@ -0,0 +1,113 @@
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 Chromium
23
+ #
24
+ # @private
25
+ #
26
+
27
+ class Profile
28
+ include ProfileHelper
29
+
30
+ def initialize(model = nil)
31
+ @model = verify_model(model)
32
+ @extensions = []
33
+ @encoded_extensions = []
34
+ @directory = nil
35
+ end
36
+
37
+ def add_extension(path)
38
+ raise Error::WebDriverError, "could not find extension at #{path.inspect}" unless File.file?(path)
39
+
40
+ @extensions << path
41
+ end
42
+
43
+ def add_encoded_extension(encoded)
44
+ @encoded_extensions << encoded
45
+ end
46
+
47
+ def directory
48
+ @directory || layout_on_disk
49
+ end
50
+
51
+ #
52
+ # Set a preference in the profile.
53
+ #
54
+ # See https://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/pref_names.cc
55
+ #
56
+
57
+ def []=(key, value)
58
+ parts = key.split('.')
59
+ parts[0..-2].inject(prefs) { |a, e| a[e] ||= {} }[parts.last] = value
60
+ end
61
+
62
+ def [](key)
63
+ parts = key.split('.')
64
+ parts.inject(prefs) { |a, e| a.fetch(e) }
65
+ end
66
+
67
+ def layout_on_disk
68
+ @directory = @model ? create_tmp_copy(@model) : Dir.mktmpdir('webdriver-chrome-profile')
69
+ FileReaper << @directory
70
+
71
+ write_prefs_to @directory
72
+
73
+ @directory
74
+ end
75
+
76
+ def as_json(*)
77
+ extensions = @extensions.map do |crx_path|
78
+ File.open(crx_path, 'rb') { |crx_file| Base64.strict_encode64 crx_file.read }
79
+ end
80
+
81
+ extensions.concat(@encoded_extensions)
82
+
83
+ opts = {'directory' => directory || layout_on_disk}
84
+ opts['extensions'] = extensions if extensions.any?
85
+ opts
86
+ end
87
+
88
+ private
89
+
90
+ def write_prefs_to(dir)
91
+ prefs_file = prefs_file_for(dir)
92
+
93
+ FileUtils.mkdir_p File.dirname(prefs_file)
94
+ File.open(prefs_file, 'w') { |file| file << JSON.generate(prefs) }
95
+ end
96
+
97
+ def prefs
98
+ @prefs ||= read_model_prefs
99
+ end
100
+
101
+ def read_model_prefs
102
+ return {} unless @model
103
+
104
+ JSON.parse File.read(prefs_file_for(@model))
105
+ end
106
+
107
+ def prefs_file_for(dir)
108
+ File.join dir, 'Default', 'Preferences'
109
+ end
110
+ end # Profile
111
+ end # Chromium
112
+ end # WebDriver
113
+ end # Selenium
@@ -0,0 +1,29 @@
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 Chromium
23
+ autoload :Features, 'selenium/webdriver/chromium/features'
24
+ autoload :Driver, 'selenium/webdriver/chromium/driver'
25
+ autoload :Profile, 'selenium/webdriver/chromium/profile'
26
+ autoload :Options, 'selenium/webdriver/chromium/options'
27
+ end # Chromium
28
+ end # WebDriver
29
+ end # Selenium
@@ -22,6 +22,7 @@ module Selenium
22
22
  class ActionBuilder
23
23
  include KeyActions # Actions specific to key inputs
24
24
  include PointerActions # Actions specific to pointer inputs
25
+ include WheelActions # Actions specific to wheel inputs
25
26
 
26
27
  attr_reader :devices
27
28
 
@@ -31,19 +32,19 @@ module Selenium
31
32
  # the mouse is moving. Keep in mind that pauses must be added for other devices in order to line up the actions
32
33
  # correctly when using asynchronous.
33
34
  #
34
- # @param [Selenium::WebDriver::Remote::Bridge] bridge the bridge for the current driver instance
35
- # @param [Selenium::WebDriver::Interactions::PointerInput] mouse PointerInput for the mouse.
36
- # @param [Selenium::WebDriver::Interactions::KeyInput] keyboard KeyInput for the keyboard.
37
- # @param [Boolean] async Whether to perform the actions asynchronously per device. Defaults to false for
38
- # backwards compatibility.
35
+ # @param [Selenium::WebDriver::Remote::Bridge] bridge the bridge for the current driver instance.
36
+ # @param [Array<Selenium::WebDriver::Interactions::InputDevices>] devices list of valid sources of input.
37
+ # @param [Boolean] async Whether to perform the actions asynchronously per device.
39
38
  # @return [ActionBuilder] A self reference.
40
39
  #
41
40
 
42
- def initialize(bridge, mouse, keyboard, async = false)
43
- # For backwards compatibility, automatically include mouse & keyboard
41
+ def initialize(bridge, devices: [], async: false, duration: 250)
44
42
  @bridge = bridge
45
- @devices = [mouse, keyboard]
43
+ @duration = duration
46
44
  @async = async
45
+ @devices = []
46
+
47
+ Array(devices).each { |device| add_input(device) }
47
48
  end
48
49
 
49
50
  #
@@ -61,9 +62,7 @@ module Selenium
61
62
  #
62
63
 
63
64
  def add_pointer_input(kind, name)
64
- new_input = Interactions.pointer(kind, name: name)
65
- add_input(new_input)
66
- new_input
65
+ add_input(Interactions.pointer(kind, name: name))
67
66
  end
68
67
 
69
68
  #
@@ -79,20 +78,39 @@ module Selenium
79
78
  #
80
79
 
81
80
  def add_key_input(name)
82
- new_input = Interactions.key(name)
83
- add_input(new_input)
84
- new_input
81
+ add_input(Interactions.key(name))
82
+ end
83
+
84
+ #
85
+ # Adds a WheelInput device
86
+ #
87
+ # @example Add a wheel input device
88
+ #
89
+ # builder = device.action
90
+ # builder.add_wheel_input('wheel2')
91
+ #
92
+ # @param [String] name name for the device
93
+ # @return [Interactions::WheelInput] The wheel input added
94
+ #
95
+
96
+ def add_wheel_input(name)
97
+ add_input(Interactions.wheel(name))
85
98
  end
86
99
 
87
100
  #
88
- # Retrieves the input device for the given name
101
+ # Retrieves the input device for the given name or type
89
102
  #
90
103
  # @param [String] name name of the input device
91
- # @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name
104
+ # @param [String] type name of the input device
105
+ # @return [Selenium::WebDriver::Interactions::InputDevice] input device with given name or type
92
106
  #
93
107
 
94
- def get_device(name)
95
- @devices.find { |device| device.name == name.to_s }
108
+ def device(name: nil, type: nil)
109
+ input = @devices.find { |device| (device.name == name.to_s || name.nil?) && (device.type == type || type.nil?) }
110
+
111
+ raise(ArgumentError, "Can not find device: #{name}") if name && input.nil?
112
+
113
+ input
96
114
  end
97
115
 
98
116
  #
@@ -115,6 +133,16 @@ module Selenium
115
133
  @devices.select { |device| device.type == Interactions::KEY }
116
134
  end
117
135
 
136
+ #
137
+ # Retrieves the current WheelInput device
138
+ #
139
+ # @return [Selenium::WebDriver::Interactions::InputDevice] current WheelInput devices
140
+ #
141
+
142
+ def wheel_inputs
143
+ @devices.select { |device| device.type == Interactions::WHEEL }
144
+ end
145
+
118
146
  #
119
147
  # Creates a pause for the given device of the given duration. If no duration is given, the pause will only wait
120
148
  # for all actions to complete in that tick.
@@ -131,7 +159,8 @@ module Selenium
131
159
  # @return [ActionBuilder] A self reference.
132
160
  #
133
161
 
134
- def pause(device, duration = nil)
162
+ def pause(device: nil, duration: 0)
163
+ device ||= pointer_input
135
164
  device.create_pause(duration)
136
165
  self
137
166
  end
@@ -152,7 +181,11 @@ module Selenium
152
181
  # @return [ActionBuilder] A self reference.
153
182
  #
154
183
 
155
- def pauses(device, number, duration = nil)
184
+ def pauses(device: nil, number: nil, duration: 0)
185
+ number ||= 2
186
+ device ||= pointer_input
187
+ duration ||= 0
188
+
156
189
  number.times { device.create_pause(duration) }
157
190
  self
158
191
  end
@@ -162,7 +195,7 @@ module Selenium
162
195
  #
163
196
 
164
197
  def perform
165
- @bridge.send_actions @devices.map(&:encode).compact
198
+ @bridge.send_actions @devices.filter_map(&:encode)
166
199
  clear_all_actions
167
200
  nil
168
201
  end
@@ -202,11 +235,16 @@ module Selenium
202
235
  #
203
236
 
204
237
  def add_input(device)
238
+ device = Interactions.send(device) if device.is_a?(Symbol) && Interactions.respond_to?(device)
239
+
240
+ raise TypeError, "#{device.inspect} is not a valid InputDevice" unless device.is_a?(Interactions::InputDevice)
241
+
205
242
  unless @async
206
243
  max_device = @devices.max { |a, b| a.actions.length <=> b.actions.length }
207
- pauses(device, max_device.actions.length)
244
+ pauses(device: device, number: max_device.actions.length) if max_device
208
245
  end
209
246
  @devices << device
247
+ device
210
248
  end
211
249
  end # ActionBuilder
212
250
  end # WebDriver
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Licensed to the Software Freedom Conservancy (SFC) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The SFC licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+
20
+ module Selenium
21
+ module WebDriver
22
+ #
23
+ # @api private
24
+ #
25
+
26
+ class ChildProcess
27
+ TimeoutError = Class.new(StandardError)
28
+
29
+ SIGTERM = 'TERM'
30
+ SIGKILL = 'KILL'
31
+
32
+ POLL_INTERVAL = 0.1
33
+
34
+ attr_accessor :detach
35
+ attr_writer :io
36
+
37
+ def self.build(*command)
38
+ new(*command)
39
+ end
40
+
41
+ def initialize(*command)
42
+ @command = command
43
+ @detach = false
44
+ @pid = nil
45
+ @status = nil
46
+ end
47
+
48
+ def io
49
+ @io ||= Platform.null_device
50
+ end
51
+
52
+ def start
53
+ options = {%i[out err] => io}
54
+ options[:pgroup] = true unless Platform.windows? # NOTE: this is a bug only in Windows 7
55
+
56
+ WebDriver.logger.debug("Starting process: #{@command} with #{options}", id: :process)
57
+ @pid = Process.spawn(*@command, options)
58
+ WebDriver.logger.debug(" -> pid: #{@pid}", id: :process)
59
+
60
+ Process.detach(@pid) if detach
61
+ end
62
+
63
+ def stop(timeout = 3)
64
+ return unless @pid
65
+ return if exited?
66
+
67
+ terminate_and_wait_else_kill(timeout)
68
+ rescue Errno::ECHILD, Errno::ESRCH => e
69
+ # Process exited earlier than terminate/kill could catch
70
+ WebDriver.logger.debug(" -> process: #{@pid} does not exist (#{e.class.name})", id: :process)
71
+ end
72
+
73
+ def alive?
74
+ @pid && !exited?
75
+ end
76
+
77
+ def exited?
78
+ return false unless @pid
79
+
80
+ WebDriver.logger.debug("Checking if #{@pid} is exited:", id: :process)
81
+ _, @status = waitpid2(@pid, Process::WNOHANG | Process::WUNTRACED) if @status.nil?
82
+ return false if @status.nil?
83
+
84
+ exit_code = @status.exitstatus || @status.termsig
85
+ WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}", id: :process)
86
+
87
+ !!exit_code
88
+ rescue Errno::ECHILD, Errno::ESRCH
89
+ WebDriver.logger.debug(" -> process: #{@pid} already finished", id: :process)
90
+ true
91
+ end
92
+
93
+ def poll_for_exit(timeout)
94
+ WebDriver.logger.debug("Polling #{timeout} seconds for exit of #{@pid}", id: :process)
95
+
96
+ end_time = Time.now + timeout
97
+ sleep POLL_INTERVAL until exited? || Time.now > end_time
98
+
99
+ raise TimeoutError, " -> #{@pid} still alive after #{timeout} seconds" unless exited?
100
+ end
101
+
102
+ def wait
103
+ return if exited?
104
+
105
+ _, @status = waitpid2(@pid)
106
+ end
107
+
108
+ private
109
+
110
+ def terminate_and_wait_else_kill(timeout)
111
+ WebDriver.logger.debug("Sending TERM to process: #{@pid}", id: :process)
112
+ terminate(@pid)
113
+ poll_for_exit(timeout)
114
+
115
+ WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
116
+ rescue TimeoutError, Errno::EINVAL
117
+ WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
118
+ kill(@pid)
119
+ wait
120
+ WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
121
+ end
122
+
123
+ def terminate(pid)
124
+ Process.kill(SIGTERM, pid)
125
+ end
126
+
127
+ def kill(pid)
128
+ Process.kill(SIGKILL, pid)
129
+ end
130
+
131
+ def waitpid2(pid, flags = 0)
132
+ Process.waitpid2(pid, flags)
133
+ end
134
+ end # ChildProcess
135
+ end # WebDriver
136
+ end # Selenium