line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package WWW::HtmlUnit::Sweet; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
WWW::HtmlUnit::Sweet - Wrapper around WWW::HtmlUnit to add some sweetness |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use WWW::HtmlUnit::Sweet; |
10
|
|
|
|
|
|
|
my $agent = WWW::HtmlUnit::Sweet->new; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
$agent->getPage('http://google.com/'); |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Type into the currently focused element |
15
|
|
|
|
|
|
|
$agent->type("Hello\n"); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# Print out the XML of the page |
18
|
|
|
|
|
|
|
print $agent->asXml; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 DESCRIPTION |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Using L as a foundation, this adds some convenience things. The main addition is that the $agent you get from ->new does some AUTOLOAD things to allow you to treat the $agent as either a browser, a window, or a page. That way you can treat it a lot more like a L object. |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
This module might change drastically, buyer beware! |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 IMPORT PARAMETERS |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
When you 'use' this module, you can pass some parameters. Any parameter that L doesn't use will be passed on to L, or ultimately L. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=over 4 |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=item * show_errors - Flag to stop the supression of stderr |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=item * error_filename - Filename to append stderr to |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=item * error_fh - Filehandle to append stderr to |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=item * errors_to_tmpfile - Send stderr to a temporary file (L) |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=back 4 |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
Useful examples: |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# Show errors on STDERR |
45
|
|
|
|
|
|
|
use WWW::HtmlUnit::Sweet show_errors => 1; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Append errors to /tmp/errors.txt |
48
|
|
|
|
|
|
|
use WWW::HtmlUnit::Sweet error_filename => '/tmp/errors.txt'; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
Note that if you don't pass anything, errors will be sent to /dev/null (or a temporary file if you don't have /dev/null). |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=cut |
53
|
|
|
|
|
|
|
|
54
|
1
|
|
|
1
|
|
658
|
use strict; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
33
|
|
55
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
284
|
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# Hold our error filehandle |
58
|
|
|
|
|
|
|
our $error_fh; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub import { |
61
|
1
|
|
|
1
|
|
9
|
my $class = shift; |
62
|
1
|
|
|
|
|
2
|
my %parameters = @_; |
63
|
|
|
|
|
|
|
|
64
|
1
|
50
|
|
|
|
5
|
if($parameters{show_errors}) { |
65
|
0
|
|
|
|
|
0
|
delete $parameters{show_errors}; |
66
|
0
|
|
|
|
|
0
|
require WWW::HtmlUnit; |
67
|
0
|
|
|
|
|
0
|
WWW::HtmlUnit->import( %parameters ); |
68
|
|
|
|
|
|
|
} else { |
69
|
1
|
50
|
33
|
|
|
34
|
if($parameters{error_filename}) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
70
|
0
|
0
|
|
|
|
0
|
open $error_fh, '>>', $parameters{error_filename} |
71
|
|
|
|
|
|
|
or die "Error opening $parameters{error_filename}, $!\n"; |
72
|
0
|
|
|
|
|
0
|
delete $parameters{error_filename}; |
73
|
|
|
|
|
|
|
} elsif($parameters{error_fh}) { |
74
|
0
|
|
|
|
|
0
|
$error_fh = $parameters{error_fh}; |
75
|
0
|
|
|
|
|
0
|
delete $parameters{error_fh}; |
76
|
|
|
|
|
|
|
} elsif($parameters{errors_to_tmpfile} || ! -c '/dev/null') { |
77
|
0
|
|
|
|
|
0
|
require IO::File; |
78
|
0
|
|
|
|
|
0
|
$error_fh = IO::File->new_tmpfile; |
79
|
0
|
|
|
|
|
0
|
delete $parameters{errors_to_tmpfile}; |
80
|
|
|
|
|
|
|
} else { |
81
|
1
|
50
|
|
|
|
43
|
open $error_fh, '>', '/dev/null' |
82
|
|
|
|
|
|
|
or die "Error opening $parameters{error_filename}, $!\n"; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# So we save STDERR, then redirect it |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
1
|
|
6
|
no warnings; # stop complaint about SAVEERR never being used again |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
60
|
|
88
|
1
|
|
|
|
|
21
|
open SAVEERR, '>&', STDERR; |
89
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
841
|
|
90
|
1
|
|
|
|
|
5
|
close STDERR; |
91
|
1
|
|
|
|
|
14
|
open STDERR, '>&', $error_fh; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Now Inline::Java will use our special filehandle instead of STDERR |
94
|
1
|
|
|
|
|
653
|
require WWW::HtmlUnit; |
95
|
1
|
|
|
|
|
6
|
WWW::HtmlUnit->import( %parameters ); |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# Now put STDERR back! |
98
|
0
|
|
|
|
|
|
close STDERR; |
99
|
0
|
|
|
|
|
|
open STDERR, '>&', SAVEERR; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
} |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=head1 METHODS |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 $agent = WWW::HtmlUnit::Sweet->new |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Create a new sweet agent. Use this kinda like looking at a browser on the screen. The methods you call will be invoked (if possible) on the current browser, window, page, or focused element. |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
The 'new' method can also take a browser version and a starting url, like this: |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $agent = WWW::HtmlUnit::Sweet->new( |
113
|
|
|
|
|
|
|
version => 'FIREFOX_3', |
114
|
|
|
|
|
|
|
url => 'http://google.com/' |
115
|
|
|
|
|
|
|
); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=cut |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
sub new { |
120
|
0
|
|
|
0
|
1
|
|
my $class = shift; |
121
|
0
|
|
|
|
|
|
my $self = { @_ }; |
122
|
0
|
|
|
|
|
|
bless $self, $class; |
123
|
0
|
|
|
|
|
|
$self->{browser} = WWW::HtmlUnit->new( $self->{version} ); |
124
|
0
|
0
|
|
|
|
|
$self->getPage( $self->{url} ) if $self->{url}; |
125
|
0
|
|
|
|
|
|
return $self; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head2 $agent->wait_for(sub { ... }, $timeout) |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
Execute the provided sub once a second until it returns true, or until the the timeout has been reached. If a timeout isn't passed, it will default to 10 seconds (which you can change by setting C<< $WWW::HtmlUnit::Sweet::default_timeout >>). This is handy for waiting for the page to finish executing some javascript, or loading. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
Example: |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# Wait for an element with id 'foo' to exist |
135
|
|
|
|
|
|
|
$agent->wait_for(sub { |
136
|
|
|
|
|
|
|
$agent->getElementById('foo') |
137
|
|
|
|
|
|
|
}); |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=cut |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
our $default_timeout = 10; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub wait_for { |
144
|
0
|
|
|
0
|
1
|
|
my ($agent, $subref, $timeout) = @_; |
145
|
0
|
|
0
|
|
|
|
$timeout ||= $default_timeout; |
146
|
0
|
|
|
|
|
|
while($timeout) { |
147
|
0
|
0
|
0
|
|
|
|
return if eval { $subref->() } && ! $@; |
|
0
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
|
sleep 1; |
149
|
0
|
|
|
|
|
|
$timeout--; |
150
|
|
|
|
|
|
|
} |
151
|
0
|
|
|
|
|
|
die "Timeout!\n"; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 AUTOLOAD, aka $agent->whatever(..) |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
This is where the sweetness starts kicking in. First it will try to call ->whatever on the browser, and if there is no method named 'whatever' there it will be called on the current window, and if there is no method named 'whatever' there it will be called on the current page in that window, and if there is no method 'whatever' there it will be called on the currently focused element. |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Examples: |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# This works at the browser level |
161
|
|
|
|
|
|
|
$agent->getPage('http://google.com/'); |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# Get the 'name' for the current window |
164
|
|
|
|
|
|
|
my $window_name = $agent->getName; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Working from the current page, get an element by ID |
167
|
|
|
|
|
|
|
my $sidebar_element = $agent->getElementById('sidebar'); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Click on the currently focused element |
170
|
|
|
|
|
|
|
$agent->click; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
This scheme works quite well because HtmlUnit itself just so happens to not overlap their method names between different classes. Lucky us! |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
Note: We also call ->toArray on results if needed. Probably at some point we'll get ALL array-like results from HtmlUnit to auto-execute ->toArray. |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=cut |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# This will make us act a bit more like Mechanize |
179
|
|
|
|
|
|
|
sub AUTOLOAD { |
180
|
0
|
|
|
0
|
|
|
my $self = shift; |
181
|
0
|
|
|
|
|
|
our $AUTOLOAD; |
182
|
0
|
|
|
|
|
|
my $method = $AUTOLOAD; $method =~ s/.*:://; |
|
0
|
|
|
|
|
|
|
183
|
0
|
0
|
|
|
|
|
return if $method eq 'DESTROY'; |
184
|
0
|
|
|
|
|
|
my $retval = eval { |
185
|
|
|
|
|
|
|
|
186
|
0
|
|
|
|
|
|
my $browser = $self->{browser}; |
187
|
0
|
|
0
|
|
|
|
my $window = $browser && $browser->getCurrentWindow; |
188
|
0
|
|
0
|
|
|
|
my $page = $window && $window->getEnclosedPage; |
189
|
0
|
|
0
|
|
|
|
my $element = $page && $page->getFocusedElement; |
190
|
|
|
|
|
|
|
|
191
|
0
|
|
|
|
|
|
my $result; |
192
|
0
|
0
|
0
|
|
|
|
if($browser && $browser->can($method)) { |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
193
|
0
|
|
|
|
|
|
$result = $browser->$method(@_); |
194
|
|
|
|
|
|
|
} elsif($window && $window->can($method)) { |
195
|
0
|
|
|
|
|
|
$result = $window->$method(@_); |
196
|
|
|
|
|
|
|
} elsif($page && $page->can($method)) { |
197
|
0
|
|
|
|
|
|
$result = $page->$method(@_); |
198
|
|
|
|
|
|
|
} elsif($element && $element->can($method)) { |
199
|
0
|
|
|
|
|
|
$result = $element->$method(@_); |
200
|
|
|
|
|
|
|
} else { |
201
|
0
|
|
|
|
|
|
die "Method $method not found!"; |
202
|
|
|
|
|
|
|
} |
203
|
0
|
0
|
0
|
|
|
|
if(ref $result && $result->can('toArray')) { |
204
|
0
|
|
|
|
|
|
return $result->toArray; |
205
|
|
|
|
|
|
|
} else { |
206
|
0
|
|
|
|
|
|
return $result; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
}; |
209
|
0
|
0
|
0
|
|
|
|
if($@ && ref($@) =~ /Exception/) { |
|
|
0
|
|
|
|
|
|
210
|
0
|
|
|
|
|
|
print STDERR "HtmlUnit ERROR: " . $@->getMessage . "\n"; |
211
|
0
|
|
|
|
|
|
die $@; # Pass it up the chain |
212
|
|
|
|
|
|
|
} elsif($@) { |
213
|
0
|
|
|
|
|
|
warn $@; |
214
|
|
|
|
|
|
|
} |
215
|
0
|
|
|
|
|
|
return $retval; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
package WWW::HtmlUnit::com::gargoylesoftware::htmlunit::html::HtmlSelect; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Fix the get_option to take nicer params |
222
|
|
|
|
|
|
|
# TODO: document this! |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub get_option { |
225
|
0
|
|
|
0
|
|
|
my ($self, %params) = @_; |
226
|
0
|
0
|
|
|
|
|
if($params{text}) { |
|
|
0
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
return eval {$self->getOptionByText($params{text})}; |
|
0
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
} elsif($params{value}) { |
229
|
0
|
|
|
|
|
|
return eval {$self->getOptionByValue($params{value})}; |
|
0
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
} |
231
|
0
|
|
|
|
|
|
die "Must pass either text or value"; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
package WWW::HtmlUnit::java::lang::Object; |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub sweeten { |
237
|
0
|
|
|
0
|
|
|
return WWW::HtmlUnit::Sweet->new(); |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head1 TODO |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Add more documentation and examples and sweetness :) |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
=head1 SEE ALSO |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
L |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=head1 AUTHOR |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
Brock Wilcox - http://thelackthereof.org/ |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head1 COPYRIGHT |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
Copyright (c) 2009-2011 Brock Wilcox . All rights |
255
|
|
|
|
|
|
|
reserved. This program is free software; you can redistribute it and/or |
256
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=cut |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
1; |
261
|
|
|
|
|
|
|
|