selenium-webdriver 4.23.0 → 4.31.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 +81 -0
- data/Gemfile +3 -0
- data/LICENSE +1 -1
- data/NOTICE +1 -1
- data/README.md +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 +1 -1
- data/lib/selenium/webdriver/atoms/findElements.js +50 -118
- data/lib/selenium/webdriver/atoms/getAttribute.js +3 -3
- data/lib/selenium/webdriver/atoms/isDisplayed.js +2 -2
- data/lib/selenium/webdriver/bidi/browsing_context.rb +73 -46
- data/lib/selenium/webdriver/bidi/log_handler.rb +4 -2
- data/lib/selenium/webdriver/{common/driver_extensions/has_web_storage.rb → bidi/network/cookies.rb} +12 -12
- data/lib/selenium/webdriver/{common/html5/shared_web_storage.rb → bidi/network/credentials.rb} +15 -25
- data/lib/selenium/webdriver/bidi/network/headers.rb +38 -0
- data/lib/selenium/webdriver/bidi/{browsing_context_info.rb → network/intercepted_auth.rb} +11 -8
- data/lib/selenium/webdriver/bidi/{navigate_result.rb → network/intercepted_item.rb} +10 -6
- data/lib/selenium/webdriver/{common/html5/session_storage.rb → bidi/network/intercepted_request.rb} +34 -26
- data/lib/selenium/webdriver/bidi/network/intercepted_response.rb +81 -0
- data/lib/selenium/webdriver/bidi/network/url_pattern.rb +65 -0
- data/lib/selenium/webdriver/bidi/network.rb +139 -0
- data/lib/selenium/webdriver/bidi/struct.rb +2 -4
- data/lib/selenium/webdriver/bidi.rb +5 -0
- data/lib/selenium/webdriver/chrome/service.rb +1 -0
- data/lib/selenium/webdriver/chromium/driver.rb +0 -1
- data/lib/selenium/webdriver/common/child_process.rb +20 -14
- data/lib/selenium/webdriver/common/driver.rb +12 -10
- data/lib/selenium/webdriver/common/driver_extensions/has_devtools.rb +4 -3
- data/lib/selenium/webdriver/common/driver_extensions/has_log_events.rb +7 -0
- data/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +7 -0
- data/lib/selenium/webdriver/common/error.rb +3 -1
- data/lib/selenium/webdriver/common/fedcm/account.rb +4 -5
- data/lib/selenium/webdriver/common/logger.rb +3 -3
- data/lib/selenium/webdriver/common/manager.rb +1 -1
- data/lib/selenium/webdriver/common/network.rb +102 -0
- data/lib/selenium/webdriver/common/service.rb +11 -4
- data/lib/selenium/webdriver/common/service_manager.rb +1 -0
- data/lib/selenium/webdriver/common/takes_screenshot.rb +1 -0
- data/lib/selenium/webdriver/common/target_locator.rb +3 -2
- data/lib/selenium/webdriver/common/wait.rb +1 -1
- data/lib/selenium/webdriver/common.rb +1 -4
- data/lib/selenium/webdriver/devtools.rb +7 -5
- data/lib/selenium/webdriver/edge/service.rb +1 -1
- data/lib/selenium/webdriver/firefox/driver.rb +0 -19
- data/lib/selenium/webdriver/firefox/service.rb +1 -0
- data/lib/selenium/webdriver/ie/driver.rb +1 -1
- data/lib/selenium/webdriver/ie/service.rb +1 -0
- data/lib/selenium/webdriver/remote/bidi_bridge.rb +22 -0
- data/lib/selenium/webdriver/remote/bridge.rb +3 -53
- data/lib/selenium/webdriver/remote/http/common.rb +2 -0
- data/lib/selenium/webdriver/remote/http/curb.rb +11 -5
- data/lib/selenium/webdriver/remote/response.rb +18 -0
- data/lib/selenium/webdriver/remote.rb +1 -1
- data/lib/selenium/webdriver/safari/driver.rb +1 -2
- data/lib/selenium/webdriver/safari/service.rb +1 -1
- data/lib/selenium/webdriver/support/guards.rb +2 -2
- data/lib/selenium/webdriver/version.rb +1 -1
- data/selenium-webdriver.gemspec +4 -2
- metadata +16 -10
- data/lib/selenium/webdriver/common/html5/local_storage.rb +0 -59
@@ -0,0 +1,139 @@
|
|
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
|
+
require_relative 'network/url_pattern'
|
20
|
+
|
21
|
+
module Selenium
|
22
|
+
module WebDriver
|
23
|
+
class BiDi
|
24
|
+
class Network
|
25
|
+
EVENTS = {
|
26
|
+
before_request: 'network.beforeRequestSent',
|
27
|
+
response_started: 'network.responseStarted',
|
28
|
+
response_completed: 'network.responseCompleted',
|
29
|
+
auth_required: 'network.authRequired',
|
30
|
+
fetch_error: 'network.fetchError'
|
31
|
+
}.freeze
|
32
|
+
|
33
|
+
PHASES = {
|
34
|
+
before_request: 'beforeRequestSent',
|
35
|
+
response_started: 'responseStarted',
|
36
|
+
auth_required: 'authRequired'
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
def initialize(bidi)
|
40
|
+
@bidi = bidi
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_intercept(phases: [], contexts: nil, url_patterns: nil, pattern_type: :string)
|
44
|
+
url_patterns = url_patterns && pattern_type ? UrlPattern.format_pattern(url_patterns, pattern_type) : nil
|
45
|
+
@bidi.send_cmd('network.addIntercept',
|
46
|
+
phases: phases,
|
47
|
+
contexts: contexts,
|
48
|
+
urlPatterns: url_patterns)
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove_intercept(intercept)
|
52
|
+
@bidi.send_cmd('network.removeIntercept', intercept: intercept)
|
53
|
+
end
|
54
|
+
|
55
|
+
def continue_with_auth(request_id, username, password)
|
56
|
+
@bidi.send_cmd(
|
57
|
+
'network.continueWithAuth',
|
58
|
+
request: request_id,
|
59
|
+
action: 'provideCredentials',
|
60
|
+
credentials: {
|
61
|
+
type: 'password',
|
62
|
+
username: username,
|
63
|
+
password: password
|
64
|
+
}
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def continue_without_auth(request_id)
|
69
|
+
@bidi.send_cmd(
|
70
|
+
'network.continueWithAuth',
|
71
|
+
request: request_id,
|
72
|
+
action: 'default'
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def cancel_auth(request_id)
|
77
|
+
@bidi.send_cmd(
|
78
|
+
'network.continueWithAuth',
|
79
|
+
request: request_id,
|
80
|
+
action: 'cancel'
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def continue_request(**args)
|
85
|
+
@bidi.send_cmd(
|
86
|
+
'network.continueRequest',
|
87
|
+
request: args[:id],
|
88
|
+
body: args[:body],
|
89
|
+
cookies: args[:cookies],
|
90
|
+
headers: args[:headers],
|
91
|
+
method: args[:method],
|
92
|
+
url: args[:url]
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
def fail_request(request_id)
|
97
|
+
@bidi.send_cmd(
|
98
|
+
'network.failRequest',
|
99
|
+
request: request_id
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
def continue_response(**args)
|
104
|
+
@bidi.send_cmd(
|
105
|
+
'network.continueResponse',
|
106
|
+
request: args[:id],
|
107
|
+
cookies: args[:cookies],
|
108
|
+
credentials: args[:credentials],
|
109
|
+
headers: args[:headers],
|
110
|
+
reasonPhrase: args[:reason],
|
111
|
+
statusCode: args[:status]
|
112
|
+
)
|
113
|
+
end
|
114
|
+
|
115
|
+
def provide_response(**args)
|
116
|
+
@bidi.send_cmd(
|
117
|
+
'network.provideResponse',
|
118
|
+
request: args[:id],
|
119
|
+
body: args[:body],
|
120
|
+
cookies: args[:cookies],
|
121
|
+
headers: args[:headers],
|
122
|
+
reasonPhrase: args[:reason],
|
123
|
+
statusCode: args[:status]
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
def set_cache_behavior(behavior, *contexts)
|
128
|
+
@bidi.send_cmd('network.setCacheBehavior', cacheBehavior: behavior, contexts: contexts)
|
129
|
+
end
|
130
|
+
|
131
|
+
def on(event, &block)
|
132
|
+
event = EVENTS[event] if event.is_a?(Symbol)
|
133
|
+
@bidi.add_callback(event, &block)
|
134
|
+
@bidi.session.subscribe(event)
|
135
|
+
end
|
136
|
+
end # Network
|
137
|
+
end # BiDi
|
138
|
+
end # WebDriver
|
139
|
+
end # Selenium
|
@@ -23,7 +23,7 @@ module Selenium
|
|
23
23
|
class Struct < ::Struct
|
24
24
|
class << self
|
25
25
|
def new(*args, &block)
|
26
|
-
super
|
26
|
+
super do
|
27
27
|
define_method(:initialize) do |**kwargs|
|
28
28
|
converted_kwargs = kwargs.transform_keys { |key| self.class.camel_to_snake(key.to_s).to_sym }
|
29
29
|
super(*converted_kwargs.values_at(*self.class.members))
|
@@ -37,8 +37,6 @@ module Selenium
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# BiDi
|
40
|
+
end # BiDi
|
43
41
|
end # WebDriver
|
44
42
|
end # Selenium
|
@@ -25,6 +25,11 @@ module Selenium
|
|
25
25
|
autoload :LogHandler, 'selenium/webdriver/bidi/log_handler'
|
26
26
|
autoload :BrowsingContext, 'selenium/webdriver/bidi/browsing_context'
|
27
27
|
autoload :Struct, 'selenium/webdriver/bidi/struct'
|
28
|
+
autoload :Network, 'selenium/webdriver/bidi/network'
|
29
|
+
autoload :InterceptedRequest, 'selenium/webdriver/bidi/network/intercepted_request'
|
30
|
+
autoload :InterceptedResponse, 'selenium/webdriver/bidi/network/intercepted_response'
|
31
|
+
autoload :InterceptedAuth, 'selenium/webdriver/bidi/network/intercepted_auth'
|
32
|
+
autoload :InterceptedItem, 'selenium/webdriver/bidi/network/intercepted_item'
|
28
33
|
|
29
34
|
def initialize(url:)
|
30
35
|
@ws = WebSocketConnection.new(url: url)
|
@@ -32,7 +32,6 @@ module Selenium
|
|
32
32
|
DriverExtensions::HasFedCmDialog,
|
33
33
|
DriverExtensions::HasNetworkConditions,
|
34
34
|
DriverExtensions::HasNetworkInterception,
|
35
|
-
DriverExtensions::HasWebStorage,
|
36
35
|
DriverExtensions::HasLaunching,
|
37
36
|
DriverExtensions::HasPermissions,
|
38
37
|
DriverExtensions::DownloadsFiles,
|
@@ -64,16 +64,10 @@ module Selenium
|
|
64
64
|
return unless @pid
|
65
65
|
return if exited?
|
66
66
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
WebDriver.logger.debug(" -> stopped #{@pid}", id: :process)
|
72
|
-
rescue TimeoutError, Errno::EINVAL
|
73
|
-
WebDriver.logger.debug(" -> sending KILL to process: #{@pid}", id: :process)
|
74
|
-
kill(@pid)
|
75
|
-
wait
|
76
|
-
WebDriver.logger.debug(" -> killed #{@pid}", id: :process)
|
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)
|
77
71
|
end
|
78
72
|
|
79
73
|
def alive?
|
@@ -91,6 +85,9 @@ module Selenium
|
|
91
85
|
WebDriver.logger.debug(" -> exit code is #{exit_code.inspect}", id: :process)
|
92
86
|
|
93
87
|
!!exit_code
|
88
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
89
|
+
WebDriver.logger.debug(" -> process: #{@pid} already finished", id: :process)
|
90
|
+
true
|
94
91
|
end
|
95
92
|
|
96
93
|
def poll_for_exit(timeout)
|
@@ -110,20 +107,29 @@ module Selenium
|
|
110
107
|
|
111
108
|
private
|
112
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
|
+
|
113
123
|
def terminate(pid)
|
114
124
|
Process.kill(SIGTERM, pid)
|
115
125
|
end
|
116
126
|
|
117
127
|
def kill(pid)
|
118
128
|
Process.kill(SIGKILL, pid)
|
119
|
-
rescue Errno::ECHILD, Errno::ESRCH
|
120
|
-
# already dead
|
121
129
|
end
|
122
130
|
|
123
131
|
def waitpid2(pid, flags = 0)
|
124
132
|
Process.waitpid2(pid, flags)
|
125
|
-
rescue Errno::ECHILD
|
126
|
-
# already dead
|
127
133
|
end
|
128
134
|
end # ChildProcess
|
129
135
|
end # WebDriver
|
@@ -104,15 +104,8 @@ module Selenium
|
|
104
104
|
# @see Script
|
105
105
|
#
|
106
106
|
|
107
|
-
def script
|
108
|
-
|
109
|
-
WebDriver.logger.deprecate('`Driver#script` as an alias for `#execute_script`',
|
110
|
-
'`Driver#execute_script`',
|
111
|
-
id: :driver_script)
|
112
|
-
execute_script(*args)
|
113
|
-
else
|
114
|
-
@script ||= WebDriver::Script.new(bridge)
|
115
|
-
end
|
107
|
+
def script
|
108
|
+
@script ||= WebDriver::Script.new(bridge)
|
116
109
|
end
|
117
110
|
|
118
111
|
#
|
@@ -188,7 +181,7 @@ module Selenium
|
|
188
181
|
bridge.quit
|
189
182
|
ensure
|
190
183
|
@service_manager&.stop
|
191
|
-
@devtools&.close
|
184
|
+
@devtools&.each_value(&:close)
|
192
185
|
end
|
193
186
|
|
194
187
|
#
|
@@ -264,6 +257,15 @@ module Selenium
|
|
264
257
|
bridge.add_virtual_authenticator(options)
|
265
258
|
end
|
266
259
|
|
260
|
+
#
|
261
|
+
# @return [Network]
|
262
|
+
# @see Network
|
263
|
+
#
|
264
|
+
|
265
|
+
def network
|
266
|
+
@network ||= WebDriver::Network.new(bridge)
|
267
|
+
end
|
268
|
+
|
267
269
|
#-------------------------------- sugar --------------------------------
|
268
270
|
|
269
271
|
#
|
@@ -27,12 +27,13 @@ module Selenium
|
|
27
27
|
# @return [DevTools]
|
28
28
|
#
|
29
29
|
|
30
|
-
def devtools
|
31
|
-
@devtools ||=
|
30
|
+
def devtools(target_type: 'page')
|
31
|
+
@devtools ||= {}
|
32
|
+
@devtools[target_type] ||= begin
|
32
33
|
require 'selenium/devtools'
|
33
34
|
Selenium::DevTools.version ||= devtools_version
|
34
35
|
Selenium::DevTools.load_version
|
35
|
-
Selenium::WebDriver::DevTools.new(url: devtools_url)
|
36
|
+
Selenium::WebDriver::DevTools.new(url: devtools_url, target_type: target_type)
|
36
37
|
end
|
37
38
|
end
|
38
39
|
end # HasDevTools
|
@@ -57,6 +57,13 @@ module Selenium
|
|
57
57
|
#
|
58
58
|
|
59
59
|
def on_log_event(kind, &block)
|
60
|
+
if browser == :firefox
|
61
|
+
WebDriver.logger.deprecate(
|
62
|
+
'Driver#on_log_event on Firefox',
|
63
|
+
'the script.add_console_message_handler or the script.add_javascript_error_handler methods',
|
64
|
+
id: :on_log_event
|
65
|
+
)
|
66
|
+
end
|
60
67
|
raise Error::WebDriverError, "Don't know how to handle #{kind} events" unless KINDS.include?(kind)
|
61
68
|
|
62
69
|
enabled = log_listeners[kind].any?
|
@@ -60,6 +60,13 @@ module Selenium
|
|
60
60
|
#
|
61
61
|
|
62
62
|
def intercept(&block)
|
63
|
+
if browser == :firefox
|
64
|
+
WebDriver.logger.deprecate(
|
65
|
+
'Driver#intercept on Firefox',
|
66
|
+
'the new bidi.network.add_intercept method',
|
67
|
+
id: :intercept
|
68
|
+
)
|
69
|
+
end
|
63
70
|
@interceptor ||= DevTools::NetworkInterceptor.new(devtools)
|
64
71
|
@interceptor.intercept(&block)
|
65
72
|
end
|
@@ -50,9 +50,11 @@ module Selenium
|
|
50
50
|
super(URLS[class_name] ? "#{msg}; #{SUPPORT_MSG} #{URLS[class_name]}" : msg)
|
51
51
|
end
|
52
52
|
|
53
|
+
# steep:ignore:start
|
53
54
|
def class_name
|
54
|
-
self.class.name
|
55
|
+
self.class.name.split('::')&.last&.to_sym
|
55
56
|
end
|
57
|
+
# steep:ignore:end
|
56
58
|
end
|
57
59
|
|
58
60
|
#
|
@@ -21,8 +21,8 @@ module Selenium
|
|
21
21
|
module WebDriver
|
22
22
|
module FedCM
|
23
23
|
# Represents an account displayed in a FedCm account list.
|
24
|
-
# See: https://
|
25
|
-
# https://
|
24
|
+
# See: https://w3c-fedid.github.io/FedCM/#dictdef-identityprovideraccount
|
25
|
+
# https://w3c-fedid.github.io/FedCM/#webdriver-accountlist
|
26
26
|
class Account
|
27
27
|
LOGIN_STATE_SIGNIN = 'SignIn'
|
28
28
|
LOGIN_STATE_SIGNUP = 'SignUp'
|
@@ -30,9 +30,7 @@ module Selenium
|
|
30
30
|
attr_reader :account_id, :email, :name, :given_name, :picture_url,
|
31
31
|
:idp_config_url, :login_state, :terms_of_service_url, :privacy_policy_url
|
32
32
|
|
33
|
-
#
|
34
|
-
#
|
35
|
-
# @param [Hash]
|
33
|
+
# steep:ignore:start
|
36
34
|
def initialize(**args)
|
37
35
|
@account_id = args['accountId']
|
38
36
|
@email = args['email']
|
@@ -44,6 +42,7 @@ module Selenium
|
|
44
42
|
@terms_of_service_url = args['termsOfServiceUrl']
|
45
43
|
@privacy_policy_url = args['privacyPolicyUrl']
|
46
44
|
end
|
45
|
+
# steep:ignore:end
|
47
46
|
end # Account
|
48
47
|
end # FedCM
|
49
48
|
end # WebDriver
|
@@ -167,7 +167,7 @@ module Selenium
|
|
167
167
|
|
168
168
|
id << :deprecations if @allowed.include?(:deprecations)
|
169
169
|
|
170
|
-
message =
|
170
|
+
message = "[DEPRECATION] #{old} is deprecated"
|
171
171
|
message << if new
|
172
172
|
". Use #{new} instead."
|
173
173
|
else
|
@@ -193,8 +193,8 @@ module Selenium
|
|
193
193
|
|
194
194
|
def discard_or_log(level, message, id)
|
195
195
|
id = Array(id)
|
196
|
-
return if
|
197
|
-
return if @allowed.any? && (
|
196
|
+
return if @ignored.intersect?(id)
|
197
|
+
return if @allowed.any? && !@allowed.intersect?(id)
|
198
198
|
|
199
199
|
return if ::Logger::Severity.const_get(level.upcase) < @logger.level
|
200
200
|
|
@@ -65,7 +65,7 @@ module Selenium
|
|
65
65
|
# Get the cookie with the given name
|
66
66
|
#
|
67
67
|
# @param [String] name the name of the cookie
|
68
|
-
# @return [Hash
|
68
|
+
# @return [Hash] the cookie, or throws a NoSuchCookieError if it wasn't found.
|
69
69
|
#
|
70
70
|
|
71
71
|
def cookie_named(name)
|
@@ -0,0 +1,102 @@
|
|
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 'forwardable'
|
21
|
+
|
22
|
+
module Selenium
|
23
|
+
module WebDriver
|
24
|
+
class Network
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
attr_reader :callbacks, :network
|
28
|
+
alias bidi network
|
29
|
+
|
30
|
+
def_delegators :network, :continue_with_auth, :continue_with_request, :continue_with_response
|
31
|
+
|
32
|
+
def initialize(bridge)
|
33
|
+
@network = BiDi::Network.new(bridge.bidi)
|
34
|
+
@callbacks = {}
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_handler(id)
|
38
|
+
intercept = callbacks[id]
|
39
|
+
network.remove_intercept(intercept['intercept'])
|
40
|
+
callbacks.delete(id)
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear_handlers
|
44
|
+
callbacks.each_key { |id| remove_handler(id) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_authentication_handler(username = nil, password = nil, *filter, pattern_type: nil, &block)
|
48
|
+
selected_block =
|
49
|
+
if username && password
|
50
|
+
proc { |auth| auth.authenticate(username, password) }
|
51
|
+
else
|
52
|
+
block
|
53
|
+
end
|
54
|
+
|
55
|
+
add_handler(
|
56
|
+
:auth_required,
|
57
|
+
BiDi::Network::PHASES[:auth_required],
|
58
|
+
BiDi::InterceptedAuth,
|
59
|
+
filter,
|
60
|
+
pattern_type: pattern_type,
|
61
|
+
&selected_block
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def add_request_handler(*filter, pattern_type: nil, &block)
|
66
|
+
add_handler(
|
67
|
+
:before_request,
|
68
|
+
BiDi::Network::PHASES[:before_request],
|
69
|
+
BiDi::InterceptedRequest,
|
70
|
+
filter,
|
71
|
+
pattern_type: pattern_type,
|
72
|
+
&block
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_response_handler(*filter, pattern_type: nil, &block)
|
77
|
+
add_handler(
|
78
|
+
:response_started,
|
79
|
+
BiDi::Network::PHASES[:response_started],
|
80
|
+
BiDi::InterceptedResponse,
|
81
|
+
filter,
|
82
|
+
pattern_type: pattern_type,
|
83
|
+
&block
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def add_handler(event_type, phase, intercept_type, filter, pattern_type: nil)
|
90
|
+
intercept = network.add_intercept(phases: [phase], url_patterns: [filter].flatten, pattern_type: pattern_type)
|
91
|
+
callback_id = network.on(event_type) do |event|
|
92
|
+
request = event['request']
|
93
|
+
intercepted_item = intercept_type.new(network, request)
|
94
|
+
yield(intercepted_item)
|
95
|
+
end
|
96
|
+
|
97
|
+
callbacks[callback_id] = intercept
|
98
|
+
callback_id
|
99
|
+
end
|
100
|
+
end # Network
|
101
|
+
end # WebDriver
|
102
|
+
end # Selenium
|
@@ -69,6 +69,7 @@ module Selenium
|
|
69
69
|
def initialize(path: nil, port: nil, log: nil, args: nil)
|
70
70
|
port ||= self.class::DEFAULT_PORT
|
71
71
|
args ||= []
|
72
|
+
path ||= env_path
|
72
73
|
|
73
74
|
@executable_path = path
|
74
75
|
@host = Platform.localhost
|
@@ -87,16 +88,22 @@ module Selenium
|
|
87
88
|
end
|
88
89
|
|
89
90
|
def launch
|
90
|
-
@executable_path ||=
|
91
|
-
default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
|
92
|
-
DriverFinder.new(default_options, self).driver_path
|
93
|
-
end
|
91
|
+
@executable_path ||= env_path || find_driver_path
|
94
92
|
ServiceManager.new(self).tap(&:start)
|
95
93
|
end
|
96
94
|
|
97
95
|
def shutdown_supported
|
98
96
|
self.class::SHUTDOWN_SUPPORTED
|
99
97
|
end
|
98
|
+
|
99
|
+
def find_driver_path
|
100
|
+
default_options = WebDriver.const_get("#{self.class.name&.split('::')&.[](2)}::Options").new
|
101
|
+
DriverFinder.new(default_options, self).driver_path
|
102
|
+
end
|
103
|
+
|
104
|
+
def env_path
|
105
|
+
ENV.fetch(self.class::DRIVER_PATH_ENV_KEY, nil)
|
106
|
+
end
|
100
107
|
end # Service
|
101
108
|
end # WebDriver
|
102
109
|
end # Selenium
|
@@ -113,6 +113,7 @@ module Selenium
|
|
113
113
|
def stop_server
|
114
114
|
connect_to_server do |http|
|
115
115
|
headers = WebDriver::Remote::Http::Common::DEFAULT_HEADERS.dup
|
116
|
+
WebDriver.logger.debug('Sending shutdown request to server', id: :driver_service)
|
116
117
|
http.get('/shutdown', headers)
|
117
118
|
end
|
118
119
|
end
|
@@ -50,6 +50,7 @@ module Selenium
|
|
50
50
|
# @param type either :tab or :window
|
51
51
|
#
|
52
52
|
|
53
|
+
# steep:ignore:start
|
53
54
|
def new_window(type = :window)
|
54
55
|
raise ArgumentError, "Valid types are :tab and :window, received: #{type.inspect}" unless %i[window
|
55
56
|
tab].include?(type)
|
@@ -70,6 +71,7 @@ module Selenium
|
|
70
71
|
window(handle)
|
71
72
|
end
|
72
73
|
end
|
74
|
+
# steep:ignore:end
|
73
75
|
|
74
76
|
#
|
75
77
|
# switch to the given window handle
|
@@ -96,12 +98,11 @@ module Selenium
|
|
96
98
|
@bridge.switch_to_window id
|
97
99
|
|
98
100
|
begin
|
99
|
-
|
101
|
+
yield
|
100
102
|
ensure
|
101
103
|
current_handles = @bridge.window_handles
|
102
104
|
original = current_handles.first unless current_handles.include? original
|
103
105
|
@bridge.switch_to_window original
|
104
|
-
returned
|
105
106
|
end
|
106
107
|
else
|
107
108
|
@bridge.switch_to_window id
|
@@ -63,10 +63,6 @@ require 'selenium/webdriver/common/action_builder'
|
|
63
63
|
require 'selenium/webdriver/common/virtual_authenticator/credential'
|
64
64
|
require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator_options'
|
65
65
|
require 'selenium/webdriver/common/virtual_authenticator/virtual_authenticator'
|
66
|
-
require 'selenium/webdriver/common/html5/shared_web_storage'
|
67
|
-
require 'selenium/webdriver/common/html5/local_storage'
|
68
|
-
require 'selenium/webdriver/common/html5/session_storage'
|
69
|
-
require 'selenium/webdriver/common/driver_extensions/has_web_storage'
|
70
66
|
require 'selenium/webdriver/common/driver_extensions/downloads_files'
|
71
67
|
require 'selenium/webdriver/common/driver_extensions/has_session_id'
|
72
68
|
require 'selenium/webdriver/common/driver_extensions/has_network_conditions'
|
@@ -102,3 +98,4 @@ require 'selenium/webdriver/common/child_process'
|
|
102
98
|
require 'selenium/webdriver/common/script'
|
103
99
|
require 'selenium/webdriver/common/fedcm/account'
|
104
100
|
require 'selenium/webdriver/common/fedcm/dialog'
|
101
|
+
require 'selenium/webdriver/common/network'
|
@@ -28,10 +28,10 @@ module Selenium
|
|
28
28
|
autoload :Request, 'selenium/webdriver/devtools/request'
|
29
29
|
autoload :Response, 'selenium/webdriver/devtools/response'
|
30
30
|
|
31
|
-
def initialize(url:)
|
31
|
+
def initialize(url:, target_type:)
|
32
32
|
@ws = WebSocketConnection.new(url: url)
|
33
33
|
@session_id = nil
|
34
|
-
start_session
|
34
|
+
start_session(target_type: target_type)
|
35
35
|
end
|
36
36
|
|
37
37
|
def close
|
@@ -81,10 +81,12 @@ module Selenium
|
|
81
81
|
|
82
82
|
private
|
83
83
|
|
84
|
-
def start_session
|
84
|
+
def start_session(target_type:)
|
85
85
|
targets = target.get_targets.dig('result', 'targetInfos')
|
86
|
-
|
87
|
-
|
86
|
+
found_target = targets.find { |target| target['type'] == target_type }
|
87
|
+
raise Error::WebDriverError, "Target type '#{target_type}' not found" unless found_target
|
88
|
+
|
89
|
+
session = target.attach_to_target(target_id: found_target['targetId'], flatten: true)
|
88
90
|
@session_id = session.dig('result', 'sessionId')
|
89
91
|
end
|
90
92
|
|