| // Copyright 2018 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webauthn/sheet_models.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| #include "base/check.h" |
| #include "base/check_op.h" |
| #include "base/feature_list.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/notreached.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "chrome/app/vector_icons/vector_icons.h" |
| #include "chrome/browser/profiles/profile_avatar_icon_util.h" |
| #include "chrome/browser/ui/webauthn/user_actions.h" |
| #include "chrome/browser/ui/webauthn/webauthn_ui_helpers.h" |
| #include "chrome/browser/webauthn/authenticator_request_dialog_model.h" |
| #include "chrome/browser/webauthn/local_authentication_token.h" |
| #include "chrome/browser/webauthn/webauthn_metrics_util.h" |
| #include "chrome/grit/browser_resources.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/signin/public/identity_manager/account_info.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "device/fido/features.h" |
| #include "device/fido/fido_constants.h" |
| #include "device/fido/fido_types.h" |
| #include "device/fido/pin.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/views/layout/layout_provider.h" |
| #include "ui/views/style/typography.h" |
| |
| #if BUILDFLAG(IS_MAC) |
| #include "device/fido/mac/util.h" |
| #endif |
| |
| namespace { |
| |
| using CredentialMech = AuthenticatorRequestDialogModel::Mechanism::Credential; |
| using EnclaveMech = AuthenticatorRequestDialogModel::Mechanism::Enclave; |
| using PasswordMech = AuthenticatorRequestDialogModel::Mechanism::Password; |
| using ICloudKeychainMech = |
| AuthenticatorRequestDialogModel::Mechanism::ICloudKeychain; |
| using Step = AuthenticatorRequestDialogModel::Step; |
| |
| constexpr int kGpmArbitraryPinMinLength = 4; |
| |
| bool IsLocalPasskeyOrEnclaveAuthenticatorOrPassword( |
| const AuthenticatorRequestDialogModel::Mechanism& mech) { |
| return (std::holds_alternative<CredentialMech>(mech.type) && |
| std::get<CredentialMech>(mech.type).value().source != |
| device::AuthenticatorType::kPhone) || |
| std::holds_alternative<EnclaveMech>(mech.type) || |
| std::holds_alternative<PasswordMech>(mech.type); |
| } |
| |
| // Possibly returns a resident key warning if the model indicates that it's |
| // needed. |
| std::u16string PossibleResidentKeyWarning( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| switch (dialog_model->resident_key_requirement) { |
| case device::ResidentKeyRequirement::kDiscouraged: |
| return std::u16string(); |
| case device::ResidentKeyRequirement::kPreferred: |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_RESIDENT_KEY_PREFERRED_PRIVACY); |
| case device::ResidentKeyRequirement::kRequired: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RESIDENT_KEY_PRIVACY); |
| } |
| NOTREACHED(); |
| } |
| |
| // Return a warning about attestation if attestation was requested, otherwise |
| // return an empty string. |
| std::u16string PossibleAttestationWarning( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| if (!dialog_model->attestation_conveyance_preference) { |
| return std::u16string(); |
| } |
| |
| switch (*dialog_model->attestation_conveyance_preference) { |
| case device::AttestationConveyancePreference::kNone: |
| return std::u16string(); |
| case device::AttestationConveyancePreference::kIndirect: |
| case device::AttestationConveyancePreference::kDirect: |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_ATTESTATION_WARNING, |
| AuthenticatorSheetModelBase::GetRelyingPartyIdStringLabel( |
| dialog_model)); |
| case device::AttestationConveyancePreference:: |
| kEnterpriseIfRPListedOnAuthenticator: |
| case device::AttestationConveyancePreference::kEnterpriseApprovedByBrowser: |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_ENTERPRISE_ATTESTATION_WARNING, |
| AuthenticatorSheetModelBase::GetRelyingPartyIdStringLabel( |
| dialog_model)); |
| } |
| } |
| |
| } // namespace |
| |
| // AuthenticatorSheetModelBase ------------------------------------------------ |
| |
| AuthenticatorSheetModelBase::AuthenticatorSheetModelBase( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : dialog_model_(dialog_model) { |
| DCHECK(dialog_model); |
| dialog_model_->observers.AddObserver(this); |
| } |
| |
| AuthenticatorSheetModelBase::AuthenticatorSheetModelBase( |
| AuthenticatorRequestDialogModel* dialog_model, |
| OtherMechanismButtonVisibility other_mechanism_button_visibility) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| other_mechanism_button_visibility_ = other_mechanism_button_visibility; |
| } |
| |
| AuthenticatorSheetModelBase::~AuthenticatorSheetModelBase() { |
| if (dialog_model_) { |
| dialog_model_->observers.RemoveObserver(this); |
| dialog_model_ = nullptr; |
| } |
| } |
| |
| // static |
| int AuthenticatorSheetModelBase::GetPreferredContentWidth() { |
| views::LayoutProvider* layout_provider = views::LayoutProvider::Get(); |
| return layout_provider->GetDistanceMetric( |
| views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) - |
| layout_provider->GetInsetsMetric(views::INSETS_DIALOG).width() - |
| layout_provider->GetInsetsMetric(views::INSETS_DIALOG_TITLE).width(); |
| } |
| |
| // static |
| std::u16string AuthenticatorSheetModelBase::GetRelyingPartyIdString( |
| const AuthenticatorRequestDialogModel* dialog_model, |
| gfx::FontList font_list) { |
| return webauthn_ui_helpers::RpIdToElidedHost( |
| dialog_model->relying_party_id, GetPreferredContentWidth(), font_list); |
| } |
| |
| // static |
| std::u16string AuthenticatorSheetModelBase::GetRelyingPartyIdStringLabel( |
| const AuthenticatorRequestDialogModel* dialog_model) { |
| gfx::FontList font_list = views::TypographyProvider::Get().GetFont( |
| views::style::CONTEXT_LABEL, views::style::STYLE_PRIMARY); |
| return AuthenticatorSheetModelBase::GetRelyingPartyIdString(dialog_model, |
| font_list); |
| } |
| |
| // static |
| std::u16string AuthenticatorSheetModelBase::GetRelyingPartyIdStringTitle( |
| const AuthenticatorRequestDialogModel* dialog_model) { |
| gfx::FontList font_list = views::TypographyProvider::Get().GetFont( |
| views::style::CONTEXT_DIALOG_TITLE, views::style::STYLE_HEADLINE_4); |
| return AuthenticatorSheetModelBase::GetRelyingPartyIdString(dialog_model, |
| font_list); |
| } |
| |
| bool AuthenticatorSheetModelBase::IsActivityIndicatorVisible() const { |
| return dialog_model_->ui_disabled_; |
| } |
| |
| bool AuthenticatorSheetModelBase::IsCancelButtonVisible() const { |
| return true; |
| } |
| |
| bool AuthenticatorSheetModelBase::IsOtherMechanismButtonVisible() const { |
| return other_mechanism_button_visibility_ == |
| OtherMechanismButtonVisibility::kVisible && |
| dialog_model_ && dialog_model_->mechanisms.size() > 1; |
| } |
| |
| std::u16string AuthenticatorSheetModelBase::GetOtherMechanismButtonLabel() |
| const { |
| switch (dialog_model()->request_type) { |
| case device::FidoRequestType::kMakeCredential: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_SAVE_ANOTHER_WAY); |
| case device::FidoRequestType::kGetAssertion: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USE_A_DIFFERENT_PASSKEY); |
| } |
| } |
| |
| std::u16string AuthenticatorSheetModelBase::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_CANCEL); |
| } |
| |
| AuthenticatorSheetModelBase::AcceptButtonState |
| AuthenticatorSheetModelBase::GetAcceptButtonState() const { |
| return AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorSheetModelBase::GetAcceptButtonLabel() const { |
| return std::u16string(); |
| } |
| |
| void AuthenticatorSheetModelBase::OnBack() { |
| if (dialog_model()) { |
| dialog_model()->StartOver(); |
| } |
| } |
| |
| void AuthenticatorSheetModelBase::OnAccept() { |
| NOTREACHED(); |
| } |
| |
| void AuthenticatorSheetModelBase::OnCancel() { |
| if (dialog_model()) { |
| webauthn::user_actions::RecordCancelClick(); |
| dialog_model()->CancelAuthenticatorRequest(); |
| } |
| } |
| |
| void AuthenticatorSheetModelBase::OnModelDestroyed( |
| AuthenticatorRequestDialogModel* model) { |
| DCHECK(model == dialog_model_); |
| dialog_model_ = nullptr; |
| } |
| |
| // AuthenticatorMechanismSelectorSheetModel ----------------------------------- |
| |
| AuthenticatorMechanismSelectorSheetModel:: |
| AuthenticatorMechanismSelectorSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT, |
| IDR_WEBAUTHN_PASSKEY_DARK); |
| webauthn::user_actions::RecordMultipleOptionsShown( |
| dialog_model->mechanisms, dialog_model->request_type); |
| } |
| |
| std::u16string AuthenticatorMechanismSelectorSheetModel::GetStepTitle() const { |
| CHECK_EQ(dialog_model()->request_type, |
| device::FidoRequestType::kMakeCredential); |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_CREATE_PASSKEY_CHOOSE_DEVICE_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorMechanismSelectorSheetModel::GetStepDescription() |
| const { |
| return u""; |
| } |
| |
| // AuthenticatorInsertAndActivateUsbSheetModel ---------------------- |
| |
| AuthenticatorInsertAndActivateUsbSheetModel:: |
| AuthenticatorInsertAndActivateUsbSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| webauthn::user_actions::RecordSecurityKeyDialogShown( |
| dialog_model->request_type); |
| vector_illustrations_.emplace(kPasskeyUsbIcon, kPasskeyUsbDarkIcon); |
| } |
| |
| bool AuthenticatorInsertAndActivateUsbSheetModel::IsActivityIndicatorVisible() |
| const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorInsertAndActivateUsbSheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GENERIC_TITLE, GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorInsertAndActivateUsbSheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USB_ACTIVATE_DESCRIPTION); |
| } |
| |
| std::vector<std::u16string> |
| AuthenticatorInsertAndActivateUsbSheetModel::GetAdditionalDescriptions() const { |
| return {PossibleAttestationWarning(dialog_model()), |
| PossibleResidentKeyWarning(dialog_model())}; |
| } |
| |
| // AuthenticatorTimeoutErrorModel --------------------------------------------- |
| |
| AuthenticatorTimeoutErrorModel::AuthenticatorTimeoutErrorModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorTimeoutErrorModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| |
| std::u16string AuthenticatorTimeoutErrorModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE); |
| } |
| |
| std::u16string AuthenticatorTimeoutErrorModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_TIMEOUT_DESCRIPTION); |
| } |
| |
| // AuthenticatorNoAvailableTransportsErrorModel ------------------------------- |
| |
| AuthenticatorNoAvailableTransportsErrorModel:: |
| AuthenticatorNoAvailableTransportsErrorModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string |
| AuthenticatorNoAvailableTransportsErrorModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| |
| std::u16string AuthenticatorNoAvailableTransportsErrorModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE); |
| } |
| |
| std::u16string |
| AuthenticatorNoAvailableTransportsErrorModel::GetStepDescription() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_DESC, |
| GetRelyingPartyIdStringLabel(dialog_model())); |
| } |
| |
| // AuthenticatorNoPasskeysErrorModel ------------------------------------------ |
| |
| AuthenticatorNoPasskeysErrorModel::AuthenticatorNoPasskeysErrorModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorNoPasskeysErrorModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| std::u16string AuthenticatorNoPasskeysErrorModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_NO_PASSKEYS_TITLE); |
| } |
| |
| std::u16string AuthenticatorNoPasskeysErrorModel::GetStepDescription() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_ERROR_NO_PASSKEYS_DESCRIPTION, |
| GetRelyingPartyIdStringLabel(dialog_model())); |
| } |
| |
| // AuthenticatorNotRegisteredErrorModel --------------------------------------- |
| |
| AuthenticatorNotRegisteredErrorModel::AuthenticatorNotRegisteredErrorModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorNotRegisteredErrorModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorNotRegisteredErrorModel::GetAcceptButtonState() const { |
| return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorNotRegisteredErrorModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY); |
| } |
| |
| std::u16string AuthenticatorNotRegisteredErrorModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_WRONG_KEY_TITLE); |
| } |
| |
| std::u16string AuthenticatorNotRegisteredErrorModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_ERROR_WRONG_KEY_SIGN_DESCRIPTION); |
| } |
| |
| void AuthenticatorNotRegisteredErrorModel::OnAccept() { |
| dialog_model()->StartOver(); |
| } |
| |
| // AuthenticatorAlreadyRegisteredErrorModel ----------------------------------- |
| |
| AuthenticatorAlreadyRegisteredErrorModel:: |
| AuthenticatorAlreadyRegisteredErrorModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorAlreadyRegisteredErrorModel::GetAcceptButtonState() const { |
| return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY); |
| } |
| |
| std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_WRONG_DEVICE_TITLE); |
| } |
| |
| std::u16string AuthenticatorAlreadyRegisteredErrorModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_ERROR_WRONG_DEVICE_REGISTER_DESCRIPTION); |
| } |
| |
| void AuthenticatorAlreadyRegisteredErrorModel::OnAccept() { |
| dialog_model()->StartOver(); |
| } |
| |
| // AuthenticatorInternalUnrecognizedErrorSheetModel --------------------------- |
| |
| AuthenticatorInternalUnrecognizedErrorSheetModel:: |
| AuthenticatorInternalUnrecognizedErrorSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorInternalUnrecognizedErrorSheetModel::GetAcceptButtonState() const { |
| return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string |
| AuthenticatorInternalUnrecognizedErrorSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY); |
| } |
| |
| std::u16string AuthenticatorInternalUnrecognizedErrorSheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_ERROR_INTERNAL_UNRECOGNIZED_TITLE); |
| } |
| |
| std::u16string |
| AuthenticatorInternalUnrecognizedErrorSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_ERROR_INTERNAL_UNRECOGNIZED_DESCRIPTION); |
| } |
| |
| void AuthenticatorInternalUnrecognizedErrorSheetModel::OnAccept() { |
| dialog_model()->StartOver(); |
| } |
| |
| // AuthenticatorChallengeFetchErrorModel |
| // --------------------------------------------- |
| |
| AuthenticatorChallengeFetchErrorModel::AuthenticatorChallengeFetchErrorModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorChallengeFetchErrorModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| |
| std::u16string AuthenticatorChallengeFetchErrorModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE); |
| } |
| |
| std::u16string AuthenticatorChallengeFetchErrorModel::GetStepDescription() |
| const { |
| // TODO(https://crbug.com/381219428): Get an approved string for this dialog. |
| return u"An error occurred trying to process this request. (UT)"; |
| } |
| |
| // AuthenticatorBlePowerOnManualSheetModel ------------------------------------ |
| |
| AuthenticatorBlePowerOnManualSheetModel:: |
| AuthenticatorBlePowerOnManualSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| vector_illustrations_.emplace(kPasskeyErrorBluetoothIcon, |
| kPasskeyErrorBluetoothDarkIcon); |
| } |
| |
| std::u16string AuthenticatorBlePowerOnManualSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_TITLE); |
| } |
| |
| std::u16string AuthenticatorBlePowerOnManualSheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_DESCRIPTION); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorBlePowerOnManualSheetModel::GetAcceptButtonState() const { |
| return dialog_model()->ble_adapter_is_powered ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kDisabled; |
| } |
| |
| std::u16string AuthenticatorBlePowerOnManualSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_POWER_ON_MANUAL_NEXT); |
| } |
| |
| void AuthenticatorBlePowerOnManualSheetModel::OnBluetoothPoweredStateChanged() { |
| dialog_model()->OnSheetModelChanged(); |
| } |
| |
| void AuthenticatorBlePowerOnManualSheetModel::OnAccept() { |
| dialog_model()->ContinueWithFlowAfterBleAdapterPowered(); |
| } |
| |
| // AuthenticatorBlePowerOnAutomaticSheetModel |
| // ------------------------------------ |
| |
| AuthenticatorBlePowerOnAutomaticSheetModel:: |
| AuthenticatorBlePowerOnAutomaticSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| vector_illustrations_.emplace(kPasskeyErrorBluetoothIcon, |
| kPasskeyErrorBluetoothDarkIcon); |
| } |
| |
| bool AuthenticatorBlePowerOnAutomaticSheetModel::IsActivityIndicatorVisible() |
| const { |
| return busy_powering_on_ble_; |
| } |
| |
| std::u16string AuthenticatorBlePowerOnAutomaticSheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_POWER_ON_AUTO_TITLE); |
| } |
| |
| std::u16string AuthenticatorBlePowerOnAutomaticSheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_BLUETOOTH_POWER_ON_AUTO_DESCRIPTION); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorBlePowerOnAutomaticSheetModel::GetAcceptButtonState() const { |
| return busy_powering_on_ble_ ? AcceptButtonState::kDisabled |
| : AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string |
| AuthenticatorBlePowerOnAutomaticSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_POWER_ON_AUTO_NEXT); |
| } |
| |
| void AuthenticatorBlePowerOnAutomaticSheetModel::OnAccept() { |
| busy_powering_on_ble_ = true; |
| dialog_model()->OnSheetModelChanged(); |
| dialog_model()->PowerOnBleAdapter(); |
| } |
| |
| #if BUILDFLAG(IS_MAC) |
| |
| // AuthenticatorBlePermissionMacSheetModel |
| // ------------------------------------ |
| |
| AuthenticatorBlePermissionMacSheetModel:: |
| AuthenticatorBlePermissionMacSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| vector_illustrations_.emplace(kPasskeyErrorBluetoothIcon, |
| kPasskeyErrorBluetoothDarkIcon); |
| } |
| |
| std::u16string AuthenticatorBlePermissionMacSheetModel::GetStepTitle() const { |
| // An empty title causes the title View to be omitted. |
| return u""; |
| } |
| |
| std::u16string AuthenticatorBlePermissionMacSheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_BLUETOOTH_PERMISSION); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorBlePermissionMacSheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| bool AuthenticatorBlePermissionMacSheetModel::IsCancelButtonVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorBlePermissionMacSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_OPEN_SETTINGS_LINK); |
| } |
| |
| void AuthenticatorBlePermissionMacSheetModel::OnAccept() { |
| dialog_model()->OpenBlePreferences(); |
| } |
| |
| // AuthenticatorTouchIdSheetModel |
| // ------------------------------------ |
| |
| AuthenticatorTouchIdSheetModel::AuthenticatorTouchIdSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| webauthn::user_actions::RecordGpmTouchIdDialogShown( |
| dialog_model->request_type); |
| } |
| |
| std::u16string AuthenticatorTouchIdSheetModel::GetStepTitle() const { |
| const std::u16string rp_id = GetRelyingPartyIdStringTitle(dialog_model()); |
| std::optional<int> id = std::nullopt; |
| switch (dialog_model()->request_type) { |
| case device::FidoRequestType::kMakeCredential: |
| id = IDS_WEBAUTHN_GPM_CREATE_PASSKEY_TITLE; |
| break; |
| case device::FidoRequestType::kGetAssertion: |
| id = dialog_model()->ui_presentation == UIPresentation::kModalImmediate |
| ? IDS_WEBAUTHN_SIGN_IN_TO_WEBSITE_DIALOG_TITLE |
| : IDS_WEBAUTHN_CHOOSE_PASSKEY_FOR_RP_TITLE; |
| break; |
| } |
| CHECK(id.has_value()); |
| return l10n_util::GetStringFUTF16(id.value(), rp_id); |
| } |
| |
| std::u16string AuthenticatorTouchIdSheetModel::GetStepDescription() const { |
| switch (dialog_model()->request_type) { |
| case device::FidoRequestType::kMakeCredential: |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_CREATE_PASSKEY_DESC, |
| base::UTF8ToUTF16(dialog_model()->GetGpmAccountEmail())); |
| |
| case device::FidoRequestType::kGetAssertion: |
| return dialog_model()->ui_presentation == UIPresentation::kModalImmediate |
| ? std::u16string() |
| : l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_TOUCH_ID_ASSERTION_DESC, |
| GetRelyingPartyIdStringLabel(dialog_model())); |
| } |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorTouchIdSheetModel::GetAcceptButtonState() const { |
| // Visible only if biometrics aren't available (fallback to password) |
| return !device::fido::mac::DeviceHasBiometricsAvailable() |
| ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| bool AuthenticatorTouchIdSheetModel::IsCancelButtonVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorTouchIdSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_TOUCH_ID_ENTER_PASSWORD); |
| } |
| |
| std::u16string AuthenticatorTouchIdSheetModel::GetCancelButtonLabel() const { |
| return dialog_model()->ui_presentation == UIPresentation::kModalImmediate |
| ? l10n_util::GetStringUTF16(IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON) |
| : l10n_util::GetStringUTF16(IDS_CANCEL); |
| } |
| |
| void AuthenticatorTouchIdSheetModel::OnAccept() { |
| if (touch_id_completed_) { |
| return; |
| } |
| webauthn::user_actions::RecordAcceptClick(); |
| touch_id_completed_ = true; |
| dialog_model()->OnTouchIDComplete(false); |
| } |
| |
| void AuthenticatorTouchIdSheetModel::OnTouchIDSensorTapped( |
| std::optional<webauthn::LocalAuthenticationToken> local_auth_token) { |
| // Ignore Touch ID ceremony status after the user has completed the ceremony. |
| if (touch_id_completed_) { |
| return; |
| } |
| if (!local_auth_token) { |
| // Authentication failed. Update the button status and rebuild the sheet, |
| // which will restart the Touch ID request if the sensor is not softlocked |
| // or display a padlock icon if it is. |
| dialog_model()->OnSheetModelChanged(); |
| return; |
| } |
| touch_id_completed_ = true; |
| dialog_model()->local_auth_token = std::move(local_auth_token); |
| dialog_model()->OnTouchIDComplete(true); |
| } |
| |
| #endif // IS_MAC |
| |
| // AuthenticatorOffTheRecordInterstitialSheetModel |
| // ----------------------------------------- |
| |
| AuthenticatorOffTheRecordInterstitialSheetModel:: |
| AuthenticatorOffTheRecordInterstitialSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| // TODO(crbug.com/40237082): Add more specific illustration once available. |
| // The "error" graphic is a large question mark, so it looks visually very |
| // similar. |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorOffTheRecordInterstitialSheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_PLATFORM_AUTHENTICATOR_OFF_THE_RECORD_INTERSTITIAL_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string |
| AuthenticatorOffTheRecordInterstitialSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_PLATFORM_AUTHENTICATOR_OFF_THE_RECORD_INTERSTITIAL_DESCRIPTION); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorOffTheRecordInterstitialSheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string |
| AuthenticatorOffTheRecordInterstitialSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE); |
| } |
| |
| void AuthenticatorOffTheRecordInterstitialSheetModel::OnAccept() { |
| dialog_model()->OnOffTheRecordInterstitialAccepted(); |
| } |
| |
| std::u16string |
| AuthenticatorOffTheRecordInterstitialSheetModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_PLATFORM_AUTHENTICATOR_OFF_THE_RECORD_INTERSTITIAL_DENY); |
| } |
| |
| // AuthenticatorPaaskSheetModel ----------------------------------------- |
| |
| AuthenticatorPaaskSheetModel::AuthenticatorPaaskSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| vector_illustrations_.emplace(kPasskeyPhoneIcon, kPasskeyPhoneDarkIcon); |
| } |
| |
| AuthenticatorPaaskSheetModel::~AuthenticatorPaaskSheetModel() = default; |
| |
| bool AuthenticatorPaaskSheetModel::IsActivityIndicatorVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorPaaskSheetModel::GetStepTitle() const { |
| switch (*dialog_model()->cable_ui_type) { |
| case AuthenticatorRequestDialogModel::CableUIType::CABLE_V1: |
| case AuthenticatorRequestDialogModel::CableUIType::CABLE_V2_SERVER_LINK: |
| // caBLEv1 and v2 server-link don't include device names. |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLE_ACTIVATE_TITLE); |
| case AuthenticatorRequestDialogModel::CableUIType::CABLE_V2_2ND_FACTOR: |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_CABLE_ACTIVATE_TITLE_DEVICE); |
| } |
| } |
| |
| std::u16string AuthenticatorPaaskSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLE_ACTIVATE_DESCRIPTION); |
| } |
| |
| // AuthenticatorClientPinEntrySheetModel |
| // ----------------------------------------- |
| |
| AuthenticatorClientPinEntrySheetModel::AuthenticatorClientPinEntrySheetModel( |
| AuthenticatorRequestDialogModel* dialog_model, |
| Mode mode, |
| device::pin::PINEntryError error) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible), |
| mode_(mode) { |
| vector_illustrations_.emplace(kPasskeyUsbIcon, kPasskeyUsbDarkIcon); |
| switch (error) { |
| case device::pin::PINEntryError::kNoError: |
| break; |
| case device::pin::PINEntryError::kInternalUvLocked: |
| error_ = l10n_util::GetStringUTF16(IDS_WEBAUTHN_UV_ERROR_LOCKED); |
| break; |
| case device::pin::PINEntryError::kInvalidCharacters: |
| error_ = l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_PIN_ENTRY_ERROR_INVALID_CHARACTERS); |
| break; |
| case device::pin::PINEntryError::kSameAsCurrentPIN: |
| error_ = l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_PIN_ENTRY_ERROR_SAME_AS_CURRENT); |
| break; |
| case device::pin::PINEntryError::kTooShort: |
| error_ = l10n_util::GetPluralStringFUTF16( |
| IDS_WEBAUTHN_PIN_ENTRY_ERROR_TOO_SHORT, dialog_model->min_pin_length); |
| break; |
| case device::pin::PINEntryError::kWrongPIN: |
| std::optional<int> attempts = dialog_model->pin_attempts; |
| error_ = |
| attempts && *attempts <= 3 |
| ? l10n_util::GetPluralStringFUTF16( |
| IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED_RETRIES, *attempts) |
| : l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_FAILED); |
| break; |
| } |
| } |
| |
| AuthenticatorClientPinEntrySheetModel:: |
| ~AuthenticatorClientPinEntrySheetModel() = default; |
| |
| void AuthenticatorClientPinEntrySheetModel::SetPinCode( |
| std::u16string pin_code) { |
| pin_code_ = std::move(pin_code); |
| } |
| |
| void AuthenticatorClientPinEntrySheetModel::SetPinConfirmation( |
| std::u16string pin_confirmation) { |
| DCHECK(mode_ == Mode::kPinSetup || mode_ == Mode::kPinChange); |
| pin_confirmation_ = std::move(pin_confirmation); |
| } |
| |
| std::u16string AuthenticatorClientPinEntrySheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_TITLE); |
| } |
| |
| std::u16string AuthenticatorClientPinEntrySheetModel::GetStepDescription() |
| const { |
| switch (mode_) { |
| case Mode::kPinChange: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_FORCE_PIN_CHANGE); |
| case Mode::kPinEntry: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_DESCRIPTION); |
| case Mode::kPinSetup: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_SETUP_DESCRIPTION); |
| } |
| } |
| |
| std::u16string AuthenticatorClientPinEntrySheetModel::GetError() const { |
| return error_; |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorClientPinEntrySheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string AuthenticatorClientPinEntrySheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT); |
| } |
| |
| void AuthenticatorClientPinEntrySheetModel::OnAccept() { |
| if ((mode_ == Mode::kPinChange || mode_ == Mode::kPinSetup) && |
| pin_code_ != pin_confirmation_) { |
| error_ = l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_ERROR_MISMATCH); |
| dialog_model()->OnSheetModelChanged(); |
| return; |
| } |
| |
| if (dialog_model()) { |
| dialog_model()->OnHavePIN(pin_code_); |
| } |
| } |
| |
| bool AuthenticatorClientPinEntrySheetModel::IsOtherMechanismButtonVisible() |
| const { |
| // Always allow restarting the request to select a different security key or |
| // hybrid authenticator. |
| return true; |
| } |
| |
| // AuthenticatorClientPinTapAgainSheetModel ---------------------- |
| |
| AuthenticatorClientPinTapAgainSheetModel:: |
| AuthenticatorClientPinTapAgainSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyUsbIcon, kPasskeyUsbDarkIcon); |
| } |
| |
| AuthenticatorClientPinTapAgainSheetModel:: |
| ~AuthenticatorClientPinTapAgainSheetModel() = default; |
| |
| bool AuthenticatorClientPinTapAgainSheetModel::IsActivityIndicatorVisible() |
| const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorClientPinTapAgainSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GENERIC_TITLE, GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorClientPinTapAgainSheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_TAP_AGAIN_DESCRIPTION); |
| } |
| |
| std::vector<std::u16string> |
| AuthenticatorClientPinTapAgainSheetModel::GetAdditionalDescriptions() const { |
| return {PossibleAttestationWarning(dialog_model()), |
| PossibleResidentKeyWarning(dialog_model())}; |
| } |
| |
| // AuthenticatorBioEnrollmentSheetModel ---------------------------------- |
| |
| // No illustration since the content already has a large animated |
| // fingerprint icon. |
| AuthenticatorBioEnrollmentSheetModel::AuthenticatorBioEnrollmentSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) {} |
| |
| AuthenticatorBioEnrollmentSheetModel::~AuthenticatorBioEnrollmentSheetModel() = |
| default; |
| |
| bool AuthenticatorBioEnrollmentSheetModel::IsActivityIndicatorVisible() const { |
| return !HasBioSamplesRemaining(); |
| } |
| |
| std::u16string AuthenticatorBioEnrollmentSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16( |
| IDS_SETTINGS_SECURITY_KEYS_BIO_ENROLLMENT_ADD_TITLE); |
| } |
| |
| std::u16string AuthenticatorBioEnrollmentSheetModel::GetStepDescription() |
| const { |
| return HasBioSamplesRemaining() |
| ? l10n_util::GetStringUTF16( |
| IDS_SETTINGS_SECURITY_KEYS_BIO_ENROLLMENT_ENROLLING_COMPLETE_LABEL) |
| : l10n_util::GetStringUTF16( |
| IDS_SETTINGS_SECURITY_KEYS_BIO_ENROLLMENT_ENROLLING_LABEL); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorBioEnrollmentSheetModel::GetAcceptButtonState() const { |
| return HasBioSamplesRemaining() ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorBioEnrollmentSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT); |
| } |
| |
| bool AuthenticatorBioEnrollmentSheetModel::IsCancelButtonVisible() const { |
| return !HasBioSamplesRemaining(); |
| } |
| |
| std::u16string AuthenticatorBioEnrollmentSheetModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_INLINE_ENROLLMENT_CANCEL_LABEL); |
| } |
| |
| void AuthenticatorBioEnrollmentSheetModel::OnAccept() { |
| dialog_model()->OnBioEnrollmentDone(); |
| } |
| |
| void AuthenticatorBioEnrollmentSheetModel::OnCancel() { |
| OnAccept(); |
| } |
| |
| bool AuthenticatorBioEnrollmentSheetModel::HasBioSamplesRemaining() const { |
| return dialog_model()->bio_samples_remaining && |
| dialog_model()->bio_samples_remaining <= 0; |
| } |
| |
| // AuthenticatorRetryUvSheetModel ------------------------------------- |
| |
| AuthenticatorRetryUvSheetModel::AuthenticatorRetryUvSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| vector_illustrations_.emplace(kPasskeyFingerprintIcon, |
| kPasskeyFingerprintDarkIcon); |
| } |
| |
| AuthenticatorRetryUvSheetModel::~AuthenticatorRetryUvSheetModel() = default; |
| |
| bool AuthenticatorRetryUvSheetModel::IsActivityIndicatorVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorRetryUvSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_UV_RETRY_TITLE); |
| } |
| |
| std::u16string AuthenticatorRetryUvSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_UV_RETRY_DESCRIPTION); |
| } |
| |
| std::u16string AuthenticatorRetryUvSheetModel::GetError() const { |
| int attempts = *dialog_model()->uv_attempts; |
| if (attempts > 3) { |
| return std::u16string(); |
| } |
| return l10n_util::GetPluralStringFUTF16( |
| IDS_WEBAUTHN_UV_RETRY_ERROR_FAILED_RETRIES, attempts); |
| } |
| |
| // AuthenticatorGenericErrorSheetModel ----------------------------------- |
| |
| // static |
| std::unique_ptr<AuthenticatorGenericErrorSheetModel> |
| AuthenticatorGenericErrorSheetModel::ForClientPinErrorSoftBlock( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| return base::WrapUnique(new AuthenticatorGenericErrorSheetModel( |
| dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE), |
| l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_CLIENT_PIN_SOFT_BLOCK_DESCRIPTION))); |
| } |
| |
| // static |
| std::unique_ptr<AuthenticatorGenericErrorSheetModel> |
| AuthenticatorGenericErrorSheetModel::ForClientPinErrorHardBlock( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| return base::WrapUnique(new AuthenticatorGenericErrorSheetModel( |
| dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE), |
| l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_CLIENT_PIN_HARD_BLOCK_DESCRIPTION))); |
| } |
| |
| // static |
| std::unique_ptr<AuthenticatorGenericErrorSheetModel> |
| AuthenticatorGenericErrorSheetModel::ForClientPinErrorAuthenticatorRemoved( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| return base::WrapUnique(new AuthenticatorGenericErrorSheetModel( |
| dialog_model, l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE), |
| l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_CLIENT_PIN_AUTHENTICATOR_REMOVED_DESCRIPTION))); |
| } |
| |
| // static |
| std::unique_ptr<AuthenticatorGenericErrorSheetModel> |
| AuthenticatorGenericErrorSheetModel::ForMissingCapability( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| return base::WrapUnique(new AuthenticatorGenericErrorSheetModel( |
| dialog_model, |
| l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE), |
| l10n_util::GetStringFUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_DESC, |
| GetRelyingPartyIdStringLabel(dialog_model)))); |
| } |
| |
| // static |
| std::unique_ptr<AuthenticatorGenericErrorSheetModel> |
| AuthenticatorGenericErrorSheetModel::ForStorageFull( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| return base::WrapUnique(new AuthenticatorGenericErrorSheetModel( |
| dialog_model, |
| l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_MISSING_CAPABILITY_TITLE), |
| l10n_util::GetStringUTF16(IDS_WEBAUTHN_STORAGE_FULL_DESC))); |
| } |
| |
| std::unique_ptr<AuthenticatorGenericErrorSheetModel> |
| AuthenticatorGenericErrorSheetModel::ForWindowsHelloNotEnabled( |
| AuthenticatorRequestDialogModel* dialog_model) { |
| return base::WrapUnique(new AuthenticatorGenericErrorSheetModel( |
| dialog_model, |
| l10n_util::GetStringUTF16(IDS_WEBAUTHN_WINDOWS_HELLO_NOT_ENABLED_TITLE), |
| l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_WINDOWS_HELLO_NOT_ENABLED_DESCRIPTION))); |
| } |
| |
| AuthenticatorGenericErrorSheetModel::AuthenticatorGenericErrorSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model, |
| std::u16string title, |
| std::u16string description) |
| : AuthenticatorSheetModelBase(dialog_model), |
| title_(std::move(title)), |
| description_(std::move(description)) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| std::u16string AuthenticatorGenericErrorSheetModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CLOSE); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorGenericErrorSheetModel::GetAcceptButtonState() const { |
| return dialog_model()->offer_try_again_in_ui ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorGenericErrorSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RETRY); |
| } |
| |
| std::u16string AuthenticatorGenericErrorSheetModel::GetStepTitle() const { |
| return title_; |
| } |
| |
| std::u16string AuthenticatorGenericErrorSheetModel::GetStepDescription() const { |
| return description_; |
| } |
| |
| void AuthenticatorGenericErrorSheetModel::OnAccept() { |
| dialog_model()->StartOver(); |
| } |
| |
| // AuthenticatorResidentCredentialConfirmationSheetView ----------------------- |
| |
| // TODO(crbug.com/40237082): Add more specific illustration once available. The |
| // "error" graphic is a large question mark, so it looks visually very similar. |
| AuthenticatorResidentCredentialConfirmationSheetView:: |
| AuthenticatorResidentCredentialConfirmationSheetView( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| AuthenticatorResidentCredentialConfirmationSheetView:: |
| ~AuthenticatorResidentCredentialConfirmationSheetView() = default; |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorResidentCredentialConfirmationSheetView::GetAcceptButtonState() |
| const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string |
| AuthenticatorResidentCredentialConfirmationSheetView::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE); |
| } |
| |
| std::u16string |
| AuthenticatorResidentCredentialConfirmationSheetView::GetStepTitle() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GENERIC_TITLE, GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string |
| AuthenticatorResidentCredentialConfirmationSheetView::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_RESIDENT_KEY_PRIVACY); |
| } |
| |
| void AuthenticatorResidentCredentialConfirmationSheetView::OnAccept() { |
| dialog_model()->OnResidentCredentialConfirmed(); |
| } |
| |
| // AuthenticatorSelectAccountSheetModel --------------------------------------- |
| |
| AuthenticatorSelectAccountSheetModel::AuthenticatorSelectAccountSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model, |
| UserVerificationMode mode) |
| : AuthenticatorSheetModelBase( |
| dialog_model, |
| mode == kPreUserVerification |
| ? OtherMechanismButtonVisibility::kVisible |
| : OtherMechanismButtonVisibility::kHidden), |
| user_verification_mode_(mode) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT, |
| IDR_WEBAUTHN_PASSKEY_DARK); |
| } |
| |
| AuthenticatorSelectAccountSheetModel::~AuthenticatorSelectAccountSheetModel() = |
| default; |
| |
| void AuthenticatorSelectAccountSheetModel::SetCurrentSelection(int selected) { |
| DCHECK_LE(0, selected); |
| DCHECK_LT(static_cast<size_t>(selected), dialog_model()->creds.size()); |
| selected_ = selected; |
| } |
| |
| void AuthenticatorSelectAccountSheetModel::OnAccept() { |
| switch (user_verification_mode_) { |
| case kPreUserVerification: |
| dialog_model()->OnAccountPreselectedIndex(selected_); |
| break; |
| case kPostUserVerification: |
| dialog_model()->OnAccountSelected(selected_); |
| break; |
| } |
| } |
| |
| std::u16string AuthenticatorSelectAccountSheetModel::GetStepTitle() const { |
| if (dialog_model()->creds.size() > 1) { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CHOOSE_PASSKEY_TITLE); |
| } |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_USE_PASSKEY_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorSelectAccountSheetModel::GetStepDescription() |
| const { |
| if (dialog_model()->creds.size() > 1) { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_CHOOSE_PASSKEY_BODY, |
| GetRelyingPartyIdStringLabel(dialog_model())); |
| } |
| return u""; |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorSelectAccountSheetModel::GetAcceptButtonState() const { |
| return dialog_model()->creds.size() == 1 ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorSelectAccountSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE); |
| } |
| |
| // AuthenticatorHybridAndSecurityKeySheetModel -------------------------------- |
| |
| AuthenticatorHybridAndSecurityKeySheetModel:: |
| AuthenticatorHybridAndSecurityKeySheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| webauthn::user_actions::RecordHybridAndSecurityKeyDialogShown( |
| dialog_model->request_type); |
| } |
| |
| AuthenticatorHybridAndSecurityKeySheetModel:: |
| ~AuthenticatorHybridAndSecurityKeySheetModel() = default; |
| |
| std::u16string AuthenticatorHybridAndSecurityKeySheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringUTF16(dialog_model()->show_security_key_on_qr_sheet |
| ? IDS_WEBAUTHN_PASSKEYS_AND_SECURITY_KEYS |
| : IDS_WEBAUTHN_PASSKEYS); |
| } |
| |
| std::u16string AuthenticatorHybridAndSecurityKeySheetModel::GetStepDescription() |
| const { |
| return u""; |
| } |
| |
| std::u16string |
| AuthenticatorHybridAndSecurityKeySheetModel::GetOtherMechanismButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_BACK); |
| } |
| |
| std::optional<std::u16string> |
| AuthenticatorHybridAndSecurityKeySheetModel::GetAttestationWarning() const { |
| if (dialog_model()->show_security_key_on_qr_sheet && |
| (dialog_model()->request_type == |
| device::FidoRequestType::kMakeCredential)) { |
| std::u16string warning = PossibleAttestationWarning(dialog_model()); |
| if (!warning.empty()) { |
| return warning; |
| } |
| } |
| return std::nullopt; |
| } |
| |
| // AuthenticatorConnectingSheetModel ------------------------------------------ |
| |
| AuthenticatorConnectingSheetModel::AuthenticatorConnectingSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_HYBRID_CONNECTING_LIGHT, |
| IDR_WEBAUTHN_HYBRID_CONNECTING_DARK); |
| } |
| |
| AuthenticatorConnectingSheetModel::~AuthenticatorConnectingSheetModel() = |
| default; |
| |
| std::u16string AuthenticatorConnectingSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_CONNECTING_TITLE); |
| } |
| |
| std::u16string AuthenticatorConnectingSheetModel::GetStepDescription() const { |
| return u""; |
| } |
| |
| // AuthenticatorConnectedSheetModel ------------------------------------------ |
| |
| AuthenticatorConnectedSheetModel::AuthenticatorConnectedSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| vector_illustrations_.emplace(kPasskeyPhoneIcon, kPasskeyPhoneDarkIcon); |
| } |
| |
| AuthenticatorConnectedSheetModel::~AuthenticatorConnectedSheetModel() = default; |
| |
| bool AuthenticatorConnectedSheetModel::IsActivityIndicatorVisible() const { |
| return false; |
| } |
| |
| std::u16string AuthenticatorConnectedSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_CONNECTED_DESCRIPTION); |
| } |
| |
| std::u16string AuthenticatorConnectedSheetModel::GetStepDescription() const { |
| return u""; |
| } |
| |
| // AuthenticatorCableErrorSheetModel ------------------------------------------ |
| |
| AuthenticatorCableErrorSheetModel::AuthenticatorCableErrorSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| } |
| |
| AuthenticatorCableErrorSheetModel::~AuthenticatorCableErrorSheetModel() = |
| default; |
| |
| std::u16string AuthenticatorCableErrorSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_ERROR_GENERIC_TITLE); |
| } |
| |
| std::u16string AuthenticatorCableErrorSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_ERROR_DESCRIPTION); |
| } |
| |
| std::u16string AuthenticatorCableErrorSheetModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CABLEV2_ERROR_CLOSE); |
| } |
| |
| // AuthenticatorCreatePasskeySheetModel |
| // -------------------------------------------------- |
| |
| AuthenticatorCreatePasskeySheetModel::AuthenticatorCreatePasskeySheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT, |
| IDR_WEBAUTHN_PASSKEY_DARK); |
| } |
| |
| AuthenticatorCreatePasskeySheetModel::~AuthenticatorCreatePasskeySheetModel() = |
| default; |
| |
| std::u16string AuthenticatorCreatePasskeySheetModel::GetStepTitle() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_CREATE_PASSKEY_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorCreatePasskeySheetModel::GetStepDescription() |
| const { |
| return u""; |
| } |
| |
| std::u16string |
| AuthenticatorCreatePasskeySheetModel::passkey_storage_description() const { |
| return l10n_util::GetStringUTF16( |
| dialog_model()->is_off_the_record |
| ? IDS_WEBAUTHN_CREATE_PASSKEY_EXTRA_INCOGNITO |
| : IDS_WEBAUTHN_CREATE_PASSKEY_EXTRA); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorCreatePasskeySheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string AuthenticatorCreatePasskeySheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE); |
| } |
| |
| void AuthenticatorCreatePasskeySheetModel::OnAccept() { |
| dialog_model()->OnCreatePasskeyAccepted(); |
| } |
| |
| // AuthenticatorGPMErrorSheetModel ------------------------------------------- |
| |
| AuthenticatorGPMErrorSheetModel::AuthenticatorGPMErrorSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| vector_illustrations_.emplace(kPasskeyErrorIcon, kPasskeyErrorDarkIcon); |
| if (dialog_model->in_onboarding_flow) { |
| RecordOnboardingEvent(webauthn::metrics::OnboardingEvents::kFailure); |
| } |
| webauthn::user_actions::RecordGpmFailureShown(); |
| switch (dialog_model->request_type) { |
| case device::FidoRequestType::kGetAssertion: |
| RecordGPMGetAssertionEvent( |
| webauthn::metrics::GPMGetAssertionEvents::kFailure); |
| break; |
| case device::FidoRequestType::kMakeCredential: |
| RecordGPMMakeCredentialEvent( |
| webauthn::metrics::GPMMakeCredentialEvents::kFailure); |
| break; |
| } |
| } |
| |
| AuthenticatorGPMErrorSheetModel::~AuthenticatorGPMErrorSheetModel() = default; |
| |
| std::u16string AuthenticatorGPMErrorSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_ERROR_TITLE); |
| } |
| |
| std::u16string AuthenticatorGPMErrorSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_ERROR_DESC); |
| } |
| |
| // AuthenticatorGPMConnectingSheetModel -------------------------------------- |
| |
| AuthenticatorGPMConnectingSheetModel::AuthenticatorGPMConnectingSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_HYBRID_CONNECTING_LIGHT, |
| IDR_WEBAUTHN_HYBRID_CONNECTING_DARK); |
| } |
| |
| AuthenticatorGPMConnectingSheetModel::~AuthenticatorGPMConnectingSheetModel() = |
| default; |
| |
| std::u16string AuthenticatorGPMConnectingSheetModel::GetStepTitle() const { |
| return u""; |
| } |
| |
| std::u16string AuthenticatorGPMConnectingSheetModel::GetStepDescription() |
| const { |
| return u""; |
| } |
| |
| // AuthenticatorMultiSourcePickerSheetModel -------------------------------- |
| |
| AuthenticatorMultiSourcePickerSheetModel:: |
| AuthenticatorMultiSourcePickerSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT, |
| IDR_WEBAUTHN_PASSKEY_DARK); |
| |
| webauthn::user_actions::RecordMultipleOptionsShown( |
| dialog_model->mechanisms, dialog_model->request_type); |
| if (std::ranges::any_of(dialog_model->mechanisms, |
| &IsLocalPasskeyOrEnclaveAuthenticatorOrPassword)) { |
| primary_passkeys_label_ = |
| l10n_util::GetStringUTF16(IDS_WEBAUTHN_THIS_DEVICE_LABEL); |
| for (size_t i = 0; i < dialog_model->mechanisms.size(); ++i) { |
| const AuthenticatorRequestDialogModel::Mechanism& mech = |
| dialog_model->mechanisms[i]; |
| if (IsLocalPasskeyOrEnclaveAuthenticatorOrPassword(mech) || |
| // iCloud Keychain appears in the primary list if present. This |
| // happens when Chrome does not have permission to enumerate |
| // credentials from iCloud Keychain. Thus this generic option is the |
| // only way for the user to trigger it. |
| std::holds_alternative<ICloudKeychainMech>(mech.type)) { |
| primary_passkey_indices_.push_back(i); |
| } else { |
| secondary_passkey_indices_.push_back(i); |
| } |
| if (std::holds_alternative<PasswordMech>(mech.type)) { |
| has_passwords_ = true; |
| } |
| } |
| return; |
| } |
| |
| for (size_t i = 0; i < dialog_model->mechanisms.size(); ++i) { |
| secondary_passkey_indices_.push_back(i); |
| } |
| } |
| |
| AuthenticatorMultiSourcePickerSheetModel:: |
| ~AuthenticatorMultiSourcePickerSheetModel() = default; |
| |
| std::u16string AuthenticatorMultiSourcePickerSheetModel::GetStepTitle() const { |
| if (has_passwords_) { |
| return u"Use a saved credential for " + |
| GetRelyingPartyIdStringTitle(dialog_model()) + u" (UT)"; |
| } |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_CHOOSE_PASSKEY_FOR_RP_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorMultiSourcePickerSheetModel::GetStepDescription() |
| const { |
| return u""; |
| } |
| |
| // AuthenticatorPriorityMechanismSheetModel -------------------------------- |
| |
| AuthenticatorPriorityMechanismSheetModel:: |
| AuthenticatorPriorityMechanismSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_PASSKEY_LIGHT, |
| IDR_WEBAUTHN_PASSKEY_DARK); |
| |
| webauthn::user_actions::RecordPriorityOptionShown( |
| dialog_model->mechanisms[*dialog_model->priority_mechanism_index]); |
| } |
| AuthenticatorPriorityMechanismSheetModel:: |
| ~AuthenticatorPriorityMechanismSheetModel() = default; |
| |
| std::u16string AuthenticatorPriorityMechanismSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_USE_PASSKEY_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorPriorityMechanismSheetModel::GetStepDescription() |
| const { |
| return u""; |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorPriorityMechanismSheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string AuthenticatorPriorityMechanismSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE); |
| } |
| |
| void AuthenticatorPriorityMechanismSheetModel::OnAccept() { |
| dialog_model()->OnUserConfirmedPriorityMechanism(); |
| } |
| |
| // AuthenticatorGpmPinSheetModelBase ------------------------------------------- |
| |
| AuthenticatorGpmPinSheetModelBase::AuthenticatorGpmPinSheetModelBase( |
| AuthenticatorRequestDialogModel* dialog_model, |
| Mode mode) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden), |
| mode_(mode) {} |
| |
| AuthenticatorGpmPinSheetModelBase::~AuthenticatorGpmPinSheetModelBase() = |
| default; |
| |
| std::u16string AuthenticatorGpmPinSheetModelBase::GetGpmAccountEmail() const { |
| std::optional<AccountInfo> account_info = dialog_model()->GetGpmAccountInfo(); |
| if (!account_info) { |
| return std::u16string(); |
| } |
| return base::UTF8ToUTF16(account_info->email); |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModelBase::GetGpmAccountName() const { |
| std::optional<AccountInfo> account_info = dialog_model()->GetGpmAccountInfo(); |
| if (!account_info) { |
| return std::u16string(); |
| } |
| return base::UTF8ToUTF16(account_info->full_name); |
| } |
| |
| gfx::Image AuthenticatorGpmPinSheetModelBase::GetGpmAccountImage() const { |
| std::optional<AccountInfo> account_info = dialog_model()->GetGpmAccountInfo(); |
| if (!account_info) { |
| return gfx::Image(); |
| } |
| gfx::Image account_image = account_info->account_image; |
| if (account_image.IsEmpty()) { |
| account_image = ui::ResourceBundle::GetSharedInstance().GetImageNamed( |
| profiles::GetPlaceholderAvatarIconResourceID()); |
| } |
| constexpr int kAvatarIconSize = 32; |
| return profiles::GetSizedAvatarIcon(account_image, |
| /*width=*/kAvatarIconSize, |
| /*height=*/kAvatarIconSize, |
| profiles::SHAPE_CIRCLE); |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModelBase::GetAccessibleDescription() |
| const { |
| std::u16string error = GetError(); |
| return error.empty() ? GetHint() : error; |
| } |
| |
| bool AuthenticatorGpmPinSheetModelBase::ui_disabled() const { |
| return dialog_model()->ui_disabled_; |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModelBase::GetStepTitle() const { |
| switch (mode_) { |
| case Mode::kPinCreate: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_CREATE_PIN_TITLE); |
| case Mode::kPinEntry: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_ENTER_PIN_TITLE); |
| } |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModelBase::GetStepDescription() const { |
| switch (mode_) { |
| case Mode::kPinCreate: |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_CREATE_PIN_DESC); |
| case Mode::kPinEntry: |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_ENTER_PIN_DESC, |
| GetRelyingPartyIdStringLabel(dialog_model())); |
| } |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModelBase::GetError() const { |
| std::optional<int> remaining_attempts = |
| dialog_model()->gpm_pin_remaining_attempts_; |
| return remaining_attempts && mode_ == Mode::kPinEntry |
| ? l10n_util::GetPluralStringFUTF16( |
| IDS_WEBAUTHN_GPM_WRONG_PIN_ERROR, *remaining_attempts) |
| : std::u16string(); |
| } |
| |
| bool AuthenticatorGpmPinSheetModelBase::IsForgotGPMPinButtonVisible() const { |
| return mode_ == Mode::kPinEntry; |
| } |
| |
| bool AuthenticatorGpmPinSheetModelBase::IsGPMPinOptionsButtonVisible() const { |
| return mode_ == Mode::kPinCreate; |
| } |
| |
| void AuthenticatorGpmPinSheetModelBase::OnAccept() { |
| webauthn::user_actions::RecordAcceptClick(); |
| dialog_model()->OnGPMPinEntered(pin_); |
| } |
| |
| void AuthenticatorGpmPinSheetModelBase::OnCancel() { |
| if (dialog_model()->in_onboarding_flow) { |
| RecordOnboardingEvent(webauthn::metrics::OnboardingEvents:: |
| kAuthenticatorGpmPinSheetCancelled); |
| } |
| AuthenticatorSheetModelBase::OnCancel(); |
| } |
| |
| void AuthenticatorGpmPinSheetModelBase::OnForgotGPMPin() const { |
| webauthn::user_actions::RecordGpmForgotPinClick(); |
| dialog_model()->OnForgotGPMPinPressed(); |
| } |
| |
| void AuthenticatorGpmPinSheetModelBase::OnGPMPinOptionChosen( |
| bool is_arbitrary) const { |
| if ((dialog_model()->step() == Step::kGPMChangeArbitraryPin || |
| dialog_model()->step() == Step::kGPMCreateArbitraryPin || |
| dialog_model()->step() == Step::kGPMEnterArbitraryPin) && |
| is_arbitrary) { |
| // The sheet already facilitates entering arbitrary pin. |
| return; |
| } |
| if ((dialog_model()->step() == Step::kGPMChangePin || |
| dialog_model()->step() == Step::kGPMCreatePin || |
| dialog_model()->step() == Step::kGPMEnterPin) && |
| !is_arbitrary) { |
| // The sheet already facilitates entering six digit pin. |
| return; |
| } |
| webauthn::user_actions::RecordGpmPinOptionChangeClick(); |
| dialog_model()->OnGPMPinOptionChanged(is_arbitrary); |
| } |
| |
| // AuthenticatorGpmPinSheetModel ----------------------------------------------- |
| |
| AuthenticatorGpmPinSheetModel::AuthenticatorGpmPinSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model, |
| int pin_digits_count, |
| Mode mode) |
| : AuthenticatorGpmPinSheetModelBase(dialog_model, mode), |
| pin_digits_count_(pin_digits_count) { |
| webauthn::user_actions::RecordGpmPinSheetShown( |
| dialog_model->request_type, |
| /*is_pin_creation=*/mode == Mode::kPinCreate, |
| /*is_arbitrary=*/false); |
| } |
| |
| AuthenticatorGpmPinSheetModel::~AuthenticatorGpmPinSheetModel() = default; |
| |
| void AuthenticatorGpmPinSheetModel::PinCharTyped(bool is_digit) { |
| if (mode_ != Mode::kPinCreate || show_digit_hint_ != is_digit) { |
| return; |
| } |
| |
| show_digit_hint_ = !is_digit; |
| dialog_model()->OnSheetModelChanged(); |
| } |
| |
| int AuthenticatorGpmPinSheetModel::pin_digits_count() const { |
| return pin_digits_count_; |
| } |
| |
| void AuthenticatorGpmPinSheetModel::SetPin(std::u16string pin) { |
| bool full_pin_typed_before = FullPinTyped(); |
| pin_ = std::move(pin); |
| bool full_pin_typed = FullPinTyped(); |
| |
| // When entering an existing PIN, the dialog completes as soon as all the |
| // digits have been typed. When creating a new PIN, the user has to hit enter |
| // to confirm. |
| if (mode_ == Mode::kPinEntry && full_pin_typed) { |
| dialog_model()->OnGPMPinEntered(pin_); |
| } else if (mode_ == Mode::kPinCreate && |
| full_pin_typed_before != full_pin_typed) { |
| dialog_model()->OnButtonsStateChanged(); |
| } |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModel::GetAccessibleName() const { |
| std::u16string pin_digits_typed_str = base::NumberToString16( |
| std::min(static_cast<int>(pin_.length()) + 1, pin_digits_count_)); |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_SIX_DIGIT_PIN_ACCESSIBILITY_LABEL, pin_digits_typed_str); |
| } |
| |
| bool AuthenticatorGpmPinSheetModel::FullPinTyped() const { |
| return static_cast<int>(pin_.length()) == pin_digits_count_; |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorGpmPinSheetModel::GetAcceptButtonState() const { |
| if (mode() == Mode::kPinCreate) { |
| return FullPinTyped() && !ui_disabled() ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kDisabled; |
| } |
| return AcceptButtonState::kNotVisible; |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_CONFIRM); |
| } |
| |
| std::u16string AuthenticatorGpmPinSheetModel::GetHint() const { |
| return mode_ == Mode::kPinCreate && show_digit_hint_ |
| ? l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_PIN_DIGIT_HINT) |
| : std::u16string(); |
| } |
| |
| // AuthenticatorGpmArbitraryPinSheetModel -------------------------------------- |
| |
| AuthenticatorGpmArbitraryPinSheetModel::AuthenticatorGpmArbitraryPinSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model, |
| Mode mode) |
| : AuthenticatorGpmPinSheetModelBase(dialog_model, mode) { |
| webauthn::user_actions::RecordGpmPinSheetShown( |
| dialog_model->request_type, |
| /*is_pin_creation=*/mode == Mode::kPinCreate, |
| /*is_arbitrary=*/true); |
| } |
| |
| AuthenticatorGpmArbitraryPinSheetModel:: |
| ~AuthenticatorGpmArbitraryPinSheetModel() = default; |
| |
| void AuthenticatorGpmArbitraryPinSheetModel::SetPin(std::u16string pin) { |
| bool accept_button_enabled = |
| GetAcceptButtonState() == AcceptButtonState::kEnabled; |
| pin_ = std::move(pin); |
| if (accept_button_enabled != |
| (GetAcceptButtonState() == AcceptButtonState::kEnabled)) { |
| dialog_model()->OnButtonsStateChanged(); |
| } |
| } |
| |
| std::u16string AuthenticatorGpmArbitraryPinSheetModel::GetAccessibleName() |
| const { |
| switch (mode_) { |
| case Mode::kPinCreate: |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_GPM_CREATE_ALPHANUMERIC_PIN_ACCESSIBILITY); |
| case Mode::kPinEntry: |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_ENTER_ALPHANUMERIC_PIN_ACCESSIBILITY_WITH_WEBSITE, |
| base::UTF8ToUTF16(dialog_model()->relying_party_id)); |
| } |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorGpmArbitraryPinSheetModel::GetAcceptButtonState() const { |
| return pin_.length() >= kGpmArbitraryPinMinLength && !ui_disabled() |
| ? AcceptButtonState::kEnabled |
| : AcceptButtonState::kDisabled; |
| } |
| |
| std::u16string AuthenticatorGpmArbitraryPinSheetModel::GetAcceptButtonLabel() |
| const { |
| return mode_ == Mode::kPinEntry |
| ? l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT) |
| : l10n_util::GetStringUTF16(IDS_CONFIRM); |
| } |
| |
| std::u16string AuthenticatorGpmArbitraryPinSheetModel::GetHint() const { |
| return mode_ == Mode::kPinCreate |
| ? l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_PIN_LENGTH_HINT) |
| : std::u16string(); |
| } |
| |
| // AuthenticatorTrustThisComputerAssertionSheetModel ------------------------- |
| |
| AuthenticatorTrustThisComputerAssertionSheetModel:: |
| AuthenticatorTrustThisComputerAssertionSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_LAPTOP_LIGHT, |
| IDR_WEBAUTHN_LAPTOP_DARK); |
| |
| webauthn::user_actions::RecordTrustDialogShown(dialog_model->request_type); |
| } |
| |
| AuthenticatorTrustThisComputerAssertionSheetModel:: |
| ~AuthenticatorTrustThisComputerAssertionSheetModel() = default; |
| |
| std::u16string AuthenticatorTrustThisComputerAssertionSheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_ASSERTION_TITLE); |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerAssertionSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_ASSERTION_DESC); |
| } |
| |
| bool AuthenticatorTrustThisComputerAssertionSheetModel::IsCancelButtonVisible() |
| const { |
| return true; |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerAssertionSheetModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CANCEL); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorTrustThisComputerAssertionSheetModel::GetAcceptButtonState() |
| const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerAssertionSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT); |
| } |
| |
| bool AuthenticatorTrustThisComputerAssertionSheetModel:: |
| IsOtherMechanismButtonVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorTrustThisComputerAssertionSheetModel:: |
| GetOtherMechanismButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_USE_A_DIFFERENT_DEVICE); |
| } |
| |
| void AuthenticatorTrustThisComputerAssertionSheetModel::OnAccept() { |
| webauthn::user_actions::RecordAcceptClick(); |
| dialog_model()->OnTrustThisComputer(); |
| } |
| |
| // AuthenticatorCreateGpmPasskeySheetModel ------------------------------------- |
| |
| AuthenticatorCreateGpmPasskeySheetModel:: |
| AuthenticatorCreateGpmPasskeySheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_GPM_PASSKEY_LIGHT, |
| IDR_WEBAUTHN_GPM_PASSKEY_DARK); |
| |
| webauthn::user_actions::RecordCreateGpmDialogShown(); |
| } |
| |
| AuthenticatorCreateGpmPasskeySheetModel:: |
| ~AuthenticatorCreateGpmPasskeySheetModel() = default; |
| |
| std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetStepTitle() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_CREATE_PASSKEY_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_CREATE_PASSKEY_DESC, |
| base::UTF8ToUTF16(dialog_model()->GetGpmAccountEmail())); |
| } |
| |
| bool AuthenticatorCreateGpmPasskeySheetModel::IsCancelButtonVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CANCEL); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorCreateGpmPasskeySheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string AuthenticatorCreateGpmPasskeySheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CREATE); |
| } |
| |
| void AuthenticatorCreateGpmPasskeySheetModel::OnAccept() { |
| webauthn::user_actions::RecordAcceptClick(); |
| dialog_model()->OnGPMCreatePasskey(); |
| } |
| |
| void AuthenticatorCreateGpmPasskeySheetModel::OnCancel() { |
| if (dialog_model()->in_onboarding_flow) { |
| RecordOnboardingEvent( |
| webauthn::metrics::OnboardingEvents::kCreateGpmPasskeySheetCancelled); |
| } |
| AuthenticatorSheetModelBase::OnCancel(); |
| } |
| |
| void AuthenticatorCreateGpmPasskeySheetModel::OnBack() { |
| if (dialog_model()->in_onboarding_flow) { |
| RecordOnboardingEvent(webauthn::metrics::OnboardingEvents:: |
| kCreateGpmPasskeySheetSaveAnotherWaySelected); |
| } |
| AuthenticatorSheetModelBase::OnBack(); |
| } |
| |
| // AuthenticatorGpmIncognitoCreateSheetModel --------------------------------- |
| AuthenticatorGpmIncognitoCreateSheetModel:: |
| AuthenticatorGpmIncognitoCreateSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| // Incognito always has a dark color scheme and so the two illustrations are |
| // the same. |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_GPM_INCOGNITO, |
| IDR_WEBAUTHN_GPM_INCOGNITO); |
| } |
| |
| AuthenticatorGpmIncognitoCreateSheetModel:: |
| ~AuthenticatorGpmIncognitoCreateSheetModel() = default; |
| |
| std::u16string AuthenticatorGpmIncognitoCreateSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_INCOGNITO_CREATE_TITLE); |
| } |
| |
| std::u16string AuthenticatorGpmIncognitoCreateSheetModel::GetStepDescription() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_GPM_INCOGNITO_CREATE_DESC); |
| } |
| |
| bool AuthenticatorGpmIncognitoCreateSheetModel::IsCancelButtonVisible() const { |
| return true; |
| } |
| |
| std::u16string AuthenticatorGpmIncognitoCreateSheetModel::GetCancelButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_CANCEL); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorGpmIncognitoCreateSheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string AuthenticatorGpmIncognitoCreateSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CONTINUE); |
| } |
| void AuthenticatorGpmIncognitoCreateSheetModel::OnAccept() { |
| dialog_model()->OnGPMConfirmOffTheRecordCreate(); |
| } |
| |
| // AuthenticatorTrustThisComputerCreationSheetModel --------------------- |
| |
| AuthenticatorTrustThisComputerCreationSheetModel:: |
| AuthenticatorTrustThisComputerCreationSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kVisible) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_LAPTOP_LIGHT, |
| IDR_WEBAUTHN_LAPTOP_DARK); |
| |
| webauthn::user_actions::RecordTrustDialogShown(dialog_model->request_type); |
| } |
| |
| AuthenticatorTrustThisComputerCreationSheetModel:: |
| ~AuthenticatorTrustThisComputerCreationSheetModel() = default; |
| |
| std::u16string AuthenticatorTrustThisComputerCreationSheetModel::GetStepTitle() |
| const { |
| return l10n_util::GetStringUTF16( |
| IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_CREATION_TITLE); |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerCreationSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_GPM_TRUST_THIS_COMPUTER_CREATION_DESC, |
| base::UTF8ToUTF16(dialog_model()->GetGpmAccountEmail())); |
| } |
| |
| bool AuthenticatorTrustThisComputerCreationSheetModel::IsCancelButtonVisible() |
| const { |
| return true; |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerCreationSheetModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_CANCEL); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorTrustThisComputerCreationSheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerCreationSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_PIN_ENTRY_NEXT); |
| } |
| |
| std::u16string |
| AuthenticatorTrustThisComputerCreationSheetModel::GetOtherMechanismButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_SAVE_ANOTHER_WAY); |
| } |
| |
| void AuthenticatorTrustThisComputerCreationSheetModel::OnAccept() { |
| webauthn::user_actions::RecordAcceptClick(); |
| dialog_model()->OnTrustThisComputer(); |
| } |
| |
| // AuthenticatorGPMLockedPinSheetModel ---------------------------------- |
| |
| AuthenticatorGPMLockedPinSheetModel::AuthenticatorGPMLockedPinSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| lottie_illustrations_.emplace(IDR_WEBAUTHN_GPM_PIN_LOCKED_LIGHT, |
| IDR_WEBAUTHN_GPM_PIN_LOCKED_DARK); |
| webauthn::user_actions::RecordGpmLockedShown(); |
| } |
| |
| AuthenticatorGPMLockedPinSheetModel::~AuthenticatorGPMLockedPinSheetModel() = |
| default; |
| |
| std::u16string AuthenticatorGPMLockedPinSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_LOCKED_GPM_PIN_TITLE); |
| } |
| |
| std::u16string AuthenticatorGPMLockedPinSheetModel::GetStepDescription() const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_LOCKED_GPM_PIN_DESCRIPTION); |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| AuthenticatorGPMLockedPinSheetModel::GetAcceptButtonState() const { |
| return AcceptButtonState::kEnabled; |
| } |
| |
| std::u16string AuthenticatorGPMLockedPinSheetModel::GetAcceptButtonLabel() |
| const { |
| return l10n_util::GetStringUTF16(IDS_WEBAUTHN_CHANGE_PIN); |
| } |
| |
| void AuthenticatorGPMLockedPinSheetModel::OnAccept() { |
| webauthn::user_actions::RecordAcceptClick(); |
| dialog_model()->OnForgotGPMPinPressed(); |
| } |
| |
| // CombinedSelectorSheetModel |
| |
| CombinedSelectorSheetModel::CombinedSelectorSheetModel( |
| AuthenticatorRequestDialogModel* dialog_model) |
| : AuthenticatorSheetModelBase(dialog_model, |
| OtherMechanismButtonVisibility::kHidden) { |
| webauthn::user_actions::RecordCombinedSelectorShown(); |
| webauthn::metrics::RecordCombinedSelectorShown( |
| /*credential_count=*/dialog_model->mechanisms.size()); |
| } |
| |
| CombinedSelectorSheetModel::SelectionStatus |
| CombinedSelectorSheetModel::GetSelectionStatus(size_t index) const { |
| if (dialog_model()->mechanisms.size() == 1) { |
| return SelectionStatus::kNone; |
| } |
| return selection_index_ == index ? SelectionStatus::kSelected |
| : SelectionStatus::kNotSelected; |
| } |
| |
| size_t CombinedSelectorSheetModel::GetSelectionIndex() const { |
| return selection_index_; |
| } |
| |
| void CombinedSelectorSheetModel::SetSelectionIndex(size_t index) { |
| selection_index_ = index; |
| } |
| |
| std::u16string CombinedSelectorSheetModel::GetStepTitle() const { |
| return l10n_util::GetStringFUTF16( |
| IDS_WEBAUTHN_SIGN_IN_TO_WEBSITE_DIALOG_TITLE, |
| GetRelyingPartyIdStringTitle(dialog_model())); |
| } |
| |
| std::u16string CombinedSelectorSheetModel::GetStepDescription() const { |
| return u""; |
| } |
| |
| AuthenticatorRequestSheetModel::AcceptButtonState |
| CombinedSelectorSheetModel::GetAcceptButtonState() const { |
| return dialog_model()->ui_disabled_ ? AcceptButtonState::kDisabledWithSpinner |
| : AcceptButtonState::kEnabled; |
| } |
| |
| bool CombinedSelectorSheetModel::IsCancelButtonVisible() const { |
| return true; |
| } |
| |
| bool CombinedSelectorSheetModel::IsActivityIndicatorVisible() const { |
| return false; |
| } |
| |
| std::u16string CombinedSelectorSheetModel::GetCancelButtonLabel() const { |
| return l10n_util::GetStringUTF16(IDS_SIGNIN_ACCESSIBLE_CLOSE_BUTTON); |
| } |
| |
| std::u16string CombinedSelectorSheetModel::GetAcceptButtonLabel() const { |
| return l10n_util::GetStringUTF16( |
| IDS_PASSWORD_MANAGER_ACCOUNT_CHOOSER_SIGN_IN); |
| } |
| |
| void CombinedSelectorSheetModel::OnAccept() { |
| webauthn::metrics::RecordCombinedSelectorAccept( |
| /*credential_count=*/dialog_model()->mechanisms.size(), |
| /*default_selected=*/selection_index_ == 0); |
| const auto& mech = dialog_model()->mechanisms.at(selection_index_); |
| webauthn::user_actions::RecordMechanismClick(mech); |
| mech.callback.Run(); |
| } |
| |
| void CombinedSelectorSheetModel::OnCancel() { |
| webauthn::metrics::RecordCombinedSelectorCancelButtonClicked(); |
| AuthenticatorSheetModelBase::OnCancel(); |
| } |