Firefox-Marionette
view release on metacpan or search on metacpan
lib/Firefox/Marionette.pm view on Meta::CPAN
}
sub BY_ID {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_ID()) HAS BEEN REPLACED BY find_id ****'
);
return 'id';
}
sub BY_NAME {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_NAME()) HAS BEEN REPLACED BY find_name ****'
);
return 'name';
}
sub BY_TAG {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_TAG()) HAS BEEN REPLACED BY find_tag ****'
);
return 'tag name';
}
sub BY_CLASS {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_CLASS()) HAS BEEN REPLACED BY find_class ****'
);
return 'class name';
}
sub BY_SELECTOR {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_SELECTOR()) HAS BEEN REPLACED BY find_selector ****'
);
return 'css selector';
}
sub BY_LINK {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_LINK()) HAS BEEN REPLACED BY find_link ****'
);
return 'link text';
}
sub BY_PARTIAL {
Carp::carp(
'**** DEPRECATED METHOD - using find(..., BY_PARTIAL()) HAS BEEN REPLACED BY find_partial ****'
);
return 'partial link text';
}
sub languages {
my ( $self, @new_languages ) = @_;
my $pref_name = 'intl.accept_languages';
my $script =
'return navigator.languages || branch.getComplexValue(arguments[0], Components.interfaces.nsIPrefLocalizedString).data.split(/,\s*/)';
my $old = $self->_context('chrome');
my @old_languages = @{
$self->script(
$self->_compress_script(
$self->_prefs_interface_preamble() . $script
),
args => [$pref_name]
)
};
$self->_context($old);
if ( scalar @new_languages ) {
$self->set_pref( $pref_name, join q[, ], @new_languages );
}
return @old_languages;
}
sub _setup_trackable {
my ( $self, $trackable ) = @_;
my $value = $trackable ? 0 : 1;
$self->set_pref( 'privacy.fingerprintingProtection', $value );
$self->set_pref( 'privacy.fingerprintingProtection.pbmode', $value );
return $self;
}
sub _setup_geo {
my ( $self, $geo ) = @_;
$self->set_pref( 'geo.enabled', 1 );
$self->set_pref( 'geo.provider.use_geoclue', 0 );
$self->set_pref( 'geo.provider.use_corelocation', 0 );
$self->set_pref( 'geo.provider.testing', 1 );
$self->set_pref( 'geo.prompt.testing', 1 );
$self->set_pref( 'geo.prompt.testing.allow', 1 );
$self->set_pref( 'geo.security.allowinsecure', 1 );
$self->set_pref( 'geo.wifi.scan', 1 );
$self->set_pref( 'permissions.default.geo', 1 );
if ( ref $geo ) {
if ( ( Scalar::Util::blessed($geo) ) && ( $geo->isa('URI') ) ) {
$self->geo( $self->json($geo) );
}
else {
$self->geo($geo);
}
}
elsif ( $geo =~ /^(?:data|http)/smx ) {
$self->geo( $self->json($geo) );
}
return $self;
}
sub tz {
my ( $self, $timezone ) = @_;
require Firefox::Marionette::Extension::Timezone;
my %parameters = ( timezone => $timezone );
$self->script(
$self->_compress_script(
Firefox::Marionette::Extension::Timezone->timezone_contents(
%parameters)
)
);
if ( $self->{timezone_extension} ) {
$self->uninstall( delete $self->{timezone_extension} );
}
my $zip = Firefox::Marionette::Extension::Timezone->new(%parameters);
$self->{timezone_extension} =
lib/Firefox/Marionette.pm view on Meta::CPAN
}
sub geo {
my ( $self, @parameters ) = @_;
my $location;
if ( scalar @parameters ) {
$location = Firefox::Marionette::GeoLocation->new(@parameters);
}
if ( defined $location ) {
$self->set_pref( 'geo.provider.network.url',
q[data:application/json,]
. JSON->new()->convert_blessed()->encode($location) );
$self->set_pref( 'geo.wifi.uri',
q[data:application/json,]
. JSON->new()->convert_blessed()->encode($location) );
if ( my $ipgeolocation_timezone = $location->tz() ) {
$self->tz($ipgeolocation_timezone);
}
return $self;
}
if ( my $geo_location = $self->_get_geolocation() ) {
my $new_location = Firefox::Marionette::GeoLocation->new($geo_location);
return $new_location;
}
return;
}
sub _get_geolocation {
my ($self) = @_;
my $result = $self->script( $self->_compress_script(<<'_JS_') );
return (async function() {
function getGeo() {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(resolve, reject, { maximumAge: 0, enableHighAccuracy: true });
} else {
reject("navigator.geolocation is unavailable");
}
})
};
return await getGeo().then((response) => { let d = new Date(); return {
"timezone_offset": d.getTimezoneOffset(),
"latitude": response["coords"]["latitude"],
"longitude": response["coords"]["longitude"],
"altitude": response["coords"]["altitude"],
"accuracy": response["coords"]["accuracy"],
"altitudeAccuracy": response["coords"]["altitudeAccuracy"],
"heading": response["coords"]["heading"],
"speed": response["coords"]["speed"],
}; }).catch((err) => { throw err.message });
})();
_JS_
if ( ( defined $result ) && ( !ref $result ) ) {
Firefox::Marionette::Exception->throw("javascript error: $result");
}
return $result;
}
sub _prefs_interface_preamble {
my ($self) = @_;
return <<'_JS_'; # modules/libpref/nsIPrefService.idl
let prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
let branch = prefs.getBranch("");
_JS_
}
sub get_pref {
my ( $self, $name ) = @_;
my $script = <<'_JS_';
let result = [ null ];
switch (branch.getPrefType(arguments[0])) {
case branch.PREF_STRING:
result = [ branch.getStringPref ? branch.getStringPref(arguments[0]) : branch.getComplexValue(arguments[0], Components.interfaces.nsISupportsString).data, 'string' ];
break;
case branch.PREF_INT:
result = [ branch.getIntPref(arguments[0]), 'integer' ];
break;
case branch.PREF_BOOL:
result = [ branch.getBoolPref(arguments[0]), 'boolean' ];
}
return result;
_JS_
my $old = $self->_context('chrome');
my ( $result, $type ) = @{
$self->script(
$self->_compress_script(
$self->_prefs_interface_preamble() . $script
),
args => [$name]
)
};
$self->_context($old);
if ($type) {
if ( $type eq 'integer' ) {
$result += 0;
}
}
return $result;
}
sub set_pref {
my ( $self, $name, $value ) = @_;
my $script = <<'_JS_';
switch (branch.getPrefType(arguments[0])) {
case branch.PREF_INT:
branch.setIntPref(arguments[0], arguments[1]);
break;
case branch.PREF_BOOL:
branch.setBoolPref(arguments[0], arguments[1] ? true : false);
break;
case branch.PREF_STRING:
default:
if (branch.setStringPref) {
branch.setStringPref(arguments[0], arguments[1]);
} else {
let newString = Components.classes["@mozilla.org/supports-string;1"].createInstance(Components.interfaces.nsISupportsString);
newString.data = arguments[1];
branch.setComplexValue(arguments[0], Components.interfaces.nsISupportsString, newString);
}
}
_JS_
my $old = $self->_context('chrome');
$self->script(
$self->_compress_script( $self->_prefs_interface_preamble() . $script ),
args => [ $name, $value ]
);
$self->_context($old);
return $self;
}
sub _clear_data_service_interface_preamble {
my ($self) = @_;
return <<'_JS_'; # toolkit/components/cleardata/nsIClearDataService.idl
let clearDataService = Components.classes["@mozilla.org/clear-data-service;1"].getService(Components.interfaces.nsIClearDataService);
_JS_
}
sub cache_keys {
my ($self) = @_;
my @names;
foreach my $name (@Firefox::Marionette::Cache::EXPORT_OK) {
if ( defined $self->check_cache_key($name) ) {
push @names, $name;
}
}
return @names;
}
sub check_cache_key {
my ( $self, $name ) = @_;
my $class = ref $self;
defined $name
or Firefox::Marionette::Exception->throw(
"$class->check_cache_value() must be passed an argument.");
$name =~ /^[[:upper:]_]+$/smx
or Firefox::Marionette::Exception->throw(
"$class->check_cache_key() must be passed an argument consisting of uppercase characters and underscores."
);
my $script = <<"_JS_";
if (typeof clearDataService.$name === undefined) {
return;
} else {
return clearDataService.$name;
}
_JS_
my $old = $self->_context('chrome');
my $result = $self->script(
$self->_compress_script(
$self->_clear_data_service_interface_preamble() . $script
)
);
$self->_context($old);
return $result;
}
sub clear_cache {
my ( $self, $flags ) = @_;
$flags = defined $flags ? $flags : Firefox::Marionette::Cache::CLEAR_ALL();
my $script = <<'_JS_';
let argument_flags = arguments[0];
let clearCache = function(flags) {
return new Promise((resolve) => {
clearDataService.deleteData(flags, function() { resolve(); });
})};
let result = (async function() {
let awaitResult = await clearCache(argument_flags);
return awaitResult;
})();
return arguments[0];
_JS_
my $old = $self->_context('chrome');
my $result = $self->script(
$self->_compress_script(
$self->_clear_data_service_interface_preamble() . $script
),
args => [$flags]
);
$self->_context($old);
return $self;
}
sub clear_pref {
my ( $self, $name ) = @_;
my $script = <<'_JS_';
branch.clearUserPref(arguments[0]);
_JS_
my $old = $self->_context('chrome');
$self->script(
$self->_compress_script( $self->_prefs_interface_preamble() . $script ),
args => [$name]
);
$self->_context($old);
return $self;
}
sub _is_chrome_user_agent {
my ( $self, $user_agent ) = @_;
if ( $user_agent =~ /Chrome/smx ) {
return 1;
}
return;
}
sub _is_safari_user_agent {
my ( $self, $user_agent ) = @_;
if ( $user_agent =~ /Safari/smx ) {
return 1;
}
return;
}
sub _is_safari_and_iphone_user_agent {
my ( $self, $user_agent ) = @_;
if ( $user_agent =~ /iPhone/smx ) {
return 1;
}
return;
}
sub _is_trident_user_agent {
my ( $self, $user_agent ) = @_;
if ( $user_agent =~ /Trident/smx ) {
return 1;
}
return;
}
sub _parse_user_agent {
my ( $self, $user_agent ) = @_;
my ( $app_version, $platform, $product, $product_sub, $vendor, $vendor_sub,
$oscpu );
# https://developer.mozilla.org/en-US/docs/Web/API/Navigator/userAgent#value
if ( !defined $user_agent ) {
$user_agent = $self->_original_agent();
}
if (
$user_agent =~ m{^
[^\/]+\/ # appCodeName
((5[.]0[ ][(][^; ]+)[^;]*;[ ] # appVersion
([^;)]+)[;)] # platform
.*)
$}smx
)
{
( my $webkit_app, $app_version, $platform ) = ( $1, $2, $3 );
$app_version .= q[)];
( $vendor, $vendor_sub, $oscpu ) = ( q[], q[], $platform );
$product = 'Gecko';
lib/Firefox/Marionette.pm view on Meta::CPAN
};
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");
POSSIBLE_REMOTE_PROXY:
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 =~ /^($proxy_name_regex)$/smx ) {
my ($untainted_entry) = ($1);
my $ssh_local_directory =
File::Spec->catfile( $temp_directory, $untainted_entry );
if (
my $proxy = $self->_matching_remote_proxy(
$ssh_local_directory, $search_local_proxy
)
)
{
$self->{_ssh} = {
port => $port,
host => $host,
user => $user,
pid => $proxy->{ssh}->{pid},
};
if ( ( defined $proxy->{firefox} )
&& ( defined $proxy->{firefox}->{pid} ) )
{
$self->{_firefox_pid} = $proxy->{firefox}->{pid};
}
if ( ( defined $proxy->{xvfb} )
&& ( defined $proxy->{xvfb}->{pid} ) )
{
$self->{_xvfb_pid} = $proxy->{xvfb}->{pid};
}
if ( ( $OSNAME eq 'MSWin32' ) || ( $OSNAME eq 'cygwin' ) ) {
$self->{_ssh}->{use_control_path} = 0;
$self->{_ssh}->{use_unix_sockets} = 0;
}
else {
$self->{_ssh}->{use_control_path} = 1;
$self->{_ssh}->{use_unix_sockets} = 1;
$self->{_ssh}->{control_path} =
File::Spec->catfile( $ssh_local_directory,
'control.sock' );
}
$self->{_remote_uname} = $proxy->{ssh}->{uname};
$self->{marionette_binary} = $proxy->{ssh}->{binary};
$self->{_initial_version} = $proxy->{firefox}->{version};
$self->_initialise_version();
$self->{_ssh_local_directory} = $ssh_local_directory;
$self->{_root_directory} = $proxy->{ssh}->{root};
$self->{_remote_root_directory} = $proxy->{ssh}->{root};
if ( defined $proxy->{ssh}->{tmp} ) {
$self->{_original_remote_tmp_directory} =
$proxy->{ssh}->{tmp};
}
$self->{profile_path} =
$self->_remote_catfile( $self->{_root_directory},
'profile', 'prefs.js' );
my $local_scp_directory =
File::Spec->catdir( $self->ssh_local_directory(), 'scp' );
$self->{_local_scp_get_directory} =
File::Spec->catdir( $local_scp_directory, 'get' );
$self->{_scp_get_file_index} =
$self->_get_max_scp_file_index(
$self->{_local_scp_get_directory} );
$self->{_local_scp_put_directory} =
File::Spec->catdir( $local_scp_directory, 'put' );
$self->{_scp_put_file_index} =
$self->_get_max_scp_file_index(
$self->{_local_scp_put_directory} );
last POSSIBLE_REMOTE_PROXY;
}
}
}
closedir $temp_handle
or Firefox::Marionette::Exception->throw(
"Failed to close directory '$temp_directory':$EXTENDED_OS_ERROR");
if ( $self->_ssh() ) {
}
else {
Firefox::Marionette::Exception->throw(
"Failed to detect existing local ssh tunnel to $user\@$host");
}
return;
}
sub ssh_local_directory {
my ($self) = @_;
return $self->{_ssh_local_directory};
}
sub _setup_ssh {
my ( $self, $host, $port, $user, $reconnect ) = @_;
if ($reconnect) {
$self->_setup_ssh_with_reconnect( $host, $port, $user );
}
else {
my $ssh_local_directory = File::Temp->newdir(
CLEANUP => 0,
TEMPLATE => File::Spec->catdir(
File::Spec->tmpdir(), 'perl_ff_m_XXXXXXXXXXX'
)
)
or Firefox::Marionette::Exception->throw(
"Failed to create temporary directory:$EXTENDED_OS_ERROR");
$self->{_ssh_local_directory} = $ssh_local_directory->dirname();
my $local_scp_directory =
File::Spec->catdir( $self->ssh_local_directory(), 'scp' );
mkdir $local_scp_directory, Fcntl::S_IRWXU()
or Firefox::Marionette::Exception->throw(
"Failed to create directory $local_scp_directory:$EXTENDED_OS_ERROR"
);
$self->{_local_scp_get_directory} =
File::Spec->catdir( $local_scp_directory, 'get' );
mkdir $self->{_local_scp_get_directory}, Fcntl::S_IRWXU()
or Firefox::Marionette::Exception->throw(
"Failed to create directory $self->{_local_scp_get_directory}:$EXTENDED_OS_ERROR"
lib/Firefox/Marionette.pm view on Meta::CPAN
my ( $self, %parameters ) = @_;
if ( defined $parameters{har} ) {
$self->{_har} = $parameters{har};
require Firefox::Marionette::Extension::HarExportTrigger;
}
if ( $parameters{stealth} ) {
$self->{stealth} = 1;
require Firefox::Marionette::Extension::Stealth;
}
return;
}
sub _determine_mime_types {
my ( $self, %parameters ) = @_;
$self->{mime_types} = [
qw(
application/x-gzip
application/gzip
application/zip
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;
$host = 'localhost';
}
my $quoted_user = defined $user ? quotemeta $user : q[];
if ( $self->_ssh() ) {
$self->_initialise_remote_uname();
}
$self->_check_visible(%parameters);
my $port = $self->_get_marionette_port();
defined $port
or Firefox::Marionette::Exception->throw(
"Existing firefox process could not be found at $user\@$host");
my $socket;
socket $socket,
$self->_using_unix_sockets_for_ssh_connection()
? Socket::PF_UNIX()
: Socket::PF_INET(), Socket::SOCK_STREAM(), 0
or Firefox::Marionette::Exception->throw(
lib/Firefox/Marionette.pm view on Meta::CPAN
my $socket = $self->_setup_local_connection_to_firefox(@arguments);
my $session_id;
( $session_id, $capabilities ) =
$self->_initial_socket_setup( $socket, $capabilities );
$self->_check_protocol_version_and_pid( $session_id, $capabilities );
$self->_post_launch_checks_and_setup($timeouts);
return $self;
}
sub _reset_marionette_port {
my ($self) = @_;
my $handle;
if ( $self->_ssh() ) {
$handle =
$self->_get_file_via_scp( {}, $self->{profile_path}, 'profile path' );
}
else {
$handle = FileHandle->new( $self->{profile_path}, Fcntl::O_RDONLY() )
or Firefox::Marionette::Exception->throw(
"Failed to open '$self->{profile_path}' for reading:$EXTENDED_OS_ERROR"
);
}
my $profile = Firefox::Marionette::Profile->parse_by_handle($handle);
close $handle
or Firefox::Marionette::Exception->throw(
"Failed to close '$self->{profile_path}':$EXTENDED_OS_ERROR");
if ( $self->_is_auto_listen_okay() ) {
$profile->set_value( 'marionette.port',
Firefox::Marionette::Profile::ANY_PORT() );
}
else {
my $port = $self->_get_empty_port();
$profile->set_value( 'marionette.defaultPrefs.port', $port );
$profile->set_value( 'marionette.port', $port );
}
if ( $self->_ssh() ) {
$self->_save_profile_via_ssh($profile);
}
else {
$profile->save( $self->{profile_path} );
}
return;
}
sub update {
my ( $self, $update_timeout ) = @_;
my $timeouts = $self->timeouts();
my $script_timeout = $timeouts->script();
my $update_timeouts = Firefox::Marionette::Timeouts->new(
script => ( $update_timeout || _DEFAULT_UPDATE_TIMEOUT() ) *
_MILLISECONDS_IN_ONE_SECOND(),
implicit => $timeouts->implicit(),
page_load => $timeouts->page_load()
);
$self->timeouts($update_timeouts);
my $old = $self->_context('chrome');
# toolkit/mozapps/update/nsIUpdateService.idl
my $update_parameters = $self->script(
$self->_compress_script(
$self->_prefs_interface_preamble() . <<'_JS_' ) );
let disabledForTesting = branch.getBoolPref("app.update.disabledForTesting");
branch.setBoolPref("app.update.disabledForTesting", false);
let updateManager = new Promise((resolve, reject) => {
var updateStatus = {};
if ("@mozilla.org/updates/update-manager;1" in Components.classes) {
let PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
let PREF_APP_UPDATE_ELEVATE_NEVER = "app.update.elevate.never";
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
}
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
}
let updateService = Components.classes["@mozilla.org/updates/update-service;1"].getService(Components.interfaces.nsIApplicationUpdateService);
let latestUpdate = null;
if (!updateService.canCheckForUpdates) {
updateStatus["updateStatusCode"] = 'CANNOT_CHECK_FOR_UPDATES';
reject(updateStatus);
}
if (!updateService.canApplyUpdates) {
updateStatus["updateStatusCode"] = 'CANNOT_APPLY_UPDATES';
reject(updateStatus);
}
if (updateService.canUsuallyStageUpdates) {
if (!updateService.canStageUpdates) {
updateStatus["updateStatusCode"] = 'CANNOT_STAGE_UPDATES';
reject(updateStatus);
}
}
if ((updateService.isOtherInstanceHandlingUpdates) && (updateService.isOtherInstanceHandlingUpdates())) {
updateStatus["updateStatusCode"] = 'ANOTHER_INSTANCE_IS_HANDLING_UPDATES';
reject(updateStatus);
}
let updateChecker = Components.classes["@mozilla.org/updates/update-checker;1"].createInstance(Components.interfaces.nsIUpdateChecker);
if (updateChecker.stopCurrentCheck) {
updateChecker.stopCurrentCheck();
}
let updateServiceListener = {
onCheckComplete: (request, updates) => {
latestUpdate = updateService.selectUpdate(updates, true);
updateStatus["numberOfUpdates"] = updates.length;
if (latestUpdate === null) {
updateStatus["updateStatusCode"] = 'NO_UPDATES_AVAILABLE';
reject(updateStatus);
} else {
for (key in latestUpdate) {
if (typeof latestUpdate[key] !== 'function') {
updateStatus[key] = latestUpdate[key];
}
}
let result = updateService.downloadUpdate(latestUpdate, false);
let updateProcessor = Components.classes["@mozilla.org/updates/update-processor;1"].createInstance(Components.interfaces.nsIUpdateProcessor);
if (updateProcessor.fixUpdateDirectoryPermissions) {
updateProcessor.fixUpdateDirectoryPermissions(true);
}
updateProcessor.processUpdate(latestUpdate);
let previousState = null;
function nowPending() {
if ((latestUpdate.state) && ((previousState == null) || (previousState != latestUpdate.state))) {
console.log("Update status is now " + latestUpdate.state);
}
previousState = latestUpdate.state;
updateStatus["state"] = latestUpdate.state;
updateStatus["statusText"] = latestUpdate.statusText;
if ((latestUpdate.state == 'pending') || (latestUpdate.state == 'pending-service')) {
updateStatus["updateStatusCode"] = 'PENDING_UPDATE';
resolve(updateStatus);
} else {
setTimeout(function() { nowPending() }, 500);
}
}
lib/Firefox/Marionette.pm view on Meta::CPAN
my $profile_directory = $self->{_profile_directory};
if ( $self->_ssh() ) {
if ( $self->_remote_uname() eq 'cygwin' ) {
$profile_directory =
$self->_execute_via_ssh( {}, 'cygpath', '-s', '-m',
$profile_directory );
chomp $profile_directory;
}
}
elsif ( $OSNAME eq 'cygwin' ) {
$profile_directory =
$self->execute( 'cygpath', '-s', '-m', $profile_directory );
}
return $profile_directory;
}
sub _get_remote_profile_directory {
my ( $self, $profile_name ) = @_;
my $profile_directory;
if ( ( $self->_remote_uname() eq 'cygwin' )
|| ( $self->_remote_uname() eq 'MSWin32' ) )
{
my $appdata_directory =
$self->_get_remote_environment_variable_via_ssh('APPDATA');
if ( $self->_remote_uname() eq 'cygwin' ) {
$appdata_directory =~ s/\\/\//smxg;
$appdata_directory =
$self->_execute_via_ssh( {}, 'cygpath', '-u',
$appdata_directory );
chomp $appdata_directory;
}
my $profile_ini_directory =
$self->_remote_catfile( $appdata_directory, 'Mozilla', 'Firefox' );
my $profile_ini_path =
$self->_remote_catfile( $profile_ini_directory, 'profiles.ini' );
my $handle = $self->_get_file_via_scp( {}, $profile_ini_path,
'profiles.ini file' );
my $config = Config::INI::Reader->read_handle($handle);
$profile_directory = $self->_remote_catfile(
Firefox::Marionette::Profile->directory(
$profile_name, $config, $profile_ini_directory
)
);
}
else {
my $profile_ini_directory;
if ( $self->_remote_uname() eq 'darwin' ) {
$profile_ini_directory = $self->_remote_catfile( 'Library',
'Application Support', 'Firefox' );
}
else {
$profile_ini_directory =
$self->_remote_catfile( '.mozilla', 'firefox' );
}
my $profile_ini_path =
$self->_remote_catfile( $profile_ini_directory, 'profiles.ini' );
my $handle = $self->_get_file_via_scp( { ignore_exit_status => 1 },
$profile_ini_path, 'profiles.ini file' )
or Firefox::Marionette::Exception->throw( 'Failed to find the file '
. $self->_ssh_address()
. ":$profile_ini_path which would indicate where the prefs.js file for the '$profile_name' is stored"
);
my $config = Config::INI::Reader->read_handle($handle);
$profile_directory = $self->_remote_catfile(
Firefox::Marionette::Profile->directory(
$profile_name, $config,
$profile_ini_directory, $self->_ssh_address()
)
);
}
return $profile_directory;
}
sub _setup_arguments {
my ( $self, %parameters ) = @_;
my @arguments = qw(-marionette);
if ( ( defined $self->{debug} ) && ( $self->{debug} !~ /^[01]$/smx ) ) {
push @arguments, '-MOZ_LOG=' . $self->{debug};
}
if ( $self->{system_access} ) {
push @arguments, '-remote-allow-system-access';
}
if ( defined $self->{window_width} ) {
push @arguments, '-width', $self->{window_width};
}
if ( defined $self->{window_height} ) {
push @arguments, '-height', $self->{window_height};
}
if ( defined $self->{console} ) {
push @arguments, '--jsconsole';
}
push @arguments, $self->_check_addons(%parameters);
push @arguments, $self->_check_visible(%parameters);
push @arguments, $self->_profile_arguments(%parameters);
if ( ( $self->{_har} ) || ( $parameters{devtools} ) ) {
push @arguments, '--devtools';
}
if ( $parameters{kiosk} ) {
push @arguments, '--kiosk';
}
return @arguments;
}
sub _profile_arguments {
my ( $self, %parameters ) = @_;
my @arguments;
if ( $parameters{restart} ) {
push @arguments,
(
'-profile', $self->_restart_profile_directory(),
'--no-remote', '--new-instance'
);
}
elsif ( $parameters{profile_name} ) {
$self->{profile_name} = $parameters{profile_name};
if ( $self->_ssh() ) {
$self->{_profile_directory} =
$self->_get_remote_profile_directory( $parameters{profile_name} );
$self->{profile_path} =
$self->_remote_catfile( $self->{_profile_directory}, 'prefs.js' );
}
else {
$self->{_profile_directory} =
Firefox::Marionette::Profile->directory(
$parameters{profile_name} );
$self->{profile_path} =
File::Spec->catfile( $self->{_profile_directory}, 'prefs.js' );
}
push @arguments, ( '-P', $self->{profile_name} );
}
else {
my $profile_directory =
$self->_setup_new_profile( $parameters{profile}, %parameters );
if ( $self->_ssh() ) {
if ( $self->_remote_uname() eq 'cygwin' ) {
$profile_directory =
$self->_execute_via_ssh( {}, 'cygpath', '-s', '-m',
$profile_directory );
chomp $profile_directory;
}
}
elsif ( $OSNAME eq 'cygwin' ) {
$profile_directory =
$self->execute( 'cygpath', '-s', '-m', $profile_directory );
}
my $mime_types_content = $self->_mime_types_content();
if ( $self->_ssh() ) {
$self->_write_mime_types_via_ssh($mime_types_content);
}
else {
my $path =
File::Spec->catfile( $profile_directory, 'mimeTypes.rdf' );
my $handle = FileHandle->new(
$path,
Fcntl::O_WRONLY() | Fcntl::O_CREAT() | Fcntl::O_EXCL(),
Fcntl::S_IRUSR() | Fcntl::S_IWUSR()
)
or Firefox::Marionette::Exception->throw(
"Failed to open '$path' for writing:$EXTENDED_OS_ERROR");
print {$handle} $mime_types_content
or Firefox::Marionette::Exception->throw(
"Failed to write to '$path':$EXTENDED_OS_ERROR");
close $handle
or Firefox::Marionette::Exception->throw(
"Failed to close '$path':$EXTENDED_OS_ERROR");
}
push @arguments,
( '-profile', $profile_directory, '--no-remote', '--new-instance' );
}
return @arguments;
}
sub _mime_types_content {
my ($self) = @_;
my $mime_types_content = <<'_RDF_';
<?xml version="1.0"?>
<RDF:RDF xmlns:NC="http://home.netscape.com/NC-rdf#"
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<RDF:Seq RDF:about="urn:mimetypes:root">
_RDF_
foreach my $mime_type ( @{ $self->{mime_types} } ) {
$mime_types_content .= <<'_RDF_';
<RDF:li RDF:resource="urn:mimetype:$mime_type"/>
_RDF_
}
$mime_types_content .= <<'_RDF_';
</RDF:Seq>
lib/Firefox/Marionette.pm view on Meta::CPAN
}
if ( defined $self->{_firefox_pid} ) {
$local_proxy->{firefox}->{pid} = $self->{_firefox_pid};
$local_proxy->{firefox}->{binary} = $self->_binary();
$local_proxy->{firefox}->{version} = $self->{_initial_version};
}
print {$local_proxy_handle} JSON::encode_json($local_proxy)
or Firefox::Marionette::Exception->throw(
"Failed to write to $local_proxy_path:$EXTENDED_OS_ERROR");
close $local_proxy_handle
or Firefox::Marionette::Exception->throw(
"Failed to close '$local_proxy_path':$EXTENDED_OS_ERROR");
return;
}
sub _setup_profile_directories {
my ( $self, $profile ) = @_;
if ( ($profile) && ( $profile->download_directory() ) ) {
if ( $self->_ssh() ) {
$self->{_root_directory} = $self->_get_remote_root_directory();
}
}
elsif ( my $ssh = $self->_ssh() ) {
$self->{_root_directory} = $self->_get_remote_root_directory();
$self->_write_local_proxy($ssh);
$self->{_profile_directory} = $self->_make_remote_directory(
$self->_remote_catfile( $self->{_root_directory}, 'profile' ) );
$self->{_download_directory} = $self->_make_remote_directory(
$self->_remote_catfile( $self->{_root_directory}, 'downloads' ) );
$self->{_remote_tmp_directory} = $self->_make_remote_directory(
$self->_remote_catfile( $self->{_root_directory}, 'tmp' ) );
}
else {
my $root_directory = $self->_root_directory();
my $profile_directory =
File::Spec->catdir( $root_directory, 'profile' );
mkdir $profile_directory, Fcntl::S_IRWXU()
or Firefox::Marionette::Exception->throw(
"Failed to create directory $profile_directory:$EXTENDED_OS_ERROR");
$self->{_profile_directory} = $profile_directory;
my $download_directory =
File::Spec->catdir( $root_directory, 'downloads' );
mkdir $download_directory, Fcntl::S_IRWXU()
or Firefox::Marionette::Exception->throw(
"Failed to create directory $download_directory:$EXTENDED_OS_ERROR"
);
$self->{_download_directory} = $download_directory;
my $tmp_directory = $self->_local_firefox_tmp_directory();
mkdir $tmp_directory, Fcntl::S_IRWXU()
or Firefox::Marionette::Exception->throw(
"Failed to create directory $tmp_directory:$EXTENDED_OS_ERROR");
}
return;
}
sub _new_profile_path {
my ($self) = @_;
my $profile_path;
if ( $self->_ssh() ) {
$profile_path =
$self->_remote_catfile( $self->{_profile_directory}, 'prefs.js' );
}
else {
$profile_path =
File::Spec->catfile( $self->{_profile_directory}, 'prefs.js' );
}
return $profile_path;
}
sub _setup_new_profile {
my ( $self, $profile, %parameters ) = @_;
$self->_setup_profile_directories($profile);
$self->{profile_path} = $self->_new_profile_path();
if ($profile) {
if ( !$profile->download_directory() ) {
my $download_directory = $self->{_download_directory};
if ( $self->_ssh() ) {
if ( $self->_remote_uname() eq 'cygwin' ) {
$download_directory =
$self->_execute_via_ssh( {}, 'cygpath', '-s', '-w',
$download_directory );
chomp $download_directory;
}
}
elsif ( $OSNAME eq 'cygwin' ) {
$download_directory =
$self->execute( 'cygpath', '-s', '-w', $download_directory );
}
$profile->download_directory($download_directory);
}
}
else {
my %profile_parameters = ();
foreach my $profile_key (qw(chatty seer nightly)) {
if ( $parameters{$profile_key} ) {
$profile_parameters{$profile_key} = 1;
}
}
if ( $self->{waterfox} ) {
$profile = Waterfox::Marionette::Profile->new(%profile_parameters);
}
else {
$profile = Firefox::Marionette::Profile->new(%profile_parameters);
}
my $download_directory = $self->{_download_directory};
my $bookmarks_path = $self->_setup_empty_bookmarks();
$self->_setup_search_json_mozlz4();
if ( ( $self->_remote_uname() )
&& ( $self->_remote_uname() eq 'cygwin' ) )
{
$download_directory =
$self->_execute_via_ssh( {}, 'cygpath', '-s', '-w',
$download_directory );
chomp $download_directory;
}
$profile->download_directory($download_directory);
$profile->set_value( 'browser.bookmarks.file', $bookmarks_path, 1 );
if (
!$self->_is_firefox_major_version_at_least(
_MIN_VERSION_FOR_LINUX_SANDBOX()
)
)
{
$profile->set_value( 'security.sandbox.content.level', 0, 0 )
; # https://wiki.mozilla.org/Security/Sandbox#Customization_Settings
( run in 2.466 seconds using v1.01-cache-2.11-cpan-5fbc6bb55f2 )