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.
- checksums.yaml +4 -4
- data/CHANGES +446 -1
- data/Gemfile +4 -0
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/README.md +3 -3
- 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 +41 -43
- data/lib/selenium/webdriver/atoms/findElements.js +52 -50
- data/lib/selenium/webdriver/atoms/getAttribute.js +6 -100
- data/lib/selenium/webdriver/atoms/isDisplayed.js +24 -96
- data/lib/selenium/webdriver/atoms/mutationListener.js +0 -0
- data/lib/selenium/webdriver/atoms.rb +5 -3
- data/lib/selenium/webdriver/bidi/browsing_context.rb +100 -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/{common/driver_extensions/has_location.rb → bidi/log/filter_by.rb} +14 -11
- 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_handler.rb +65 -0
- data/lib/selenium/webdriver/bidi/log_inspector.rb +147 -0
- data/lib/selenium/webdriver/bidi/network.rb +82 -0
- data/lib/selenium/webdriver/bidi/session.rb +51 -0
- data/lib/selenium/webdriver/bidi/struct.rb +42 -0
- data/lib/selenium/webdriver/bidi.rb +67 -0
- data/lib/selenium/webdriver/chrome/driver.rb +9 -29
- data/lib/selenium/webdriver/chrome/features.rb +9 -67
- 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 +5 -19
- data/lib/selenium/webdriver/chrome.rb +0 -16
- data/lib/selenium/webdriver/chromium/driver.rb +61 -0
- data/lib/selenium/webdriver/chromium/features.rb +99 -0
- data/lib/selenium/webdriver/chromium/options.rb +243 -0
- data/lib/selenium/webdriver/chromium/profile.rb +113 -0
- data/lib/selenium/webdriver/chromium.rb +29 -0
- data/lib/selenium/webdriver/common/action_builder.rb +60 -22
- data/lib/selenium/webdriver/common/child_process.rb +136 -0
- data/lib/selenium/webdriver/common/driver.rb +54 -89
- 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_fedcm_dialog.rb +55 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_file_downloads.rb +65 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_launching.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +9 -3
- data/lib/selenium/webdriver/common/driver_extensions/has_network_conditions.rb +0 -2
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +8 -68
- 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/driver_finder.rb +97 -0
- data/lib/selenium/webdriver/common/element.rb +8 -8
- data/lib/selenium/webdriver/common/error.rb +29 -4
- data/lib/selenium/webdriver/common/fedcm/account.rb +49 -0
- data/lib/selenium/webdriver/common/fedcm/dialog.rb +74 -0
- data/lib/selenium/webdriver/common/fedcm.rb +27 -0
- 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 -71
- data/lib/selenium/webdriver/common/{driver_extensions/has_network_connection.rb → interactions/pointer_cancel.rb} +19 -11
- 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 +114 -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/local_driver.rb +53 -0
- data/lib/selenium/webdriver/common/logger.rb +93 -28
- data/lib/selenium/webdriver/common/manager.rb +2 -29
- data/lib/selenium/webdriver/common/network.rb +64 -0
- data/lib/selenium/webdriver/common/options.rb +19 -19
- data/lib/selenium/webdriver/common/platform.rb +12 -52
- data/lib/selenium/webdriver/common/port_prober.rb +1 -1
- data/lib/selenium/webdriver/common/profile_helper.rb +1 -1
- data/lib/selenium/webdriver/common/proxy.rb +4 -4
- data/lib/selenium/webdriver/common/script.rb +45 -0
- data/lib/selenium/webdriver/common/search_context.rb +10 -8
- data/lib/selenium/webdriver/common/selenium_manager.rb +104 -0
- data/lib/selenium/webdriver/common/service.rb +24 -26
- data/lib/selenium/webdriver/common/service_manager.rb +9 -15
- data/lib/selenium/webdriver/common/shadow_root.rb +2 -3
- data/lib/selenium/webdriver/common/socket_lock.rb +3 -3
- data/lib/selenium/webdriver/common/socket_poller.rb +3 -3
- data/lib/selenium/webdriver/common/takes_screenshot.rb +6 -5
- data/lib/selenium/webdriver/common/target_locator.rb +5 -5
- 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/wait.rb +1 -1
- data/lib/selenium/webdriver/common/websocket_connection.rb +176 -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 +27 -5
- 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 +17 -114
- data/lib/selenium/webdriver/edge/driver.rb +9 -3
- data/lib/selenium/webdriver/edge/features.rb +8 -4
- data/lib/selenium/webdriver/edge/options.rb +17 -5
- data/lib/selenium/webdriver/edge/profile.rb +2 -2
- data/lib/selenium/webdriver/edge/service.rb +8 -7
- data/lib/selenium/webdriver/edge.rb +0 -2
- data/lib/selenium/webdriver/firefox/driver.rb +9 -2
- data/lib/selenium/webdriver/firefox/features.rb +11 -7
- data/lib/selenium/webdriver/firefox/options.rb +10 -16
- data/lib/selenium/webdriver/firefox/profile.rb +21 -17
- data/lib/selenium/webdriver/firefox/profiles_ini.rb +1 -1
- data/lib/selenium/webdriver/firefox/service.rb +1 -18
- data/lib/selenium/webdriver/firefox/util.rb +46 -0
- data/lib/selenium/webdriver/firefox.rb +1 -14
- data/lib/selenium/webdriver/ie/driver.rb +7 -1
- data/lib/selenium/webdriver/ie/features.rb +34 -0
- data/lib/selenium/webdriver/ie/options.rb +6 -4
- data/lib/selenium/webdriver/ie/service.rb +1 -22
- data/lib/selenium/webdriver/ie.rb +4 -17
- data/lib/selenium/webdriver/remote/bidi_bridge.rb +66 -0
- data/lib/selenium/webdriver/remote/{commands.rb → bridge/commands.rb} +22 -9
- data/lib/selenium/webdriver/remote/bridge/locator_converter.rb +76 -0
- data/lib/selenium/webdriver/remote/bridge.rb +158 -100
- data/lib/selenium/webdriver/remote/capabilities.rb +5 -55
- data/lib/selenium/webdriver/remote/driver.rb +35 -14
- data/lib/selenium/webdriver/remote/features.rb +75 -0
- data/lib/selenium/webdriver/remote/http/common.rb +26 -6
- data/lib/selenium/webdriver/remote/http/curb.rb +10 -6
- data/lib/selenium/webdriver/remote/http/default.rb +8 -14
- data/lib/selenium/webdriver/remote/response.rb +14 -22
- data/lib/selenium/webdriver/remote/server_error.rb +2 -2
- data/lib/selenium/webdriver/remote.rb +3 -2
- data/lib/selenium/webdriver/safari/driver.rb +7 -1
- data/lib/selenium/webdriver/safari/features.rb +5 -3
- data/lib/selenium/webdriver/safari/options.rb +5 -1
- data/lib/selenium/webdriver/safari/service.rb +10 -4
- data/lib/selenium/webdriver/safari.rb +1 -15
- data/lib/selenium/webdriver/support/color.rb +22 -22
- data/lib/selenium/webdriver/support/event_firing_bridge.rb +4 -4
- data/lib/selenium/webdriver/support/guards/guard.rb +14 -14
- data/lib/selenium/webdriver/support/guards/guard_condition.rb +1 -3
- data/lib/selenium/webdriver/support/guards.rb +4 -4
- 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 +7 -5
- data/selenium-webdriver.gemspec +22 -16
- metadata +137 -47
- 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
@@ -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
|
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
|
-
@
|
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
|
-
@
|
47
|
-
@socket_thread.exit
|
48
|
-
socket.close
|
38
|
+
@ws.close
|
49
39
|
end
|
50
40
|
|
51
41
|
def callbacks
|
52
|
-
@
|
42
|
+
@ws.callbacks
|
53
43
|
end
|
54
44
|
|
55
45
|
def send_cmd(method, **params)
|
56
|
-
|
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
|
-
|
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
|
-
|
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/
|
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 <
|
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/
|
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
|
-
|
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/
|
20
|
+
require 'selenium/webdriver/chromium/options'
|
21
21
|
|
22
22
|
module Selenium
|
23
23
|
module WebDriver
|
24
24
|
module Edge
|
25
|
-
class Options <
|
25
|
+
class Options < Chromium::Options
|
26
26
|
KEY = 'ms:edgeOptions'
|
27
27
|
BROWSER = 'MicrosoftEdge'
|
28
28
|
|
29
|
-
|
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
|
32
|
-
|
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/
|
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 <
|
29
|
+
class Profile < Chromium::Profile
|
30
30
|
end # Profile
|
31
31
|
end # Edge
|
32
32
|
end # WebDriver
|