| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Selenium::Client::Driver; |
|
2
|
|
|
|
|
|
|
$Selenium::Client::Driver::VERSION = '2.01'; |
|
3
|
1
|
|
|
1
|
|
213998
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
46
|
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
90
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
8
|
use parent qw{Selenium::Remote::Driver}; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
391576
|
no warnings qw{experimental}; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
58
|
|
|
9
|
1
|
|
|
1
|
|
7
|
use feature qw{signatures state}; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
203
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# ABSTRACT: Drop-In replacement for Selenium::Remote::Driver that supports selenium 4 |
|
12
|
|
|
|
|
|
|
|
|
13
|
1
|
|
|
1
|
|
10
|
use Scalar::Util; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
75
|
|
|
14
|
1
|
|
|
1
|
|
648
|
use Carp::Always; |
|
|
1
|
|
|
|
|
1038
|
|
|
|
1
|
|
|
|
|
5
|
|
|
15
|
1
|
|
|
1
|
|
75
|
use Data::Dumper; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
64
|
|
|
16
|
1
|
|
|
1
|
|
6
|
use JSON; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
39
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
1
|
|
|
1
|
|
893
|
use Selenium::Client; |
|
|
1
|
|
|
|
|
5
|
|
|
|
1
|
|
|
|
|
98
|
|
|
19
|
1
|
|
|
1
|
|
649
|
use Selenium::Client::Commands; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
73
|
|
|
20
|
1
|
|
|
1
|
|
669
|
use Selenium::Client::WebElement; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
51
|
|
|
21
|
1
|
|
|
1
|
|
655
|
use Selenium::Client::WDKeys; |
|
|
1
|
|
|
|
|
4
|
|
|
|
1
|
|
|
|
|
8573
|
|
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#Getters/Setters |
|
25
|
|
|
|
|
|
|
|
|
26
|
0
|
|
|
0
|
|
|
sub _param ( $self, $default, $param, $value = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
27
|
0
|
|
0
|
|
|
|
$self->{$param} //= $default; |
|
28
|
0
|
0
|
|
|
|
|
$self->{$param} = $value if defined $value; |
|
29
|
0
|
|
|
|
|
|
return $self->{$param}; |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
|
|
32
|
0
|
|
|
0
|
1
|
|
sub driver ( $self, $driver = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
33
|
0
|
|
|
|
|
|
return $self->_param( undef, 'driver', $driver ); |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
|
|
36
|
0
|
|
|
0
|
1
|
|
sub base_url ( $self, $url = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
37
|
0
|
|
|
|
|
|
return $self->_param( '', 'base_url', $url ); |
|
38
|
|
|
|
|
|
|
} |
|
39
|
|
|
|
|
|
|
|
|
40
|
0
|
|
|
0
|
1
|
|
sub remote_server_addr ( $self, $addr = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
41
|
0
|
|
|
|
|
|
return $self->_param( 'localhost', 'remote_server_addr', $addr ); |
|
42
|
|
|
|
|
|
|
} |
|
43
|
|
|
|
|
|
|
|
|
44
|
0
|
|
|
0
|
1
|
|
sub port ( $self, $port = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
45
|
0
|
|
|
|
|
|
return $self->_param( 4444, 'port', $port ); |
|
46
|
|
|
|
|
|
|
} |
|
47
|
|
|
|
|
|
|
|
|
48
|
0
|
|
|
0
|
1
|
|
sub browser_name ( $self, $name = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
49
|
0
|
|
|
|
|
|
return $self->_param( 'firefox', 'browser_name', $name ); |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
0
|
|
|
0
|
1
|
|
sub platform ( $self, $platform = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
53
|
0
|
|
|
|
|
|
return $self->_param( 'ANY', 'platform', $platform ); |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
0
|
|
|
0
|
1
|
|
sub version ( $self, $version = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
57
|
0
|
|
|
|
|
|
return $self->_param( '', 'version', $version ); |
|
58
|
|
|
|
|
|
|
} |
|
59
|
|
|
|
|
|
|
|
|
60
|
0
|
|
|
0
|
1
|
|
sub webelement_class ( $self, $class = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
61
|
0
|
|
|
|
|
|
return $self->_param( 'Selenium::Client::WebElement', 'webelement_class', $class ); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
0
|
|
|
0
|
1
|
|
sub default_finder ( $self, $finder = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
65
|
0
|
|
|
|
|
|
return $self->_param( 'xpath', 'default_finder', $finder ); |
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
|
|
68
|
0
|
|
|
0
|
1
|
|
sub session ( $self, $session = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
69
|
0
|
|
|
|
|
|
return $self->_param( undef, 'session', $session ); |
|
70
|
|
|
|
|
|
|
} |
|
71
|
|
|
|
|
|
|
|
|
72
|
0
|
|
|
0
|
1
|
|
sub session_id ( $self, $id = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
73
|
0
|
|
|
|
|
|
return $self->session->{sessionId}; |
|
74
|
|
|
|
|
|
|
} |
|
75
|
|
|
|
|
|
|
|
|
76
|
0
|
|
|
0
|
0
|
|
sub remote_conn ( $self, $conn = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
77
|
0
|
|
|
|
|
|
return $self->_param( undef, 'remote_conn', $conn ); |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
|
|
80
|
0
|
|
|
0
|
1
|
|
sub error_handler ( $self, $handler = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
$handler //= sub { |
|
82
|
0
|
|
|
0
|
|
|
my ( undef, $msg, $params ) = @_; |
|
83
|
0
|
|
|
|
|
|
die "Internal Death: $msg\n" . Dumper($params); |
|
84
|
0
|
|
0
|
|
|
|
}; |
|
85
|
0
|
0
|
|
|
|
|
die "error handler must be subroutine ref" unless ref $handler eq 'CODE'; |
|
86
|
0
|
|
|
0
|
|
|
return $self->_param( sub { }, 'error_handler', $handler ); |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
0
|
|
|
0
|
1
|
|
sub ua ( $self, $ua = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
90
|
0
|
|
|
|
|
|
return $self->_param( undef, 'ua', $ua ); |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
0
|
|
|
0
|
0
|
|
sub commands ($self) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
94
|
0
|
|
0
|
|
|
|
$self->{commands} //= Selenium::Client::Commands->new; |
|
95
|
0
|
|
|
|
|
|
return $self->{commands}; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
|
|
98
|
0
|
|
|
0
|
1
|
|
sub auto_close ( $self, $ac = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
99
|
0
|
0
|
|
|
|
|
return $self->_param( JSON::true, 'auto_close', $ac ? JSON::true : JSON::false ); |
|
100
|
|
|
|
|
|
|
} |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Only here for compatibility |
|
103
|
|
|
|
|
|
|
sub pid { |
|
104
|
0
|
|
|
0
|
0
|
|
return $$; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
#TODO these bools may need JSONizing |
|
108
|
0
|
|
|
0
|
1
|
|
sub javascript ( $self, $js = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
109
|
0
|
0
|
|
|
|
|
return $self->_param( JSON::true, 'javascript', $js ? JSON::true : JSON::false ); |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
|
|
112
|
0
|
|
|
0
|
1
|
|
sub accept_ssl_certs ( $self, $ssl = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
113
|
0
|
0
|
|
|
|
|
return $self->_param( JSON::true, 'accept_ssl_certs', $ssl ? JSON::true : JSON::false ); |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
0
|
|
|
0
|
1
|
|
sub proxy ( $self, $proxy = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
117
|
0
|
0
|
|
|
|
|
if ($proxy) { |
|
118
|
0
|
0
|
|
|
|
|
die "Proxy must be a hashref" unless ref $proxy eq 'HASH'; |
|
119
|
0
|
0
|
|
|
|
|
if ( $proxy->{proxyType} =~ /^pac$/i ) { |
|
120
|
0
|
0
|
|
|
|
|
if ( not defined $proxy->{proxyAutoconfigUrl} ) { |
|
|
|
0
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
die "proxyAutoconfigUrl not provided\n"; |
|
122
|
|
|
|
|
|
|
} |
|
123
|
|
|
|
|
|
|
elsif ( not( $proxy->{proxyAutoconfigUrl} =~ /^(http|file)/g ) ) { |
|
124
|
0
|
|
|
|
|
|
die "proxyAutoconfigUrl should be of format http:// or file://"; |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
|
|
127
|
0
|
0
|
|
|
|
|
if ( $proxy->{proxyAutoconfigUrl} =~ /^file/ ) { |
|
128
|
0
|
|
|
|
|
|
my $pac_url = $proxy->{proxyAutoconfigUrl}; |
|
129
|
0
|
|
|
|
|
|
my $file = $pac_url; |
|
130
|
0
|
|
|
|
|
|
$file =~ s{^file://}{}; |
|
131
|
|
|
|
|
|
|
|
|
132
|
0
|
0
|
|
|
|
|
if ( !-e $file ) { |
|
133
|
0
|
|
|
|
|
|
die "proxyAutoConfigUrl file does not exist: '$pac_url'"; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
} |
|
138
|
0
|
|
|
|
|
|
return $self->_param( undef, 'proxy', $proxy ); |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
#TODO what the hell is the difference between these two in practice? |
|
142
|
0
|
|
|
0
|
1
|
|
sub extra_capabilities ( $self, $caps = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
return $self->_param( undef, 'extra_capabilities', $caps ); |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
0
|
|
|
0
|
0
|
|
sub desired_capabilities ( $self, $caps = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
147
|
0
|
|
|
|
|
|
return $self->_param( undef, 'desired_capabilities', $caps ); |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
0
|
|
|
0
|
0
|
|
sub capabilities ( $self, $caps = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
|
return $self->_param( undef, 'desired_capabilities', $caps ); |
|
152
|
|
|
|
|
|
|
} |
|
153
|
|
|
|
|
|
|
|
|
154
|
0
|
|
|
0
|
1
|
|
sub firefox_profile ( $self, $profile = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
155
|
0
|
0
|
|
|
|
|
if ($profile) { |
|
156
|
0
|
0
|
0
|
|
|
|
unless ( Scalar::Util::blessed($profile) && $profile->isa('Selenium::Firefox::Profile') ) { |
|
157
|
0
|
|
|
|
|
|
die "firefox_profile must be a Selenium::Firefox::Profile"; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
} |
|
160
|
0
|
|
|
|
|
|
return $self->_param( undef, 'firefox_profile', $profile ); |
|
161
|
|
|
|
|
|
|
} |
|
162
|
|
|
|
|
|
|
|
|
163
|
0
|
|
|
0
|
1
|
|
sub debug ( $self, $debug = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
164
|
0
|
|
|
|
|
|
return $self->_param( 0, 'debug', $debug ); |
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
0
|
|
|
0
|
0
|
|
sub headless ( $self, $headless = 0 ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
168
|
0
|
|
|
|
|
|
return $self->_param( 0, 'headless', $headless ); |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
0
|
|
|
0
|
1
|
|
sub inner_window_size ( $self, $size = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
172
|
0
|
0
|
|
|
|
|
if ( ref $size eq 'ARRAY' ) { |
|
173
|
0
|
0
|
|
|
|
|
die "inner_window_size must have two elements: [ height, width ]" |
|
174
|
|
|
|
|
|
|
unless scalar @$size == 2; |
|
175
|
|
|
|
|
|
|
|
|
176
|
0
|
|
|
|
|
|
foreach my $dim (@$size) { |
|
177
|
0
|
0
|
|
|
|
|
die 'inner_window_size only accepts integers, not: ' . $dim |
|
178
|
|
|
|
|
|
|
unless Scalar::Util::looks_like_number($dim); |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
} |
|
182
|
0
|
|
|
|
|
|
return $self->_param( undef, 'inner_window_size', $size ); |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# TODO do we care about this at all? |
|
186
|
|
|
|
|
|
|
# At the time of writing, Geckodriver uses a different endpoint than |
|
187
|
|
|
|
|
|
|
# the java bindings for executing synchronous and asynchronous |
|
188
|
|
|
|
|
|
|
# scripts. As a matter of fact, Geckodriver does conform to the W3C |
|
189
|
|
|
|
|
|
|
# spec, but as are bound to support both while the java bindings |
|
190
|
|
|
|
|
|
|
# transition to full spec support, we need some way to handle the |
|
191
|
|
|
|
|
|
|
# difference. |
|
192
|
0
|
|
|
0
|
|
|
sub _execute_script_suffix ( $self, $suffix = undef ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
return $self->_param( undef, '_execute_script_suffix', $suffix ); |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
#TODO generate find_element_by crap statically |
|
197
|
|
|
|
|
|
|
#with 'Selenium::Remote::Finders'; |
|
198
|
|
|
|
|
|
|
|
|
199
|
0
|
|
|
0
|
1
|
|
sub new ( $class, %options ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
200
|
0
|
|
|
|
|
|
my $self = bless( { %options, is_wd3 => 1 }, $class ); |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# map the options |
|
203
|
0
|
|
|
|
|
|
my %optmap = ( |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# SRD / common options |
|
206
|
|
|
|
|
|
|
browser_name => 'browser', |
|
207
|
|
|
|
|
|
|
debug => 'debug', |
|
208
|
|
|
|
|
|
|
remote_server_addr => 'host', |
|
209
|
|
|
|
|
|
|
port => 'port', |
|
210
|
|
|
|
|
|
|
auto_close => 'auto_close', |
|
211
|
|
|
|
|
|
|
ua => 'ua', |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Stuff that does not work from SRD: |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# version - good luck getting random versions of browsers to work!!! LOL!!!! |
|
216
|
|
|
|
|
|
|
# platform - TODO currently unsupported |
|
217
|
|
|
|
|
|
|
# accept_ssl_certs - NOT IN THE SPEC, U CAN POUND SAND IF U AINT AN INTERMEDIATE SIGNER, HA HA HA HA. |
|
218
|
|
|
|
|
|
|
# firefox_provile - NOT IN THE SPEC |
|
219
|
|
|
|
|
|
|
# javascript - piss off, use mechanize if you want this off |
|
220
|
|
|
|
|
|
|
# default_finder - TODO currently unsupported |
|
221
|
|
|
|
|
|
|
# session_id - TODO currently unsupported |
|
222
|
|
|
|
|
|
|
# pageLoadStrategy - NOT IN THE SPEC |
|
223
|
|
|
|
|
|
|
# extra_capabilities - NOT IN THE SPEC |
|
224
|
|
|
|
|
|
|
# base_url - TODO currently unsupported |
|
225
|
|
|
|
|
|
|
# inner window size - XXX well, this function doesn't even work on selenium 4 so we *can't* support it. |
|
226
|
|
|
|
|
|
|
# error_handler - TODO will probably have to shim this, may not be possible idk |
|
227
|
|
|
|
|
|
|
# webelement_class- just no. |
|
228
|
|
|
|
|
|
|
# proxy - TODO currently unsupported, not sure this is even in the spec. |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# SCD exclusive options |
|
231
|
|
|
|
|
|
|
driver => 'driver', |
|
232
|
|
|
|
|
|
|
driver_version => 'driver_version', |
|
233
|
|
|
|
|
|
|
headless => 'headless', |
|
234
|
|
|
|
|
|
|
fatal => 'fatal', |
|
235
|
|
|
|
|
|
|
post_callbacks => 'post_callbacks', |
|
236
|
|
|
|
|
|
|
normalize => 'normalize', |
|
237
|
|
|
|
|
|
|
prefix => 'prefix', |
|
238
|
|
|
|
|
|
|
scheme => 'scheme', |
|
239
|
|
|
|
|
|
|
nofetch => 'nofetch', |
|
240
|
|
|
|
|
|
|
client_dir => 'client_dir', |
|
241
|
|
|
|
|
|
|
post_callbacks => 'post_callbacks', # TODO see error_handler note above |
|
242
|
|
|
|
|
|
|
); |
|
243
|
|
|
|
|
|
|
|
|
244
|
0
|
|
|
|
|
|
my $driver = $self->driver(); |
|
245
|
0
|
0
|
|
|
|
|
if ( !$driver ) { |
|
246
|
|
|
|
|
|
|
|
|
247
|
0
|
|
|
|
|
|
my %actual; |
|
248
|
0
|
|
|
|
|
|
foreach my $option ( keys(%options) ) { |
|
249
|
0
|
0
|
|
|
|
|
if ( !exists $optmap{$option} ) { |
|
250
|
0
|
|
|
|
|
|
warn "Passed unsupported option '$option', which has been dropped."; |
|
251
|
0
|
|
|
|
|
|
next; |
|
252
|
|
|
|
|
|
|
} |
|
253
|
0
|
|
|
|
|
|
$actual{ $optmap{$option} } = $options{$option}; |
|
254
|
|
|
|
|
|
|
} |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# Set the version explicitly, as these are conflicting names between the two modules. |
|
257
|
0
|
|
|
|
|
|
$actual{version} = 'stable'; |
|
258
|
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
|
$driver = Selenium::Client->new(%actual); |
|
260
|
0
|
|
|
|
|
|
$self->driver($driver); |
|
261
|
|
|
|
|
|
|
} |
|
262
|
0
|
|
|
|
|
|
my $status = $driver->Status(); |
|
263
|
0
|
0
|
|
|
|
|
die "Got bad status back from server!" unless $status->{ready}; |
|
264
|
|
|
|
|
|
|
|
|
265
|
0
|
0
|
|
|
|
|
if ( !$self->session ) { |
|
266
|
0
|
0
|
|
|
|
|
if ( $self->desired_capabilities ) { |
|
267
|
0
|
|
|
|
|
|
$self->new_desired_session( $self->desired_capabilities ); |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
else { |
|
270
|
|
|
|
|
|
|
# Connect to remote server & establish a new session |
|
271
|
0
|
|
|
|
|
|
$self->new_session( $self->extra_capabilities ); |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
0
|
0
|
|
|
|
|
if ( !( defined $self->session ) ) { |
|
276
|
0
|
|
|
|
|
|
die "Could not establish a session with the remote server\n"; |
|
277
|
|
|
|
|
|
|
} |
|
278
|
|
|
|
|
|
|
|
|
279
|
0
|
0
|
|
|
|
|
if ( $self->inner_window_size ) { |
|
280
|
0
|
|
|
|
|
|
my $size = $self->inner_window_size; |
|
281
|
0
|
|
|
|
|
|
$self->set_inner_window_size(@$size); |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
#Set debug if needed |
|
285
|
0
|
0
|
|
|
|
|
$self->debug_on() if $self->debug; |
|
286
|
|
|
|
|
|
|
|
|
287
|
0
|
|
|
|
|
|
return $self; |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
0
|
|
|
0
|
1
|
|
sub new_from_caps ( $self, %args ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
291
|
0
|
0
|
|
|
|
|
if ( not exists $args{desired_capabilities} ) { |
|
292
|
0
|
|
|
|
|
|
$args{desired_capabilities} = {}; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
0
|
|
|
|
|
|
return $self->new(%args); |
|
295
|
|
|
|
|
|
|
} |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
#TODO do we need this? |
|
298
|
|
|
|
0
|
|
|
sub DESTROY { |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# This is an internal method used the Driver & is not supposed to be used by |
|
302
|
|
|
|
|
|
|
# end user. This method is used by Driver to set up all the parameters |
|
303
|
|
|
|
|
|
|
# (url & JSON), send commands & receive processed response from the server. |
|
304
|
0
|
|
|
0
|
|
|
sub _execute_command ( $self, $res, $params = {} ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
305
|
0
|
0
|
|
|
|
|
print "Executing $res->{command}\n" if $self->{debug}; |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
#XXX Sometimes the params are in $res. Whee. |
|
308
|
0
|
|
|
|
|
|
foreach my $key ( keys(%$res) ) { |
|
309
|
0
|
0
|
|
|
|
|
$params->{$key} = $res->{$key} unless grep { $key eq $_ } qw{command sessionid elementid}; |
|
|
0
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
} |
|
311
|
|
|
|
|
|
|
|
|
312
|
0
|
0
|
|
|
|
|
my $macguffin = $self->commands->needs_driver( $res->{command} ) ? $self->driver : $self->session; |
|
313
|
0
|
0
|
|
|
|
|
$macguffin = $self->commands->needs_scd( $res->{command} ) ? $self : $macguffin; |
|
314
|
0
|
0
|
|
|
|
|
die "Could not acquire driver/session!" unless $macguffin; |
|
315
|
0
|
|
|
|
|
|
local $@; |
|
316
|
0
|
|
|
|
|
|
my $result; |
|
317
|
|
|
|
|
|
|
eval { |
|
318
|
0
|
|
|
|
|
|
my $resp = $self->commands->request( $macguffin, $res->{command}, $params ); |
|
319
|
0
|
|
|
|
|
|
$result = $self->commands->parse_response( $macguffin, $res->{command}, $resp ); |
|
320
|
0
|
|
|
|
|
|
1; |
|
321
|
0
|
0
|
|
|
|
|
} or do { |
|
322
|
0
|
0
|
|
|
|
|
return $self->error_handler->( $macguffin, $@, { %$params, %$res } ) if $self->error_handler; |
|
323
|
0
|
|
|
|
|
|
die $@; |
|
324
|
|
|
|
|
|
|
}; |
|
325
|
0
|
|
|
|
|
|
return $result; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
0
|
|
|
0
|
1
|
|
sub has_javascript { 1 } |
|
329
|
|
|
|
|
|
|
|
|
330
|
0
|
|
|
0
|
1
|
|
sub new_session ( $self, $extra_capabilities = {} ) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
331
|
0
|
|
0
|
|
|
|
$extra_capabilities //= {}; |
|
332
|
0
|
|
0
|
|
|
|
my $caps = { |
|
333
|
|
|
|
|
|
|
'platformName' => $self->platform, |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
#'javascriptEnabled' => $self->javascript, |
|
336
|
|
|
|
|
|
|
'version' => $self->version // '', |
|
337
|
|
|
|
|
|
|
'acceptInsecureCerts' => $self->accept_ssl_certs, |
|
338
|
|
|
|
|
|
|
%$extra_capabilities, |
|
339
|
|
|
|
|
|
|
}; |
|
340
|
|
|
|
|
|
|
|
|
341
|
0
|
|
0
|
|
|
|
$caps->{browserName} //= $self->browser_name; |
|
342
|
|
|
|
|
|
|
|
|
343
|
0
|
0
|
|
|
|
|
if ( defined $self->proxy ) { |
|
344
|
0
|
|
|
|
|
|
$caps->{proxy} = $self->proxy; |
|
345
|
|
|
|
|
|
|
} |
|
346
|
|
|
|
|
|
|
|
|
347
|
0
|
0
|
0
|
|
|
|
if ( $caps->{browserName} |
|
|
|
|
0
|
|
|
|
|
|
348
|
|
|
|
|
|
|
&& $caps->{browserName} =~ /firefox/i |
|
349
|
|
|
|
|
|
|
&& $self->firefox_profile ) { |
|
350
|
0
|
|
|
|
|
|
$caps->{firefox_profile} = $self->firefox_profile->_encode; |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
|
|
353
|
0
|
|
|
|
|
|
my %options = ( driver => 'auto', browser => $self->browser_name, debug => $self->debug, headless => $self->headless, capabilities => $caps ); |
|
354
|
|
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
|
return $self->_request_new_session( \%options ); |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
sub new_desired_session { |
|
359
|
0
|
|
|
0
|
1
|
|
my ( $self, $caps ) = @_; |
|
360
|
0
|
|
|
|
|
|
return $self->new_session($caps); |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub _request_new_session { |
|
364
|
0
|
|
|
0
|
|
|
my ( $self, $args ) = @_; |
|
365
|
|
|
|
|
|
|
|
|
366
|
0
|
|
|
|
|
|
my $ret = $self->_execute_command( { command => 'newSession' }, $args->{capabilities} ); |
|
367
|
0
|
|
|
|
|
|
my ( $capabilities, $session ) = ( $ret->{capabilities}, $ret->{session} ); |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
#die "Failed to get caps back from newSession" unless $capabilities->isa("Selenium::Capabilities"); |
|
370
|
|
|
|
|
|
|
#die "Failed to get session back from newSession" unless $session->isa("Selenium::Session"); |
|
371
|
0
|
|
|
|
|
|
$self->session($session); |
|
372
|
0
|
|
|
|
|
|
$self->capabilities($capabilities); |
|
373
|
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
|
return $self; |
|
375
|
|
|
|
|
|
|
} |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub is_webdriver_3 { |
|
378
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
379
|
0
|
|
|
|
|
|
return $self->{is_wd3}; |
|
380
|
|
|
|
|
|
|
} |
|
381
|
|
|
|
|
|
|
|
|
382
|
0
|
|
|
0
|
1
|
|
sub debug_on ($self) { |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
|
$self->{debug} = 1; |
|
384
|
0
|
|
|
|
|
|
$self->driver->{debug} = 1; |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub debug_off { |
|
388
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
|
389
|
0
|
|
|
|
|
|
$self->{debug} = 0; |
|
390
|
0
|
|
|
|
|
|
$self->driver->{debug} = 0; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub get_sessions { |
|
394
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
|
395
|
0
|
|
|
|
|
|
return $self->driver->{sessions}; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub get_capabilities { |
|
399
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
|
400
|
0
|
|
|
|
|
|
return $self->capabilities; |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
1; |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
__END__ |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=pod |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=encoding UTF-8 |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=head1 NAME |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
Selenium::Client::Driver - Drop-In replacement for Selenium::Remote::Driver that supports selenium 4 |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
=head1 VERSION |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
version 2.01 |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
Drop-in replacement for L<Selenium::Remote::Driver> which supports selenium 4. |
|
422
|
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
See the documentation for L<Selenium::Remote::Driver> for how to use this module unless otherwise noted below. |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Also, we support all valid L<Selenium::Client> constructor arguments, so you will likely want to consult those. |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
There are also a number of constructor options from L<Selenium::Remote::Driver> which are either entirely incompatible with selenium 4, are unimplemented or were bad ideas in the first place: |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=over 4 |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=item C<platform> - TODO. Will have to work in Selenium::Client first. |
|
432
|
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=item C<default_finder> - TODO. Will need a shim in Selenium::Client::Commands. |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=item C<extra_capabilities> - TODO. Use the options relevant to Selenium::Client instead |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=item C<base_url> - TODO. Will have to work in Selenium::Client first. |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=item C<session_id> - TODO. I don't even know if you can do this with the W3C spec. |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=item C<inner_window_size> - TODO. This function doesn't work right on any browser so we could only do a "best effort" try. |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item C<error_handler> - TODO. While post_callbacks are supported, there is no shim to make old error_handler subs work as post_callbacks. |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=item C<proxy> - TODO. not sure this is even possible with S4 caps. |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=item C<accept_ssl_certs> - Not in the W3C spec. Just make a self-signed CA and slap that sucker in /etc/ssl/certs, then use that to issue your self-signed certs. |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=item C<firefox_profile> - Not in the W3C spec. If you can't get it done with moz:firefoxOptions, it ain't getting done. |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=item C<pageLoadStrategy> - Not in the W3C spec. If you want to properly wait on page loads, you will need either a view-source based state-machine or executing scripts. Welcome to hell. |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=item C<webelement_class> - Subclass Selenium::Client::Driver instead |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
=item C<javascript> - Are you really using selenium to disable javascript? Seek Help. |
|
456
|
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
=item C<version> - good luck getting random versions of browsers to work!!! LOL!!!! Playwright patches them rather than rely on perpetually broken driver binaries. |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
=back |
|
460
|
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
Furthermore, selenium 4 totally fails at dealing with cookies and alerts. |
|
462
|
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=head1 ALTERNATIVES |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
My advice is to give up on this nonsense and... |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
use Playwright; |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Instead. Or, wait until someone implements a WC3 compliant selenium server using playwright and we can end the madness. |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
Please see those modules/websites for more information related to this module. |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
=over 4 |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=item * |
|
478
|
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
L<Selenium::Client|Selenium::Client> |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
=back |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head1 BUGS |
|
484
|
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website |
|
486
|
|
|
|
|
|
|
L<https://github.com/troglodyne-internet-widgets/selenium-client-perl/issues> |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
|
489
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
|
490
|
|
|
|
|
|
|
feature. |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=head1 AUTHORS |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
Current Maintainers: |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=over 4 |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=item * |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
George S. Baugh <george@troglodyne.net> |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
=back |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Copyright (c) 2024 Troglodyne LLC |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
510
|
|
|
|
|
|
|
of this software and associated documentation files (the "Software"), to deal |
|
511
|
|
|
|
|
|
|
in the Software without restriction, including without limitation the rights |
|
512
|
|
|
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
513
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the Software is |
|
514
|
|
|
|
|
|
|
furnished to do so, subject to the following conditions: |
|
515
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all |
|
516
|
|
|
|
|
|
|
copies or substantial portions of the Software. |
|
517
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
518
|
|
|
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
519
|
|
|
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
520
|
|
|
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
521
|
|
|
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
522
|
|
|
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
523
|
|
|
|
|
|
|
SOFTWARE. |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=cut |