AnyEvent-Chromi

 view release on metacpan or  search on metacpan

lib/AnyEvent/Chromi.pm  view on Meta::CPAN

package AnyEvent::Chromi;

use strict;

use AnyEvent::Socket;
use AnyEvent::Handle;
 
use Protocol::WebSocket::Handshake::Client;
use Protocol::WebSocket::Handshake::Server;
use Protocol::WebSocket::Frame;

use JSON::XS;
use URI::Escape;
use Log::Any qw($log);

our $VERSION = '1.01';

sub new
{
    my ($class, %args) = @_;
    my $self = {};
    bless $self, $class;

    $self->{mode} = $args{mode} // 'server';
    $self->{port} = $args{port} // 7441;
    $self->{on_connect} = $args{on_connect} if defined $args{on_connect};
    if($self->{mode} eq 'client') {
        $self->_start_client();
    }
    else {
        $self->_start_server();
    }

    return $self;
}

sub call
{
    my ($self, $method, $args, $cb) = @_;
    if(not $self->is_connected) {
        $log->warning("can't call $method: not connected");
        return;
    }
    my $id = int(rand(100000000));
    my $msg = "chromi $id $method";
    if($args) {
        $msg .= " " . uri_escape(encode_json($args));
    }
    my $frame = Protocol::WebSocket::Frame->new($msg);
    if($cb) {
        $self->{callbacks}{$id} = $cb;
    }
    $self->{handle}->push_write($frame->to_bytes);
}

sub is_connected
{
    my ($self) = @_;
    return $self->{connected};
}

sub _setup_connection
{
    my ($self, $fh) = @_;

    my $ws_handshake = $self->{mode} eq 'client' ? Protocol::WebSocket::Handshake::Client->new(url => 'ws://localhost') :
                                                   Protocol::WebSocket::Handshake::Server->new;
    my $ws_frame = Protocol::WebSocket::Frame->new;
    
    $self->{handle} = AnyEvent::Handle->new(fh => $fh);

    $self->{handle}->on_error(

lib/AnyEvent/Chromi.pm  view on Meta::CPAN


AnyEvent::Chromi - Remotely control Google Chrome from Perl

=head2 SYNOPSIS

    # Start in client mode (need "chromix-server" or examples/server.pl)
    my $chromi AnyEvent::Chromi->new(mode => 'client', on_connect => sub {
        my ($chromi) = @_;
        ...
        $chromi->call(...);
    });

    # Start in server mode
    my $chromi AnyEvent::Chromi->new(mode => 'server');

=head2 DESCRIPTION

AnyEvent::Chromi allows you to remotely control Google Chrome from a Perl script.
It requires the Chromi extension L<https://github.com/smblott-github/chromi>, which
exposes all of the Chrome Extensions API via a websocket connection.

=head2 METHODS

=over 4

=item $chromi = AnyEvent::Chromi->new(mode => ..., on_connect => ...);

=over 4

=item mode => 'client|server'

If 'server' (default), it will start a websocket server on port 7441 and wait
for the connection from Chrome (initiated by the Chromi extension). This is the
most practical way to use AnyEvent::Chromi if you write a long-running script,
because it doesn't require a separate daemon.

If 'client', it will connect to port 7441 itself, expecting a websocket server, like
the one provided by chromix-server, or by the examples/server.pl script.

=item port => N

Use port N instead of 7441.

=item on_connect => sub { my ($chromi) = @_; ... }

Will be executed as soon as Chrome connects (in server mode), or as the connection
to the websocket server is done.

=back

=item $chromi->call($method, $args, $cb)

Call the Chrome extension method C<$method>, e.g. C<chrome.windows.getAll>.

C<$args> is expected to be a ARRAYREF with the arguments for the method. It will be
converted to JSON by AnyEvent::Chromi.

C<$cb> is a callback for when the reply is received. The first argument to the callback is
the status (either "done" or "error"), and the second is a ARRAYREF with the data.

Note: you need to make sure that the JSON::XS serialization is generating the proper
data types. This is particularly important for booleans, where C<Types::Serialiser::true>
and C<Types::Serialiser::false> can be used.

=item $chromi->is_connected

In server mode: returns true if Chrome is connected and awaits commands.

In client mode: returns true if connected to chromix-server.

=back

=head2 EXAMPLES

=over

=item *

List all tabs

    $chromi->call(
        'chrome.windows.getAll', [{ populate => Types::Serialiser::true }],
        sub {
            my ($status, $reply) = @_;
            $status eq 'done' or return;
            defined $reply and ref $reply eq 'ARRAY' or return;
            map { say "$_->{url}" } @{$reply->[0]{tabs}};
            $cv->send();
        }

=item * Focus a tab

    $chromi->call(
        'chrome.tabs.update', [$tab_id, { active => Types::Serialiser::true }],
    );

=back

See also the "examples" directory:

=over

=item examples/client.pl

Lists the URLs of all tabs. Requires chromix-server

=item examples/server.pl

chromix-server replacement written in Perl. Additionally to chromix-server, it
also properly supports multiple clients with one or more chrome instances.

=back

=head2 AUTHOR

David Schweikert <david@schweikert.ch>, heavily influenced by Chromi/Chromix by
Stephen Blott.

=head2 SEE ALSO

=over



( run in 0.643 second using v1.01-cache-2.11-cpan-39bf76dae61 )