Firefox-Marionette
view release on metacpan or search on metacpan
lib/Firefox/Marionette.pm view on Meta::CPAN
application/pdf
application/octet-stream
application/msword
application/vnd.openxmlformats-officedocument.wordprocessingml.document
application/vnd.openxmlformats-officedocument.wordprocessingml.template
application/vnd.ms-word.document.macroEnabled.12
application/vnd.ms-word.template.macroEnabled.12
application/vnd.ms-excel
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
application/vnd.openxmlformats-officedocument.spreadsheetml.template
application/vnd.ms-excel.sheet.macroEnabled.12
application/vnd.ms-excel.template.macroEnabled.12
application/vnd.ms-excel.addin.macroEnabled.12
application/vnd.ms-excel.sheet.binary.macroEnabled.12
application/vnd.ms-powerpoint
application/vnd.openxmlformats-officedocument.presentationml.presentation
application/vnd.openxmlformats-officedocument.presentationml.template
application/vnd.openxmlformats-officedocument.presentationml.slideshow
application/vnd.ms-powerpoint.addin.macroEnabled.12
application/vnd.ms-powerpoint.presentation.macroEnabled.12
application/vnd.ms-powerpoint.template.macroEnabled.12
application/vnd.ms-powerpoint.slideshow.macroEnabled.12
application/vnd.ms-access
)
];
my %known_mime_types;
foreach my $mime_type ( @{ $self->{mime_types} } ) {
$known_mime_types{$mime_type} = 1;
}
foreach my $mime_type ( @{ $parameters{mime_types} } ) {
if ( !$known_mime_types{$mime_type} ) {
push @{ $self->{mime_types} }, $mime_type;
$known_mime_types{$mime_type} = 1;
}
}
return;
}
sub _check_for_existing_local_firefox_process {
my ($self) = @_;
my $profile_path =
File::Spec->catfile( $self->{_profile_directory}, 'prefs.js' );
my $profile_handle = FileHandle->new($profile_path);
my $port;
if ($profile_handle) {
while ( my $line = <$profile_handle> ) {
if ( $line =~ /^user_pref[(]"marionette[.]port",[ ](\d+)[)];$/smx )
{
($port) = ($1);
}
}
}
return $port || _DEFAULT_PORT();
}
sub _reconnected {
my ($self) = @_;
return $self->{_reconnected};
}
sub _check_reconnecting_firefox_process_is_alive {
my ( $self, $pid ) = @_;
if ( $OSNAME eq 'MSWin32' ) {
if (
Win32::Process::Open(
my $process, $pid, _WIN32_PROCESS_INHERIT_FLAGS()
)
)
{
$self->{_win32_firefox_process} = $process;
return $pid;
}
}
elsif ( kill 0, $pid ) {
return $pid;
}
return;
}
sub _get_local_name_regex {
my ($self) = @_;
my $local_name_regex = qr/firefox_marionette_local_/smx;
if ( $self->{reconnect_index} ) {
my $quoted_index = quotemeta $self->{reconnect_index};
$local_name_regex = qr/${local_name_regex}${quoted_index}\-/smx;
}
$local_name_regex = qr/${local_name_regex}\w+/smx;
return $local_name_regex;
}
sub _get_local_reconnect_pid {
my ($self) = @_;
my $temp_directory = File::Spec->tmpdir();
my $temp_handle = DirHandle->new($temp_directory)
or Firefox::Marionette::Exception->throw(
"Failed to open directory '$temp_directory':$EXTENDED_OS_ERROR");
my $alive_pid;
my $local_name_regex = $self->_get_local_name_regex();
TEMP_DIR_LISTING: while ( my $tainted_entry = $temp_handle->read() ) {
next if ( $tainted_entry eq File::Spec->curdir() );
next if ( $tainted_entry eq File::Spec->updir() );
if ( $tainted_entry =~ /^($local_name_regex)$/smx ) {
my ($untainted_entry) = ($1);
my $possible_root_directory =
File::Spec->catfile( $temp_directory, $untainted_entry );
my $local_proxy = $self->_read_possible_proxy_path(
File::Spec->catfile( $possible_root_directory, 'reconnect' ) );
if ( ( defined $local_proxy->{firefox} )
&& ( defined $local_proxy->{firefox}->{binary} ) )
{
if ( $self->_binary() ne $local_proxy->{firefox}->{binary} ) {
next TEMP_DIR_LISTING;
}
}
elsif ( $self->_binary() ) {
next TEMP_DIR_LISTING;
}
if ( ( defined $local_proxy->{firefox} )
&& ( $local_proxy->{firefox}->{pid} ) )
{
if (
my $check_pid =
$self->_check_reconnecting_firefox_process_is_alive(
$local_proxy->{firefox}->{pid}
)
)
{
$alive_pid = $check_pid;
}
else {
next TEMP_DIR_LISTING;
}
}
else {
next TEMP_DIR_LISTING;
}
if ( ( defined $local_proxy->{xvfb} )
&& ( defined $local_proxy->{xvfb}->{pid} )
&& ( kill 0, $local_proxy->{xvfb}->{pid} ) )
{
$self->{_xvfb_pid} = $local_proxy->{xvfb}->{pid};
}
$self->{_initial_version} = $local_proxy->{firefox}->{version};
$self->{_root_directory} = $possible_root_directory;
$self->_setup_profile();
}
}
closedir $temp_handle
or Firefox::Marionette::Exception->throw(
"Failed to close directory '$temp_directory':$EXTENDED_OS_ERROR");
return $alive_pid;
}
sub _setup_profile {
my ($self) = @_;
if ( $self->{profile_name} ) {
$self->{_profile_directory} =
Firefox::Marionette::Profile->directory( $self->{profile_name} );
$self->{profile_path} =
File::Spec->catfile( $self->{_profile_directory}, 'prefs.js' );
}
else {
$self->{_profile_directory} =
File::Spec->catfile( $self->{_root_directory}, 'profile' );
$self->{_download_directory} =
File::Spec->catfile( $self->{_root_directory}, 'downloads' );
$self->{profile_path} =
File::Spec->catfile( $self->{_profile_directory}, 'prefs.js' );
}
return;
}
sub _reconnect {
my ( $self, %parameters ) = @_;
if ( $parameters{profile_name} ) {
$self->{profile_name} = $parameters{profile_name};
}
$self->{_reconnected} = 1;
if ( my $ssh = $self->_ssh() ) {
if ( my $pid = $self->_firefox_pid() ) {
if ( $self->_remote_process_running($pid) ) {
$self->{_firefox_pid} = $pid;
}
}
}
else {
if ( my $pid = $self->_get_local_reconnect_pid() ) {
if (
( kill 0, $pid )
&& ( my $port =
$self->_check_for_existing_local_firefox_process() )
)
{
$self->{_firefox_pid} = $pid;
}
}
}
my ( $host, $user );
if ( my $ssh = $self->_ssh() ) {
$host = $self->_ssh()->{host};
$user = $self->_ssh()->{user};
}
elsif (( $OSNAME eq 'MSWin32' )
|| ( $OSNAME eq 'cygwin' ) )
{
$user = Win32::LoginName();
$host = 'localhost';
}
else {
$user = getpwuid $EFFECTIVE_USER_ID;
lib/Firefox/Marionette.pm view on Meta::CPAN
}
if ( $self->{_win32_ssh_process} ) {
$self->{_win32_ssh_process}->GetExitCode( my $exit_code );
if ( $exit_code != Win32::Process::STILL_ACTIVE() ) {
$self->{_child_error} = $exit_code;
delete $self->{_win32_ssh_process};
}
}
$self->_reap_other_win32_ssh_processes();
}
elsif ( my $ssh = $self->_ssh() ) {
while ( ( my $pid = waitpid _ANYPROCESS(), POSIX::WNOHANG() ) > 0 ) {
if ( ( $ssh->{pid} ) && ( $pid == $ssh->{pid} ) ) {
$self->{_child_error} = $CHILD_ERROR;
}
elsif ( ( $self->xvfb_pid() ) && ( $pid == $self->xvfb_pid() ) ) {
$self->{_xvfb_child_error} = $CHILD_ERROR;
delete $self->{xvfb_pid};
delete $self->{_xvfb_display_number};
}
}
}
else {
while ( ( my $pid = waitpid _ANYPROCESS(), POSIX::WNOHANG() ) > 0 ) {
if ( ( $self->_firefox_pid() )
&& ( $pid == $self->_firefox_pid() ) )
{
$self->{_child_error} = $CHILD_ERROR;
}
elsif (( $self->_local_ssh_pid() )
&& ( $pid == $self->_local_ssh_pid() ) )
{
$self->{_child_error} = $CHILD_ERROR;
}
elsif ( ( $self->xvfb_pid() ) && ( $pid == $self->xvfb_pid() ) ) {
$self->{_xvfb_child_error} = $CHILD_ERROR;
delete $self->{xvfb_pid};
delete $self->{_xvfb_display_number};
}
}
}
return;
}
sub _reap_other_win32_ssh_processes {
my ($self) = @_;
my @other_processes;
foreach my $process ( @{ $self->{_other_win32_ssh_processes} } ) {
$process->GetExitCode( my $exit_code );
if ( $exit_code == Win32::Process::STILL_ACTIVE() ) {
push @other_processes, $process;
}
}
$self->{_other_win32_ssh_processes} = \@other_processes;
return;
}
sub _remote_process_running {
my ( $self, $remote_pid ) = @_;
my $now = time;
if ( ( defined $self->{last_remote_alive_status} )
&& ( $self->{last_remote_kill_time} >= $now ) )
{
return $self->{last_remote_alive_status};
}
$self->{last_remote_kill_time} = $now;
my $remote_uname = $self->_remote_uname();
if ( !defined $remote_uname ) {
return;
}
elsif ( $remote_uname eq 'MSWin32' ) {
return $self->_win32_remote_process_running($remote_pid);
}
else {
return $self->_generic_remote_process_running($remote_pid);
}
}
sub _win32_remote_process_running {
my ( $self, $remote_pid ) = @_;
my $binary = 'tasklist';
my @arguments = ( '/FI', q["PID eq ] . $remote_pid . q["] );
$self->{last_remote_alive_status} = 0;
foreach my $line ( split /\r?\n/smx, $self->execute( $binary, @arguments ) )
{
if ( $line =~ /^firefox[.]exe[ ]+(\d+)[ ]/smx ) {
if ( $1 == $remote_pid ) {
$self->{last_remote_alive_status} = 1;
}
}
}
return $self->{last_remote_alive_status};
}
sub _generic_remote_process_running {
my ( $self, $remote_pid ) = @_;
my $result = $self->_execute_via_ssh(
{ return_exit_status => 1 },
(
$self->_remote_uname() eq 'cygwin'
? ( '/bin/kill', '-W' )
: ('kill')
),
'-0',
$remote_pid
);
if ( $result == 0 ) {
$self->{last_remote_alive_status} = 1;
}
else {
$self->{last_remote_alive_status} = 0;
}
return $self->{last_remote_alive_status};
}
sub alive {
my ($self) = @_;
if ( $self->_adb() ) {
my $parameters;
my $binary = q[adb];
my @arguments =
( qw(-s), $self->_adb_serial(), qw(shell am stack list) );
my $handle =
$self->_get_local_handle_for_generic_command_output( $parameters,
$binary, @arguments );
my $quoted_package_name = quotemeta $self->_adb_package_name();
my $quoted_component_name = quotemeta $self->_adb_component_name();
my $found = 0;
while ( my $line = <$handle> ) {
if ( $line =~
/^[ ]+taskId=\d+:[ ]$quoted_package_name\/${quoted_component_name}[ ]+/smx
)
{
$found = 1;
}
}
return $found;
}
if ( my $ssh = $self->_ssh() ) {
$self->_reap();
if ( defined $ssh->{pid} ) {
if ( $OSNAME eq 'MSWin32' ) {
$self->_reap_other_win32_ssh_processes();
if ( $self->{_win32_ssh_process} ) {
$self->{_win32_ssh_process}->GetExitCode( my $exit_code );
$self->_reap();
if ( $exit_code == Win32::Process::STILL_ACTIVE() ) {
return 1;
}
}
return 0;
}
else {
return kill 0, $ssh->{pid};
}
}
elsif ( $self->_firefox_pid() ) {
return $self->_remote_process_running( $self->_firefox_pid() );
}
}
elsif ( $OSNAME eq 'MSWin32' ) {
$self->_reap_other_win32_ssh_processes();
if ( $self->{_win32_firefox_process} ) {
$self->{_win32_firefox_process}->GetExitCode( my $exit_code );
$self->_reap();
if ( $exit_code == Win32::Process::STILL_ACTIVE() ) {
return 1;
}
}
return 0;
}
elsif ( $self->_firefox_pid() ) {
$self->_reap();
return kill 0, $self->_firefox_pid();
}
return;
lib/Firefox/Marionette.pm view on Meta::CPAN
}
else {
$sock_addr =
Socket::pack_sockaddr_in( $port, Socket::inet_aton($host) );
}
return $sock_addr;
}
sub _using_unix_sockets_for_ssh_connection {
my ($self) = @_;
if ( my $ssh = $self->_ssh() ) {
if ( $ssh->{use_unix_sockets} ) {
return 1;
}
}
return 0;
}
sub _network_connection_and_initial_read_from_marionette {
my ( $self, $socket, $sock_addr ) = @_;
my ( $port, $host ) = Socket::unpack_sockaddr_in($sock_addr);
$host = Socket::inet_ntoa($host);
my $connected;
if ( connect $socket, $sock_addr ) {
my $number_of_bytes = sysread $socket,
$self->{_initial_octet_read_from_marionette_socket}, 1;
if ($number_of_bytes) {
$connected = 1;
}
elsif ( defined $number_of_bytes ) {
sleep 1;
}
else {
Firefox::Marionette::Exception->throw(
"Failed to read from connection to $host on port $port:$EXTENDED_OS_ERROR"
);
}
}
elsif ( $EXTENDED_OS_ERROR == POSIX::ECONNREFUSED() ) {
sleep 1;
}
elsif (( $OSNAME eq 'MSWin32' )
&& ( $EXTENDED_OS_ERROR == _WIN32_CONNECTION_REFUSED() ) )
{
sleep 1;
}
else {
Firefox::Marionette::Exception->throw(
"Failed to connect to $host on port $port:$EXTENDED_OS_ERROR");
}
return $connected;
}
sub _setup_local_connection_to_firefox {
my ( $self, @arguments ) = @_;
my $host = _DEFAULT_HOST();
my $port;
my $socket;
my $sock_addr;
my $connected;
while ( ( !$connected ) && ( $self->alive() ) ) {
if ( $self->_adb() ) {
Firefox::Marionette::Exception->throw(
'TODO: Cannot connect to android yet. Patches welcome');
}
$socket = undef;
socket $socket,
$self->_using_unix_sockets_for_ssh_connection()
? Socket::PF_UNIX()
: Socket::PF_INET(), Socket::SOCK_STREAM(), 0
or Firefox::Marionette::Exception->throw(
"Failed to create a socket:$EXTENDED_OS_ERROR");
binmode $socket;
$port ||= $self->_get_marionette_port_or_undef();
next if ( !defined $port );
$sock_addr ||= $self->_get_sock_addr( $host, $port );
next if ( !defined $sock_addr );
$connected =
$self->_network_connection_and_initial_read_from_marionette( $socket,
$sock_addr );
}
$self->_reap();
if ( ( $self->alive() ) && ($socket) ) {
}
else {
my $error_message =
$self->error_message()
? $self->error_message()
: q[Firefox was not launched];
Firefox::Marionette::Exception->throw($error_message);
}
return $socket;
}
sub _remote_catfile {
my ( $self, @parts ) = @_;
if ( ( $self->_remote_uname() ) && ( $self->_remote_uname() eq 'MSWin32' ) )
{
return join q[\\], @parts;
}
else {
return join q[/], @parts;
}
}
sub _ssh_address {
my ($self) = @_;
my $address;
if ( defined $self->{_ssh}->{user} ) {
$address = join q[], $self->{_ssh}->{user}, q[@], $self->{_ssh}->{host};
}
else {
$address = $self->{_ssh}->{host};
}
return $address;
}
sub _ssh_arguments {
my ( $self, %parameters ) = @_;
my @arguments = qw(-2 -a -T);
if ( ( $parameters{graphical} ) || ( $parameters{master} ) ) {
if ( ( defined $self->_visible() ) && ( $self->_visible() eq 'local' ) )
{
push @arguments, '-X';
}
}
if ( my $ssh = $self->_ssh() ) {
if ( my $port = $ssh->{port} ) {
push @arguments, ( '-p' => $port, );
}
}
return ( @arguments, $self->_ssh_common_arguments(%parameters) );
}
sub _ssh_exec {
my ( $self, @parameters ) = @_;
if ( $self->debug() ) {
warn q[** ] . ( join q[ ], 'ssh', @parameters ) . "\n";
}
my $dev_null = File::Spec->devnull();
open STDERR, q[>], $dev_null
or Firefox::Marionette::Exception->throw(
"Failed to redirect STDERR to $dev_null:$EXTENDED_OS_ERROR");
lib/Firefox/Marionette.pm view on Meta::CPAN
@{ $self->_response_result_value($response) };
}
}
else {
if ( $response->ignored_exception() ) {
return;
}
if (
(
$self->marionette_protocol() == _MARIONETTE_PROTOCOL_VERSION_3()
)
|| ( $self->{_initial_packet_size} != _OLD_INITIAL_PACKET_SIZE() )
)
{
return Firefox::Marionette::Element->new( $self,
%{ $self->_response_result_value($response) } );
}
else {
return Firefox::Marionette::Element->new( $self,
ELEMENT => $self->_response_result_value($response) );
}
}
}
sub active_frame {
my ($self) = @_;
my $message_id = $self->_new_message_id();
$self->_send_request(
[
_COMMAND(), $message_id, $self->_command('WebDriver:GetActiveFrame')
]
);
my $response = $self->_get_response($message_id);
if ( defined $self->_response_result_value($response) ) {
if ( ref $self->_response_result_value($response) ) {
return Firefox::Marionette::Element->new( $self,
%{ $self->_response_result_value($response) } );
}
else {
return Firefox::Marionette::Element->new( $self,
ELEMENT => $self->_response_result_value($response) );
}
}
else {
return;
}
}
sub title {
my ($self) = @_;
my $message_id = $self->_new_message_id();
$self->_send_request(
[ _COMMAND(), $message_id, $self->_command('WebDriver:GetTitle') ] );
my $response = $self->_get_response($message_id);
return $self->_response_result_value($response);
}
sub quit {
my ( $self, $flags ) = @_;
my $ssh_local_directory = $self->ssh_local_directory();
if ( !$self->alive() ) {
my $socket = delete $self->{_socket};
if ($socket) {
close $socket
or Firefox::Marionette::Exception->throw(
"Failed to close socket to firefox:$EXTENDED_OS_ERROR");
}
$self->_terminate_xvfb();
}
elsif ( $self->_socket() ) {
eval {
if ( $self->_session_id() ) {
$self->_quit_over_marionette($flags);
delete $self->{session_id};
}
$self->_terminate_xvfb();
1;
} or do {
warn "Caught an exception while quitting:$EVAL_ERROR\n";
};
eval {
if ( $self->_ssh() ) {
$self->_cleanup_remote_filesystem();
$self->_terminate_master_control_via_ssh();
}
$self->_cleanup_local_filesystem();
delete $self->{creation_pid};
} or do {
warn "Caught an exception while cleaning up:$EVAL_ERROR\n";
};
$self->_terminate_process();
}
else {
$self->_terminate_process();
}
if ( !$self->_reconnected() ) {
if ($ssh_local_directory) {
File::Path::rmtree( $ssh_local_directory, 0, 0 );
}
elsif ( defined $self->root_directory() ) {
File::Path::rmtree( $self->root_directory(), 0, 0 );
}
}
return $self->child_error();
}
sub _quit_over_marionette {
my ( $self, $flags ) = @_;
$flags ||=
['eAttemptQuit']; # ["eConsiderQuit", "eAttemptQuit", "eForceQuit"]
my $message_id = $self->_new_message_id();
$self->_send_request(
[
_COMMAND(), $message_id,
$self->_command('Marionette:Quit'), { flags => $flags }
]
);
my $response = $self->_get_response($message_id);
my $socket = delete $self->{_socket};
if ( $OSNAME eq 'MSWin32' ) {
if ( defined $self->{_win32_ssh_process} ) {
lib/Firefox/Marionette.pm view on Meta::CPAN
}
sub _session_id {
my ($self) = @_;
return $self->{session_id};
}
sub _new_message_id {
my ($self) = @_;
$self->{last_message_id} += 1;
return $self->{last_message_id};
}
sub addons {
my ($self) = @_;
return $self->{addons};
}
sub _convert_request_to_old_protocols {
my ( $self, $original ) = @_;
my $new;
if ( $self->marionette_protocol() == _MARIONETTE_PROTOCOL_VERSION_3() ) {
$new = $original;
}
else {
$new->{ $self->{_old_protocols_key} } =
$original->[ _OLD_PROTOCOL_NAME_INDEX() ];
$new->{parameters} = $original->[ _OLD_PROTOCOL_PARAMETERS_INDEX() ];
if ( ( ref $new->{parameters} )
&& ( ( ref $new->{parameters} ) eq 'HASH' ) )
{
if ( defined $new->{parameters}->{id} ) {
$new->{parameters}->{element} = $new->{parameters}->{id};
}
foreach my $key (
sort { $a cmp $b }
keys %{ $original->[ _OLD_PROTOCOL_PARAMETERS_INDEX() ] }
)
{
next if ( $key eq $self->{_old_protocols_key} );
$new->{$key} = $new->{parameters}->{$key};
}
}
}
return $new;
}
sub _send_request {
my ( $self, $object ) = @_;
$object = $self->_convert_request_to_old_protocols($object);
my $encoder = JSON->new()->convert_blessed()->ascii();
if ( $self->debug() ) {
$encoder->canonical(1);
}
my $json = $encoder->encode($object);
my $length = length $json;
if ( $self->debug() ) {
warn ">> $length:$json\n";
}
my $result;
if ( $self->alive() ) {
$result = syswrite $self->_socket(), "$length:$json";
}
if ( !defined $result ) {
my $socket_error = $EXTENDED_OS_ERROR;
if ( $self->alive() ) {
Firefox::Marionette::Exception->throw(
"Failed to send request to firefox:$socket_error");
}
else {
my $error_message =
$self->error_message() ? $self->error_message() : q[];
Firefox::Marionette::Exception->throw($error_message);
}
}
return;
}
sub _handle_socket_read_failure {
my ($self) = @_;
my $socket_error = $EXTENDED_OS_ERROR;
if ( $self->alive() ) {
Firefox::Marionette::Exception->throw(
"Failed to read size of response from socket to firefox:$socket_error"
);
}
else {
my $error_message =
$self->error_message() ? $self->error_message() : q[];
Firefox::Marionette::Exception->throw($error_message);
}
return;
}
sub _read_from_socket {
my ($self) = @_;
my $number_of_bytes_in_response;
my $initial_buffer;
while ( ( !defined $number_of_bytes_in_response ) && ( $self->alive() ) ) {
my $number_of_bytes;
my $octet;
if ( $self->{_initial_octet_read_from_marionette_socket} ) {
$octet = delete $self->{_initial_octet_read_from_marionette_socket};
$number_of_bytes = length $octet;
}
else {
$number_of_bytes = sysread $self->_socket(), $octet, 1;
}
if ( defined $number_of_bytes ) {
$initial_buffer .= $octet;
}
else {
$self->_handle_socket_read_failure();
}
if ( $initial_buffer =~ s/^(\d+)://smx ) {
($number_of_bytes_in_response) = ($1);
}
}
if ( !defined $self->{_initial_packet_size} ) {
$self->{_initial_packet_size} = $number_of_bytes_in_response;
}
my $number_of_bytes_already_read = 0;
my $json = q[];
while (( defined $number_of_bytes_in_response )
&& ( $number_of_bytes_already_read < $number_of_bytes_in_response )
&& ( $self->alive() ) )
{
my $number_of_bytes_read = sysread $self->_socket(), my $buffer,
$number_of_bytes_in_response - $number_of_bytes_already_read;
if ( defined $number_of_bytes_read ) {
$json .= $buffer;
$number_of_bytes_already_read += $number_of_bytes_read;
}
else {
my $socket_error = $EXTENDED_OS_ERROR;
if ( $self->alive() ) {
Firefox::Marionette::Exception->throw(
"Failed to read response from socket to firefox:$socket_error"
);
}
else {
my $error_message =
$self->error_message() ? $self->error_message() : q[];
Firefox::Marionette::Exception->throw($error_message);
}
}
}
if ( ( $self->debug() ) && ( defined $number_of_bytes_in_response ) ) {
warn "<< $number_of_bytes_in_response:$json\n";
}
return $self->_decode_json($json);
}
sub _decode_json {
my ( $self, $json ) = @_;
my $parameters;
eval { $parameters = JSON::decode_json($json); } or do {
if ( $self->alive() ) {
if ($EVAL_ERROR) {
chomp $EVAL_ERROR;
die "$EVAL_ERROR\n";
}
}
else {
my $error_message =
$self->error_message() ? $self->error_message() : q[];
Firefox::Marionette::Exception->throw($error_message);
}
};
return $parameters;
}
sub _socket {
my ($self) = @_;
return $self->{_socket};
}
sub _get_response {
my ( $self, $message_id, $parameters, $options ) = @_;
my $next_message = $self->_read_from_socket();
my $response =
Firefox::Marionette::Response->new( $next_message, $parameters,
$options );
if ( $self->marionette_protocol() == _MARIONETTE_PROTOCOL_VERSION_3() ) {
while ( $response->message_id() < $message_id ) {
$next_message = $self->_read_from_socket();
$response =
Firefox::Marionette::Response->new( $next_message, $parameters );
}
}
return $response;
}
sub _signal_number {
my ( $proto, $name ) = @_;
my %signals_by_name;
my $idx = 0;
foreach my $sig_name (@sig_names) {
$signals_by_name{$sig_name} = $sig_nums[$idx];
$idx += 1;
}
return $signals_by_name{$name};
}
sub DESTROY {
my ($self) = @_;
local $CHILD_ERROR = 0;
if ( ( defined $self->{creation_pid} )
&& ( $self->{creation_pid} == $PROCESS_ID ) )
{
if ( $self->{survive} ) {
if ( $self->_session_id() ) {
$self->delete_session();
}
}
else {
$self->quit();
}
lib/Firefox/Marionette.pm view on Meta::CPAN
=item * network.http.accept-encoding
=item * network.http.accept-encoding.secure
=item * privacy.donottrackheader.enabled
=back
In addition, this method will accept a hash of values as parameters as well. When a hash is provided, this method will alter specific parts of the normal Firefox User Agent. These hash parameters are;
=over 4
=item * os - The desired operating system, known values are "linux", "win32", "darwin", "freebsd", "netbsd", "openbsd" and "dragonfly"
=item * version - A specific version of firefox, such as 120.
=item * arch - A specific version of the architecture, such as "x86_64" or "aarch64" or "s390x".
=item * increment - A specific offset from the actual version of firefox, such as -5
=back
These parameters can be used to set a user agent string like so;
use Firefox::Marionette();
use strict;
my $firefox = Firefox::Marionette->new();
$firefox->agent(os => 'freebsd', version => 118);
# user agent is now equal to
# Mozilla/5.0 (X11; FreeBSD amd64; rv:109.0) Gecko/20100101 Firefox/118.0
$firefox->agent(os => 'linux', arch => 's390x', version => 115);
# user agent is now equal to
# Mozilla/5.0 (X11; Linux s390x; rv:109.0) Gecko/20100101 Firefox/115.0
If the C<stealth> parameter has supplied to the L<new|/new> method, it will also attempt to create known specific javascript functions to imitate the required browser. If the database built by L<build-bcd-for-firefox|https://metacpan.org/pod/build-b...
=over 4
=item * L<https://browserleaks.com/javascript>
=item * L<https://www.amiunique.org/fingerprint>
=item * L<https://bot.sannysoft.com/>
=item * L<https://lraj22.github.io/browserfeatcl/>
=back
Importantly, this will break L<feature detection|https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection> for any website that relies on it.
See L<IMITATING OTHER BROWSERS|/IMITATING-OTHER-BROWSERS> a discussion of these types of techniques. These changes are not foolproof, but it is interesting to see what can be done with modern browsers. All this behaviour should be regarded as extre...
=head2 alert_text
Returns the message shown in a currently displayed modal message box
=head2 alive
This method returns true or false depending on if the Firefox process is still running.
=head2 application_type
returns the application type for the Marionette protocol. Should be 'gecko'.
=head2 arch
returns the architecture of the machine running firefox. Should be something like 'x86_64' or 'arm'. This is only intended for test suite support.
=head2 aria_label
accepts an L<element|Firefox::Marionette::Element> as the parameter. It returns the L<ARIA label|https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label> for the L<element|Firefox::Marionette::Element>.
=head2 aria_role
accepts an L<element|Firefox::Marionette::Element> as the parameter. It returns the L<ARIA role|https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles> for the L<element|Firefox::Marionette::Element>.
=head2 async_script
accepts a scalar containing a javascript function that is executed in the browser. This method returns L<itself|Firefox::Marionette> to aid in chaining methods.
The executing javascript is subject to the L<script|Firefox::Marionette::Timeouts#script> timeout, which, by default is 30 seconds.
=head2 attribute
accepts an L<element|Firefox::Marionette::Element> as the first parameter and a scalar attribute name as the second parameter. It returns the initial value of the attribute with the supplied name. This method will return the initial content from th...
use Firefox::Marionette();
my $firefox = Firefox::Marionette->new()->go('https://metacpan.org/');
my $element = $firefox->find_id('metacpan_search-input');
!defined $element->attribute('value') or die "attribute is defined but did not exist in the html source!";
$element->type('Test::More');
!defined $element->attribute('value') or die "attribute has changed but only the property should have changed!";
=head2 await
accepts a subroutine reference as a parameter and then executes the subroutine. If a L<not found|Firefox::Marionette::Exception::NotFound> exception is thrown, this method will sleep for L<sleep_time_in_ms|/sleep_time_in_ms> milliseconds and then ex...
use Firefox::Marionette();
my $firefox = Firefox::Marionette->new(sleep_time_in_ms => 5)->go('https://metacpan.org/');
$firefox->find_id('metacpan_search-input')->type('Test::More');
$firefox->await(sub { $firefox->find_class('autocomplete-suggestion'); })->click();
=head2 back
causes the browser to traverse one step backward in the joint history of the current browsing context. The browser will wait for the one step backward to complete or the session's L<page_load|Firefox::Marionette::Timeouts#page_load> duration to elap...
=head2 debug
accept a boolean and return the current value of the debug setting. This allows the dynamic setting of debug.
=head2 default_binary_name
just returns the string 'firefox'. Only of interest when sub-classing.
( run in 4.390 seconds using v1.01-cache-2.11-cpan-14f38c9f855 )