blob: 584f444905e40ee52e0c8e952ee7f1300bd1da62 [file] [log] [blame]
// 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/views/webid/account_selection_modal_view.h"
#include <iostream>
#include <memory>
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "base/i18n/case_conversion.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/accessibility/accessibility_state_utils.h"
#include "chrome/browser/image_fetcher/image_decoder_impl.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/ui/monogram_utils.h"
#include "chrome/browser/ui/views/controls/hover_button.h"
#include "chrome/browser/ui/views/webid/account_selection_view_base.h"
#include "chrome/browser/ui/views/webid/fedcm_account_selection_view_desktop.h"
#include "chrome/browser/ui/views/webid/webid_utils.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "components/image_fetcher/core/image_fetcher_impl.h"
#include "components/strings/grit/components_strings.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "content/public/browser/identity_request_dialog_controller.h"
#include "content/public/browser/web_contents.h"
#include "skia/ext/image_operations.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/ui_base_features.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/image/canvas_image_source.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/controls/progress_bar.h"
#include "ui/views/controls/scroll_view.h"
#include "ui/views/controls/separator.h"
#include "ui/views/controls/styled_label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_view.h"
#include "ui/views/layout/layout_types.h"
#include "ui/views/style/platform_style.h"
#include "ui/views/style/typography.h"
#include "ui/views/view_class_properties.h"
// The size of the spacing used between children elements.
constexpr int kBetweenChildSpacing = 4;
// The size of the vertical padding for most elements in the dialog.
constexpr int kVerticalPadding = 8;
// The width of the modal dialog.
constexpr int kDialogWidth = 448;
// The margins of the modal dialog.
constexpr int kDialogMargin = 20;
class BackgroundImageView : public views::ImageView {
METADATA_HEADER(BackgroundImageView, views::ImageView)
public:
explicit BackgroundImageView(base::WeakPtr<content::WebContents> web_contents)
: web_contents_(web_contents) {
constexpr int kBackgroundWidth = 408;
constexpr int kBackgroundHeight = 100;
SetImageSize(gfx::Size(kBackgroundWidth, kBackgroundHeight));
UpdateBackgroundImage();
}
BackgroundImageView(const BackgroundImageView&) = delete;
BackgroundImageView& operator=(const BackgroundImageView&) = delete;
~BackgroundImageView() override = default;
void UpdateBackgroundImage() {
CHECK(web_contents_);
const bool is_dark_mode = color_utils::IsDark(
web_contents_->GetColorProvider().GetColor(ui::kColorDialogBackground));
gfx::ImageSkia* background =
ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
is_dark_mode ? IDR_WEBID_MODAL_ICON_BACKGROUND_DARK
: IDR_WEBID_MODAL_ICON_BACKGROUND_LIGHT);
SetImage(*background);
}
void OnThemeChanged() override {
views::ImageView::OnThemeChanged();
UpdateBackgroundImage();
}
private:
// Web contents is used to determine whether to show the light or dark mode
// image.
base::WeakPtr<content::WebContents> web_contents_;
};
BEGIN_METADATA(BackgroundImageView)
END_METADATA
AccountSelectionModalView::AccountSelectionModalView(
const std::u16string& rp_for_display,
const std::optional<std::u16string>& idp_title,
blink::mojom::RpContext rp_context,
content::WebContents* web_contents,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
AccountSelectionViewBase::Observer* observer,
views::WidgetObserver* widget_observer)
: AccountSelectionViewBase(web_contents,
observer,
widget_observer,
std::move(url_loader_factory),
rp_for_display) {
SetModalType(ui::MODAL_TYPE_CHILD);
SetOwnedByWidget(true);
set_fixed_width(kDialogWidth);
SetShowTitle(false);
SetShowCloseButton(false);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
kBetweenChildSpacing));
SetButtons(ui::DIALOG_BUTTON_NONE);
title_ = webid::GetTitle(rp_for_display_, idp_title, rp_context);
}
AccountSelectionModalView::~AccountSelectionModalView() = default;
void AccountSelectionModalView::AddProgressBar() {
// Change top margin of header to accommodate progress bar.
CHECK(header_view_);
constexpr int kVerifyingTopMargin = 13;
static_cast<views::BoxLayout*>(header_view_->GetLayoutManager())
->set_inside_border_insets(gfx::Insets::TLBR(
/*top=*/kVerifyingTopMargin, /*left=*/kDialogMargin,
/*bottom=*/kVerticalPadding,
/*right=*/kDialogMargin));
// Add progress bar.
constexpr int kModalProgressBarHeight = 3;
views::ProgressBar* progress_bar =
AddChildViewAt(std::make_unique<views::ProgressBar>(), 0);
progress_bar->SetPreferredHeight(kModalProgressBarHeight);
progress_bar->SetPreferredCornerRadii(std::nullopt);
// Use an infinite animation: SetValue(-1).
progress_bar->SetValue(-1);
progress_bar->SetBackgroundColor(SK_ColorLTGRAY);
progress_bar->SetPreferredSize(
gfx::Size(kDialogWidth, kModalProgressBarHeight));
progress_bar->SizeToPreferredSize();
has_progress_bar_ = true;
}
void AccountSelectionModalView::UpdateDialogPosition() {
constrained_window::UpdateWebContentsModalDialogPosition(
GetWidget(), web_modal::WebContentsModalDialogManager::FromWebContents(
web_contents_.get())
->delegate()
->GetWebContentsModalDialogHost());
if (accessibility_state_utils::IsScreenReaderEnabled()) {
GetInitiallyFocusedView()->RequestFocus();
}
}
void AccountSelectionModalView::InitDialogWidget() {
if (!web_contents_) {
return;
}
if (dialog_widget_) {
UpdateDialogPosition();
return;
}
views::Widget* widget =
constrained_window::ShowWebModalDialogViews(this, web_contents_.get());
if (!widget) {
return;
}
UpdateDialogPosition();
// Add the widget observer, if available. It is null in tests.
if (widget_observer_) {
widget->AddObserver(widget_observer_);
}
dialog_widget_ = widget->GetWeakPtr();
occlusion_observation_.Observe(widget);
}
std::unique_ptr<views::View>
AccountSelectionModalView::CreatePlaceholderAccountRow() {
std::unique_ptr<views::View> placeholder_account_icon =
std::make_unique<views::View>();
placeholder_account_icon->SetPreferredSize(
gfx::Size(kModalAvatarSize, kModalAvatarSize));
placeholder_account_icon->SizeToPreferredSize();
placeholder_account_icon->SetBackground(views::CreateRoundedRectBackground(
gfx::kGoogleGrey200, kModalAvatarSize));
constexpr int kPlaceholderAccountRowPadding = 16;
auto row = std::make_unique<views::View>();
row->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets::VH(/*vertical=*/kPlaceholderAccountRowPadding,
/*horizontal=*/kDialogMargin + kModalHorizontalSpacing),
/*between_child_spacing=*/kModalHorizontalSpacing));
row->AddChildView(std::move(placeholder_account_icon));
constexpr int kPlaceholderVerticalSpacing = 2;
views::View* const text_column =
row->AddChildView(std::make_unique<views::View>());
text_column->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kVertical)
.SetMainAxisAlignment(views::LayoutAlignment::kCenter)
.SetCrossAxisAlignment(views::LayoutAlignment::kStart)
.SetDefault(views::kMarginsKey,
gfx::Insets::VH(/*vertical=*/kPlaceholderVerticalSpacing,
/*horizontal=*/0));
constexpr int kPlaceholderRadius = 5;
constexpr int kPlaceholderTextHeight = 10;
constexpr int kPlaceholderAccountNameWidth = 79;
constexpr int kPlaceholderAccountEmailWidth = 134;
views::View* placeholder_account_name =
text_column->AddChildView(std::make_unique<views::View>());
placeholder_account_name->SetPreferredSize(
gfx::Size(kPlaceholderAccountNameWidth, kPlaceholderTextHeight));
placeholder_account_name->SizeToPreferredSize();
placeholder_account_name->SetBackground(views::CreateRoundedRectBackground(
gfx::kGoogleGrey200, kPlaceholderRadius));
views::View* placeholder_account_email =
text_column->AddChildView(std::make_unique<views::View>());
placeholder_account_email->SetPreferredSize(
gfx::Size(kPlaceholderAccountEmailWidth, kPlaceholderTextHeight));
placeholder_account_email->SizeToPreferredSize();
placeholder_account_email->SetBackground(views::CreateRoundedRectBackground(
gfx::kGoogleGrey200, kPlaceholderRadius));
return row;
}
std::unique_ptr<views::View> AccountSelectionModalView::CreateButtonRow(
std::optional<views::Button::PressedCallback> continue_callback =
std::nullopt,
std::optional<views::Button::PressedCallback> use_other_account_callback =
std::nullopt,
std::optional<views::Button::PressedCallback> back_callback =
std::nullopt) {
const views::LayoutProvider* layout_provider = views::LayoutProvider::Get();
std::unique_ptr<views::View> button_container =
std::make_unique<views::View>();
constexpr int kButtonRowTopPadding = 24;
button_container->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetMainAxisAlignment(views::LayoutAlignment::kEnd)
.SetIgnoreDefaultMainAxisMargins(true)
.SetDefault(views::kMarginsKey,
gfx::Insets::TLBR(
/*top=*/0, /*left=*/
layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_BUTTON_HORIZONTAL),
/*bottom=*/0, /*right=*/0))
.SetInteriorMargin(gfx::Insets::TLBR(/*top=*/kButtonRowTopPadding,
/*left=*/kDialogMargin,
/*bottom=*/kDialogMargin,
/*right=*/kDialogMargin));
std::unique_ptr<views::MdTextButton> cancel_button =
std::make_unique<views::MdTextButton>(
base::BindRepeating(
&AccountSelectionViewBase::Observer::OnCloseButtonClicked,
base::Unretained(observer_)),
l10n_util::GetStringUTF16(IDS_CANCEL));
cancel_button_ = cancel_button.get();
// When a continue button is present, the cancel button should be more
// prominent (Tonal) to align with common practice.
cancel_button->SetStyle(continue_callback ? ui::ButtonStyle::kTonal
: ui::ButtonStyle::kDefault);
cancel_button->SetAppearDisabledInInactiveWidget(true);
button_container->AddChildView(std::move(cancel_button));
if (continue_callback) {
std::unique_ptr<views::MdTextButton> continue_button =
std::make_unique<views::MdTextButton>(
std::move(*continue_callback),
l10n_util::GetStringUTF16(IDS_SIGNIN_CONTINUE));
continue_button_ = continue_button.get();
continue_button->SetStyle(ui::ButtonStyle::kProminent);
continue_button->SetAppearDisabledInInactiveWidget(true);
button_container->AddChildView(std::move(continue_button));
}
if (!(use_other_account_callback || back_callback)) {
return button_container;
}
CHECK(!use_other_account_callback || !back_callback);
// Use other account or back button shown on the far left needs to be in its
// own child container because we want it aligned to the start of the button
// row container, whereas the other buttons are aligned to the end of the
// button row container.
std::unique_ptr<views::FlexLayoutView> leftmost_button_container =
std::make_unique<views::FlexLayoutView>();
leftmost_button_container->SetProperty(
views::kFlexBehaviorKey,
views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
views::MaximumFlexSizeRule::kUnbounded));
if (use_other_account_callback) {
std::unique_ptr<views::MdTextButton> use_other_account_button =
std::make_unique<views::MdTextButton>(
std::move(*use_other_account_callback),
l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_USE_OTHER_ACCOUNT));
use_other_account_button_ = use_other_account_button.get();
use_other_account_button->SetStyle(ui::ButtonStyle::kDefault);
use_other_account_button->SetAppearDisabledInInactiveWidget(true);
leftmost_button_container->AddChildView(
std::move(use_other_account_button));
} else {
CHECK(back_callback);
std::unique_ptr<views::MdTextButton> back_button =
std::make_unique<views::MdTextButton>(
std::move(*back_callback),
l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_BACK));
back_button_ = back_button.get();
back_button->SetStyle(ui::ButtonStyle::kDefault);
back_button->SetAppearDisabledInInactiveWidget(true);
leftmost_button_container->AddChildView(std::move(back_button));
}
button_container->AddChildViewAt(std::move(leftmost_button_container), 0);
return button_container;
}
std::unique_ptr<views::View>
AccountSelectionModalView::CreateAccountChooserHeader(
const content::IdentityProviderMetadata& idp_metadata =
content::IdentityProviderMetadata()) {
std::unique_ptr<views::View> header = std::make_unique<views::View>();
header->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets::TLBR(/*top=*/kDialogMargin, /*left=*/kDialogMargin,
/*bottom=*/kVerticalPadding, /*right=*/kDialogMargin),
/*between_child_spacing=*/kVerticalPadding));
// Add IDP icon, if available. Otherwise, fallback to the default globe icon.
header->AddChildView(CreateBrandIconImageView(idp_metadata.brand_icon_url));
// Add the title.
title_label_ = header->AddChildView(
std::make_unique<views::Label>(title_, views::style::CONTEXT_DIALOG_TITLE,
views::style::STYLE_HEADLINE_4));
SetLabelProperties(title_label_);
title_label_->SetFocusBehavior(FocusBehavior::ALWAYS);
// Add the body.
body_label_ = header->AddChildView(std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_ACCOUNT_SELECTION_CHOOSE_AN_ACCOUNT),
views::style::CONTEXT_DIALOG_BODY_TEXT, views::style::STYLE_BODY_4));
SetLabelProperties(body_label_);
body_label_->SetFocusBehavior(FocusBehavior::ALWAYS);
return header;
}
std::unique_ptr<views::View>
AccountSelectionModalView::CreateMultipleAccountChooser(
const std::vector<IdentityProviderDisplayData>& idp_display_data_list) {
auto scroll_view = std::make_unique<views::ScrollView>();
scroll_view->SetHorizontalScrollBarMode(
views::ScrollView::ScrollBarMode::kDisabled);
views::View* const content =
scroll_view->SetContents(std::make_unique<views::View>());
content->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets::VH(/*vertical=*/kVerticalPadding,
/*horizontal=*/kDialogMargin)));
// Add separator before the account rows.
content->AddChildView(std::make_unique<views::Separator>());
int num_rows = 0;
constexpr int kMultipleAccountsVerticalPadding = 2;
for (const auto& idp_display_data : idp_display_data_list) {
for (const auto& account : idp_display_data.accounts) {
content->AddChildView(CreateAccountRow(
account, idp_display_data,
/*clickable_position=*/num_rows++,
/*should_include_idp=*/false,
/*is_modal_dialog=*/true,
/*additional_vertical_padding=*/kMultipleAccountsVerticalPadding));
// Add separator after each account row.
content->AddChildView(std::make_unique<views::Separator>());
}
}
const int per_account_size = content->GetPreferredSize().height() / num_rows;
scroll_view->ClipHeightTo(0, static_cast<int>(per_account_size * 3.5f));
return scroll_view;
}
void AccountSelectionModalView::ShowMultiAccountPicker(
const std::vector<IdentityProviderDisplayData>& idp_display_data_list,
bool show_back_button,
bool is_choose_an_account) {
DCHECK(!show_back_button);
RemoveNonHeaderChildViews();
ConfigureBrandImageView(brand_icon_,
idp_display_data_list[0].idp_metadata.brand_icon_url);
// Show the "Choose an account to continue" label.
CHECK(body_label_);
body_label_->SetVisible(/*visible=*/true);
account_chooser_ =
AddChildView(CreateMultipleAccountChooser(idp_display_data_list));
std::optional<views::Button::PressedCallback> use_other_account_callback =
std::nullopt;
// TODO(crbug.com/324052630): Support add account with multi IDP API.
if (idp_display_data_list[0].idp_metadata.supports_add_account) {
use_other_account_callback = base::BindRepeating(
&AccountSelectionViewBase::Observer::OnLoginToIdP,
base::Unretained(observer_),
idp_display_data_list[0].idp_metadata.config_url,
idp_display_data_list[0].idp_metadata.idp_login_url);
}
AddChildView(CreateButtonRow(/*continue_callback=*/std::nullopt,
std::move(use_other_account_callback)));
InitDialogWidget();
// TODO(crbug.com/324052630): Connect with multi IDP API.
}
void AccountSelectionModalView::ShowVerifyingSheet(
const content::IdentityRequestAccount& account,
const IdentityProviderDisplayData& idp_display_data,
const std::u16string& title) {
// A different type of sheet must have been shown prior to ShowVerifyingSheet.
// This might change if we choose to integrate auto re-authn with button mode.
CHECK(dialog_widget_);
queued_announcement_ = l10n_util::GetStringUTF16(IDS_VERIFY_SHEET_TITLE);
// When a user signs in to the IdP with a returning account while the loading
// modal is shown, we can exit without updating the UI.
if (account.browser_trusted_login_state != Account::LoginState::kSignUp &&
has_progress_bar_) {
InitDialogWidget();
return;
}
AddProgressBar();
// Disable account chooser.
CHECK(account_chooser_);
for (const auto& account_row : account_chooser_->children()) {
account_row->SetEnabled(false);
}
// Disable text buttons.
if (use_other_account_button_) {
use_other_account_button_->SetEnabled(false);
}
if (back_button_) {
back_button_->SetEnabled(false);
}
if (continue_button_) {
continue_button_->SetEnabled(false);
}
InitDialogWidget();
}
std::unique_ptr<views::View>
AccountSelectionModalView::CreateSingleAccountChooser(
const IdentityProviderDisplayData& idp_display_data,
const content::IdentityRequestAccount& account,
bool should_hover,
bool show_disclosure_label,
bool show_separator,
int additional_row_vertical_padding) {
auto row = std::make_unique<views::View>();
row->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical,
gfx::Insets::VH(/*vertical=*/0, /*horizontal=*/kDialogMargin)));
// Add separator before the account row.
if (show_separator) {
row->AddChildView(std::make_unique<views::Separator>());
}
// Add account row.
row->AddChildView(CreateAccountRow(
account, idp_display_data,
should_hover ? std::make_optional<int>(0) : std::nullopt,
/*should_include_idp=*/false,
/*is_modal_dialog=*/true, additional_row_vertical_padding));
// Add separator after the account row.
if (show_separator) {
row->AddChildView(std::make_unique<views::Separator>());
}
// Add disclosure label.
if (show_disclosure_label) {
std::unique_ptr<views::StyledLabel> disclosure_label =
CreateDisclosureLabel(idp_display_data);
disclosure_label->SetDefaultTextStyle(views::style::STYLE_BODY_4);
disclosure_label->SizeToFit(views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));
disclosure_label->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
/*top=*/kVerticalSpacing, /*left=*/0, /*bottom=*/0, /*right=*/0)));
queued_announcement_ = disclosure_label->GetText();
row->AddChildView(std::move(disclosure_label));
}
return row;
}
void AccountSelectionModalView::ShowSingleAccountConfirmDialog(
const content::IdentityRequestAccount& account,
const IdentityProviderDisplayData& idp_display_data,
bool show_back_button) {
RemoveNonHeaderChildViews();
ConfigureBrandImageView(brand_icon_,
idp_display_data.idp_metadata.brand_icon_url);
// Show the "Choose an account to continue" label.
CHECK(body_label_);
body_label_->SetVisible(/*visible=*/true);
account_chooser_ = AddChildView(CreateSingleAccountChooser(
idp_display_data, account,
/*should_hover=*/true,
/*show_disclosure_label=*/false,
/*show_separator=*/true,
/*additional_row_vertical_padding=*/kVerticalPadding));
std::optional<views::Button::PressedCallback> use_other_account_callback =
std::nullopt;
if (idp_display_data.idp_metadata.supports_add_account) {
use_other_account_callback = base::BindRepeating(
&AccountSelectionViewBase::Observer::OnLoginToIdP,
base::Unretained(observer_), idp_display_data.idp_metadata.config_url,
idp_display_data.idp_metadata.idp_login_url);
}
AddChildView(CreateButtonRow(/*continue_callback=*/std::nullopt,
std::move(use_other_account_callback)));
InitDialogWidget();
// TODO(crbug.com/324052630): Connect with multi IDP API.
}
void AccountSelectionModalView::ShowFailureDialog(
const std::u16string& idp_for_display,
const content::IdentityProviderMetadata& idp_metadata) {
NOTREACHED_IN_MIGRATION()
<< "ShowFailureDialog is only implemented for AccountSelectionBubbleView";
}
void AccountSelectionModalView::ShowErrorDialog(
const std::u16string& idp_for_display,
const content::IdentityProviderMetadata& idp_metadata,
const std::optional<TokenError>& error) {
NOTREACHED_IN_MIGRATION()
<< "ShowErrorDialog is only implemented for AccountSelectionBubbleView";
}
void AccountSelectionModalView::ShowLoadingDialog() {
header_view_ = AddChildView(CreateAccountChooserHeader());
AddProgressBar();
AddChildView(CreatePlaceholderAccountRow());
AddChildView(CreateButtonRow());
InitDialogWidget();
}
void AccountSelectionModalView::ShowRequestPermissionDialog(
const content::IdentityRequestAccount& account,
const IdentityProviderDisplayData& idp_display_data) {
RemoveNonHeaderChildViews();
ConfigureBrandImageView(brand_icon_,
idp_display_data.idp_metadata.brand_icon_url);
// Hide the "Choose an account to continue" label.
CHECK(body_label_);
body_label_->SetVisible(/*visible=*/false);
account_chooser_ = AddChildView(CreateSingleAccountChooser(
idp_display_data, account,
/*should_hover=*/false,
/*show_disclosure_label=*/account.login_state ==
Account::LoginState::kSignUp,
/*show_separator=*/false,
/*additional_row_vertical_padding=*/0));
AddChildView(CreateButtonRow(
base::BindRepeating(
&AccountSelectionViewBase::Observer::OnAccountSelected,
base::Unretained(observer_), std::cref(account),
std::cref(idp_display_data)),
/*use_other_account_callback=*/std::nullopt,
base::BindRepeating(
&AccountSelectionViewBase::Observer::OnBackButtonClicked,
base::Unretained(observer_))));
InitDialogWidget();
}
void AccountSelectionModalView::ShowSingleReturningAccountDialog(
const std::vector<IdentityProviderDisplayData>& idp_data_list) {
NOTREACHED_IN_MIGRATION()
<< "ShowSingleReturningAccountDialog is only implemented for "
"AccountSelectionBubbleView";
}
std::unique_ptr<views::View>
AccountSelectionModalView::CreateBrandIconImageView(
const GURL& brand_icon_url) {
// Create IDP brand icon image view.
std::unique_ptr<BrandIconImageView> brand_icon_image_view =
std::make_unique<BrandIconImageView>(
base::BindOnce(&AccountSelectionViewBase::AddIdpImage,
weak_ptr_factory_.GetWeakPtr()),
kModalIdpIconSize, /*should_circle_crop=*/true);
brand_icon_ = brand_icon_image_view.get();
brand_icon_image_view->SetImageSize(
gfx::Size(kModalIdpIconSize, kModalIdpIconSize));
if (brand_icon_url.is_valid()) {
ConfigureBrandImageView(brand_icon_image_view.get(), brand_icon_url);
} else {
brand_icon_image_view->SetImage(ui::ImageModel::FromVectorIcon(
kWebidGlobeIcon, ui::kColorIconSecondary, kModalIdpIconSize));
brand_icon_image_view->SetVisible(true);
}
// Create background image view.
std::unique_ptr<BackgroundImageView> background_image_view =
std::make_unique<BackgroundImageView>(web_contents_);
// Put background image view into a FillLayout container.
std::unique_ptr<views::View> background_container =
std::make_unique<views::View>();
background_container->SetUseDefaultFillLayout(true);
background_container->AddChildView(std::move(background_image_view));
// Put brand icon image view into a BoxLayout container.
std::unique_ptr<views::BoxLayoutView> icon_container =
std::make_unique<views::BoxLayoutView>();
icon_container->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
icon_container->SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
icon_container->AddChildView(std::move(brand_icon_image_view));
// Put BoxLayout container into FillLayout container to stack the views. This
// stacks the IDP icon on top of the background image.
background_container->AddChildView(std::move(icon_container));
return background_container;
}
void AccountSelectionModalView::CloseDialog() {
if (!dialog_widget_) {
return;
}
CancelDialog();
// Remove the widget observer, if available. It is null in tests.
if (widget_observer_) {
dialog_widget_->RemoveObserver(widget_observer_);
}
dialog_widget_.reset();
}
std::string AccountSelectionModalView::GetDialogTitle() const {
return base::UTF16ToUTF8(title_label_->GetText());
}
std::u16string AccountSelectionModalView::GetQueuedAnnouncementForTesting() {
return queued_announcement_;
}
views::View* AccountSelectionModalView::GetInitiallyFocusedView() {
// If title has not been announced before, focus and announce the title.
if (!has_announced_title_) {
has_announced_title_ = true;
return title_label_;
}
// Make the queued announcement, if available. This can either be the
// disclosure text or the verifying status.
if (!queued_announcement_.empty()) {
GetViewAccessibility().AnnounceAlert(queued_announcement_);
queued_announcement_ = u"";
}
// If there is a progress bar and an account chooser, we are on the verifying
// sheet so focus on the cancel button.
if (has_progress_bar_ && account_chooser_) {
return cancel_button_;
}
// If there is a continue button, focus on the continue button.
if (continue_button_) {
return continue_button_;
}
// Default to the title.
return title_label_;
}
void AccountSelectionModalView::RemoveNonHeaderChildViews() {
// If removing progress bar, adjust the header margins so the rest of the
// dialog doesn't get shifted when the progress bar is removed.
if (has_progress_bar_) {
CHECK(header_view_);
static_cast<views::BoxLayout*>(header_view_->GetLayoutManager())
->set_inside_border_insets(gfx::Insets::TLBR(
/*top=*/kDialogMargin, /*left=*/kDialogMargin,
/*bottom=*/kVerticalPadding, /*right=*/kDialogMargin));
has_progress_bar_ = false;
}
// Make sure not to keep dangling pointers around first. We do not reset
// `header_view_`, `title_label_`, `body_label_` and `brand_icon_` because
// this method does not remove the header.
use_other_account_button_ = nullptr;
back_button_ = nullptr;
continue_button_ = nullptr;
cancel_button_ = nullptr;
account_chooser_ = nullptr;
const std::vector<raw_ptr<views::View, VectorExperimental>> child_views =
children();
for (views::View* child_view : child_views) {
if (child_view != header_view_) {
RemoveChildView(child_view);
delete child_view;
}
}
}
BEGIN_METADATA(AccountSelectionModalView)
END_METADATA