selenium-webdriver 4.1.0 → 4.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES +182 -1
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/bin/linux/selenium-manager +0 -0
- data/bin/macos/selenium-manager +0 -0
- data/bin/windows/selenium-manager.exe +0 -0
- data/lib/selenium/server.rb +35 -26
- data/lib/selenium/webdriver/atoms/findElements.js +0 -0
- data/lib/selenium/webdriver/atoms/getAttribute.js +0 -0
- data/lib/selenium/webdriver/atoms/isDisplayed.js +0 -0
- data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
- data/lib/selenium/webdriver/atoms.rb +2 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +88 -0
- data/lib/selenium/webdriver/bidi/browsing_context_info.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/base_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/console_log_entry.rb +35 -0
- data/lib/selenium/webdriver/bidi/log/filter_by.rb +40 -0
- data/lib/selenium/webdriver/bidi/log/generic_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log/javascript_log_entry.rb +33 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +143 -0
- data/lib/selenium/webdriver/bidi/navigate_result.rb +33 -0
- data/lib/selenium/webdriver/bidi/session.rb +51 -0
- data/lib/selenium/webdriver/bidi.rb +56 -0
- data/lib/selenium/webdriver/chrome/driver.rb +19 -28
- data/lib/selenium/webdriver/chrome/features.rb +6 -68
- data/lib/selenium/webdriver/chrome/options.rb +3 -223
- data/lib/selenium/webdriver/chrome/profile.rb +3 -83
- data/lib/selenium/webdriver/chrome/service.rb +4 -19
- data/lib/selenium/webdriver/chrome.rb +0 -14
- data/lib/selenium/webdriver/chromium/driver.rb +61 -0
- data/lib/selenium/webdriver/chromium/features.rb +103 -0
- data/lib/selenium/webdriver/chromium/options.rb +261 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium/service.rb +42 -0
- data/lib/selenium/webdriver/chromium.rb +32 -0
- data/lib/selenium/webdriver/common/action_builder.rb +72 -22
- data/lib/selenium/webdriver/common/child_process.rb +124 -0
- data/lib/selenium/webdriver/common/driver.rb +50 -70
- data/lib/selenium/webdriver/common/driver_extensions/downloads_files.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/full_page_screenshot.rb +0 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_addons.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_apple_permissions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_authentication.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/{has_remote_status.rb → has_bidi.rb} +10 -5
- data/lib/selenium/webdriver/common/driver_extensions/has_casting.rb +10 -1
- data/lib/selenium/webdriver/common/driver_extensions/has_cdp.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_context.rb +1 -4
- data/lib/selenium/webdriver/common/driver_extensions/has_debugger.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_location.rb +1 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +1 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +2 -69
- data/lib/selenium/webdriver/common/driver_extensions/has_permissions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_pinned_scripts.rb +1 -3
- data/lib/selenium/webdriver/common/element.rb +8 -8
- data/lib/selenium/webdriver/common/error.rb +1 -3
- data/lib/selenium/webdriver/common/html5/shared_web_storage.rb +2 -2
- data/lib/selenium/webdriver/common/interactions/input_device.rb +10 -4
- data/lib/selenium/webdriver/common/interactions/interaction.rb +12 -25
- data/lib/selenium/webdriver/common/interactions/interactions.rb +24 -4
- data/lib/selenium/webdriver/common/interactions/key_actions.rb +5 -1
- data/lib/selenium/webdriver/common/interactions/key_input.rb +11 -27
- data/lib/selenium/webdriver/common/interactions/none_input.rb +10 -8
- data/lib/selenium/webdriver/common/interactions/pause.rb +49 -0
- data/lib/selenium/webdriver/common/interactions/pointer_actions.rb +59 -70
- data/lib/selenium/webdriver/common/interactions/pointer_cancel.rb +45 -0
- data/lib/selenium/webdriver/common/interactions/pointer_event_properties.rb +63 -0
- data/lib/selenium/webdriver/common/interactions/pointer_input.rb +15 -84
- data/lib/selenium/webdriver/common/interactions/pointer_move.rb +60 -0
- data/lib/selenium/webdriver/common/interactions/pointer_press.rb +85 -0
- data/lib/selenium/webdriver/common/interactions/scroll.rb +59 -0
- data/lib/selenium/webdriver/common/interactions/scroll_origin.rb +48 -0
- data/lib/selenium/webdriver/common/interactions/typing_interaction.rb +54 -0
- data/lib/selenium/webdriver/common/interactions/wheel_actions.rb +113 -0
- data/lib/selenium/webdriver/common/interactions/wheel_input.rb +42 -0
- data/lib/selenium/webdriver/common/keys.rb +1 -0
- data/lib/selenium/webdriver/common/logger.rb +10 -2
- data/lib/selenium/webdriver/common/manager.rb +0 -27
- data/lib/selenium/webdriver/common/options.rb +32 -17
- data/lib/selenium/webdriver/common/platform.rb +8 -5
- data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
- data/lib/selenium/webdriver/common/proxy.rb +1 -1
- data/lib/selenium/webdriver/common/search_context.rb +0 -6
- data/lib/selenium/webdriver/common/selenium_manager.rb +89 -0
- data/lib/selenium/webdriver/common/service.rb +16 -8
- data/lib/selenium/webdriver/common/service_manager.rb +4 -13
- data/lib/selenium/webdriver/common/shadow_root.rb +2 -3
- data/lib/selenium/webdriver/common/socket_lock.rb +2 -2
- data/lib/selenium/webdriver/common/socket_poller.rb +1 -1
- data/lib/selenium/webdriver/common/takes_screenshot.rb +2 -3
- data/lib/selenium/webdriver/common/target_locator.rb +2 -3
- data/lib/selenium/webdriver/common/timeouts.rb +2 -2
- data/lib/selenium/webdriver/common/virtual_authenticator/credential.rb +85 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator.rb +72 -0
- data/lib/selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options.rb +62 -0
- data/lib/selenium/webdriver/common/websocket_connection.rb +164 -0
- data/lib/selenium/webdriver/common/window.rb +6 -6
- data/lib/selenium/webdriver/common/zipper.rb +1 -1
- data/lib/selenium/webdriver/common.rb +19 -3
- data/lib/selenium/webdriver/devtools/console_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/exception_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/mutation_event.rb +0 -2
- data/lib/selenium/webdriver/devtools/network_interceptor.rb +173 -0
- data/lib/selenium/webdriver/devtools/pinned_script.rb +0 -2
- data/lib/selenium/webdriver/devtools/request.rb +1 -3
- data/lib/selenium/webdriver/devtools/response.rb +1 -3
- data/lib/selenium/webdriver/devtools.rb +6 -113
- data/lib/selenium/webdriver/edge/driver.rb +20 -3
- data/lib/selenium/webdriver/edge/features.rb +4 -4
- data/lib/selenium/webdriver/edge/options.rb +3 -5
- data/lib/selenium/webdriver/edge/profile.rb +2 -2
- data/lib/selenium/webdriver/edge/service.rb +2 -2
- data/lib/selenium/webdriver/firefox/driver.rb +20 -2
- data/lib/selenium/webdriver/firefox/features.rb +6 -6
- data/lib/selenium/webdriver/firefox/options.rb +9 -3
- data/lib/selenium/webdriver/firefox/profile.rb +7 -11
- data/lib/selenium/webdriver/firefox/service.rb +0 -1
- data/lib/selenium/webdriver/firefox/util.rb +46 -0
- data/lib/selenium/webdriver/firefox.rb +1 -14
- data/lib/selenium/webdriver/ie/driver.rb +20 -1
- data/lib/selenium/webdriver/ie/service.rb +1 -2
- data/lib/selenium/webdriver/ie.rb +0 -14
- data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +15 -8
- data/lib/selenium/webdriver/remote/bridge.rb +59 -30
- data/lib/selenium/webdriver/remote/capabilities.rb +34 -12
- data/lib/selenium/webdriver/remote/driver.rb +14 -15
- data/lib/selenium/webdriver/remote/http/curb.rb +0 -2
- data/lib/selenium/webdriver/remote/http/default.rb +7 -12
- data/lib/selenium/webdriver/remote/response.rb +2 -3
- data/lib/selenium/webdriver/remote.rb +0 -1
- data/lib/selenium/webdriver/safari/driver.rb +20 -1
- data/lib/selenium/webdriver/safari/features.rb +0 -2
- data/lib/selenium/webdriver/safari/options.rb +5 -1
- data/lib/selenium/webdriver/safari.rb +1 -15
- data/lib/selenium/webdriver/support/color.rb +20 -20
- data/lib/selenium/webdriver/support/guards/guard.rb +0 -2
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
- data/lib/selenium/webdriver/support/guards.rb +1 -1
- data/lib/selenium/webdriver/support/relative_locator.rb +0 -1
- data/lib/selenium/webdriver/support/select.rb +3 -1
- data/lib/selenium/webdriver/version.rb +1 -1
- data/lib/selenium/webdriver.rb +4 -4
- data/selenium-webdriver.gemspec +13 -11
- metadata +68 -59
- data/lib/selenium/webdriver/remote/http/persistent.rb +0 -65
- data/lib/selenium/webdriver/support/cdp/domain.rb.erb +0 -63
- data/lib/selenium/webdriver/support/cdp_client_generator.rb +0 -108
|
@@ -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,164 @@
|
|
|
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 send_cmd(**payload)
|
|
56
|
+
id = next_id
|
|
57
|
+
data = payload.merge(id: id)
|
|
58
|
+
WebDriver.logger.debug "WebSocket -> #{data}"[...MAX_LOG_MESSAGE_SIZE]
|
|
59
|
+
data = JSON.generate(data)
|
|
60
|
+
out_frame = WebSocket::Frame::Outgoing::Client.new(version: ws.version, data: data, type: 'text')
|
|
61
|
+
socket.write(out_frame.to_s)
|
|
62
|
+
|
|
63
|
+
wait.until { messages.delete(id) }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
# We should be thread-safe to use the hash without synchronization
|
|
69
|
+
# because its keys are WebSocket message identifiers and they should be
|
|
70
|
+
# unique within a devtools session.
|
|
71
|
+
def messages
|
|
72
|
+
@messages ||= {}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def process_handshake
|
|
76
|
+
socket.print(ws.to_s)
|
|
77
|
+
ws << socket.readpartial(1024)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def attach_socket_listener
|
|
81
|
+
Thread.new do
|
|
82
|
+
Thread.current.abort_on_exception = true
|
|
83
|
+
Thread.current.report_on_exception = false
|
|
84
|
+
|
|
85
|
+
until socket.eof?
|
|
86
|
+
incoming_frame << socket.readpartial(1024)
|
|
87
|
+
|
|
88
|
+
while (frame = incoming_frame.next)
|
|
89
|
+
message = process_frame(frame)
|
|
90
|
+
next unless message['method']
|
|
91
|
+
|
|
92
|
+
params = message['params']
|
|
93
|
+
callbacks[message['method']].each do |callback|
|
|
94
|
+
@callback_threads.add(callback_thread(params, &callback))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
rescue *CONNECTION_ERRORS
|
|
99
|
+
Thread.stop
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def incoming_frame
|
|
104
|
+
@incoming_frame ||= WebSocket::Frame::Incoming::Client.new(version: ws.version)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def process_frame(frame)
|
|
108
|
+
message = frame.to_s
|
|
109
|
+
|
|
110
|
+
# Firefox will periodically fail on unparsable empty frame
|
|
111
|
+
return {} if message.empty?
|
|
112
|
+
|
|
113
|
+
message = JSON.parse(message)
|
|
114
|
+
messages[message['id']] = message
|
|
115
|
+
WebDriver.logger.debug "WebSocket <- #{message}"[...MAX_LOG_MESSAGE_SIZE]
|
|
116
|
+
|
|
117
|
+
message
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def callback_thread(params)
|
|
121
|
+
Thread.new do
|
|
122
|
+
Thread.current.abort_on_exception = true
|
|
123
|
+
|
|
124
|
+
# We might end up blocked forever when we have an error in event.
|
|
125
|
+
# For example, if network interception event raises error,
|
|
126
|
+
# the browser will keep waiting for the request to be proceeded
|
|
127
|
+
# before returning back to the original thread. In this case,
|
|
128
|
+
# we should at least print the error.
|
|
129
|
+
Thread.current.report_on_exception = true
|
|
130
|
+
|
|
131
|
+
yield params
|
|
132
|
+
rescue *CONNECTION_ERRORS
|
|
133
|
+
Thread.stop
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def wait
|
|
138
|
+
@wait ||= Wait.new(timeout: RESPONSE_WAIT_TIMEOUT, interval: RESPONSE_WAIT_INTERVAL)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def socket
|
|
142
|
+
@socket ||= if URI(@url).scheme == 'wss'
|
|
143
|
+
socket = TCPSocket.new(ws.host, ws.port)
|
|
144
|
+
socket = OpenSSL::SSL::SSLSocket.new(socket, OpenSSL::SSL::SSLContext.new)
|
|
145
|
+
socket.sync_close = true
|
|
146
|
+
socket.connect
|
|
147
|
+
|
|
148
|
+
socket
|
|
149
|
+
else
|
|
150
|
+
TCPSocket.new(ws.host, ws.port)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def ws
|
|
155
|
+
@ws ||= WebSocket::Handshake::Client.new(url: @url)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def next_id
|
|
159
|
+
@id ||= 0
|
|
160
|
+
@id += 1
|
|
161
|
+
end
|
|
162
|
+
end # BiDi
|
|
163
|
+
end # WebDriver
|
|
164
|
+
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
|
-
'
|
|
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
|
-
'
|
|
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
|
-
'
|
|
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,
|
|
@@ -22,6 +22,7 @@ require 'selenium/webdriver/common/platform'
|
|
|
22
22
|
require 'selenium/webdriver/common/proxy'
|
|
23
23
|
require 'selenium/webdriver/common/log_entry'
|
|
24
24
|
require 'selenium/webdriver/common/file_reaper'
|
|
25
|
+
require 'selenium/webdriver/common/selenium_manager'
|
|
25
26
|
require 'selenium/webdriver/common/service'
|
|
26
27
|
require 'selenium/webdriver/common/service_manager'
|
|
27
28
|
require 'selenium/webdriver/common/socket_lock'
|
|
@@ -38,15 +39,28 @@ require 'selenium/webdriver/common/logger'
|
|
|
38
39
|
require 'selenium/webdriver/common/logs'
|
|
39
40
|
require 'selenium/webdriver/common/manager'
|
|
40
41
|
require 'selenium/webdriver/common/search_context'
|
|
42
|
+
require 'selenium/webdriver/common/interactions/interaction'
|
|
43
|
+
require 'selenium/webdriver/common/interactions/interactions'
|
|
44
|
+
require 'selenium/webdriver/common/interactions/pointer_event_properties'
|
|
45
|
+
require 'selenium/webdriver/common/interactions/pointer_cancel'
|
|
46
|
+
require 'selenium/webdriver/common/interactions/pointer_move'
|
|
47
|
+
require 'selenium/webdriver/common/interactions/pointer_press'
|
|
48
|
+
require 'selenium/webdriver/common/interactions/typing_interaction'
|
|
49
|
+
require 'selenium/webdriver/common/interactions/pause'
|
|
41
50
|
require 'selenium/webdriver/common/interactions/key_actions'
|
|
42
51
|
require 'selenium/webdriver/common/interactions/pointer_actions'
|
|
43
|
-
require 'selenium/webdriver/common/interactions/interactions'
|
|
44
52
|
require 'selenium/webdriver/common/interactions/input_device'
|
|
45
|
-
require 'selenium/webdriver/common/interactions/interaction'
|
|
46
53
|
require 'selenium/webdriver/common/interactions/none_input'
|
|
47
54
|
require 'selenium/webdriver/common/interactions/key_input'
|
|
48
55
|
require 'selenium/webdriver/common/interactions/pointer_input'
|
|
56
|
+
require 'selenium/webdriver/common/interactions/scroll'
|
|
57
|
+
require 'selenium/webdriver/common/interactions/wheel_input'
|
|
58
|
+
require 'selenium/webdriver/common/interactions/scroll_origin'
|
|
59
|
+
require 'selenium/webdriver/common/interactions/wheel_actions'
|
|
49
60
|
require 'selenium/webdriver/common/action_builder'
|
|
61
|
+
require 'selenium/webdriver/common/virtual_authenticator/credential'
|
|
62
|
+
require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options'
|
|
63
|
+
require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator'
|
|
50
64
|
require 'selenium/webdriver/common/html5/shared_web_storage'
|
|
51
65
|
require 'selenium/webdriver/common/html5/local_storage'
|
|
52
66
|
require 'selenium/webdriver/common/html5/session_storage'
|
|
@@ -54,7 +68,6 @@ require 'selenium/webdriver/common/driver_extensions/has_web_storage'
|
|
|
54
68
|
require 'selenium/webdriver/common/driver_extensions/downloads_files'
|
|
55
69
|
require 'selenium/webdriver/common/driver_extensions/has_location'
|
|
56
70
|
require 'selenium/webdriver/common/driver_extensions/has_session_id'
|
|
57
|
-
require 'selenium/webdriver/common/driver_extensions/has_remote_status'
|
|
58
71
|
require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
|
|
59
72
|
require 'selenium/webdriver/common/driver_extensions/has_network_connection'
|
|
60
73
|
require 'selenium/webdriver/common/driver_extensions/has_network_interception'
|
|
@@ -66,6 +79,7 @@ require 'selenium/webdriver/common/driver_extensions/prints_page'
|
|
|
66
79
|
require 'selenium/webdriver/common/driver_extensions/uploads_files'
|
|
67
80
|
require 'selenium/webdriver/common/driver_extensions/full_page_screenshot'
|
|
68
81
|
require 'selenium/webdriver/common/driver_extensions/has_addons'
|
|
82
|
+
require 'selenium/webdriver/common/driver_extensions/has_bidi'
|
|
69
83
|
require 'selenium/webdriver/common/driver_extensions/has_devtools'
|
|
70
84
|
require 'selenium/webdriver/common/driver_extensions/has_authentication'
|
|
71
85
|
require 'selenium/webdriver/common/driver_extensions/has_logs'
|
|
@@ -81,3 +95,5 @@ require 'selenium/webdriver/common/takes_screenshot'
|
|
|
81
95
|
require 'selenium/webdriver/common/driver'
|
|
82
96
|
require 'selenium/webdriver/common/element'
|
|
83
97
|
require 'selenium/webdriver/common/shadow_root'
|
|
98
|
+
require 'selenium/webdriver/common/websocket_connection'
|
|
99
|
+
require 'selenium/webdriver/common/child_process'
|
|
@@ -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
|