blob: 2bc8bbc578a1037e624550d577cd76bd51d0b3e2 [file] [log] [blame]
// Copyright 2021 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/user_education/user_education_service_factory.h"
#include <memory>
#include <optional>
#include "base/synchronization/lock.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/feature_engagement/tracker_factory.h"
#include "chrome/browser/headless/headless_mode_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/user_education/browser_user_education_storage_service.h"
#include "chrome/browser/user_education/user_education_service.h"
#include "chrome/common/chrome_switches.h"
#include "components/feature_engagement/public/tracker.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/user_education/common/session/user_education_idle_observer.h"
#include "components/user_education/common/session/user_education_idle_policy.h"
#include "components/user_education/common/session/user_education_session_manager.h"
#include "content/public/browser/browser_context.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chromeos/components/mgs/managed_guest_session_utils.h"
#endif
namespace {
// Idle observer that doesn't do anything.
class StubIdleObserver : public user_education::UserEducationIdleObserver {
public:
StubIdleObserver() = default;
~StubIdleObserver() override = default;
// UserEducationIdleObserver:
std::optional<base::Time> MaybeGetNewLastActiveTime() const override {
return std::nullopt;
}
};
} // namespace
// These are found in chrome/browser/ui/user_education, so extern the factory
// methods to create the necessary objects.
extern std::unique_ptr<user_education::UserEducationIdleObserver>
CreatePollingIdleObserver();
extern std::unique_ptr<RecentSessionObserver> CreateRecentSessionObserver(
Profile& profile);
UserEducationServiceFactory* UserEducationServiceFactory::GetInstance() {
static base::NoDestructor<UserEducationServiceFactory> instance;
return instance.get();
}
// static
UserEducationService* UserEducationServiceFactory::GetForBrowserContext(
content::BrowserContext* profile) {
return static_cast<UserEducationService*>(
GetInstance()->GetServiceForBrowserContext(profile, true));
}
UserEducationServiceFactory::UserEducationServiceFactory()
: ProfileKeyedServiceFactory(
"UserEducationService",
ProfileSelections::Builder()
.WithRegular(ProfileSelection::kOriginalOnly)
// TODO(crbug.com/40257657): Check if this service is needed in
// Guest mode.
.WithGuest(ProfileSelection::kOriginalOnly)
// The service is needed by the System Profile OTR (that manages
// the Profile Picker) to control the IPHs displayed in the
// Profile Picker.
.WithSystem(ProfileSelection::kOffTheRecordOnly)
// TODO(crbug.com/41488885): Check if this service is needed for
// Ash Internals.
.WithAshInternals(ProfileSelection::kOriginalOnly)
.Build()) {}
UserEducationServiceFactory::~UserEducationServiceFactory() = default;
// static
std::unique_ptr<UserEducationService>
UserEducationServiceFactory::BuildServiceInstanceForBrowserContextImpl(
content::BrowserContext* context,
bool disable_idle_polling) {
Profile* const profile = Profile::FromBrowserContext(context);
// Create the user education service.
auto result = std::make_unique<UserEducationService>(
profile, ProfileAllowsUserEducation(profile));
// Set up the session manager.
result->user_education_session_manager().Init(
&result->user_education_storage_service(),
disable_idle_polling ? std::make_unique<StubIdleObserver>()
: CreatePollingIdleObserver(),
std::make_unique<user_education::UserEducationIdlePolicy>());
// Possibly install a session observer. This isn't public, since it's
// self-contained and mostly for tracking state.
if (result->recent_session_tracker()) {
result->recent_session_observer_ = CreateRecentSessionObserver(*profile);
result->recent_session_observer_->Init(*result->recent_session_tracker());
}
return result;
}
// static
bool UserEducationServiceFactory::ProfileAllowsUserEducation(Profile* profile) {
#if BUILDFLAG(CHROME_FOR_TESTING)
// IPH is always disabled in Chrome for Testing.
return false;
#else
// In order to do user education, the browser must have a UI and not be an
// "off-the-record" or in a demo or guest mode.
if (profile->IsIncognitoProfile() || profile->IsGuestSession() ||
profiles::IsDemoSession() || profiles::IsChromeAppKioskSession()) {
return false;
}
#if BUILDFLAG(IS_CHROMEOS)
if (chromeos::IsManagedGuestSession()) {
return false;
}
#endif
if (headless::IsHeadlessMode()) {
return false;
}
// If `--no-first-run` is enabled and we have not put IPH into test mode, User
// Education is not eligible. (No FRE implies no startup bubbles, which
// precludes any IPH that might show, as it may be used for performance
// testing.)
//
// If IPH is in test mode (all features blocked, or all but a specific feature
// is blocked) then zero or more IPH are under test and all other IPH are
// already blocked. Since tests are no-FRE, not making this additional check
// would break all IPH tests.
const bool no_first_run =
base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoFirstRun);
const bool in_feature_test_mode =
feature_engagement::TrackerFactory::GetForBrowserContext(profile)
->IsInFeatureTestMode();
if (no_first_run && !in_feature_test_mode) {
return false;
}
return true;
#endif // BUILDFLAG(CHROME_FOR_TESTING)
}
std::unique_ptr<KeyedService>
UserEducationServiceFactory::BuildServiceInstanceForBrowserContext(
content::BrowserContext* context) const {
return BuildServiceInstanceForBrowserContextImpl(context,
disable_idle_polling_);
}