WebAuthn: Centralize icon logic and use color icons for immediate mode

Implementation
Light: photos.app.goo.gl/jYhmR21XkMRuQGoW8
Dark: photos.app.goo.gl/wAxw9D81wghxHbw28

Bug: 393055190
Change-Id: If0ab5e1c94f30a82a19297e441e6a4a87af9e44b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6494562
Commit-Queue: Adem Derinel <derinel@google.com>
Reviewed-by: Ken Buchanan <kenrb@chromium.org>
Reviewed-by: Alex Ilin <alexilin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1453801}
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index ab5b009b..c02b08d 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -217,6 +217,7 @@
     "web.icon",
     "webauthn/camera.icon",
     "webauthn/icloud_keychain.icon",
+    "webauthn/icloud_keychain_color.icon",
     "webauthn/passkey_aoa.icon",
     "webauthn/passkey_aoa_dark.icon",
     "webauthn/passkey_error.icon",
@@ -232,6 +233,7 @@
     "webauthn/usb_security_key.icon",
     "webauthn/webauthn_error.icon",
     "webauthn/webauthn_error_dark.icon",
+    "webauthn/windows_hello_color.icon",
     "webid/webid_arrow.icon",
     "webid/webid_globe.icon",
     "zoom_in.icon",
diff --git a/chrome/app/vector_icons/webauthn/icloud_keychain_color.icon b/chrome/app/vector_icons/webauthn/icloud_keychain_color.icon
new file mode 100644
index 0000000..4cd16f4d
--- /dev/null
+++ b/chrome/app/vector_icons/webauthn/icloud_keychain_color.icon
@@ -0,0 +1,61 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 170,
+FILL_RULE_NONZERO,
+PATH_COLOR_ARGB, 0xFF, 0x4C, 0xD3, 0x6D,
+MOVE_TO, 84.03f, 11.96f,
+H_LINE_TO, 84,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, -37.57f, 38.1f,
+R_CUBIC_TO, 0, 15.83f, 9.78f, 30, 24.57f, 35.63f,
+R_V_LINE_TO, 59.13f,
+R_LINE_TO, 13.74f, 13.74f,
+R_LINE_TO, 6.66f, -6.66f,
+R_LINE_TO, -1.95f, -5.21f,
+R_H_LINE_TO, -0.2f,
+R_V_LINE_TO, -56.5f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, -24.34f, -40.77f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, 19.1f, -37.46f,
+CLOSE,
+NEW_PATH,
+FILL_RULE_NONZERO,
+PATH_COLOR_ARGB, 0xFF, 0xFD, 0xCE, 0x48,
+MOVE_TO, 57.59f, 11.96f,
+R_H_LINE_TO, -0.01f,
+ARC_TO, 38.13f, 38.13f, 0, 0, 0, 20, 50.06f,
+R_CUBIC_TO, 0, 15.83f, 9.78f, 30, 24.57f, 35.63f,
+R_V_LINE_TO, 59.13f,
+R_LINE_TO, 13.74f, 13.74f,
+R_LINE_TO, 6.67f, -6.66f,
+R_LINE_TO, -1.96f, -5.21f,
+R_H_LINE_TO, -0.2f,
+R_V_LINE_TO, -56.5f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, -24.33f, -40.77f,
+R_ARC_TO, 46.38f, 46.38f, 0, 0, 1, 19.1f, -37.46f,
+CLOSE,
+NEW_PATH,
+FILL_RULE_NONZERO,
+PATH_COLOR_ARGB, 0xFF, 0x44, 0xA4, 0xF9,
+MOVE_TO, 111.01f, 11.93f,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, -38.13f, 38.14f,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, 24.56f, 35.62f,
+R_V_LINE_TO, 7.9f,
+R_H_LINE_TO, 0.16f,
+R_LINE_TO, -0.16f, 51.23f,
+R_LINE_TO, 13.75f, 13.74f,
+R_LINE_TO, 21.99f, -21.99f,
+R_LINE_TO, -13.63f, -13.63f,
+R_LINE_TO, 18.3f, -18.3f,
+R_LINE_TO, -16.2f, -16.2f,
+R_H_LINE_TO, -0.23f,
+R_V_LINE_TO, -1.71f,
+R_ARC_TO, 38.13f, 38.13f, 0, 0, 0, 27.73f, -36.66f,
+ARC_TO, 38.13f, 38.13f, 0, 0, 0, 111, 11.93f,
+CLOSE,
+R_MOVE_TO, 0.23f, 13.69f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, 10.88f, 10.88f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, -10.88f, 10.88f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, -10.87f, -10.88f,
+R_ARC_TO, 10.88f, 10.88f, 0, 0, 1, 10.87f, -10.88f,
+CLOSE
diff --git a/chrome/app/vector_icons/webauthn/windows_hello_color.icon b/chrome/app/vector_icons/webauthn/windows_hello_color.icon
new file mode 100644
index 0000000..d814772
--- /dev/null
+++ b/chrome/app/vector_icons/webauthn/windows_hello_color.icon
@@ -0,0 +1,38 @@
+// Copyright 2025 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+CANVAS_DIMENSIONS, 200,
+PATH_COLOR_ARGB, 0xFF, 0x00, 0x89, 0xD0,
+FILL_RULE_NONZERO,
+MOVE_TO, 40.7f, 11.1f,
+R_CUBIC_TO, -11.6f, 2.8f, -18.9f, 12.1f, -18.9f, 23.9f,
+R_CUBIC_TO, 0, 6.6f, 1.8f, 11.3f, 6.1f, 16.2f,
+R_CUBIC_TO, 11, 12.5f, 32.7f, 9.5f, 40, -5.5f,
+R_CUBIC_TO, 2.7f, -5.6f, 2.8f, -15.6f, 0.2f, -21.2f,
+R_CUBIC_TO, -4.6f, -10, -16.8f, -16, -27.4f, -13.4f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x00, 0x89, 0xD0,
+FILL_RULE_NONZERO,
+MOVE_TO, 148.7f, 11,
+R_CUBIC_TO, -7.9f, 2, -14.1f, 7.3f, -17.2f, 14.7f,
+R_CUBIC_TO, -3.2f, 7.7f, -1, 19.9f, 4.8f, 26.2f,
+R_CUBIC_TO, 5.7f, 6.2f, 19.2f, 9, 27.1f, 5.7f,
+R_CUBIC_TO, 15.9f, -6.6f, 20, -27.8f, 8, -40,
+R_CUBIC_TO, -5.5f, -5.6f, -15.3f, -8.4f, -22.7f, -6.6f,
+CLOSE,
+NEW_PATH,
+PATH_COLOR_ARGB, 0xFF, 0x00, 0x89, 0xD0,
+FILL_RULE_NONZERO,
+MOVE_TO, 6.2f, 108,
+R_CUBIC_TO, -4, 2.4f, -6.6f, 8, -5.7f, 12.2f,
+R_CUBIC_TO, 2.1f, 9.3f, 23.1f, 31.9f, 38.8f, 41.6f,
+R_CUBIC_TO, 38.5f, 23.9f, 82.9f, 23.9f, 121.4f, 0,
+R_CUBIC_TO, 15.7f, -9.7f, 36.7f, -32.3f, 38.8f, -41.6f,
+R_CUBIC_TO, 1.4f, -6.7f, -5.1f, -14.2f, -12.3f, -14.2f,
+R_CUBIC_TO, -5, 0, -8.5f, 2.3f, -14.2f, 9.4f,
+R_CUBIC_TO, -40.7f, 51.2f, -105.3f, 51.2f, -146, 0,
+R_CUBIC_TO, -2.9f, -3.5f, -6.5f, -7.1f, -8.1f, -7.9f,
+R_CUBIC_TO, -3.9f, -2.1f, -8.8f, -1.9f, -12.7f, 0.5f,
+CLOSE
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
index 47f1b63..6b7fcab 100644
--- a/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
+++ b/chrome/browser/webauthn/authenticator_request_dialog_controller.cc
@@ -25,6 +25,7 @@
 #include "base/feature_list.h"
 #include "base/functional/bind.h"
 #include "base/functional/callback.h"
+#include "base/functional/overloaded.h"
 #include "base/i18n/string_compare.h"
 #include "base/location.h"
 #include "base/memory/weak_ptr.h"
@@ -41,6 +42,7 @@
 #include "chrome/browser/password_manager/chrome_webauthn_credentials_delegate_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/signin_ui_util.h"
+#include "chrome/browser/ui/passwords/ui_utils.h"
 #include "chrome/browser/ui/webauthn/ambient/ambient_signin_controller.h"
 #include "chrome/browser/ui/webauthn/passkey_upgrade_request_controller.h"
 #include "chrome/browser/ui/webauthn/user_actions.h"
@@ -380,6 +382,64 @@
          ui_presentation == UIPresentation::kModalImmediate;
 }
 
+// Returns the vector icon associated with the given mechanism type.
+// For Mechanism::WindowsAPI, the effective transport must be provided.
+const gfx::VectorIcon& GetMechanismIcon(
+    const Mechanism::Type& type,
+    content::AuthenticatorRequestClientDelegate::UIPresentation ui_presentation,
+    std::optional<AuthenticatorTransport> effective_transport = std::nullopt) {
+  return std::visit(
+      base::Overloaded{
+          [ui_presentation](const Mechanism::Credential& credential)
+              -> const gfx::VectorIcon& {
+            if (ui_presentation == UIPresentation::kModalImmediate) {
+              switch (credential.value().source) {
+                case AuthenticatorType::kICloudKeychain:
+                  return kIcloudKeychainColorIcon;
+                case AuthenticatorType::kEnclave:
+                  return GooglePasswordManagerVectorIcon();
+                case AuthenticatorType::kWinNative:
+                  return kWindowsHelloColorIcon;
+                case AuthenticatorType::kTouchID:
+                  return vector_icons::kProductRefreshIcon;
+                default:
+                  break;
+              }
+            }
+            // Default icon for non-immediate mode or other credential sources.
+            return GetCredentialIcon(credential.value().source);
+          },
+          [](const Mechanism::Password&) -> const gfx::VectorIcon& {
+            return GooglePasswordManagerVectorIcon();
+          },
+          [](const Mechanism::Transport& transport) -> const gfx::VectorIcon& {
+            return GetTransportIcon(transport.value());
+          },
+          [&effective_transport](
+              const Mechanism::WindowsAPI&) -> const gfx::VectorIcon& {
+            CHECK(effective_transport.has_value());
+            return GetTransportIcon(*effective_transport);
+          },
+          [](const Mechanism::ICloudKeychain&) -> const gfx::VectorIcon& {
+            // Always use the standard iCloud Keychain icon here.
+            return kIcloudKeychainIcon;
+          },
+          [](const Mechanism::Phone&) -> const gfx::VectorIcon& {
+            return kSmartphoneIcon;
+          },
+          [](const Mechanism::AddPhone&) -> const gfx::VectorIcon& {
+            return kQrcodeGeneratorIcon;
+          },
+          [](const Mechanism::Enclave&) -> const gfx::VectorIcon& {
+            // Always use the standard password manager icon here.
+            return vector_icons::kPasswordManagerIcon;
+          },
+          [](const Mechanism::SignInAgain&) -> const gfx::VectorIcon& {
+            return vector_icons::kSyncIcon;
+          }},
+      type);
+}
+
 }  // namespace
 
 AuthenticatorRequestDialogController::EphemeralState::EphemeralState() =
@@ -2157,10 +2217,11 @@
         specific_local_passkeys_listed = true;
       }
       std::u16string name = base::UTF8ToUTF16(cred.user.name.value_or(""));
+      Mechanism::Type mechanism_type =
+          Mechanism::Credential({cred.source, cred.user.id});
       auto& mechanism = model_->mechanisms.emplace_back(
-          AuthenticatorRequestDialogModel::Mechanism::Credential(
-              {cred.source, cred.user.id}),
-          name, name, GetCredentialIcon(cred.source),
+          mechanism_type, name, name,
+          GetMechanismIcon(mechanism_type, ui_presentation()),
           base::BindRepeating(
               base::IgnoreResult(
                   &AuthenticatorRequestDialogController::OnAccountPreselected),
@@ -2240,8 +2301,10 @@
           device::AuthenticatorAttachment::kCrossPlatform) {
     const std::u16string name =
         l10n_util::GetStringUTF16(IDS_WEBAUTHN_SOURCE_GOOGLE_PASSWORD_MANAGER);
+    Mechanism::Type mechanism_type = Mechanism::Enclave();
     Mechanism mechanism(
-        Mechanism::Enclave(), name, name, vector_icons::kPasswordManagerIcon,
+        mechanism_type, name, name,
+        GetMechanismIcon(mechanism_type, ui_presentation()),
         base::BindRepeating(&AuthenticatorRequestDialogController::StartEnclave,
                             base::Unretained(this)));
     mechanism.description = base::UTF8ToUTF16(model_->GetGpmAccountEmail());
@@ -2249,15 +2312,17 @@
   }
   if (enclave_enabled_status_ ==
           EnclaveEnabledStatus::kEnabledAndReauthNeeded &&
-          UIPresentation::kModal == ui_presentation() &&
+      UIPresentation::kModal == ui_presentation() &&
       model_->relying_party_id != "google.com") {
     // Show a button that lets the user sign in again to restore sync. This
     // cancels the request, so we can't do it for conditional UI requests.
     // TODO(crbug.com/345413738): add support for conditional UI.
     const std::u16string name =
         l10n_util::GetStringUTF16(IDS_WEBAUTHN_SIGN_IN_AGAIN_TITLE);
+    Mechanism::Type mechanism_type = Mechanism::SignInAgain();
     Mechanism enclave(
-        Mechanism::SignInAgain(), name, name, vector_icons::kSyncIcon,
+        mechanism_type, name, name,
+        GetMechanismIcon(mechanism_type, ui_presentation()),
         base::BindRepeating(
             &AuthenticatorRequestDialogController::ReauthForSyncRestore,
             base::Unretained(this)));
@@ -2275,8 +2340,10 @@
            device::FidoRequestHandlerBase::RecognizedCredential::kUnknown)) {
     const std::u16string name =
         l10n_util::GetStringUTF16(IDS_WEBAUTHN_TRANSPORT_ICLOUD_KEYCHAIN);
+    Mechanism::Type mechanism_type = Mechanism::ICloudKeychain();
     model_->mechanisms.emplace_back(
-        Mechanism::ICloudKeychain(), name, name, kIcloudKeychainIcon,
+        mechanism_type, name, name,
+        GetMechanismIcon(mechanism_type, ui_presentation()),
         base::BindRepeating(
             &AuthenticatorRequestDialogController::StartICloudKeychain,
             base::Unretained(this)));
@@ -2310,9 +2377,10 @@
       gfx::ElideString(name16, kMaxLongNameChars, &long_name);
       gfx::ElideString(name16, kMaxShortNameChars, &short_name);
 
+      Mechanism::Type mechanism_type = Mechanism::Phone(phone_name);
       model_->mechanisms.emplace_back(
-          Mechanism::Phone(phone_name), std::move(long_name),
-          std::move(short_name), kSmartphoneIcon,
+          mechanism_type, std::move(long_name), std::move(short_name),
+          GetMechanismIcon(mechanism_type, ui_presentation()),
           base::BindRepeating(
               &AuthenticatorRequestDialogController::ContactPhone,
               base::Unretained(this), phone_name));
@@ -2354,8 +2422,10 @@
         !include_usb_option;
     std::u16string label = l10n_util::GetStringUTF16(
         GetHybridButtonLabel(merge_usb_and_hybrid, specific_phones_listed));
+    Mechanism::Type mechanism_type = Mechanism::AddPhone();
     model_->mechanisms.emplace_back(
-        Mechanism::AddPhone(), label, label, kQrcodeGeneratorIcon,
+        mechanism_type, label, label,
+        GetMechanismIcon(mechanism_type, ui_presentation()),
         base::BindRepeating(
             &AuthenticatorRequestDialogController::StartGuidedFlowForAddPhone,
             base::Unretained(this)));
@@ -2371,9 +2441,11 @@
       continue;
     }
 
+    Mechanism::Type mechanism_type = Mechanism::Transport(transport);
     model_->mechanisms.emplace_back(
-        Mechanism::Transport(transport), GetTransportDescription(transport),
-        GetTransportShortDescription(transport), GetTransportIcon(transport),
+        mechanism_type, GetTransportDescription(transport),
+        GetTransportShortDescription(transport),
+        GetMechanismIcon(mechanism_type, ui_presentation()),
         base::BindRepeating(
             &AuthenticatorRequestDialogController::StartGuidedFlowForTransport,
             base::Unretained(this), transport));
@@ -2389,8 +2461,10 @@
     int label,
     AuthenticatorTransport transport) {
   const std::u16string desc = l10n_util::GetStringUTF16(label);
+  Mechanism::Type mechanism_type = Mechanism::WindowsAPI();
   model_->mechanisms.emplace_back(
-      Mechanism::WindowsAPI(), desc, desc, GetTransportIcon(transport),
+      mechanism_type, desc, desc,
+      GetMechanismIcon(mechanism_type, ui_presentation(), transport),
       base::BindRepeating(
           &AuthenticatorRequestDialogController::StartWinNativeApi,
           base::Unretained(this)));
@@ -2610,9 +2684,10 @@
 
 void AuthenticatorRequestDialogController::PopulatePasswords() {
   for (const auto& password : passwords_) {
+    Mechanism::Type mechanism_type = Mechanism::Password();
     Mechanism mechanism(
-        Mechanism::Password(), password->username_value,
-        password->username_value, vector_icons::kPasswordManagerIcon,
+        mechanism_type, password->username_value, password->username_value,
+        GetMechanismIcon(mechanism_type, ui_presentation()),
         base::BindRepeating(
             &AuthenticatorRequestDialogModel::OnPasswordCredentialSelected,
             base::Unretained(model_),