Firefox-Marionette

 view release on metacpan or  search on metacpan

lib/Firefox/Marionette.pm  view on Meta::CPAN

  let CHUNK_SZ = 0x8000;
  let c = [];
  let array = new Uint8Array(exportedKey);
  for (let i = 0; i < array.length; i += CHUNK_SZ) {
    c.push(String.fromCharCode.apply(null, array.subarray(i, i + CHUNK_SZ)));
  }
  let b64Key = window.btoa(c.join(""));
  let urlSafeKey = b64Key.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
  return urlSafeKey;
})();
return privateKey;
_JS_
        my $old = $self->_context('chrome');
        $parameters{private_key} = $self->script(
            $self->_compress_script($script),
            args => [ $parameters{private_key} ]
        );
        $self->_context($old);
    }
    if ( !defined $parameters{sign_count} ) {
        $parameters{sign_count} = 0;
    }
    my $authenticator         = $self->_get_webauthn_authenticator(%parameters);
    my %credential_parameters = (
        authenticatorId      => $authenticator->id(),
        credentialId         => $parameters{id},
        isResidentCredential => $parameters{is_resident},
        rpId                 => $parameters{host},
        privateKey           => $parameters{private_key},
        signCount            => $parameters{sign_count},
        userHandle           => $parameters{user},
    );
    my $message_id = $self->_new_message_id();
    $self->_send_request(
        [
            _COMMAND(), $message_id, $self->_command('WebAuthn:AddCredential'),
            \%credential_parameters,
        ]
    );
    if ( defined $credential_parameters{userHandle} ) {
        $credential_parameters{userHandle} =
          MIME::Base64::decode_base64url( $credential_parameters{userHandle} );
    }
    my $response = $self->_get_response($message_id);
    return Firefox::Marionette::WebAuthn::Credential->new(
        %credential_parameters);
}

sub _get_webauthn_authenticator {
    my ( $self, %parameters ) = @_;
    my $authenticator;
    if ( $parameters{authenticator} ) {
        $authenticator = $parameters{authenticator};
    }
    else {
        $authenticator = $self->webauthn_authenticator();
    }
    return $authenticator;
}

sub webauthn_credentials {
    my ( $self, $parameter_authenticator ) = @_;
    my $authenticator = $self->_get_webauthn_authenticator(
        authenticator => $parameter_authenticator );
    my $message_id = $self->_new_message_id();
    $self->_send_request(
        [
            _COMMAND(),
            $message_id,
            $self->_command('WebAuthn:GetCredentials'),
            {
                authenticatorId => $authenticator->id(),
            }
        ]
    );
    my $response = $self->_get_response($message_id);
    return map { Firefox::Marionette::WebAuthn::Credential->new( %{$_} ) }
      map { $self->_decode_credential_user_handle($_) }
      @{ $self->_response_result_value($response) };
}

sub _decode_credential_user_handle {
    my ( $self, $credential ) = @_;
    if ( $credential->{userHandle} eq q[] ) {
        $credential->{userHandle} = undef;
    }
    else {
        $credential->{userHandle} =
          MIME::Base64::decode_base64url( $credential->{userHandle} );
    }
    return $credential;
}

sub delete_webauthn_all_credentials {
    my ( $self, $parameter_authenticator ) = @_;
    my $authenticator = $self->_get_webauthn_authenticator(
        authenticator => $parameter_authenticator );
    my $message_id = $self->_new_message_id();
    $self->_send_request(
        [
            _COMMAND(),
            $message_id,
            $self->_command('WebAuthn:RemoveAllCredentials'),
            {
                authenticatorId => $authenticator->id(),
            }
        ]
    );
    my $response = $self->_get_response($message_id);
    return $self;
}

sub delete_webauthn_credential {
    my ( $self, $credential, $parameter_authenticator ) = @_;
    my $authenticator = $self->_get_webauthn_authenticator(
        authenticator => $parameter_authenticator );
    my $message_id = $self->_new_message_id();
    $self->_send_request(
        [
            _COMMAND(),
            $message_id,
            $self->_command('WebAuthn:RemoveCredential'),
            {
                authenticatorId => $authenticator->id(),
                credentialId    => $credential->id(),
            }
        ]
    );
    my $response = $self->_get_response($message_id);
    return $self;
}

sub _login_interface_preamble {
    my ($self) = @_;

    return <<'_JS_';    # toolkit/components/passwordmgr/nsILoginManager.idl
let loginManager = Components.classes["@mozilla.org/login-manager;1"].getService(Components.interfaces.nsILoginManager);
_JS_
}

sub fill_login {
    my ($self) = @_;

    my $found;
    my $browser_uri = URI->new( $self->uri() );
  FORM: foreach my $form ( $self->find_tag('form') ) {
        my $action     = $form->attribute('action');
        my $action_uri = URI->new_abs( $action, $browser_uri );
        my $old        = $self->_context('chrome');
        my @logins     = $self->_translate_firefox_logins(
            @{
                $self->script(
                    $self->_compress_script(
                        $self->_login_interface_preamble()

lib/Firefox/Marionette.pm  view on Meta::CPAN

=item * user - The username for the login.

=item * password - The password for the login.

=item * origin - The scheme + hostname that the form-based login L<was submitted to|https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#attr-action>.  Forms with no L<action attribute|https://developer.mozilla.org/en-US/docs/Web/HTML/Eleme...

=item * realm - The HTTP Realm for which the login was requested. When an HTTP server sends a 401 result, the WWW-Authenticate header includes a realm. See L<RFC 2617|https://datatracker.ietf.org/doc/html/rfc2617>.  If the realm is not specified, or ...

=item * user_field - The name attribute for the username input in a form. Non-form logins should not specify this field.

=item * password_field - The name attribute for the password input in a form. Non-form logins should not specify this field.

=back

or a L<Firefox::Marionette::Login|Firefox::Marionette::Login> object as the first parameter and adds the login to the Firefox login database.

    use Firefox::Marionette();
    use UUID();

    my $firefox = Firefox::Marionette->new();

    # for http auth logins

    my $http_auth_login = Firefox::Marionette::Login->new(host => 'https://pause.perl.org', user => 'AUSER', password => 'qwerty', realm => 'PAUSE');
    $firefox->add_login($http_auth_login);
    $firefox->go('https://pause.perl.org/pause/authenquery')->accept_alert(); # this goes to the page and submits the http auth popup

    # for form based login

    my $form_login = Firefox::Marionette::Login(host => 'https://github.com', user => 'me2@example.org', password => 'uiop[]', user_field => 'login', password_field => 'password');
    $firefox->add_login($form_login);

    # or just directly

    $firefox->add_login(host => 'https://github.com', user => 'me2@example.org', password => 'uiop[]', user_field => 'login', password_field => 'password');

Note for HTTP Authentication, the L<realm|https://datatracker.ietf.org/doc/html/rfc2617#section-2> must perfectly match the correct L<realm|https://datatracker.ietf.org/doc/html/rfc2617#section-2> supplied by the server.

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 add_site_header

accepts a host name and a hash of HTTP headers to include in every future HTTP Request that is being sent to that particular host.

    use Firefox::Marionette();
    use UUID();

    my $firefox = Firefox::Marionette->new();
    my $uuid = UUID::uuid();
    $firefox->add_site_header( 'metacpan.org', 'Track-my-automated-tests' => $uuid );
    $firefox->go('https://metacpan.org/');

these headers are added to any existing headers going to the metacpan.org site, but no other site.  To clear site headers, see the L<delete_site_header|/delete_site_header> method

=head2 add_webauthn_authenticator

accepts a hash of the following keys;

=over 4

=item * has_resident_key - boolean value to indicate if the authenticator will support L<client side discoverable credentials|https://www.w3.org/TR/webauthn-2/#client-side-discoverable-credential>

=item * has_user_verification - boolean value to determine if the L<authenticator|https://www.w3.org/TR/webauthn-2/#virtual-authenticators> supports L<user verification|https://www.w3.org/TR/webauthn-2/#user-verification>.

=item * is_user_consenting - boolean value to determine the result of all L<user consent|https://www.w3.org/TR/webauthn-2/#user-consent> L<authorization gestures|https://www.w3.org/TR/webauthn-2/#authorization-gesture>, and by extension, any L<test o...

=item * is_user_verified - boolean value to determine the result of L<User Verification|https://www.w3.org/TR/webauthn-2/#user-verification> performed on the L<Virtual Authenticator|https://www.w3.org/TR/webauthn-2/#virtual-authenticators>. If set to...

=item * protocol - the L<protocol|Firefox::Marionette::WebAuthn::Authenticator#protocol> spoken by the authenticator.  This may be L<CTAP1_U2F|Firefox::Marionette::WebAuthn::Authenticator#CTAP1_U2F>, L<CTAP2|Firefox::Marionette::WebAuthn::Authenticat...

=item * transport - the L<transport|Firefox::Marionette::WebAuthn::Authenticator#transport> simulated by the authenticator.  This may be L<BLE|Firefox::Marionette::WebAuthn::Authenticator#BLE>, L<HYBRID|Firefox::Marionette::WebAuthn::Authenticator#HY...

=back

It returns the newly created L<authenticator|Firefox::Marionette::WebAuthn::Authenticator>.

    use Firefox::Marionette();
    use Crypt::URandom();

    my $user_name = MIME::Base64::encode_base64( Crypt::URandom::urandom( 10 ), q[] ) . q[@example.com];
    my $firefox = Firefox::Marionette->new( webauthn => 0 );
    my $authenticator = $firefox->add_webauthn_authenticator( transport => Firefox::Marionette::WebAuthn::Authenticator::INTERNAL(), protocol => Firefox::Marionette::WebAuthn::Authenticator::CTAP2() );
    $firefox->go('https://webauthn.io');
    $firefox->find_id('input-email')->type($user_name);
    $firefox->find_id('register-button')->click();
    $firefox->await(sub { sleep 1; $firefox->find_class('alert-success'); });
    $firefox->find_id('login-button')->click();
    $firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });

=head2 add_webauthn_credential

accepts a hash of the following keys;

=over 4

=item * authenticator - contains the L<authenticator|Firefox::Marionette::WebAuthn::Authenticator> that the credential will be added to.  If this parameter is not supplied, the credential will be added to the default authenticator, if one exists.

=item * host - contains the domain that this credential is to be used for.  In the language of L<WebAuthn|https://www.w3.org/TR/webauthn-2>, this field is referred to as the L<relying party identifier|https://www.w3.org/TR/webauthn-2/#relying-party-i...

=item * id - contains the unique id for this credential, also known as the L<Credential ID|https://www.w3.org/TR/webauthn-2/#credential-id>.  If this is not supplied, one will be generated.

=item * is_resident - contains a boolean that if set to true, a L<client-side discoverable credential|https://w3c.github.io/webauthn/#client-side-discoverable-credential> is created. If set to false, a L<server-side credential|https://w3c.github.io/w...

=item * private_key - either a L<RFC5958|https://www.rfc-editor.org/rfc/rfc5958> encoded private key encoded using L<encode_base64url|MIME::Base64::encode_base64url> or a hash containing the following keys;

=over 8

=item * name - contains the name of the private key algorithm, such as "RSA-PSS" (the default), "RSASSA-PKCS1-v1_5", "ECDSA" or "ECDH".

=item * size - contains the modulus length of the private key.  This is only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.

=item * hash - contains the name of the hash algorithm, such as "SHA-512" (the default).  This is only valid for "RSA-PSS" or "RSASSA-PKCS1-v1_5" private keys.

=item * curve - contains the name of the curve for the private key, such as "P-384" (the default).  This is only valid for "ECDSA" or "ECDH" private keys.

=back

=item * sign_count - contains the initial value for a L<signature counter|https://w3c.github.io/webauthn/#signature-counter> associated to the L<public key credential source|https://w3c.github.io/webauthn/#public-key-credential-source>.  It will defa...

=item * user - contains the L<userHandle|https://w3c.github.io/webauthn/#public-key-credential-source-userhandle> associated to the credential encoded using L<encode_base64url|MIME::Base64::encode_base64url>.  This property is optional.

=back

It returns the newly created L<credential|Firefox::Marionette::WebAuthn::Credential>.  If of course, the credential is just created, it probably won't be much good by itself.  However, you can use it to recreate a credential, so long as you know all ...

    use Firefox::Marionette();
    use Crypt::URandom();

    my $user_name = MIME::Base64::encode_base64( Crypt::URandom::urandom( 10 ), q[] ) . q[@example.com];
    my $firefox = Firefox::Marionette->new();
    $firefox->go('https://webauthn.io');
    $firefox->find_id('input-email')->type($user_name);
    $firefox->find_id('register-button')->click();
    $firefox->await(sub { sleep 1; $firefox->find_class('alert-success'); });
    $firefox->find_id('login-button')->click();
    $firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });
    foreach my $credential ($firefox->webauthn_credentials()) {
        $firefox->delete_webauthn_credential($credential);

# ... time passes ...

        $firefox->add_webauthn_credential(
                  id            => $credential->id(),
                  host          => $credential->host(),
                  user          => $credential->user(),
                  private_key   => $credential->private_key(),
                  is_resident   => $credential->is_resident(),
                  sign_count    => $credential->sign_count(),
                              );
    }
    $firefox->go('about:blank');
    $firefox->clear_cache(Firefox::Marionette::Cache::CLEAR_COOKIES());
    $firefox->go('https://webauthn.io');
    $firefox->find_id('input-email')->type($user_name);
    $firefox->find_id('login-button')->click();
    $firefox->await(sub { sleep 1; $firefox->find_class('hero confetti'); });

=head2 addons

returns if pre-existing addons (extensions/themes) are allowed to run.  This will be true for Firefox versions less than 55, as L<-safe-mode|http://kb.mozillazine.org/Command_line_arguments#List_of_command_line_arguments_.28incomplete.29> cannot be a...

=head2 agent

accepts an optional value for the L<User-Agent|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent> header and sets this using the profile preferences and inserting L<javascript|/script> into the current page. It returns the current ...

This method can be used to set a user agent string like so;

    use Firefox::Marionette();
    use strict;

    # useragents.me should only be queried once a month or less.
    # these UA strings should be cached locally.

    my %user_agent_strings = map { $_->{ua} => $_->{pct} } @{$firefox->json("https://www.useragents.me/api")->{data}};
    my ($user_agent) = reverse sort { $user_agent_strings{$a} <=> $user_agent_strings{$b} } keys %user_agent_strings;

    my $firefox = Firefox::Marionette->new();
    $firefox->agent($user_agent); # agent is now the most popular agent from useragents.me

If the user agent string that is passed as a parameter looks like a L<Chrome|https://www.google.com/chrome/>, L<Edge|https://microsoft.com/edge> or L<Safari|https://www.apple.com/safari/> user agent string, then this method will also try and change o...

=over 4

=item * general.appversion.override

=item * general.oscpu.override

=item * general.platform.override

=item * network.http.accept

=item * network.http.accept-encoding

=item * network.http.accept-encoding.secure

=item * privacy.donottrackheader.enabled

lib/Firefox/Marionette.pm  view on Meta::CPAN

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 delete_header

accepts a list of HTTP header names to delete from future HTTP Requests.

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    $firefox->delete_header( 'User-Agent', 'Accept', 'Accept-Encoding' );

will remove the L<User-Agent|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent>, L<Accept|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept> and L<Accept-Encoding|https://developer.mozilla.org/en-US/docs/Web/HTTP/Hea...

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 delete_login

accepts a L<login|Firefox::Marionette::Login> as a parameter.

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    foreach my $login ($firefox->logins()) {
        if ($login->user() eq 'me@example.org') {
            $firefox->delete_login($login);
        }
    }

will remove the logins with the username matching 'me@example.org'.

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 delete_logins

This method empties the password database.

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    $firefox->delete_logins();

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 delete_session

deletes the current WebDriver session.

=head2 delete_site_header

accepts a host name and a list of HTTP headers names to delete from future HTTP Requests.

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    $firefox->delete_header( 'metacpan.org', 'User-Agent', 'Accept', 'Accept-Encoding' );

will remove the L<User-Agent|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent>, L<Accept|https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept> and L<Accept-Encoding|https://developer.mozilla.org/en-US/docs/Web/HTTP/Hea...

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 delete_webauthn_all_credentials

This method accepts an optional L<authenticator|Firefox::Marionette::WebAuthn::Authenticator>, in which case it will delete all L<credentials|Firefox::Marionette::WebAuthn::Credential> from this authenticator.  If no parameter is supplied, the defaul...

    my $firefox = Firefox::Marionette->new();
    my $authenticator = $firefox->add_webauthn_authenticator( transport => Firefox::Marionette::WebAuthn::Authenticator::INTERNAL(), protocol => Firefox::Marionette::WebAuthn::Authenticator::CTAP2() );
    $firefox->delete_webauthn_all_credentials($authenticator);
    $firefox->delete_webauthn_all_credentials();

=head2 delete_webauthn_authenticator

This method accepts an optional L<authenticator|Firefox::Marionette::WebAuthn::Authenticator>, in which case it will delete this authenticator from the current Firefox instance.  If no parameter is supplied, the default authenticator will be deleted.

    my $firefox = Firefox::Marionette->new();
    my $authenticator = $firefox->add_webauthn_authenticator( transport => Firefox::Marionette::WebAuthn::Authenticator::INTERNAL(), protocol => Firefox::Marionette::WebAuthn::Authenticator::CTAP2() );
    $firefox->delete_webauthn_authenticator($authenticator);
    $firefox->delete_webauthn_authenticator();

=head2 delete_webauthn_credential

This method accepts either a L<credential|Firefox::Marionette::WebAuthn::Credential> and an L<authenticator|Firefox::Marionette::WebAuthn::Authenticator>, in which case it will remove the credential from the supplied authenticator or

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    my $authenticator = $firefox->add_webauthn_authenticator( transport => Firefox::Marionette::WebAuthn::Authenticator::INTERNAL(), protocol => Firefox::Marionette::WebAuthn::Authenticator::CTAP2() );
    foreach my $credential ($firefox->webauthn_credentials($authenticator)) {
        $firefox->delete_webauthn_credential($credential, $authenticator);
    }

just a L<credential|Firefox::Marionette::WebAuthn::Credential>, in which case it will remove the credential from the default authenticator.

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    ...
    foreach my $credential ($firefox->webauthn_credentials()) {
        $firefox->delete_webauthn_credential($credential);
    }

This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 developer

returns true if the L<current version|/browser_version> of firefox is a L<developer edition|https://www.mozilla.org/en-US/firefox/developer/> (does the minor version number end with an 'b\d+'?) version.

=head2 dismiss_alert

dismisses a currently displayed modal message box

=head2 displays

accepts an optional regex to filter against the L<usage for the display|Firefox::Marionette::Display#usage> and returns a list of all the L<known displays|https://en.wikipedia.org/wiki/List_of_common_resolutions> as a L<Firefox::Marionette::Display|F...

    use Firefox::Marionette();
    use Encode();
    use v5.10;

    my $firefox = Firefox::Marionette->new( visible => 1, kiosk => 1 )->go('http://metacpan.org');;
    my $element = $firefox->find_id('metacpan_search-input');
    foreach my $display ($firefox->displays(qr/iphone/smxi)) {
        say 'Can Firefox resize for "' . Encode::encode('UTF-8', $display->usage(), 1) . '"?';
        if ($firefox->resize($display->width(), $display->height())) {
            say 'Now displaying with a Pixel aspect ratio of ' . $display->par();
            say 'Now displaying with a Storage aspect ratio of ' . $display->sar();
            say 'Now displaying with a Display aspect ratio of ' . $display->dar();
        } else {
            say 'Apparently NOT!';
        }
    }

=head2 downloaded

accepts a filesystem path and returns a matching filehandle.  This is trivial for locally running firefox, but sufficiently complex to justify the method for a remote firefox running over ssh.

    use Firefox::Marionette();
    use v5.10;

    my $firefox = Firefox::Marionette->new( host => '10.1.2.3' )->go('https://metacpan.org/');

    $firefox->find_class('page-content')->find_id('metacpan_search-input')->type('Test::More');

    $firefox->await(sub { $firefox->find_class('autocomplete-suggestion'); })->click();

    $firefox->find_partial('Download')->click();

    while(!$firefox->downloads()) { sleep 1 }

    foreach my $path ($firefox->downloads()) {

        my $handle = $firefox->downloaded($path);

        # do something with downloaded file handle

    }

=head2 downloading

lib/Firefox/Marionette.pm  view on Meta::CPAN


=head2 tz

accepts a L<Olson TZ identifier|https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List> as the first parameter. This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

=head2 title

returns the current L<title|https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title> of the window.

=head2 type

accepts an L<element|Firefox::Marionette::Element> as the first parameter and a string as the second parameter.  It sends the string to the specified L<element|Firefox::Marionette::Element> in the current page, such as filling out a text box. This me...

=head2 uname

returns the $^O ($OSNAME) compatible string to describe the platform where firefox is running.

=head2 update

queries the Update Services and applies any available updates.  L<Restarts|/restart> the browser if necessary to complete the update.  This function is experimental and currently has not been successfully tested on Win32 or MacOS.

    use Firefox::Marionette();
    use v5.10;

    my $firefox = Firefox::Marionette->new();

    my $update = $firefox->update();

    while($update->successful()) {
        $update = $firefox->update();
    }

    say "Updated to " . $update->display_version() . " - Build ID " . $update->build_id();

    $firefox->quit();

returns a L<status|Firefox::Marionette::UpdateStatus> object that contains useful information about any updates that occurred.

=head2 uninstall

accepts the GUID for the addon to uninstall.  The GUID is returned when from the L<install|/install> method.  This method returns L<itself|Firefox::Marionette> to aid in chaining methods.

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();

    my $extension_id = $firefox->install('/full/path/to/gnu_terry_pratchett-0.4-an+fx.xpi');

    # do something

    $firefox->uninstall($extension_id); # not recommended to uninstall this extension IRL.

=head2 uri

returns the current L<URI|URI> of current top level browsing context for Desktop.  It is equivalent to the javascript C<document.location.href>

=head2 webauthn_authenticator

returns the default L<WebAuthn authenticator|Firefox::Marionette::WebAuthn::Authenticator> created when the L<new|/new> method was called.

=head2 webauthn_credentials

This method accepts an optional L<authenticator|Firefox::Marionette::WebAuthn::Authenticator>, in which case it will return all the L<credentials|Firefox::Marionette::WebAuthn::Credential> attached to this authenticator.  If no parameter is supplied,...

    use Firefox::Marionette();
    use v5.10;

    my $firefox = Firefox::Marionette->new();
    foreach my $credential ($firefox->webauthn_credentials()) {
       say "Credential host is " . $credential->host();
    }

    # OR

    my $authenticator = $firefox->add_webauthn_authenticator( transport => Firefox::Marionette::WebAuthn::Authenticator::INTERNAL(), protocol => Firefox::Marionette::WebAuthn::Authenticator::CTAP2() );
    foreach my $credential ($firefox->webauthn_credentials($authenticator)) {
       say "Credential host is " . $credential->host();
    }

=head2 webauthn_set_user_verified

This method accepts a boolean for the L<is_user_verified|Firefox::Marionette::WebAuthn::Authenticator#is_user_verified> field and an optional L<authenticator|Firefox::Marionette::WebAuthn::Authenticator> (the default authenticator will be used otherw...

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    $firefox->webauthn_set_user_verified(1);

=head2 wheel

accepts a L<element|Firefox::Marionette::Element> parameter, or a C<( x =E<gt> 0, y =E<gt> 0 )> type hash manually describing exactly where to move the mouse from and returns an action for use in the L<perform|/perform> method that corresponding with...

=over 4

=item * origin - the origin of the C(<x =E<gt> 0, y =E<gt> 0)> co-ordinates.  Should be either C<viewport>, C<pointer> or an L<element|Firefox::Marionette::Element>.

=item * duration - Number of milliseconds over which to distribute the move. If not defined, the duration defaults to 0.

=item * deltaX - the change in X co-ordinates during the wheel.  If not defined, deltaX defaults to 0.

=item * deltaY - the change in Y co-ordinates during the wheel.  If not defined, deltaY defaults to 0.

=back

=head2 win32_organisation

accepts a parameter of a Win32 product name and returns the matching organisation.  Only of interest when sub-classing.

=head2 win32_product_names

returns a hash of known Windows product names (such as 'Mozilla Firefox') with priority orders.  The lower the priority will determine the order that this module will check for the existence of this product.  Only of interest when sub-classing.

=head2 window_handle

returns the L<current window's handle|Firefox::Marionette::WebWindow>. On desktop this typically corresponds to the currently selected tab.  returns an opaque server-assigned identifier to this window that uniquely identifies it within this Marionett...

    use Firefox::Marionette();

    my $firefox = Firefox::Marionette->new();
    my $original_window = $firefox->window_handle();
    my $javascript_window = $firefox->script('return window'); # only works for Firefox 121 and later
    if ($javascript_window ne $original_window) {
        die "That was unexpected!!! What happened?";
    }

=head2 window_handles

returns a list of top-level L<browsing contexts|Firefox::Marionette::WebWindow>. On desktop this typically corresponds to the set of open tabs for browser windows, or the window itself for non-browser chrome windows.  Each window handle is assigned b...

    use Firefox::Marionette();
    use 5.010;

    my $firefox = Firefox::Marionette->new();
    my $original_window = $firefox->window_handle();
    $firefox->new_window( type => 'tab' );
    $firefox->new_window( type => 'window' );



( run in 0.799 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )