blob: 397a78438ba192a591ed233769a0d6abbd3f5fcc [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.
#ifndef CHROME_BROWSER_UI_VIEWS_WEBID_ACCOUNT_SELECTION_MODAL_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_WEBID_ACCOUNT_SELECTION_MODAL_VIEW_H_
#include <memory>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "chrome/browser/ui/views/webid/account_selection_view_base.h"
#include "components/image_fetcher/core/image_fetcher.h"
#include "content/public/browser/webid/identity_request_account.h"
#include "content/public/browser/webid/identity_request_dialog_controller.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/progress_bar.h"
#include "ui/views/controls/throbber.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/window/dialog_delegate.h"
namespace views {
class BoxLayoutView;
} // namespace views
namespace webid {
class AccountSelectionModalView;
class AccountSelectionModalDelegate : public views::DialogDelegate {
public:
explicit AccountSelectionModalDelegate(
std::unique_ptr<AccountSelectionModalView> account_selection_modal_view);
~AccountSelectionModalDelegate() override;
AccountSelectionModalDelegate(const AccountSelectionModalDelegate&) = delete;
AccountSelectionModalDelegate& operator=(
const AccountSelectionModalDelegate&) = delete;
// views::DialogDelegate:
views::View* GetInitiallyFocusedView() override;
// TODO (kylixrd): Investigate removal of these overrides.
views::Widget* GetWidget() override;
const views::Widget* GetWidget() const override;
private:
AccountSelectionModalView* GetAccountSelectionView();
};
// This view is used for the "active" flow for fedCM. This is only ever
// shown as a result of user action (e.g. clicking a button).
class AccountSelectionModalView : public views::BoxLayoutView,
public AccountSelectionViewBase {
METADATA_HEADER(AccountSelectionModalView, views::BoxLayoutView)
public:
AccountSelectionModalView(
const content::RelyingPartyData& rp_data,
const std::optional<std::u16string>& idp_title,
blink::mojom::RpContext rp_context,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
FedCmAccountSelectionView* owner);
~AccountSelectionModalView() override;
AccountSelectionModalView(const AccountSelectionModalView&) = delete;
AccountSelectionModalView& operator=(const AccountSelectionModalView&) =
delete;
// AccountSelectionViewBase:
void ShowMultiAccountPicker(
const std::vector<IdentityRequestAccountPtr>& accounts,
const std::vector<IdentityProviderDataPtr>& idp_list,
const gfx::Image& rp_icon,
bool show_back_button) override;
void ShowVerifyingSheet(const IdentityRequestAccountPtr& account,
const std::u16string& title) override;
void ShowSingleAccountConfirmDialog(const IdentityRequestAccountPtr& account,
bool show_back_button) override;
void ShowFailureDialog(
const std::u16string& idp_for_display,
const content::IdentityProviderMetadata& idp_metadata) override;
void ShowErrorDialog(
const std::u16string& idp_for_display,
const content::IdentityProviderMetadata& idp_metadata,
const std::optional<content::IdentityCredentialTokenError>& error)
override;
void ShowRequestPermissionDialog(
const IdentityRequestAccountPtr& account) override;
std::string GetDialogTitle() const override;
std::optional<std::string> GetDialogSubtitle() const override;
std::u16string dialog_title() const { return title_; }
// views::BoxLayoutView:
void VisibilityChanged(View* starting_from, bool is_visible) override;
views::View* GetInitiallyFocusedView();
void UpdateTitleAndSubtitle(
const content::RelyingPartyData& rp_data) override;
std::u16string GetQueuedAnnouncementForTesting();
private:
friend class AccountSelectionModalViewTest;
// Returns a View for header of an account chooser. It contains text to prompt
// the user to sign in to an RP with an account from an IDP.
std::unique_ptr<views::View> CreateHeader();
// Returns a View for multiple account chooser. It contains the info for each
// account in a button, so the user can pick an account.
std::unique_ptr<views::View> CreateMultipleAccountChooser(
const std::vector<IdentityRequestAccountPtr>& accounts);
// Returns a View to display account rows. It contains one row per account.
// `should_hover` determines whether the rows are clickable.
// `show_separator` determines whether rows should be surrounded by a
// separator.
std::unique_ptr<views::View> CreateAccountRows(
const std::vector<IdentityRequestAccountPtr>& accounts,
bool should_hover,
bool show_separator,
bool is_request_permission_dialog);
// Returns a View for an account row that acts as a placeholder.
std::unique_ptr<views::View> CreatePlaceholderAccountRow();
// Returns a View for a row of custom buttons. A cancel button is always
// shown. A continue button, use other account button and/or back button is
// shown if `continue_callback`, `use_other_account_callback` and/or
// `back_callback` is specified respectively. If `use_other_account_callback`
// is specified, `back_callback` should NOT be specified and vice versa.
std::unique_ptr<views::View> CreateButtonRow(
std::optional<views::Button::PressedCallback> continue_callback,
std::optional<views::Button::PressedCallback> use_other_account_callback,
std::optional<views::Button::PressedCallback> back_callback);
// Returns a View containing the background and icon containers. The icon
// containers are not visible until they are configured through
// `ConfigureBrandImageView`.
std::unique_ptr<views::View> CreateIconHeaderView();
// Returns a BoxLayoutView containing a spinner.
std::unique_ptr<views::BoxLayoutView> CreateSpinnerIconView();
// Returns a BoxLayoutView containing the IDP icon. If the image cannot be
// fetched, a globe icon is shown.
std::unique_ptr<views::BoxLayoutView> CreateIdpIconView();
// Returns a BoxLayoutView containing the IDP icon, arrow icon and RP icon in
// that order, horizontally.
std::unique_ptr<views::BoxLayoutView> CreateCombinedIconsView();
// Hides `header_icon_spinner_` and shows `idp_brand_icon_` upon successfully
// setting the IDP icon.
void OnIdpBrandIconSet();
// Hides `header_icon_spinner_`, `idp_brand_icon_` and shows `combined_icons_`
// upon successfully setting the IDP and RP icons.
void OnCombinedIconsSet();
// Removes all child views and dangling pointers and adjust header with
// progress bar and body label if needed.
void RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded();
// Removes `combined_icons_` and all its child views, if available.
void MaybeRemoveCombinedIconsView();
// Notifies the observer of the account selection and updates the continue
// button into a spinner button.
void OnContinueButtonClicked(const IdentityRequestAccountPtr& account,
const ui::Event& event);
// Notifies the observer of the use other account button being clicked.
void OnUseOtherAccountButtonClicked(const GURL& idp_config_url,
const GURL& idp_login_url,
const ui::Event& event);
// Updates the button to have a spinner appear in the middle of it.
void ReplaceButtonWithSpinner(
views::MdTextButton* button,
ui::ColorId spinner_color = ui::kColorButtonForeground,
ui::ColorId button_color = ui::kColorButtonBackground);
// Helper method to show the given accounts.
void ShowAccounts(const std::vector<IdentityRequestAccountPtr>& accounts,
bool is_single_account_chooser);
// The following are raw_ptrs for views in the header. These do not need to be
// reset by RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded().
// View containing the header.
raw_ptr<views::View> header_view_ = nullptr;
// View containing the header icons.
raw_ptr<views::View> header_icon_view_ = nullptr;
// View containing the title.
raw_ptr<views::Label> title_label_ = nullptr;
// View containing the body.
raw_ptr<views::Label> body_label_ = nullptr;
// View containing the IDP brand icon image. This view is constructed in the
// loading dialog but is only visible after the loading dialog.
raw_ptr<BrandIconImageView> idp_brand_icon_ = nullptr;
// View containing the spinner in the header. This spinner is shown until the
// IDP brand icon is fetched.
raw_ptr<views::Throbber> header_icon_spinner_ = nullptr;
// View containing the IDP brand icon image meant to be shown in the request
// permission dialog together with the RP icon. This icon is a smaller version
// of `idp_brand_icon_` because it has to share the space in the header with
// the RP icon.
raw_ptr<BrandIconImageView> combined_icons_idp_brand_icon_ = nullptr;
// View containing the RP brand icon image in the request permission dialog.
raw_ptr<BrandIconImageView> combined_icons_rp_brand_icon_ = nullptr;
// BoxLayoutView containing the IDP icon, arrow icon and RP icon in that
// order, horizontally. This view is constructed in the loading dialog but
// will be made visible only in the request permission dialog.
raw_ptr<views::BoxLayoutView> combined_icons_ = nullptr;
// The following are raw_ptrs for view outside of the header. These MUST be
// reset in RemoveNonHeaderChildViewsAndUpdateHeaderIfNeeded(), as not
// resetting could result in a UAF.
// View containing the use other account button.
raw_ptr<views::MdTextButton> use_other_account_button_ = nullptr;
// View containing the back button.
raw_ptr<views::View> back_button_ = nullptr;
// View containing the continue button.
raw_ptr<views::MdTextButton> continue_button_ = nullptr;
// View containing the cancel button.
raw_ptr<views::View> cancel_button_ = nullptr;
// View containing the account chooser.
raw_ptr<views::View> account_chooser_ = nullptr;
// View containing the view to focus on in the verifying sheet.
raw_ptr<views::View> verifying_focus_view_ = nullptr;
// Whether a spinner is present.
bool has_spinner_{false};
// Whether the title has been announced for accessibility.
bool has_announced_title_{false};
// The announcement that should be made upon view focus, if screen reader is
// turned on.
std::u16string queued_announcement_;
// The IDP for use in the title; nullopt in case of multi-IDP.
std::optional<std::u16string> idp_title_;
// The RP context to use in the title (e.g. "Sign In", "Use").
blink::mojom::RpContext rp_context_;
// The title for the modal dialog.
std::u16string title_;
// The subtitle for the modal dialog.
std::u16string subtitle_;
// Used to ensure that callbacks are not run if the AccountSelectionModalView
// is destroyed.
base::WeakPtrFactory<AccountSelectionModalView> weak_ptr_factory_{this};
};
} // namespace webid
#endif // CHROME_BROWSER_UI_VIEWS_WEBID_ACCOUNT_SELECTION_MODAL_VIEW_H_