blob: 40560a8f05201145e324b8bfb4533dbe561e7201 [file] [log] [blame]
// Copyright 2018 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_PROFILES_AVATAR_TOOLBAR_BUTTON_H_
#define CHROME_BROWSER_UI_VIEWS_PROFILES_AVATAR_TOOLBAR_BUTTON_H_
#include "base/auto_reset.h"
#include "base/callback_list.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/events/event.h"
class AvatarToolbarButtonStateManager;
class Browser;
class BrowserView;
struct AccountInfo;
class GaiaId;
// Enum used for testing. It allows overriding different delay values based on
// their usage in the `AvatarToolbarButton` through helper testing functions.
enum class AvatarDelayType {
// Delay for the name to stop showing.
kNameGreeting,
// Delay for the on sign-in state.
kOnSignin,
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Delay for the SigninPending mode to show the "Verify it's you" text.
kSigninPendingText,
// Delay for the History Sync Opt-in entry point.
kHistorySyncOptin,
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
};
// This class takes care the Profile Avatar Button.
// Primarily applies UI configuration.
// It's data (text, icon, etc...) content are computed through the
// `AvatarToolbarButtonStateManager`, when relying on Chrome and Profile changes
// in order to adapt the expected content shown in the button.
class AvatarToolbarButton : public ToolbarButton,
signin::IdentityManager::Observer {
METADATA_HEADER(AvatarToolbarButton, ToolbarButton)
public:
class Observer : public base::CheckedObserver {
public:
virtual void OnMouseExited() {}
virtual void OnBlur() {}
virtual void OnIPHPromoChanged(bool has_promo) {}
virtual void OnIconUpdated() {}
virtual void OnButtonPressed() {}
~Observer() override = default;
};
explicit AvatarToolbarButton(BrowserView* browser);
AvatarToolbarButton(const AvatarToolbarButton&) = delete;
AvatarToolbarButton& operator=(const AvatarToolbarButton&) = delete;
~AvatarToolbarButton() override;
void UpdateText();
// Sets the button state to show the provided text with the provided
// accessibility label and action.
//
// If the `explicit_action` is set, it will override the default action of the
// button, otherwise the default action will be used.
//
// Returns a callback to be used when the button state should be reset, i.e.
// shown text should be hidden and the explicit action should stop being used.
[[nodiscard]] base::ScopedClosureRunner SetExplicitButtonState(
const std::u16string& text,
std::optional<std::u16string> accessibility_label,
std::optional<base::RepeatingCallback<void(bool is_source_accelerator)>>
explicit_action);
// Returns whether the button currently has an explicit state set.
bool HasExplicitButtonState() const;
// Control whether the button action is active or not.
// One reason to disable the action; when a bubble is shown from this button
// (and not the profile menu), we want to disable the button action, however
// the button should remain in an "active" state from a UI perspective.
void SetButtonActionDisabled(bool disabled);
bool IsButtonActionDisabled() const;
// Attempts showing the In-Product-Help for profile Switching.
void MaybeShowProfileSwitchIPH();
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX)
// Attempts showing the In-Product-Help when a supervised user signs-in in a
// profile.
void MaybeShowSupervisedUserSignInIPH();
// Attempts showing the In-Product-Help listing benefits for signed-in users
// after the sync-to-signin migration.
void MaybeShowSignInBenefitsIPH();
#endif
// Attempts showing the In-Product-Help in a subsequent web sign-in when the
// explicit browser sign-in preference was remembered.
void MaybeShowExplicitBrowserSigninPreferenceRememberedIPH(
const AccountInfo& account_info);
// Returns true if a text is set and is visible.
bool IsLabelPresentAndVisible() const;
// ToolbarButton:
void OnMouseExited(const ui::MouseEvent& event) override;
void OnBlur() override;
void OnThemeChanged() override;
void UpdateIcon() override;
void Layout(PassKey) override;
int GetIconSize() const override;
SkColor GetForegroundColor(ButtonState state) const override;
std::optional<SkColor> GetHighlightTextColor() const override;
std::optional<SkColor> GetHighlightBorderColor() const override;
bool ShouldPaintBorder() const override;
bool ShouldBlendHighlightColor() const override;
void AddedToWidget() override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
void ButtonPressed(bool is_source_accelerator = false);
// Methods to register or remove observers.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Can be used in tests to reduce or remove the delay before showing the IPH.
[[nodiscard]] static base::AutoReset<base::TimeDelta>
SetScopedIPHMinDelayAfterCreationForTesting(base::TimeDelta delay);
// These helper functions allow tests to be time independent; tests that are
// time dependent tend to create a lot of flakiness.
//
// This function allows to set an infinite delay for time dependent parts. By
// default tests should have this function called for all types, and then
// calling `TriggerTimeoutForTesting()` when needing to force trigger the
// ending of the delay. This allows to properly test the behavior before and
// after delay expiry while controlling those events..
[[nodiscard]] static base::AutoReset<std::optional<base::TimeDelta>>
CreateScopedInfiniteDelayOverrideForTesting(AvatarDelayType delay_type);
// Clears the active state (makes it inactive).
void ClearActiveStateForTesting();
#if BUILDFLAG(ENABLE_DICE_SUPPORT)
// Specific override for the SigninPending text delay. Setting a zero value
// make it possible to test the creation of browser after the delay has
// reached.
// The delay start time is shared in a ProfileUserData which makes it harder
// to access in case no browser are visible anymore, making the
// `TriggerTimeoutForTesting()` not enough for testing.
[[nodiscard]] static base::AutoReset<std::optional<base::TimeDelta>>
CreateScopedZeroDelayOverrideSigninPendingTextForTesting();
// WARNING: Do not use this method to test the Promo flows. Only used when
// necessary to bypass resetting the profile - e.g. when attempting to reach
// the limit counts.
void ForceShowingPromoForTesting();
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
private:
FRIEND_TEST_ALL_PREFIXES(AvatarToolbarButtonTest,
HighlightMeetsMinimumContrast);
// signin::IdentityManager::Observer:
void OnPrimaryAccountChanged(
const signin::PrimaryAccountChangeEvent& event_details) override;
void OnExtendedAccountInfoUpdated(const AccountInfo& info) override;
// ui::PropertyHandler:
void AfterPropertyChange(const void* key, int64_t old_value) override;
// Updates the layout insets depending on whether it is a chip or a button.
void UpdateLayoutInsets();
// Updates the inkdrop highlight and ripple properties depending on the state
// and whether the chip is expanded.
void UpdateInkdrop();
void UpdateAccessibilityLabel();
// Lists of observers.
base::ObserverList<Observer, true> observer_list_;
std::unique_ptr<AvatarToolbarButtonStateManager> state_manager_;
const raw_ptr<Browser> browser_;
// Time when this object was created.
const base::TimeTicks creation_time_;
// Do not show the IPH right when creating the window, so that the IPH has a
// separate animation.
static base::TimeDelta g_iph_min_delay_after_creation;
// Controls the action of the button, on press.
// Setting this to true will stop the button reaction but the button will
// remain in active state, not affecting it's UI in any way.
bool button_action_disabled_ = false;
// Gaia Id of the account that was signed in from having it's choice
// remembered following a web sign-in event but waiting for the available
// account information to be fetched in order to show the sign in IPH.
GaiaId gaia_id_for_signin_choice_remembered_;
base::ScopedObservation<signin::IdentityManager,
signin::IdentityManager::Observer>
identity_manager_observation_{this};
base::WeakPtrFactory<AvatarToolbarButton> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_UI_VIEWS_PROFILES_AVATAR_TOOLBAR_BUTTON_H_