File Coverage

blib/lib/Firefox/Application.pm
Criterion Covered Total %
statement 21 74 28.3
branch 4 22 18.1
condition 1 17 5.8
subroutine 5 19 26.3
pod 13 14 92.8
total 44 146 30.1


line stmt bran cond sub pod time code
1             package Firefox::Application;
2 82     82   269780 use strict;
  82         230  
  82         2350  
3              
4 82     82   50519 use MozRepl::RemoteObject ();
  82         2409864  
  82         2004  
5 82     82   2837 use URI ();
  82         18658  
  82         1665  
6 82     82   485 use Carp qw(carp croak);
  82         223  
  82         81283  
7              
8             our $VERSION = '0.80';
9              
10             =head1 NAME
11              
12             Firefox::Application - inspect and automate the Firefox UI
13              
14             =head1 SYNOPSIS
15              
16             use Firefox::Application;
17             my $ff = Firefox::Application->new();
18              
19             This module will let you automate Firefox through the
20             Mozrepl plugin. You need to have installed
21             that plugin in your Firefox.
22              
23             For more examples see L.
24              
25             =head1 METHODS
26              
27             =head2 C<< Firefox::Application->new( %args ) >>
28              
29             use Firefox::Application;
30             my $ff = Firefox::Application->new();
31              
32             Creates a new instance and connects it to Firefox.
33              
34             Note that Firefox must have the C
35             extension installed and enabled.
36              
37             The following options are recognized:
38              
39             =over 4
40              
41             =item *
42              
43             C - name of the program to launch if we can't connect to it on
44             the first try.
45              
46             =item *
47              
48             C - array reference to log levels, passed through to L
49              
50             =item *
51              
52             C - L buffer size, if the default of 1MB is not enough
53              
54             =item *
55              
56             C - a premade L instance or a connection string
57             suitable for initializing one.
58              
59             =item *
60              
61             C - whether to enable L command queueing
62              
63             =item *
64              
65             C - class for the API wrapper
66              
67             You almost never want to use this parameter, as Firefox::Application
68             asks Firefox about its version.
69              
70             =back
71              
72             =cut
73              
74             sub new {
75 78     78 1 40966 my ($class, %args) = @_;
76 78   50     682 my $loglevel = delete $args{ log } || [qw[ error ]];
77 78 50       342 my $use_queue = exists $args{ use_queue } ? delete $args{ use_queue } : 1;
78 78         197 my $api = delete $args{ api };
79 78 50       334 if (! ref $args{ repl }) {
80 78         352 my @passthrough = qw(repl js_JSON launch);
81 78 100       202 my %options = map { exists $args{ $_ } ? ($_ => delete $args{ $_ }) : () }
  234         709  
82             @passthrough;
83             $args{ repl } = MozRepl::RemoteObject->install_bridge(
84             log => $loglevel,
85             use_queue => $use_queue,
86             bufsize => delete $args{ bufsize },
87 78         798 %options,
88             );
89             };
90            
91             # Now install the proper API
92 0 0         if (! $api) {
93 0           my $info = $args{ repl }->appinfo;
94 0           my $v = $info->{version};
95 0 0         $v =~ s!^(\d+.\d+).*!$1!
96             or $v = '3.0'; # Wild guess
97            
98 0 0         if ($v >= 4) {
    0          
99 0           $api = 'Firefox::Application::API40';
100             } elsif ($v >= 3.6) {
101 0           $api = 'Firefox::Application::API36';
102             } else {
103 0           $api = 'Firefox::Application::API35';
104             };
105             };
106 0           MozRepl::RemoteObject::require_module( $api );
107            
108 0           bless \%args, $api;
109             };
110              
111             sub DESTROY {
112 0     0     my ($self) = @_;
113 0           local $@;
114             #warn "App cleaning up";
115 0 0         if (my $repl = delete $self->{ repl }) {
116 0           %$self = (); # wipe out all references we keep
117             # but keep $repl alive until we can dispose of it
118             # as the last thing, now:
119 0           $repl = undef;
120             };
121             #warn "App cleaned up";
122             }
123              
124             =head2 C<< $ff->repl >>
125              
126             my ($value,$type) = $ff->repl->expr('2+2');
127              
128             Gets the L instance that is used.
129              
130             =cut
131              
132 0     0 1   sub repl { $_[0]->{repl} };
133              
134             =head1 APPLICATION INFORMATION
135              
136             =cut
137              
138             =head2 C<< $ff->appinfo >>
139              
140             my $info = $ff->appinfo;
141             print 'ID : ', $info->{ID};
142             print 'name : ', $info->{name};
143             print 'version : ', $info->{version};
144              
145             Returns information about Firefox.
146              
147             =cut
148              
149             sub appinfo {
150 0     0 1   $_[0]->repl->appinfo
151             };
152              
153             =head2 C<< $ff->addons( %args ) >>
154              
155             for my $addon ($ff->addons) {
156             print sprintf "Name: %s\n", $addon->{name};
157             print sprintf "Version: %s\n", $addon->{version};
158             print sprintf "GUID: %s\n", $addon->{id};
159             };
160              
161             Returns the list of installed addons as Cs (FF 3.5+)
162             or Cs (FF4+).
163             See L
164             or L,
165             depending on your Firefox version.
166              
167             =head2 C<< $ff->locales( %args ) >>
168              
169             for my $locale ($ff->locales) {
170             print sprintf "Name: %s\n", $locale->{name};
171             print sprintf "Version: %s\n", $locale->{version};
172             print sprintf "GUID: %s\n", $locale->{id};
173             };
174              
175             Returns the list of installed locales as Cs.
176              
177             =head2 C<< $ff->themes( %args ) >>
178              
179             for my $theme ($ff->themes) {
180             print sprintf "Name: %s\n", $theme->{name};
181             print sprintf "Version: %s\n", $theme->{version};
182             print sprintf "GUID: %s\n", $theme->{id};
183             };
184              
185             Returns the list of installed locales as Cs.
186              
187             =head2 C<< $ff->updateitems( %args ) >>
188              
189             for my $item ($ff->updateitems) {
190             print sprintf "Name: %s\n", $item->{name};
191             print sprintf "Version: %s\n", $item->{version};
192             print sprintf "GUID: %s\n", $item->{id};
193             };
194              
195             Returns the list of updateable items. The type of item
196             can be restricted by the C option.
197              
198             =over 4
199              
200             =item * C - type of items to fetch
201              
202             C - fetch any item
203              
204             C - fetch add-ons
205              
206             C - fetch locales
207              
208             C - fetch themes
209              
210             =back
211              
212             =cut
213              
214             sub profileService {
215 0     0 0   my ($self) = @_;
216            
217 0           my $profileService = $self->repl->declare(<<'JS')->();
218             function () {
219             return Components.classes["@mozilla.org/toolkit/profile-service;1"]
220             .createInstance(Components.interfaces.nsIToolkitProfileService);
221             }
222             JS
223             }
224              
225             =head2 C<< $ff->current_profile >>
226              
227             print $ff->current_profile->{name}, "\n";
228              
229             Returns currently selected profile as C.
230              
231             See L.
232              
233             =cut
234              
235             sub current_profile {
236 0     0 1   my ($self) = @_;
237             $self->profileService->{selectedProfile}
238 0           }
239              
240             =head2 C<< $ff->find_profile( $name ) >>
241              
242             print $ff->find_profile("")->{localDir}, "\n";
243              
244             Returns the profile given its name. Dies
245             if the profile cannot be found.
246              
247             =cut
248              
249             sub find_profile {
250 0     0 1   my ($self,$name) = @_;
251 0           $self->profileService->getProfileByName($name);
252             }
253              
254             =head2 C<< $ff->profiles >>
255              
256             for ($ff->profiles) {
257             print "Profile: ", $_->{name}, "\n";
258             }
259              
260             Lists the installed profiles as Cs.
261              
262             See L.
263              
264             =cut
265              
266             sub profiles {
267 0     0 1   my ($self) = @_;
268            
269 0           my $getProfiles = $self->repl->declare(<<'JS', 'list');
270             function () {
271             var toolkitProfileService = Components.classes["@mozilla.org/toolkit/profile-service;1"]
272             .createInstance(Components.interfaces.nsIToolkitProfileService);
273             var res = new Array;
274             var i = toolkitProfileService.profiles;
275             while( i.hasMoreElements() ) {
276             res.push( i.getNext() );
277             };
278             return res
279             }
280             JS
281 0           $getProfiles->()
282             }
283              
284             =head1 UI METHODS
285              
286             =head2 C<< $ff->addTab( %options ) >>
287              
288             my $new = $ff->addTab();
289              
290             Creates a new tab and returns it.
291             The tab will be automatically closed upon program exit.
292              
293             If you want the tab to remain open, pass a false value to the the C< autoclose >
294             option.
295              
296             The recognized options are:
297              
298             =over 4
299              
300             =item *
301              
302             C - the repl to use. By default it will use C<< $ff->repl >>.
303              
304             =item *
305              
306             C - whether to automatically close the tab at program exit. Default is
307             to close the tab.
308              
309             =back
310              
311             =cut
312              
313             =head2 C<< $ff->selectedTab( %options ) >>
314              
315             my $curr = $ff->selectedTab();
316              
317             Sets the currently active tab.
318              
319             =cut
320              
321              
322             =head2 C<< $ff->closeTab( $tab [,$repl] ) >>
323              
324             $ff->closeTab( $tab );
325              
326             Close the given tab.
327              
328             =cut
329              
330             =head2 C<< $ff->openTabs( [$repl] ) >>
331              
332             my @tab_info = $ff->openTabs();
333             print "$_->{title}, $_->{location}, \n"
334             for @tab_info;
335              
336             Returns a list of information about the currently open tabs.
337              
338             =cut
339              
340              
341             =head2 C<< $ff->activateTab( [ $tab [, $repl ]] ) >>
342              
343             $ff->activateTab( $mytab ); # bring to foreground
344            
345             Activates the tab passed in.
346              
347             =cut
348              
349             sub activateTab {
350 0     0 1   my ($self, $tab, $repl ) = @_;
351 0   0       $repl ||= $self->repl;
352 0 0         croak "No tab given"
353             unless $tab;
354 0           $self->browser( $repl )->{tabContainer}->{selectedItem} = $tab;
355             };
356              
357             =head2 C<< $ff->browser( [$repl] ) >>
358              
359             my $b = $ff->browser();
360              
361             Returns the current Firefox browser instance, or opens a new browser
362             window if none is available, and returns its browser instance.
363              
364             If you need to call this as a class method, pass in the L
365             bridge to use.
366              
367             =cut
368              
369             sub browser {
370 0     0 1   my ($self,$repl) = @_;
371 0   0       $repl ||= $self->repl;
372 0           return $repl->declare(<<'JS')->();
373             function() {
374             var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
375             .getService(Components.interfaces.nsIWindowMediator);
376             var win = wm.getMostRecentWindow('navigator:browser');
377             if (! win) {
378             // No browser windows are open, so open a new one.
379             win = window.open('about:blank');
380             };
381             return win.gBrowser
382             // return win.getBrowser()
383             }
384             JS
385             };
386              
387             =head2 C<< $ff->getMostRecentWindow >>
388              
389             Returns the most recently used Firefox window.
390              
391             =cut
392              
393             sub getMostRecentWindow {
394 0     0 1   my ($self) = @_;
395 0           my $get = $self->repl->declare(<<'JS');
396             function() {
397             var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
398             .getService(Components.interfaces.nsIWindowMediator);
399             return wm.getMostRecentWindow('navigator:browser');
400             }
401             JS
402 0           return $get->()
403             };
404              
405             =head2 C<< $ff->set_tab_content( $tab, $html [,$repl] ) >>
406              
407             $ff->set_tab_content('

Hello

');
408              
409             This is a more general method that allows you to replace
410             the HTML of an arbitrary tab, and not only the tab that
411             WWW::Mechanize::Firefox is associated with.
412              
413             It has the flaw of not waiting until the tab has
414             loaded.
415              
416             =cut
417              
418             sub set_tab_content {
419 0     0 1   my ($self, $tab, $content, $repl) = @_;
420 0           my $url = URI->new('data:');
421 0           $url->media_type("text/html");
422 0           $url->data($content);
423            
424 0   0       $tab ||= $self->tab;
425 0   0       $repl ||= $self->repl;
426            
427 0           $tab->{linkedBrowser}->loadURI("".$url);
428             };
429              
430             =head2 C<< $ff->quit( %options ) >>
431              
432             $ff->quit( restart => 1 ); # restart
433             $ff->quit(); # quit
434              
435             Quits or restarts the application
436              
437             =cut
438              
439             sub quit {
440 0     0 1   my ($self, %options) = @_;
441 0   0       my $repl = $options{ repl } || $self->repl;
442             my $flags = $options{ restart }
443 0 0         ? 0x13 # force-quit
444             : 0x03 # force-quit + restart
445             ;
446            
447 0           my $get_startup = $repl->declare(<<'JS');
448             function() {
449             return Components.classes["@mozilla.org/toolkit/app-startup;1"]
450             .getService(Components.interfaces.nsIAppStartup);
451             }
452             JS
453 0           my $s = $get_startup->();
454 0           $s->quit($flags);
455             };
456              
457             =head2 C<< $ff->bool_ff_to_perl $val >>
458              
459             Normalizes the (checkbox) truth value C<$val> to 1 or 0.
460              
461             Different Firefox versions return C or C
462             as the checkbox values. This function converts
463             a Firefox checkbox value to 1 or 0.
464              
465             =cut
466              
467             # FF 31 has 1,0
468             sub bool_ff_to_perl {
469 0     0 1   my( $self, $value )= @_;
470 0           $value
471             }
472              
473             =head2 C<< $ff->bool_perl_to_ff $val >>
474              
475             Normalizes the truth value C<$val> to 1 or 0.
476              
477             Different Firefox versions want C or C
478             as the checkbox values. This function converts
479             a Perl truth value to 1 or 0 respectively C or C,
480             depending on what Firefox wants.
481              
482             =cut
483              
484             # FF 31 has 1,0
485             sub bool_perl_to_ff {
486 0     0 1   my( $self, $value )= @_;
487 0 0         $value ? 1 : 0
488             }
489              
490             =head1 TODO
491              
492             =over 4
493              
494             =item *
495              
496             Consider how to roll L
497             into this module for convenient / versatile launching of Firefox
498              
499             =back
500              
501             =head1 AUTHOR
502              
503             Max Maischein C
504              
505             =head1 COPYRIGHT (c)
506              
507             Copyright 2009-2013 by Max Maischein C.
508              
509             =head1 LICENSE
510              
511             This module is released under the same terms as Perl itself.
512              
513             =cut
514              
515             1;