This commit is contained in:
2024-10-14 00:08:40 +02:00
parent dbfba56f66
commit 1462d52e13
4572 changed files with 2658864 additions and 0 deletions

View File

@@ -0,0 +1,220 @@
package Selenium::Firefox::Binary;
$Selenium::Firefox::Binary::VERSION = '1.33';
use strict;
use warnings;
# ABSTRACT: Subroutines for locating and properly initializing the Firefox Binary
use File::Which qw/which/;
use Selenium::Firefox::Profile;
require Exporter;
our @ISA = qw/Exporter/;
our @EXPORT_OK = qw/firefox_path setup_firefox_binary_env/;
sub _firefox_windows_path {
# TODO: make this slightly less dumb
my @program_files = (
$ENV{PROGRAMFILES} // 'C:\Program Files',
$ENV{'PROGRAMFILES(X86)'} // 'C:\Program Files (x86)',
);
foreach (@program_files) {
my $binary_path = $_ . '\Mozilla Firefox\firefox.exe';
return $binary_path if -x $binary_path;
}
# Fall back to a completely naive strategy
warn
q/We couldn't find a viable firefox.EXE; you may want to specify it via the binary attribute./;
return which('firefox');
}
sub _firefox_darwin_path {
my $default_firefox =
'/Applications/Firefox.app/Contents/MacOS/firefox-bin';
if ( -e $default_firefox && -x $default_firefox ) {
return $default_firefox;
}
else {
return which('firefox-bin');
}
}
sub _firefox_unix_path {
# TODO: maybe which('firefox3'), which('firefox2') ?
return which('firefox') || '/usr/bin/firefox';
}
sub firefox_path {
my $path;
if ( $^O eq 'MSWin32' ) {
$path = _firefox_windows_path();
}
elsif ( $^O eq 'darwin' ) {
$path = _firefox_darwin_path();
}
else {
$path = _firefox_unix_path;
}
if ( not -x $path ) {
die $path . ' is not an executable file.';
}
return $path;
}
# We want the profile to persist to the end of the session, not just
# the end of this function.
my $profile;
sub setup_firefox_binary_env {
my ( $port, $marionette_port, $caller_profile ) = @_;
$profile = $caller_profile || Selenium::Firefox::Profile->new;
$profile->add_webdriver( $port, $marionette_port );
$profile->add_marionette($marionette_port);
# For non-geckodriver/marionette startup, we instruct Firefox to
# use the profile by specifying the appropriate environment
# variables for it to hook onto.
if ( !$marionette_port ) {
$ENV{'XRE_PROFILE_PATH'} = $profile->_layout_on_disk;
$ENV{'MOZ_NO_REMOTE'} = '1'; # able to launch multiple instances
$ENV{'MOZ_CRASHREPORTER_DISABLE'} = '1'; # disable breakpad
$ENV{'NO_EM_RESTART'} =
'1'; # prevent the binary from detaching from the console.log
}
else {
# In case the user created an old Firefox, which would've set
# those ENV variables, and then wanted to create a new Firefox
# afterwards, the env variables would still be around, and the
# new Firefox would respect the XRE_PROFILE_PATH and try to
# load it in the new geckodriver Firefox, which would cause an
# extension compatibility check
my @env_vars = qw/
XRE_PROFILE_PATH
MOZ_NO_REMOTE
MOZ_CRASHREPORTER_DISABLE
NO_EM_RESTART
/;
foreach (@env_vars) {
delete $ENV{$_};
}
}
return $profile;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Selenium::Firefox::Binary - Subroutines for locating and properly initializing the Firefox Binary
=head1 VERSION
version 1.33
=head1 SUBROUTINES
=head2 firefox_path
Return the path to the firefox binary on your system.
=head2 setup_firefox_binary_env
Sets various environment variables to make firefox work correctly with webDriver.
=head1 SEE ALSO
Please see those modules/websites for more information related to this module.
=over 4
=item *
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
=back
=head1 BUGS
Please report any bugs or feature requests on the bugtracker website
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.
=head1 AUTHORS
Current Maintainers:
=over 4
=item *
Daniel Gempesaw <gempesaw@gmail.com>
=item *
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
=back
Previous maintainers:
=over 4
=item *
Luke Closs <cpan@5thplane.com>
=item *
Mark Stosberg <mark@stosberg.com>
=back
Original authors:
=over 4
=item *
Aditya Ivaturi <ivaturi@gmail.com>
=back
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
Copyright (c) 2014-2017 Daniel Gempesaw
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=cut

View File

@@ -0,0 +1,470 @@
package Selenium::Firefox::Profile;
$Selenium::Firefox::Profile::VERSION = '1.33';
# ABSTRACT: Use custom profiles with Selenium::Remote::Driver
# TODO: convert this to Moo!
use strict;
use warnings;
use Archive::Zip qw( :ERROR_CODES );
use Carp qw(croak);
use Cwd qw(abs_path);
use File::Copy qw(copy);
use File::Temp;
use File::Basename qw(dirname);
use IO::Uncompress::Unzip 2.030 qw($UnzipError);
use JSON qw(decode_json);
use MIME::Base64;
use Scalar::Util qw(blessed looks_like_number);
use XML::Simple;
sub new {
my $class = shift;
my %args = @_;
my $profile_dir;
if ( $args{profile_dir} && -d $args{profile_dir} ) {
$profile_dir = $args{profile_dir};
}
else {
$profile_dir = File::Temp->newdir();
}
# TODO: accept user prefs, boolean prefs, and extensions in
# constructor
my $self = {
profile_dir => $profile_dir,
user_prefs => {},
extensions => []
};
bless $self, $class or die "Can't bless $class: $!";
return $self;
}
sub set_preference {
my ( $self, %prefs ) = @_;
foreach ( keys %prefs ) {
my $value = $prefs{$_};
my $clean_value = '';
if ( JSON::is_bool($value) ) {
$self->set_boolean_preference( $_, $value );
next;
}
elsif ( $value =~ /^(['"]).*\1$/ or looks_like_number($value) ) {
# plain integers: 0, 1, 32768, or integers wrapped in strings:
# "0", "1", "20140204". in either case, there's nothing for us
# to do.
$clean_value = $value;
}
else {
# otherwise it's hopefully a string that we'll need to
# quote on our own
$clean_value = '"' . $value . '"';
}
$self->{user_prefs}->{$_} = $clean_value;
}
}
sub set_boolean_preference {
my ( $self, %prefs ) = @_;
foreach ( keys %prefs ) {
my $value = $prefs{$_};
$self->{user_prefs}->{$_} = $value ? 'true' : 'false';
}
}
sub get_preference {
my ( $self, $pref ) = @_;
return $self->{user_prefs}->{$pref};
}
sub add_extension {
my ( $self, $xpi ) = @_;
croak 'File not found: ' . $xpi unless -e $xpi;
my $xpi_abs_path = abs_path($xpi);
croak '$xpi_abs_path: extensions must be in .xpi format'
unless $xpi_abs_path =~ /\.xpi$/;
push( @{ $self->{extensions} }, $xpi_abs_path );
}
sub add_webdriver {
my ( $self, $port, $is_marionette ) = @_;
my $prefs = $self->_load_prefs;
my $current_user_prefs = $self->{user_prefs};
$self->set_preference(
%{ $prefs->{mutable} },
# having the user prefs here allows them to overwrite the
# mutable loaded prefs
%{$current_user_prefs},
# but the frozen ones cannot be overwritten
%{ $prefs->{frozen} },
'webdriver_firefox_port' => $port
);
if ( !$is_marionette ) {
$self->_add_webdriver_xpi;
}
return $self;
}
sub _load_prefs {
# The appropriate webdriver preferences are stored in an adjacent
# JSON file; it's useful things like disabling default browser
# checks and setting an empty single page as the start up tab
# configuration. Unfortunately, these change with each version of
# webdriver.
my $this_dir = dirname( abs_path(__FILE__) );
my $default_prefs_filename = $this_dir . '/webdriver_prefs.json';
my $json;
{
local $/;
open( my $fh, '<', $default_prefs_filename );
$json = <$fh>;
close($fh);
}
my $prefs = decode_json($json);
return $prefs;
}
sub _add_webdriver_xpi {
my ($self) = @_;
my $this_dir = dirname( abs_path(__FILE__) );
my $webdriver_extension = $this_dir . '/webdriver.xpi';
$self->add_extension($webdriver_extension);
}
sub add_marionette {
my ( $self, $port ) = @_;
return if !$port;
$self->set_preference( 'marionette.defaultPrefs.port', $port );
}
sub _encode {
my $self = shift;
# The remote webdriver accepts the Firefox profile as a base64
# encoded zip file
$self->_layout_on_disk();
my $zip = Archive::Zip->new();
$zip->addTree( $self->{profile_dir} );
my $string = "";
open( my $fh, ">", \$string );
binmode($fh);
unless ( $zip->writeToFileHandle($fh) == AZ_OK ) {
die 'write error';
}
return encode_base64( $string, '' );
}
sub _layout_on_disk {
my $self = shift;
$self->_write_preferences();
$self->_install_extensions();
return $self->{profile_dir};
}
sub _write_preferences {
my $self = shift;
my $userjs = $self->{profile_dir} . "/user.js";
open( my $fh, ">>", $userjs )
or die "Cannot open $userjs for writing preferences: $!";
foreach ( keys %{ $self->{user_prefs} } ) {
print $fh 'user_pref("'
. $_ . '", '
. $self->get_preference($_) . ');' . "\n";
}
close($fh);
}
sub _install_extensions {
my $self = shift;
my $extension_dir = $self->{profile_dir} . "/extensions/";
mkdir $extension_dir unless -d $extension_dir;
# TODO: handle extensions that need to be unpacked
foreach my $xpi ( @{ $self->{extensions} } ) {
# For Firefox to recognize the extension, we have to put the
# .xpi in the /extensions/ folder and change the filename to
# its id, which is found in the install.rdf in the root of the
# zip.
my $rdf_string = $self->_extract_install_rdf($xpi);
my $rdf = XMLin($rdf_string);
my $name = $rdf->{Description}->{'em:id'};
my $xpi_dest = $extension_dir . $name . ".xpi";
copy( $xpi, $xpi_dest )
or croak "Error copying $_ to $xpi_dest : $!";
}
}
sub _extract_install_rdf {
my ( $self, $xpi ) = @_;
my $unzipped = IO::Uncompress::Unzip->new($xpi)
or die "Cannot unzip $xpi: $UnzipError";
my $install_rdf = '';
while ( $unzipped->nextStream ) {
my $filename = $unzipped->getHeaderInfo->{Name};
if ( $filename eq 'install.rdf' ) {
my $buffer;
while ( ( my $status = $unzipped->read($buffer) ) > 0 ) {
$install_rdf .= $buffer;
}
return $install_rdf;
}
}
croak
'Invalid Firefox extension: could not find install.rdf in the .XPI at: '
. $xpi;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Selenium::Firefox::Profile - Use custom profiles with Selenium::Remote::Driver
=head1 VERSION
version 1.33
=head1 DESCRIPTION
You can use this module to create a custom Firefox Profile for your
Selenium tests. Currently, you can set browser preferences and add
extensions to the profile before passing it in the constructor for a
new L<Selenium::Remote::Driver> or L<Selenium::Firefox>.
=head1 SYNPOSIS
use Selenium::Remote::Driver;
use Selenium::Firefox::Profile;
my $profile = Selenium::Firefox::Profile->new;
$profile->set_preference(
'browser.startup.homepage' => 'http://www.google.com',
'browser.cache.disk.capacity' => 358400
);
$profile->set_boolean_preference(
'browser.shell.checkDefaultBrowser' => 0
);
$profile->add_extension('t/www/redisplay.xpi');
my $driver = Selenium::Remote::Driver->new(
'firefox_profile' => $profile
);
$driver->get('http://www.google.com');
print $driver->get_title();
=head1 CONSTRUCTOR
=head2 new (%args)
profile_dir - <string> directory to look for the firefox profile. Defaults to a Tempdir.
=head1 METHODS
=head2 set_preference
Set string and integer preferences on the profile object. You can set
multiple preferences at once. If you need to set a boolean preference,
either use JSON::true/JSON::false, or see C<set_boolean_preference()>.
$profile->set_preference("quoted.integer.pref" => '"20140314220517"');
# user_pref("quoted.integer.pref", "20140314220517");
$profile->set_preference("plain.integer.pref" => 9005);
# user_pref("plain.integer.pref", 9005);
$profile->set_preference("string.pref" => "sample string value");
# user_pref("string.pref", "sample string value");
=head2 set_boolean_preference
Set preferences that require boolean values of 'true' or 'false'. You
can set multiple preferences at once. For string or integer
preferences, use C<set_preference()>.
$profile->set_boolean_preference("false.pref" => 0);
# user_pref("false.pref", false);
$profile->set_boolean_preference("true.pref" => 1);
# user_pref("true.pref", true);
=head2 get_preference
Retrieve the computed value of a preference. Strings will be double
quoted and boolean values will be single quoted as "true" or "false"
accordingly.
$profile->set_boolean_preference("true.pref" => 1);
print $profile->get_preference("true.pref") # true
$profile->set_preference("string.pref" => "an extra set of quotes");
print $profile->get_preference("string.pref") # "an extra set of quotes"
=head2 add_extension
Add an existing C<.xpi> to the profile by providing its path. This
only works with packaged C<.xpi> files, not plain/un-packed extension
directories.
$profile->add_extension('t/www/redisplay.xpi');
=head2 add_webdriver
Primarily for internal use, we set the appropriate firefox preferences
for a new geckodriver session.
=head2 add_webdriver_xpi
Primarily for internal use. This adds the fxgoogle .xpi that is used
for webdriver communication in FF47 and older. For FF48 and newer, the
old method using an extension to orchestrate the webdriver
communication with the Firefox browser has been obsoleted by the
introduction of C<geckodriver>.
=head2 add_marionette
Primarily for internal use, configure Marionette to the
current Firefox profile.
=head1 SEE ALSO
Please see those modules/websites for more information related to this module.
=over 4
=item *
L<Selenium::Remote::Driver|Selenium::Remote::Driver>
=item *
L<http://kb.mozillazine.org/About:config_entries|http://kb.mozillazine.org/About:config_entries>
=item *
L<https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences|https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences>
=back
=head1 BUGS
Please report any bugs or feature requests on the bugtracker website
L<https://github.com/teodesian/Selenium-Remote-Driver/issues>
When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.
=head1 AUTHORS
Current Maintainers:
=over 4
=item *
Daniel Gempesaw <gempesaw@gmail.com>
=item *
Emmanuel Peroumalnaïk <peroumalnaik.emmanuel@gmail.com>
=back
Previous maintainers:
=over 4
=item *
Luke Closs <cpan@5thplane.com>
=item *
Mark Stosberg <mark@stosberg.com>
=back
Original authors:
=over 4
=item *
Aditya Ivaturi <ivaturi@gmail.com>
=back
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
Copyright (c) 2014-2017 Daniel Gempesaw
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
=cut

View File

@@ -0,0 +1,69 @@
{
"frozen": {
"app.update.auto": false,
"app.update.enabled": false,
"browser.displayedE10SNotice": 4,
"browser.download.manager.showWhenStarting": false,
"browser.EULA.override": true,
"browser.EULA.3.accepted": true,
"browser.link.open_external": 2,
"browser.link.open_newwindow": 2,
"browser.offline": false,
"browser.reader.detectedFirstArticle": true,
"browser.safebrowsing.enabled": false,
"browser.safebrowsing.malware.enabled": false,
"browser.search.update": false,
"browser.selfsupport.url" : "",
"browser.sessionstore.resume_from_crash": false,
"browser.shell.checkDefaultBrowser": false,
"browser.tabs.warnOnClose": false,
"browser.tabs.warnOnOpen": false,
"datareporting.healthreport.service.enabled": false,
"datareporting.healthreport.uploadEnabled": false,
"datareporting.healthreport.service.firstRun": false,
"datareporting.healthreport.logging.consoleEnabled": false,
"datareporting.policy.dataSubmissionEnabled": false,
"datareporting.policy.dataSubmissionPolicyAccepted": false,
"devtools.errorconsole.enabled": true,
"dom.disable_open_during_load": false,
"extensions.autoDisableScopes": 10,
"extensions.blocklist.enabled": false,
"extensions.checkCompatibility.nightly": false,
"extensions.logging.enabled": true,
"extensions.update.enabled": false,
"extensions.update.notifyUser": false,
"javascript.enabled": true,
"network.manage-offline-status": false,
"network.http.phishy-userpass-length": 255,
"offline-apps.allow_by_default": true,
"prompts.tab_modal.enabled": false,
"security.csp.enable": false,
"security.fileuri.origin_policy": 3,
"security.fileuri.strict_origin_policy": false,
"signon.rememberSignons": false,
"toolkit.networkmanager.disable": true,
"toolkit.telemetry.prompted": 2,
"toolkit.telemetry.enabled": false,
"toolkit.telemetry.rejected": true,
"xpinstall.signatures.required": false,
"xpinstall.whitelist.required": false
},
"mutable": {
"browser.dom.window.dump.enabled": true,
"browser.laterrun.enabled": false,
"browser.newtab.url": "about:blank",
"browser.newtabpage.enabled": false,
"browser.startup.page": 0,
"browser.startup.homepage": "about:blank",
"browser.startup.homepage_override.mstone": "ignore",
"browser.usedOnWindows10.introURL": "about:blank",
"dom.max_chrome_script_run_time": 30,
"dom.max_script_run_time": 30,
"dom.report_all_js_exceptions": true,
"javascript.options.showInConsole": true,
"startup.homepage_welcome_url": "about:blank",
"startup.homepage_welcome_url.additional": "about:blank",
"webdriver_accept_untrusted_certs": true,
"webdriver_assume_untrusted_issuer": true
}
}