package ChromeTool; # # A thin (and partial) Perl wrapper around the Chrome Developer Tool Protocol # please see the following page for details on the protocol: # http://code.google.com/p/chromedevtools/wiki/ChromeDevToolsProtocol # # Author: Deepak Gulati [deepak.gulati at gmail dot com] # Version: 0.01 # Date: 29 Jun 2010 # use strict; use IO::Socket::INET; use JSON::Any; use Data::Dumper; my $crlf = "\r\n"; my $handshake = "ChromeDevToolsHandshake"; my $tool = "DevToolsService"; my $tool_v8 = "V8Debugger"; my %headers = ( "cl" => "Content-Length", "t" => "Tool", "d" => "Destination", ); sub _header { my($hdr, $val) = @_; return $headers{$hdr} . ":" . $val; } sub _cmd { my $command = shift; return '{"command":"' . $command . '"}'; } sub _cmd_data { my ($command, $data) = @_; $command = "evaluate_javascript" if $command eq 'eval'; $data =~ s/\"/\\\"/g; return '{"command":"' . $command . '","data":"' . $data . '"}'; } sub _post { my $sock = shift; my ($headers, $command) = @_; my $payload = join $crlf, @$headers, $crlf . $command; print $sock $payload; return {}; } sub _post_and_parse { my $sock = shift; my ($headers, $command) = @_; my $payload = join $crlf, @$headers, $crlf . $command; print $sock $payload; my $resp = ""; my $len = 0; #read 4 lines. lines 1-3 are headers Content-Length, Tool, Destination #line 4 is a blank newline, after which the actual result follows for (0..3) { $resp = <$sock>; if($resp =~ /Content\-Length\:(\d+)/i) { $len = $1; } } #die "Did not get a valid ping response" unless $len; if($len > 0) { $sock->read($resp, $len); my $obj = JSON::Any->jsonToObj($resp); #TODO: wrap this under try tiny? return $obj; } return {}; } sub new { my $class = shift; my $port = 9222; my $sock = IO::Socket::INET->new( PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp') or die $!; print $sock $handshake, $crlf; my $resp = <$sock>; $resp =~ s/\s+$//; die "Could not handshake" if $resp ne $handshake; my %hash = ( _sock => $sock ); return bless \%hash, $class; } sub AUTOLOAD { our $AUTOLOAD; if( $AUTOLOAD =~ m/::(\w+)$/ ) { my $method = $1; $method = "list_tabs" if $method eq 'tabs'; my $self = shift; my $tab = shift; my $data = shift; my $sock = $self->{_sock}; my $command = $data ? _cmd_data($method, $data) : _cmd($method); my $headers = [ _header('cl', length($command)), #Content-Length _header('t', ($method eq 'eval') ? $tool_v8 : $tool), #Tool _header('d', $tab), #Destination ]; my $resp = {}; if($method eq 'eval') { $resp = _post($sock, $headers, $command); } else { $resp = _post_and_parse($sock, $headers, $command); } if($resp->{result} == 0) { return $resp->{data}; } } return undef; } sub DESTROY { } 1;