File Coverage

blib/lib/Firefox/Application.pm
Criterion Covered Total %
statement 24 77 31.1
branch 4 22 18.1
condition 1 17 5.8
subroutine 6 20 30.0
pod 13 14 92.8
total 48 150 32.0


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

Hello

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