[Uno-D] Add sync passphrase dialog

Screenshot: https://storage.cloud.google.com/chromium-translation-screenshots/8e37738d4f94ab6fe523e6b40e7e73a1b9bd29cc

Screenshot after entering invalid password:
https://screenshot.googleplex.com/ARRkChmwAXUkr8m

Screencast:
https://screencast.googleplex.com/cast/NDgxNTIxMDg5MTE4MjA4MHw2ZWNmNTg0OS1mMQ

Low-Coverage-Reason: HARD_TO_TEST some UI glue code in ProfileMenuView
Bug: b/356603680
Change-Id: I6c843c305da7533579f38386cc63db6107deb054
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5838370
Reviewed-by: Peter Boström <[email protected]>
Reviewed-by: Allen Bauer <[email protected]>
Commit-Queue: David Roger <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1358261}
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index a50a49e..139655d 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -5473,6 +5473,8 @@
         "idle_dialog.h",
         "sharing_hub/sharing_hub_bubble_controller_desktop_impl.cc",
         "sharing_hub/sharing_hub_bubble_controller_desktop_impl.h",
+        "sync/sync_passphrase_dialog.cc",
+        "sync/sync_passphrase_dialog.h",
         "views/frame/opaque_browser_frame_view.cc",
         "views/frame/opaque_browser_frame_view.h",
         "views/frame/opaque_browser_frame_view_layout.cc",
diff --git a/chrome/browser/ui/sync/sync_passphrase_dialog.cc b/chrome/browser/ui/sync/sync_passphrase_dialog.cc
new file mode 100644
index 0000000..231de3cc
--- /dev/null
+++ b/chrome/browser/ui/sync/sync_passphrase_dialog.cc
@@ -0,0 +1,156 @@
+// Copyright 2024 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/sync/sync_passphrase_dialog.h"
+
+#include <functional>
+#include <string>
+
+#include "base/functional/bind.h"
+#include "base/functional/callback_forward.h"
+#include "base/functional/callback_helpers.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.h"
+#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/branded_strings.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/google/core/common/google_util.h"
+#include "components/strings/grit/components_strings.h"
+#include "components/sync/service/sync_service.h"
+#include "components/sync/service/sync_user_settings.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/dialog_model.h"
+#include "ui/base/models/dialog_model_field.h"
+#include "ui/base/models/dialog_model_host.h"
+#include "ui/base/window_open_disposition.h"
+#include "url/gurl.h"
+
+namespace {
+
+// Opens the sync dashboard webpage where the user may clear their sync data.
+void OpenSyncDashboardAndCloseDialog(BrowserWindowInterface& browser,
+                                     ui::DialogModel* model) {
+  GURL sync_dashboard_url = google_util::AppendGoogleLocaleParam(
+      GURL(chrome::kSyncGoogleDashboardURL),
+      g_browser_process->GetApplicationLocale());
+  browser.OpenGURL(sync_dashboard_url,
+                   WindowOpenDisposition::NEW_BACKGROUND_TAB);
+  model->host()->Close();
+}
+
+// Returns the content of the password field.
+const std::u16string GetSyncPassphraseFieldText(ui::DialogModel* model) {
+  return model->GetPasswordFieldByUniqueId(kSyncPassphrasePasswordFieldId)
+      ->text();
+}
+
+// Callback for the password field. Disables the button when the field is
+// empty.
+void OnPasswordFieldChanged(ui::DialogModel* model) {
+  std::u16string passphrase = GetSyncPassphraseFieldText(model);
+  ui::DialogModel::Button* ok_button =
+      model->GetButtonByUniqueId(kSyncPassphraseOkButtonFieldId);
+  model->SetButtonEnabled(ok_button, !passphrase.empty());
+}
+
+// Callback for the password field. Invalidates the field if the passphrase is
+// incorrect. `decrypt_data_callback` is piped into this function.
+bool HandlePassphraseError(ui::DialogModel* model, bool passphrase_is_valid) {
+  if (!passphrase_is_valid) {
+    ui::DialogModelPasswordField* password_field =
+        model->GetPasswordFieldByUniqueId(kSyncPassphrasePasswordFieldId);
+    password_field->Invalidate();
+  }
+  return passphrase_is_valid;
+}
+
+}  // namespace
+
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kSyncPassphrasePasswordFieldId);
+DEFINE_ELEMENT_IDENTIFIER_VALUE(kSyncPassphraseOkButtonFieldId);
+
+void ShowSyncPassphraseDialog(
+    Browser& browser,
+    base::RepeatingCallback<bool(const std::u16string&)>
+        decrypt_data_callback) {
+  ui::DialogModel::Builder dialog_builder;
+
+  // Link for the footnote.
+  base::RepeatingClosure link_closure =
+      base::BindRepeating(&OpenSyncDashboardAndCloseDialog, std::ref(browser),
+                          dialog_builder.model());
+  ui::DialogModelLabel::TextReplacement link_replacement =
+      ui::DialogModelLabel::CreateLink(IDS_SYNC_PASSPHRASE_DIALOG_FOOTER_LINK,
+                                       std::move(link_closure));
+
+  // The OK button is initially disabled, as the passphrase must be non-empty.
+  ui::DialogModel::Button::Params ok_button_params;
+  ok_button_params.SetEnabled(false);
+  ok_button_params.SetId(kSyncPassphraseOkButtonFieldId);
+
+  // Callback for the OK button.
+  // If the passphrase is correct, the dialog is closed. If it's incorrect, the
+  // text field is cleared but remains open for the user to try again.
+  base::RepeatingCallback<bool()> ok_callback =
+      base::BindRepeating(&GetSyncPassphraseFieldText,
+                          base::Unretained(dialog_builder.model()))
+          .Then(decrypt_data_callback)
+          .Then(base::BindRepeating(&HandlePassphraseError,
+                                    base::Unretained(dialog_builder.model())));
+
+  dialog_builder.SetInternalName("SyncPassphraseDialog")
+      .SetTitle(l10n_util::GetStringUTF16(IDS_SYNC_PASSPHRASE_DIALOG_TITLE))
+      .AddParagraph(ui::DialogModelLabel(IDS_SYNC_PASSPHRASE_DIALOG_BODY))
+      .AddPasswordField(
+          kSyncPassphrasePasswordFieldId,
+          /*label=*/std::u16string(),
+          /*accessible_text=*/
+          l10n_util::GetStringUTF16(IDS_SYNC_PASSPHRASE_LABEL),
+          l10n_util::GetStringUTF16(IDS_SETTINGS_INCORRECT_PASSPHRASE_ERROR))
+      .AddOkButton(std::move(ok_callback), ok_button_params)
+      .AddCancelButton(base::DoNothing())
+      .SetFootnote(ui::DialogModelLabel::CreateWithReplacement(
+          IDS_SYNC_PASSPHRASE_DIALOG_FOOTER, std::move(link_replacement)));
+
+  // Listen to password field change events, to disable the OK button when the
+  // passphrase is empty.
+  ui::DialogModel* model = dialog_builder.model();
+  auto subscription =
+      model->GetPasswordFieldByUniqueId(kSyncPassphrasePasswordFieldId)
+          ->AddOnFieldChangedCallback(base::BindRepeating(
+              &OnPasswordFieldChanged, base::Unretained(model)));
+  // Dummy callback to own the subscription.
+  dialog_builder.SetDialogDestroyingCallback(
+      base::BindOnce([](base::CallbackListSubscription subscription) {},
+                     std::move(subscription)));
+
+  chrome::ShowBrowserModal(&browser, dialog_builder.Build());
+}
+
+bool SyncPassphraseDialogDecryptData(syncer::SyncService* sync_service,
+                                     const std::u16string& passphrase) {
+  if (!sync_service || !sync_service->IsEngineInitialized()) {
+    // Even though this is a failure, return true so that the dialog closes and
+    // the user does not need to try again.
+    return true;
+  }
+
+  syncer::SyncUserSettings* sync_user_settings =
+      sync_service->GetUserSettings();
+
+  if (!sync_user_settings->IsPassphraseRequired()) {
+    return true;
+  }
+
+  if (passphrase.empty()) {
+    // Empty passphrases are not allowed.
+    return false;
+  }
+
+  return sync_user_settings->SetDecryptionPassphrase(
+      base::UTF16ToUTF8(passphrase));
+}
diff --git a/chrome/browser/ui/sync/sync_passphrase_dialog.h b/chrome/browser/ui/sync/sync_passphrase_dialog.h
new file mode 100644
index 0000000..30d2c082
--- /dev/null
+++ b/chrome/browser/ui/sync/sync_passphrase_dialog.h
@@ -0,0 +1,33 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_SYNC_SYNC_PASSPHRASE_DIALOG_H_
+#define CHROME_BROWSER_UI_SYNC_SYNC_PASSPHRASE_DIALOG_H_
+
+#include <string>
+
+#include "base/functional/callback_forward.h"
+#include "ui/base/interaction/element_identifier.h"
+
+namespace syncer {
+class SyncService;
+}
+
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kSyncPassphrasePasswordFieldId);
+DECLARE_ELEMENT_IDENTIFIER_VALUE(kSyncPassphraseOkButtonFieldId);
+
+class Browser;
+
+// Factory function to create and show the Sync passphrase dialog.
+// `decrypt_data_callback` should return whether the passphrease was correct.
+void ShowSyncPassphraseDialog(
+    Browser& browser,
+    base::RepeatingCallback<bool(const std::u16string&)> decrypt_data_callback);
+
+// Decrypts sync data. Returns true in case of success.
+// When this returns false, the user will be prompted to try again.
+bool SyncPassphraseDialogDecryptData(syncer::SyncService* sync_service,
+                                     const std::u16string& passphrase);
+
+#endif  // CHROME_BROWSER_UI_SYNC_SYNC_PASSPHRASE_DIALOG_H_
diff --git a/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc b/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc
new file mode 100644
index 0000000..23e1a9a7
--- /dev/null
+++ b/chrome/browser/ui/sync/sync_passphrase_dialog_browsertest.cc
@@ -0,0 +1,98 @@
+// Copyright 2024 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/sync/sync_passphrase_dialog.h"
+
+#include <string>
+
+#include "base/functional/bind.h"
+#include "base/functional/callback_helpers.h"
+#include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/test/interaction/interactive_browser_test.h"
+#include "components/signin/public/base/signin_switches.h"
+#include "content/public/test/browser_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/interaction/element_identifier.h"
+#include "ui/base/interaction/polling_state_observer.h"
+#include "ui/views/controls/textfield/textfield.h"
+
+namespace {
+DEFINE_LOCAL_STATE_IDENTIFIER_VALUE(
+    ui::test::PollingStateObserver<std::u16string>,
+    kPolledTextfieldContent);
+}
+
+class SyncPassphraseDialogBrowserTest : public InteractiveBrowserTest {
+ public:
+  SyncPassphraseDialogBrowserTest() {
+    feature_list_.InitWithFeatures(
+        /*enabled_features=*/{switches::kExplicitBrowserSigninUIOnDesktop,
+                              switches::kImprovedSigninUIOnDesktop},
+        /*disabled_features=*/{});
+  }
+
+  // Sets the password and waits for the state to be propagated.
+  // `kPolledTextfieldContent` must be initialized with `PollState()` before
+  // this can be used.
+  MultiStep SetPassordText(const std::u16string& text) {
+    return Steps(EnterText(kTextFieldName, text),
+                 WaitForState(kPolledTextfieldContent, text));
+  }
+
+ protected:
+  const std::string kTextFieldName = "Texfield";
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SyncPassphraseDialogBrowserTest, PixelTest) {
+  const std::u16string kPassphrase = u"Passphrase";
+
+  base::MockRepeatingCallback<bool(const std::u16string&)> decrypt_callback;
+  Browser* browser_ptr = browser();
+  views::Textfield* textfield = nullptr;
+
+  // Reject the first passphrase, and accept the second.
+  EXPECT_CALL(decrypt_callback, Run(kPassphrase))
+      .WillOnce(testing::Return(false))
+      .WillOnce(testing::Return(true));
+
+  RunTestSequence(
+      // Show the dialog.
+      Do([&decrypt_callback, browser_ptr] {
+        ShowSyncPassphraseDialog(*browser_ptr, decrypt_callback.Get());
+      }),
+      WaitForShow(kSyncPassphraseOkButtonFieldId),
+      // Check that the button is initially disabled.
+      WaitForViewProperty(kSyncPassphraseOkButtonFieldId, views::View, Enabled,
+                          false),
+      SetOnIncompatibleAction(OnIncompatibleAction::kIgnoreAndContinue,
+                              "Screenshot not supported in all test modes."),
+      ScreenshotSurface(kSyncPassphraseOkButtonFieldId, "InitialState",
+                        "5838370"),
+      // Find the underlying `views::Textfield` and track its state.
+      NameDescendantViewByType<views::Textfield>(kSyncPassphrasePasswordFieldId,
+                                                 kTextFieldName),
+      WithView(kTextFieldName,
+               [&textfield](views::Textfield* t) {
+                 CHECK(t);
+                 textfield = t;
+               }),
+      PollState(kPolledTextfieldContent,
+                [&textfield]() { return textfield->GetText(); }),
+      // Enter some text, the button is enabled.
+      SetPassordText(kPassphrase),
+      WaitForViewProperty(kSyncPassphraseOkButtonFieldId, views::View, Enabled,
+                          true),
+      ScreenshotSurface(kSyncPassphraseOkButtonFieldId, "WithText", "5838370"),
+      // Submit wrong passphrase, field is cleared and button disabled again.
+      PressButton(kSyncPassphraseOkButtonFieldId),
+      WaitForState(kPolledTextfieldContent, std::u16string()),
+      WaitForViewProperty(kSyncPassphraseOkButtonFieldId, views::View, Enabled,
+                          false),
+      ScreenshotSurface(kSyncPassphraseOkButtonFieldId, "Invalid", "5838370"),
+      // Submit correct passphrase, the dialog closes.
+      SetPassordText(kPassphrase), PressButton(kSyncPassphraseOkButtonFieldId),
+      WaitForHide(kSyncPassphraseOkButtonFieldId));
+}
diff --git a/chrome/browser/ui/sync/sync_passphrase_dialog_unittest.cc b/chrome/browser/ui/sync/sync_passphrase_dialog_unittest.cc
new file mode 100644
index 0000000..063e8b4
--- /dev/null
+++ b/chrome/browser/ui/sync/sync_passphrase_dialog_unittest.cc
@@ -0,0 +1,29 @@
+// Copyright 2024 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/sync/sync_passphrase_dialog.h"
+
+#include "components/sync/service/sync_user_settings.h"
+#include "components/sync/test/test_sync_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SyncPassphraseDialog, SyncPassphraseDialogDecryptData) {
+  constexpr char kPassphraseUTF8[] = "passphrase";
+  // `base::UTF8ToUTF16()` cannot be called on string literals, so a duplicate
+  // string is needed.
+  constexpr char16_t kPassphraseUTF16[] = u"passphrase";
+  syncer::TestSyncService sync_service;
+  syncer::TestSyncUserSettings* sync_user_settings =
+      sync_service.GetUserSettings();
+  sync_user_settings->SetPassphraseRequired(kPassphraseUTF8);
+  ASSERT_TRUE(sync_user_settings->IsPassphraseRequired());
+
+  // Wrong passphrase.
+  EXPECT_FALSE(SyncPassphraseDialogDecryptData(&sync_service, u"wrong"));
+  EXPECT_TRUE(sync_user_settings->IsPassphraseRequired());
+
+  // Correct passphrase.
+  EXPECT_TRUE(SyncPassphraseDialogDecryptData(&sync_service, kPassphraseUTF16));
+  EXPECT_FALSE(sync_user_settings->IsPassphraseRequired());
+}
diff --git a/chrome/browser/ui/views/chrome_typography.h b/chrome/browser/ui/views/chrome_typography.h
index dbb0b36..a77b2df 100644
--- a/chrome/browser/ui/views/chrome_typography.h
+++ b/chrome/browser/ui/views/chrome_typography.h
@@ -92,14 +92,8 @@
   CHROME_TEXT_STYLE_START = views::style::VIEWS_TEXT_STYLE_END,
 #endif
 
-  // Similar to views::style::STYLE_PRIMARY but with a monospaced typeface.
-  STYLE_PRIMARY_MONOSPACED = CHROME_TEXT_STYLE_START,
-
-  // Similar to views::style::STYLE_SECONDARY but with a monospaced typeface.
-  STYLE_SECONDARY_MONOSPACED,
-
   // A solid shade of red.
-  STYLE_RED,
+  STYLE_RED = CHROME_TEXT_STYLE_START,
 
   // A solid shade of green.
   STYLE_GREEN,
diff --git a/chrome/browser/ui/views/chrome_typography_provider.cc b/chrome/browser/ui/views/chrome_typography_provider.cc
index cac043ab..57f4e0fe 100644
--- a/chrome/browser/ui/views/chrome_typography_provider.cc
+++ b/chrome/browser/ui/views/chrome_typography_provider.cc
@@ -135,8 +135,8 @@
     details.weight = gfx::Font::Weight::SEMIBOLD;
   }
 
-  if (style == STYLE_PRIMARY_MONOSPACED ||
-      style == STYLE_SECONDARY_MONOSPACED) {
+  if (style == views::style::STYLE_PRIMARY_MONOSPACED ||
+      style == views::style::STYLE_SECONDARY_MONOSPACED) {
 #if BUILDFLAG(IS_MAC)
     details.typeface = "Menlo";
 #elif BUILDFLAG(IS_WIN)
@@ -157,9 +157,9 @@
     context = views::style::CONTEXT_LABEL;
 
   // Monospaced styles have the same colors as their normal counterparts.
-  if (style == STYLE_PRIMARY_MONOSPACED) {
+  if (style == views::style::STYLE_PRIMARY_MONOSPACED) {
     style = views::style::STYLE_PRIMARY;
-  } else if (style == STYLE_SECONDARY_MONOSPACED) {
+  } else if (style == views::style::STYLE_SECONDARY_MONOSPACED) {
     style = views::style::STYLE_SECONDARY;
   }
 
diff --git a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
index e03d7c9..a845d1b 100644
--- a/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
+++ b/chrome/browser/ui/views/passwords/password_generation_popup_view_views.cc
@@ -311,7 +311,7 @@
   DCHECK(!password_label_);
   password_label_ = AddChildView(std::make_unique<views::Label>(
       controller_->password(), views::style::CONTEXT_DIALOG_BODY_TEXT,
-      STYLE_SECONDARY_MONOSPACED));
+      views::style::STYLE_SECONDARY_MONOSPACED));
   layout->SetFlexForView(password_label_, 1);
   UpdateAccessibleNameAndDescription();
 }
diff --git a/chrome/browser/ui/views/passwords/views_utils.cc b/chrome/browser/ui/views/passwords/views_utils.cc
index 8962f20..e04652d 100644
--- a/chrome/browser/ui/views/passwords/views_utils.cc
+++ b/chrome/browser/ui/views/passwords/views_utils.cc
@@ -136,7 +136,7 @@
   std::unique_ptr<views::Label> label = std::make_unique<views::Label>(
       GetDisplayPassword(form), views::style::CONTEXT_DIALOG_BODY_TEXT);
   if (!form.IsFederatedCredential()) {
-    label->SetTextStyle(STYLE_SECONDARY_MONOSPACED);
+    label->SetTextStyle(views::style::STYLE_SECONDARY_MONOSPACED);
     label->SetObscured(true);
     label->SetElideBehavior(gfx::TRUNCATE);
   } else {
@@ -247,8 +247,8 @@
       std::make_unique<ui::SimpleComboboxModel>(
           std::vector<ui::SimpleComboboxModel::Item>(passwords.begin(),
                                                      passwords.end())),
-      views::style::CONTEXT_BUTTON, STYLE_PRIMARY_MONOSPACED, kDisplayArrow,
-      std::move(reveal_password_callback));
+      views::style::CONTEXT_BUTTON, views::style::STYLE_PRIMARY_MONOSPACED,
+      kDisplayArrow, std::move(reveal_password_callback));
   combobox->SetText(form.password_value);
   combobox->SetPasswordIconTooltips(
       l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_SHOW_PASSWORD),
diff --git a/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc b/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc
index 324440e..58105f7 100644
--- a/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc
+++ b/chrome/browser/ui/views/plus_addresses/plus_address_creation_dialog_delegate.cc
@@ -343,7 +343,7 @@
           .SetText(l10n_util::GetStringUTF16(
               IDS_PLUS_ADDRESS_MODAL_GENERATION_TEMPORARY_LABEL_CONTENT))
           .SetTextContext(views::style::CONTEXT_LABEL)
-          .SetTextStyle(STYLE_SECONDARY_MONOSPACED)
+          .SetTextStyle(views::style::STYLE_SECONDARY_MONOSPACED)
           .CopyAddressTo(&generation_message_)
           .SetProperty(views::kMarginsKey, kMargins)
           .SetProperty(
@@ -374,7 +374,7 @@
   auto address_label =
       views::Builder<views::Label>()
           .SetTextContext(views::style::CONTEXT_LABEL)
-          .SetTextStyle(STYLE_SECONDARY_MONOSPACED)
+          .SetTextStyle(views::style::STYLE_SECONDARY_MONOSPACED)
           .SetProperty(
               views::kElementIdentifierKey,
               PlusAddressCreationView::kPlusAddressSuggestedEmailElementId)
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 11a42517..9aa5b04 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include "base/feature_list.h"
+#include "base/functional/bind.h"
 #include "base/functional/callback.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
@@ -48,6 +49,7 @@
 #include "chrome/browser/ui/profiles/profile_colors_util.h"
 #include "chrome/browser/ui/profiles/profile_picker.h"
 #include "chrome/browser/ui/profiles/profile_view_utils.h"
+#include "chrome/browser/ui/sync/sync_passphrase_dialog.h"
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/views/accessibility/non_accessible_image_view.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
@@ -361,6 +363,19 @@
           browser(), syncer::TrustedVaultUserActionTriggerForUMA::kProfileMenu);
       break;
     case AvatarSyncErrorType::kPassphraseError:
+#if !BUILDFLAG(IS_CHROMEOS)
+      if (base::FeatureList::IsEnabled(switches::kImprovedSigninUIOnDesktop)) {
+        ShowSyncPassphraseDialog(
+            *browser(), base::BindRepeating(
+                            &SyncPassphraseDialogDecryptData,
+                            base::Unretained(SyncServiceFactory::GetForProfile(
+                                browser()->profile()))));
+      } else {
+        chrome::ShowSettingsSubPage(browser(), chrome::kSyncSetupSubPage);
+      }
+      break;
+#endif
+    // Intentional fallthrough on ChromeOS.
     case AvatarSyncErrorType::kSettingsUnconfirmedError:
       chrome::ShowSettingsSubPage(browser(), chrome::kSyncSetupSubPage);
       break;