Firefox-Marionette
view release on metacpan or search on metacpan
#! /usr/bin/env perl
use strict;
use warnings;
use Getopt::Long();
use File::Spec();
use FileHandle();
use Fcntl();
use Socket();
use JSON();
MAIN: {
my %options;
Getopt::Long::GetOptions(\%options, 'version', 'marionette', 'headless', 'profile:s', 'no-remote', 'new-instance', 'devtools', 'safe-mode');
my $browser_version = "112.0.2";
if ($options{version}) {
$| = 1;
print "Mozilla Firefox $browser_version\n";
exit 0;
}
socket my $server, Socket::PF_INET(), Socket::SOCK_STREAM(), 0 or die "Failed to create a socket:$!";
bind $server, Socket::sockaddr_in( 0, Socket::INADDR_LOOPBACK() ) or die "Failed to bind socket:$!";
listen $server, Socket::SOMAXCONN() or die "Failed to listen:$!";
my $port = ( Socket::sockaddr_in( getsockname $server ) )[0];
my $prefs_path = File::Spec->catfile($options{profile}, 'prefs.js');
my $old_prefs_handle = FileHandle->new($prefs_path, Fcntl::O_RDONLY()) or die "Failed to open $prefs_path for reading:$!";
my $new_prefs_path = File::Spec->catfile($options{profile}, 'prefs.new');
my $new_prefs_handle = FileHandle->new($new_prefs_path, Fcntl::O_CREAT() | Fcntl::O_EXCL() | Fcntl::O_WRONLY(), Fcntl::S_IRUSR() | Fcntl::S_IWUSR()) or die "Failed to open $new_prefs_path for writing:$!";
while(my $line = <$old_prefs_handle>) {
if ($line =~ /^user_pref\("marionette.port",[ ]0\);/smx) {
print {$new_prefs_handle} qq[user_pref("marionette.port", $port);\n] or die "Failed to write to $new_prefs_path:$!";
} else {
print {$new_prefs_handle} $line or die "Failed to write to $new_prefs_path:$!";
}
}
close $new_prefs_handle or die "Failed to close $new_prefs_path:$!";
close $old_prefs_handle or die "Failed to close $prefs_path:$!";
rename $new_prefs_path, $prefs_path or die "Failed to rename $new_prefs_path to $prefs_path:$!";
my $paddr = accept(my $client, $server);
my $old = select $client; $| = 1; select $old;
syswrite $client, qq[50:{"applicationType":"gecko","marionetteProtocol":3}] or die "Failed to write to socket:$!";
my $request = _get_request($client);
my $platform = $^O;
my $headless = $options{headless} ? 'true' : 'false';
my $response_type = 1;
my $profile_path = $options{profile};
$profile_path =~ s/\\/\\\\/smxg;
my $capabilities = qq([1,1,null,{"sessionId":"5a5f9a08-0faa-4794-aa85-ee85980ce422","capabilities":{"browserName":"firefox","browserVersion":"$browser_version","platformName":"$platform","acceptInsecureCerts":false,"pageLoadStrategy":"normal","setWi...
my $capability_length = length $capabilities;
syswrite $client, $capability_length . q[:] . $capabilities or die "Failed to write to socket:$!";
my $context = "content";
my $addon_number = 0;
while(1) {
$request = _get_request($client);
my $message_id = $request->[1];
if ($request->[2] eq 'Marionette:Quit') {
my $response_body = qq([$response_type,$message_id,null,{"cause":"shutdown","forced":false,"in_app":true}]);
_send_response_body($client, $response_body);
last;
} elsif ($request->[2] eq 'Addon:Install') {
$addon_number += 1;
my $response_body = qq([$response_type,$message_id,null,{"value":"6eea9fdc37a5d8fbcbbecd57ee7272669e828a3${addon_number}\@temporary-addon"}]);
_send_response_body($client, $response_body);
} elsif ($request->[2] eq 'Addon:Uninstall') {
$addon_number += 1;
my $response_body = qq([$response_type,$message_id,null,{"value":null}]);
_send_response_body($client, $response_body);
} elsif ($request->[2] eq 'WebDriver:Print') {
syswrite $client, qq(1475:[$response_type,$message_id,null,{"value":"JVBERi0xLjUKJbXtrvsKNCAwIG9iago8PCAvTGVuZ3RoIDUgMCBSCiAgIC9GaWx0ZXIgL0ZsYXRlRGVjb2RlCj4+CnN0cmVhbQp4nDNUMABCXUMgYW5ppJCcy1XIFahQyGVkoWdsaqQApUxNTfWMDQwVzI0hdFGqQrhCHpehAggWpSvoJx...
} elsif ($request->[2] eq 'WebDriver:TakeScreenshot') {
syswrite $client, qq(423:[$response_type,$message_id,null,{"value":"iVBORw0KGgoAAAANSUhEUgAABVYAAAAICAYAAAAShaQyAAAA8UlEQVR4Xu3YsQ0AIAwEMbL/0ICYgOud+isr1c2+txwBAgQIECBAgAABAgQIECBAgAABAgQIfAuMsPptZUiAAAECBAgQIECAAAECBAgQIECAAIEnIKx6BAIECBAgQIAAAQI...
} elsif ($request->[2] eq 'Marionette:GetContext') {
my $response_body = qq([$response_type,$message_id,null,{"value":"$context"}]);
_send_response_body($client, $response_body);
} elsif ($request->[2] eq 'Marionette:SetContext') {
my $response_body = qq([$response_type,$message_id,null,{"value":null}]);
_send_response_body($client, $response_body);
} elsif ($request->[2] eq 'WebDriver:ExecuteScript') {
my $response_body;
if ($request->[3]->{script} eq 'return navigator.userAgent') {
my $trimmed_browser_version = $browser_version;
$trimmed_browser_version =~ s/^(\d+(?:[.]\d+)?).*$/$1/smx;
$response_body = qq([$response_type,$message_id,null,{"value":"Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/$trimmed_browser_version"}]);
} else {
my $now = time;
$response_body = qq([$response_type,$message_id,null,{"value":{"guid":"root________","index":0,"type":2,"title":"","dateAdded":$now,"lastModified":$now,"childCount":5}}]);
}
_send_response_body($client, $response_body);
} else {
die "Unsupported method in stub firefox";
}
}
close $client or die "Failed to close socket:$!";
exit 0;
}
sub _send_response_body {
my ($client, $response_body) = @_;
( run in 1.328 second using v1.01-cache-2.11-cpan-5511b514fd6 )