blob: 4bd6a6ec6e701782668c6764edb85bd850a6489f [file] [log] [blame]
// Copyright 2014 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/signin/chrome_signin_client.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/chrome_device_id_helper.h"
#include "chrome/browser/signin/force_signin_verifier.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/pref_names.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/bookmarks/browser/url_and_title.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/metrics/metrics_service.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/cookie_settings_util.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/base/signin_client.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_switches.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_change_event.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "components/version_info/channel.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "ui/base/models/tree_node_iterator.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/signin/wait_for_network_callback_helper_ash.h"
#include "chromeos/ash/components/network/network_handler.h"
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include <optional>
#include "chrome/browser/lacros/account_manager/account_manager_util.h"
#include "chromeos/crosapi/mojom/account_manager.mojom.h"
#include "chromeos/startup/browser_params_proxy.h"
#include "components/account_manager_core/account.h"
#include "components/account_manager_core/account_manager_util.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#endif
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/ui/browser.h"
#endif
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/profiles/profile_picker.h"
#endif
#if !BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_registry_factory.h"
#include "extensions/common/manifest.h"
#endif
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_oauth_multilogin_delegate_impl.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_handler_browser_impl.h"
#include "chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.h"
#endif // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
namespace {
// List of sources for which sign out is always allowed.
// TODO(crbug.com/40162614): core product logic should not rely on metric
// sources/callsites. Consider removing such logic, potentially as part of
// introducing a cross-platform SigninManager.
signin_metrics::ProfileSignout kAlwaysAllowedSignoutSources[] = {
// Allowed, because data has not been synced yet.
signin_metrics::ProfileSignout::kAbortSignin,
// Allowed, because the primary account must be cleared when the account is
// removed from device. Only used on Android and Lacros.
signin_metrics::ProfileSignout::kAccountRemovedFromDevice,
// Allowed, for tests.
signin_metrics::ProfileSignout::kForceSignoutAlwaysAllowedForTest,
// Allowed, because access to this entry point is controlled to only be
// enabled if the user may turn off sync.
signin_metrics::ProfileSignout::kUserClickedRevokeSyncConsentSettings,
// Allowed, because the dialog offers the option to the user to sign out.
// Note that the dialog is only shown on iOS and isn't planned to be shown
// on the other platforms since they already support user policies (no need
// for a notification in that case). Still, the metric is added to the
// kAlwaysAllowedSignoutSources for coherence.
signin_metrics::ProfileSignout::
kUserClickedSignoutFromUserPolicyNotificationDialog,
// Allowed, because the profile was signed out and the account was signed in
// to the web only before showing the sync confirmation dialog. The account
// was signed in to the profile in order to show the sync confirmation.
signin_metrics::ProfileSignout::kCancelSyncConfirmationOnWebOnlySignedIn,
// Allowed as the user wasn't signed in initially and data has not been
// synced yet.
signin_metrics::ProfileSignout::kCancelSyncConfirmationRemoveAccount,
// Data not synced yet.
// Used when moving the primary account (e.g. profile switch).
signin_metrics::ProfileSignout::kMovePrimaryAccount,
// Allowed as the profile is being deleted anyway.
signin_metrics::ProfileSignout::kSignoutDuringProfileDeletion,
};
// Returns the histogram suffix name per group of `signin_metrics::AccessPoint`.
std::string_view NameOfGroupedAccessPointHistogram(
signin_metrics::AccessPoint access_point) {
switch (access_point) {
case signin_metrics::AccessPoint::ACCESS_POINT_WEB_SIGNIN:
return ".PreUnoWebSignin";
case signin_metrics::AccessPoint::
ACCESS_POINT_CHROME_SIGNIN_INTERCEPT_BUBBLE:
return ".UnoSigninBubble";
case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER:
case signin_metrics::AccessPoint::ACCESS_POINT_FOR_YOU_FRE:
case signin_metrics::AccessPoint::
ACCESS_POINT_SIGNIN_INTERCEPT_FIRST_RUN_EXPERIENCE:
case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE:
return ".ProfileCreation";
case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN:
return ".ProfileMenu";
default:
return ".Other";
}
}
void RecordBookmarksCounts(signin_metrics::AccessPoint access_point,
signin::ConsentLevel consent_level,
size_t all_bookmarks_count,
size_t bar_bookmarks_count) {
static constexpr std::string_view kBaseHistogramName = "Signin.Bookmarks";
std::string_view consent_level_token =
consent_level == signin::ConsentLevel::kSignin ? ".OnSignin" : ".OnSync";
std::string all_bookmarks_histogram_name =
base::StrCat({kBaseHistogramName, consent_level_token, ".AllBookmarks"});
base::UmaHistogramCounts1000(all_bookmarks_histogram_name,
all_bookmarks_count);
base::UmaHistogramCounts1000(
base::StrCat({all_bookmarks_histogram_name,
NameOfGroupedAccessPointHistogram(access_point)}),
all_bookmarks_count);
std::string bar_bookmarks_histogram_name =
base::StrCat({kBaseHistogramName, consent_level_token, ".BookmarksBar"});
base::UmaHistogramCounts1000(bar_bookmarks_histogram_name,
bar_bookmarks_count);
base::UmaHistogramCounts1000(
base::StrCat({bar_bookmarks_histogram_name,
NameOfGroupedAccessPointHistogram(access_point)}),
bar_bookmarks_count);
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
void RecordExtensionsCounts(signin_metrics::AccessPoint access_point,
signin::ConsentLevel consent_level,
int extensions_count) {
static constexpr std::string_view kBaseHistogramName = "Signin.Extensions";
std::string_view consent_level_token =
consent_level == signin::ConsentLevel::kSignin ? ".OnSignin" : ".OnSync";
base::UmaHistogramCounts1000(
base::StrCat({kBaseHistogramName, consent_level_token}),
extensions_count);
base::UmaHistogramCounts1000(
base::StrCat({kBaseHistogramName, consent_level_token,
NameOfGroupedAccessPointHistogram(access_point)}),
extensions_count);
}
#endif
} // namespace
ChromeSigninClient::ChromeSigninClient(Profile* profile)
: wait_for_network_callback_helper_(
#if BUILDFLAG(IS_CHROMEOS_ASH)
std::make_unique<WaitForNetworkCallbackHelperAsh>()
#else
std::make_unique<WaitForNetworkCallbackHelperChrome>()
#endif
),
profile_(profile) {
}
ChromeSigninClient::~ChromeSigninClient() = default;
void ChromeSigninClient::DoFinalInit() {
VerifySyncToken();
}
// static
bool ChromeSigninClient::ProfileAllowsSigninCookies(Profile* profile) {
scoped_refptr<content_settings::CookieSettings> cookie_settings =
CookieSettingsFactory::GetForProfile(profile);
return signin::SettingsAllowSigninCookies(cookie_settings.get());
}
PrefService* ChromeSigninClient::GetPrefs() {
return profile_->GetPrefs();
}
scoped_refptr<network::SharedURLLoaderFactory>
ChromeSigninClient::GetURLLoaderFactory() {
if (url_loader_factory_for_testing_) {
return url_loader_factory_for_testing_;
}
return profile_->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
}
network::mojom::CookieManager* ChromeSigninClient::GetCookieManager() {
return profile_->GetDefaultStoragePartition()
->GetCookieManagerForBrowserProcess();
}
network::mojom::NetworkContext* ChromeSigninClient::GetNetworkContext() {
return profile_->GetDefaultStoragePartition()->GetNetworkContext();
}
bool ChromeSigninClient::AreSigninCookiesAllowed() {
return ProfileAllowsSigninCookies(profile_);
}
bool ChromeSigninClient::AreSigninCookiesDeletedOnExit() {
scoped_refptr<content_settings::CookieSettings> cookie_settings =
CookieSettingsFactory::GetForProfile(profile_);
return signin::SettingsDeleteSigninCookiesOnExit(cookie_settings.get());
}
void ChromeSigninClient::AddContentSettingsObserver(
content_settings::Observer* observer) {
HostContentSettingsMapFactory::GetForProfile(profile_)->AddObserver(observer);
}
void ChromeSigninClient::RemoveContentSettingsObserver(
content_settings::Observer* observer) {
HostContentSettingsMapFactory::GetForProfile(profile_)->RemoveObserver(
observer);
}
bool ChromeSigninClient::IsClearPrimaryAccountAllowed(
bool has_sync_account) const {
return GetSignoutDecision(has_sync_account,
/*signout_source=*/std::nullopt) ==
SigninClient::SignoutDecision::ALLOW;
}
bool ChromeSigninClient::IsRevokeSyncConsentAllowed() const {
return GetSignoutDecision(/*has_sync_account=*/true,
/*signout_source=*/std::nullopt) !=
SigninClient::SignoutDecision::REVOKE_SYNC_DISALLOWED;
}
void ChromeSigninClient::PreSignOut(
base::OnceCallback<void(SignoutDecision)> on_signout_decision_reached,
signin_metrics::ProfileSignout signout_source_metric,
bool has_sync_account) {
DCHECK(on_signout_decision_reached);
DCHECK(!on_signout_decision_reached_) << "SignOut already in-progress!";
on_signout_decision_reached_ = std::move(on_signout_decision_reached);
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
// `signout_source_metric` is `signin_metrics::ProfileSignout::kAbortSignin`
// if the user declines sync in the signin process. In case the user accepts
// the managed account but declines sync, we should keep the window open.
// `signout_source_metric` is
// `signin_metrics::ProfileSignout::kRevokeSyncFromSettings` when the user
// turns off sync from the settings, we should also keep the window open at
// this point.
// TODO(crbug.com/40280466): Check for managed accounts to be modified
// when aligning Managed vs Consumer accounts.
bool user_declines_sync_after_consenting_to_management =
(signout_source_metric == signin_metrics::ProfileSignout::kAbortSignin ||
signout_source_metric ==
signin_metrics::ProfileSignout::kRevokeSyncFromSettings ||
signout_source_metric == signin_metrics::ProfileSignout::
kCancelSyncConfirmationOnWebOnlySignedIn) &&
enterprise_util::UserAcceptedAccountManagement(profile_);
// These sign out won't remove the policy cache, keep the window opened.
bool keep_window_opened =
signout_source_metric ==
signin_metrics::ProfileSignout::kGoogleServiceNamePatternChanged ||
signout_source_metric ==
signin_metrics::ProfileSignout::kServerForcedDisable ||
signout_source_metric == signin_metrics::ProfileSignout::kPrefChanged ||
user_declines_sync_after_consenting_to_management;
if (signin_util::IsForceSigninEnabled() && !profile_->IsSystemProfile() &&
!profile_->IsGuestSession() && !profile_->IsChild() &&
!keep_window_opened) {
BrowserList::CloseAllBrowsersWithProfile(
profile_,
base::BindRepeating(&ChromeSigninClient::OnCloseBrowsersSuccess,
base::Unretained(this), signout_source_metric,
/*should_sign_out=*/true, has_sync_account),
base::BindRepeating(&ChromeSigninClient::OnCloseBrowsersAborted,
base::Unretained(this)),
signout_source_metric == signin_metrics::ProfileSignout::kAbortSignin ||
signout_source_metric == signin_metrics::ProfileSignout::
kAuthenticationFailedWithForceSignin ||
signout_source_metric ==
signin_metrics::ProfileSignout::
kCancelSyncConfirmationOnWebOnlySignedIn);
} else {
#else
{
#endif
std::move(on_signout_decision_reached_)
.Run(GetSignoutDecision(has_sync_account, signout_source_metric));
}
}
bool ChromeSigninClient::AreNetworkCallsDelayed() {
return wait_for_network_callback_helper_->AreNetworkCallsDelayed();
}
void ChromeSigninClient::DelayNetworkCall(base::OnceClosure callback) {
wait_for_network_callback_helper_->DelayNetworkCall(std::move(callback));
}
std::unique_ptr<GaiaAuthFetcher> ChromeSigninClient::CreateGaiaAuthFetcher(
GaiaAuthConsumer* consumer,
gaia::GaiaSource source) {
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
if (BoundSessionCookieRefreshService* bound_session_cookie_refresh_service =
BoundSessionCookieRefreshServiceFactory::GetForProfile(profile_);
bound_session_cookie_refresh_service) {
CHECK(switches::IsBoundSessionCredentialsEnabled(profile_->GetPrefs()));
return std::make_unique<ThrottledGaiaAuthFetcher>(
consumer, source, GetURLLoaderFactory(),
bound_session_cookie_refresh_service->GetBoundSessionThrottlerParams(),
std::make_unique<BoundSessionRequestThrottledHandlerBrowserImpl>(
*bound_session_cookie_refresh_service));
}
#endif // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
return std::make_unique<GaiaAuthFetcher>(consumer, source,
GetURLLoaderFactory());
}
version_info::Channel ChromeSigninClient::GetClientChannel() {
return chrome::GetChannel();
}
void ChromeSigninClient::OnPrimaryAccountChanged(
signin::PrimaryAccountChangeEvent event_details) {
for (signin::ConsentLevel consent_level :
{signin::ConsentLevel::kSignin, signin::ConsentLevel::kSync}) {
// Only record metrics when setting the primary account.
switch (event_details.GetEventTypeFor(consent_level)) {
case signin::PrimaryAccountChangeEvent::Type::kNone:
case signin::PrimaryAccountChangeEvent::Type::kCleared:
break;
case signin::PrimaryAccountChangeEvent::Type::kSet:
CHECK(event_details.GetSetPrimaryAccountAccessPoint().has_value());
signin_metrics::AccessPoint access_point =
event_details.GetSetPrimaryAccountAccessPoint().value();
std::optional<size_t> all_bookmarks_count = GetAllBookmarksCount();
std::optional<size_t> bar_bookmarks_count =
GetBookmarkBarBookmarksCount();
if (all_bookmarks_count.has_value() &&
bar_bookmarks_count.has_value()) {
RecordBookmarksCounts(access_point, consent_level,
all_bookmarks_count.value(),
bar_bookmarks_count.value());
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
std::optional<size_t> extensions_count = GetExtensionsCount();
if (extensions_count.has_value()) {
RecordExtensionsCounts(access_point, consent_level,
extensions_count.value());
}
#endif
#if !BUILDFLAG(IS_CHROMEOS_ASH)
RecordOpenTabCount(access_point, consent_level);
#endif
}
}
}
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
std::unique_ptr<signin::BoundSessionOAuthMultiLoginDelegate>
ChromeSigninClient::CreateBoundSessionOAuthMultiloginDelegate() const {
if (BoundSessionCookieRefreshService* bound_session_cookie_refresh_service =
BoundSessionCookieRefreshServiceFactory::GetForProfile(profile_);
bound_session_cookie_refresh_service) {
return std::make_unique<BoundSessionOAuthMultiLoginDelegateImpl>(
bound_session_cookie_refresh_service->GetWeakPtr());
}
return nullptr;
}
#endif
SigninClient::SignoutDecision ChromeSigninClient::GetSignoutDecision(
bool has_sync_account,
const std::optional<signin_metrics::ProfileSignout> signout_source) const {
// TODO(crbug.com/40239707): Revisit |kAlwaysAllowedSignoutSources| in general
// and for Lacros main profile.
for (const auto& always_allowed_source : kAlwaysAllowedSignoutSources) {
if (!signout_source.has_value()) {
break;
}
if (signout_source.value() == always_allowed_source) {
return SigninClient::SignoutDecision::ALLOW;
}
}
if (is_clear_primary_account_allowed_for_testing_.has_value()) {
return is_clear_primary_account_allowed_for_testing_.value();
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// The primary account in Lacros main profile must be the device account and
// can't be changed/cleared.
if (profile_->IsMainProfile()) {
return SigninClient::SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED;
}
#endif
#if BUILDFLAG(IS_ANDROID)
// On Android we do not allow supervised users to sign out.
// We also don't allow sign out on ChromeOS, though this is enforced outside
// the scope of this method.
// Other platforms do not restrict signout of supervised users.
if (profile_->IsChild()) {
return SigninClient::SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED;
}
#endif
// Android allows signing out of Managed accounts.
#if !BUILDFLAG(IS_ANDROID)
// Check if managed user.
if (enterprise_util::UserAcceptedAccountManagement(profile_)) {
if (base::FeatureList::IsEnabled(kDisallowManagedProfileSignout)) {
// Allow revoke sync but disallow signout regardless of consent level of
// the primary account.
return SigninClient::SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED;
}
// Syncing users are not allowed to revoke sync or signout. Signed in non-
// syncing users don't have any signout restrictions related to management.
if (has_sync_account) {
return SigninClient::SignoutDecision::REVOKE_SYNC_DISALLOWED;
}
}
#endif
return SigninClient::SignoutDecision::ALLOW;
}
void ChromeSigninClient::VerifySyncToken() {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
// We only verify the token once when Profile is just created.
if (signin_util::IsForceSigninEnabled() && !force_signin_verifier_) {
force_signin_verifier_ = std::make_unique<ForceSigninVerifier>(
profile_, IdentityManagerFactory::GetForProfile(profile_),
base::BindOnce(&ChromeSigninClient::OnTokenFetchComplete,
base::Unretained(this)));
}
#endif
}
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
void ChromeSigninClient::OnTokenFetchComplete(bool token_is_valid) {
// If the token is valid we do need to do anything special and let the user
// proceed.
if (token_is_valid) {
return;
}
// Token is not valid, we close all the browsers and open the Profile
// Picker.
should_display_user_manager_ = true;
BrowserList::CloseAllBrowsersWithProfile(
profile_,
base::BindRepeating(
&ChromeSigninClient::OnCloseBrowsersSuccess, base::Unretained(this),
signin_metrics::ProfileSignout::kAuthenticationFailedWithForceSignin,
// Do not sign the user out to allow them to reauthenticate from the
// profile picker.
/*should_sign_out=*/false,
// Sync value is not used since we are not signing out.
/*has_sync_account=*/false),
/*on_close_aborted=*/base::DoNothing(),
/*skip_beforeunload=*/true);
}
#endif
std::optional<size_t> ChromeSigninClient::GetAllBookmarksCount() {
bookmarks::BookmarkModel* bookmarks =
BookmarkModelFactory::GetForBrowserContext(profile_);
if (!bookmarks || !bookmarks->root_node()) {
return std::nullopt;
}
// Recursive traversal of the root node, counting URLs only.
size_t count = 0;
ui::TreeNodeIterator<const bookmarks::BookmarkNode> iterator(
bookmarks->root_node());
while (iterator.has_next()) {
const bookmarks::BookmarkNode* const node = iterator.Next();
// Skip folders.
if (node->is_url()) {
++count;
}
}
return count;
}
std::optional<size_t> ChromeSigninClient::GetBookmarkBarBookmarksCount() {
bookmarks::BookmarkModel* bookmarks =
BookmarkModelFactory::GetForBrowserContext(profile_);
if (!bookmarks || !bookmarks->bookmark_bar_node()) {
return std::nullopt;
}
// It is intended that we only count the visible bookmarks on the bar, meaning
// we are not interested in the bookmarks within a folder or subfolder of the
// bar. Counting the children only gets us the first layer that appears on the
// bar which is the count we need (Note: a folder on that layer counts as 1).
return bookmarks->bookmark_bar_node()->children().size();
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
std::optional<size_t> ChromeSigninClient::GetExtensionsCount() {
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistryFactory::GetForBrowserContext(profile_);
if (!registry) {
return std::nullopt;
}
size_t user_installed_extension_count = 0;
for (auto& extension : registry->enabled_extensions()) {
// Mimics the count done for the Histograms `Extensions.LoadExtensionUser2`
// that counts the user installed extensions.
if (extension->is_extension() &&
!extensions::Manifest::IsExternalLocation(extension->location()) &&
!extensions::Manifest::IsUnpackedLocation(extension->location()) &&
!extensions::Manifest::IsComponentLocation(extension->location())) {
++user_installed_extension_count;
}
}
return user_installed_extension_count;
}
#endif
#if !BUILDFLAG(IS_CHROMEOS_ASH)
void ChromeSigninClient::RecordOpenTabCount(
signin_metrics::AccessPoint access_point,
signin::ConsentLevel consent_level) {
size_t tabs_count = 0;
#if BUILDFLAG(IS_ANDROID)
for (const TabModel* model : TabModelList::models()) {
// Note: Even though on Android only a single regular profile is supported,
// there can also be an incognito profile which should be excluded here.
if (model->GetProfile() != profile_) {
continue;
}
tabs_count += model->GetTabCount();
}
#else // !BUILDFLAG(IS_ANDROID)
for (Browser* browser : *BrowserList::GetInstance()) {
if (browser->profile() != profile_) {
continue;
}
if (TabStripModel* tab_strip_model = browser->tab_strip_model()) {
tabs_count += tab_strip_model->count();
}
}
#endif // !BUILDFLAG(IS_ANDROID)
signin_metrics::RecordOpenTabCountOnSignin(access_point, consent_level,
tabs_count);
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
// Returns the account that must be auto-signed-in to the Main Profile in
// Lacros.
// This is, when available, the account used to sign into the Chrome OS
// session. This may be a Gaia account or a Microsoft Active Directory
// account. This field will be null for Guest sessions, Managed Guest
// sessions, Demo mode, and Kiosks. Note that this is different from the
// concept of a Primary Account in the browser. A user may not be signed into
// a Lacros browser Profile, or may be signed into a browser Profile with an
// account which is different from the account which they used to sign into
// the device - aka Device Account.
// Also note that this will be null for Secondary / non-Main Profiles in
// Lacros, because they do not start with the Chrome OS Device Account
// signed-in by default.
std::optional<account_manager::Account>
ChromeSigninClient::GetInitialPrimaryAccount() {
if (!profile_->IsMainProfile()) {
return std::nullopt;
}
const crosapi::mojom::AccountPtr& device_account =
chromeos::BrowserParamsProxy::Get()->DeviceAccount();
if (!device_account) {
return std::nullopt;
}
return account_manager::FromMojoAccount(device_account);
}
// Returns whether the account that must be auto-signed-in to the main profile
// in Lacros is a child account.
// Returns false for guest session, public session, kiosk, demo mode and Active
// Directory account.
// Returns null for secondary / non-main profiles in LaCrOS.
std::optional<bool> ChromeSigninClient::IsInitialPrimaryAccountChild() const {
if (!profile_->IsMainProfile()) {
return std::nullopt;
}
const bool is_child_session =
chromeos::BrowserParamsProxy::Get()->SessionType() ==
crosapi::mojom::SessionType::kChildSession;
return is_child_session;
}
void ChromeSigninClient::RemoveAccount(
const account_manager::AccountKey& account_key) {
std::optional<account_manager::Account> device_account =
GetInitialPrimaryAccount();
if (device_account.has_value() && device_account->key == account_key) {
DLOG(ERROR)
<< "The primary account should not be removed from the main profile";
return;
}
g_browser_process->profile_manager()
->GetAccountProfileMapper()
->RemoveAccount(profile_->GetPath(), account_key);
}
void ChromeSigninClient::RemoveAllAccounts() {
if (GetInitialPrimaryAccount().has_value()) {
DLOG(ERROR) << "It is not allowed to remove the initial primary account.";
return;
}
DCHECK(!profile_->IsMainProfile());
g_browser_process->profile_manager()
->GetAccountProfileMapper()
->RemoveAllAccounts(profile_->GetPath());
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
void ChromeSigninClient::SetURLLoaderFactoryForTest(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
url_loader_factory_for_testing_ = url_loader_factory;
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Do not make network requests in unit tests. ash::NetworkHandler should
// not be used and is not expected to have been initialized in unit tests.
wait_for_network_callback_helper_
->DisableNetworkCallsDelayedForTesting( // IN-TEST
url_loader_factory_for_testing_ &&
!ash::NetworkHandler::IsInitialized());
#endif // BUILDFLAG(IS_CHROMEOS_ASH)
}
void ChromeSigninClient::OnCloseBrowsersSuccess(
const signin_metrics::ProfileSignout signout_source_metric,
bool should_sign_out,
bool has_sync_account,
const base::FilePath& profile_path) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
if (signin_util::IsForceSigninEnabled() && force_signin_verifier_.get()) {
force_signin_verifier_->Cancel();
}
#endif
if (should_sign_out) {
std::move(on_signout_decision_reached_)
.Run(GetSignoutDecision(has_sync_account, signout_source_metric));
}
LockForceSigninProfile(profile_path);
// After sign out, lock the profile and show UserManager if necessary.
if (should_display_user_manager_) {
ShowUserManager(profile_path);
} else {
should_display_user_manager_ = true;
}
}
void ChromeSigninClient::OnCloseBrowsersAborted(
const base::FilePath& profile_path) {
should_display_user_manager_ = true;
// Disallow sign-out (aborted).
std::move(on_signout_decision_reached_)
.Run(SignoutDecision::REVOKE_SYNC_DISALLOWED);
}
void ChromeSigninClient::LockForceSigninProfile(
const base::FilePath& profile_path) {
ProfileAttributesEntry* entry =
g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetProfileAttributesWithPath(profile_->GetPath());
if (!entry) {
return;
}
entry->LockForceSigninProfile(true);
}
void ChromeSigninClient::ShowUserManager(const base::FilePath& profile_path) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH)
ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
ProfilePicker::EntryPoint::kProfileLocked));
#endif
}