[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;