blob: db46f1414b3f35a2b4f23b254b2e5c03f4df8699 [file] [log] [blame]
// Copyright 2023 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_PRIVACY_SANDBOX_TRACKING_PROTECTION_NOTICE_SERVICE_H_
#define CHROME_BROWSER_PRIVACY_SANDBOX_TRACKING_PROTECTION_NOTICE_SERVICE_H_
#include <memory>
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/ui/browser_tab_strip_tracker.h"
#include "chrome/browser/ui/browser_tab_strip_tracker_delegate.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/privacy_sandbox/tracking_protection_onboarding.h"
#include "components/user_education/common/feature_promo_controller.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/page.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#if BUILDFLAG(IS_ANDROID)
#error This file should only be included on desktop.
#endif
class Profile;
namespace privacy_sandbox {
// A service which contains the logic tracking some user interactions with the
// browser, in order to determine when the best time is to Show the Onboarding
// Notice, then actually displays it.
// If the profile is not to be shown the notice at all due to ineligibility,
// then this service doesn't observe anything (Except the
// TrackingProtectionOnboarding Service).
// We are observing two different types of interactions:
// 1. Using the TabStripModelObserver: All updates to the tabs: This allows
// us to show/hide the notice on all tabs (including tabs that we started
// observing newly created webcontents) after the user selects a new one.
//
// 2. Using the WebContentsObserver: Navigation updates to the active
// webcontent: This allows us to show/hide the notice based on the
// navigation, in case the user doesn't update the tab, but only its
// webcontent through navigation.
class TrackingProtectionNoticeService
: public KeyedService,
public TabStripModelObserver,
public BrowserTabStripTrackerDelegate,
public TrackingProtectionOnboarding::Observer {
public:
TrackingProtectionNoticeService(
Profile* profile,
TrackingProtectionOnboarding* onboarding_service);
~TrackingProtectionNoticeService() override;
enum class TrackingProtectionMetricsNoticeEvent {
kNoticeObjectCreated = 0,
kActiveTabChanged = 1,
kNavigationFinished = 2,
kUpdateNoticeVisibility = 3,
kBrowserTypeNonNormal = 4,
kNoticeShowingButShouldnt = 5,
kInactiveWebcontentUpdated = 6,
kLocationIconNonSecure = 7,
kLocationIconNonVisible = 8,
kNoticeAlreadyShowing = 9,
kNoticeRequestedAndShown = 10,
kNoticeRequestedButNotShown = 11,
kPromoPreviouslyDismissed = 12,
kMaxValue = kPromoPreviouslyDismissed,
};
class TabHelper : public content::WebContentsObserver,
public content::WebContentsUserData<TabHelper> {
public:
TabHelper(const TabHelper&) = delete;
TabHelper& operator=(const TabHelper&) = delete;
~TabHelper() override;
// Static method that tells us if the helper is needed. This is to be
// checked before creating the helper so we don't unnecessarily create one
// for every WebContent.
static bool IsHelperNeeded(Profile* profile);
private:
friend class content::WebContentsUserData<TabHelper>;
explicit TabHelper(content::WebContents* web_contents);
// contents::WebContentsObserver:
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
// contents::WebContentsObserver:
void PrimaryPageChanged(content::Page& page) override;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
private:
// IPH Based Tracking Protection Notice, in charge of showing/hiding the IPH
// Promo based on page eligibility and user navigation.
class BaseIPHNotice {
public:
BaseIPHNotice(Profile* profile,
TrackingProtectionOnboarding* onboarding_service,
TrackingProtectionNoticeService* notice_service);
virtual ~BaseIPHNotice();
void MaybeUpdateNoticeVisibility(content::WebContents* web_content);
TrackingProtectionNoticeService* notice_service() const {
return notice_service_;
}
// Fires when the notice is closed (For any reason)
virtual void OnNoticeClosed(
base::Time showed_when,
user_education::FeaturePromoController* promo_controller);
private:
virtual bool WasPromoPreviouslyDismissed(Browser* browser);
virtual bool IsPromoShowing(Browser* browser);
virtual bool MaybeShowPromo(Browser* browser);
virtual void HidePromo(Browser* browser);
bool IsLocationBarEligible(Browser* browser);
virtual TrackingProtectionOnboarding::NoticeType GetNoticeType() = 0;
virtual const base::Feature& GetIPHFeature() = 0;
raw_ptr<Profile> profile_;
raw_ptr<TrackingProtectionOnboarding> onboarding_service_;
raw_ptr<TrackingProtectionNoticeService> notice_service_;
};
class OnboardingNotice : public BaseIPHNotice {
public:
OnboardingNotice(Profile* profile,
TrackingProtectionOnboarding* onboarding_service,
TrackingProtectionNoticeService* notice_service);
private:
TrackingProtectionOnboarding::NoticeType GetNoticeType() override;
const base::Feature& GetIPHFeature() override;
};
class OffboardingNotice : public BaseIPHNotice {
public:
OffboardingNotice(Profile* profile,
TrackingProtectionOnboarding* onboarding_service,
TrackingProtectionNoticeService* notice_service);
private:
TrackingProtectionOnboarding::NoticeType GetNoticeType() override;
const base::Feature& GetIPHFeature() override;
void OnNoticeClosed(
base::Time showed_when,
user_education::FeaturePromoController* promo_controller) override;
};
class SilentOnboardingNotice : public BaseIPHNotice {
public:
SilentOnboardingNotice(Profile* profile,
TrackingProtectionOnboarding* onboarding_service,
TrackingProtectionNoticeService* notice_service);
private:
TrackingProtectionOnboarding::NoticeType GetNoticeType() override;
const base::Feature& GetIPHFeature() override;
bool WasPromoPreviouslyDismissed(Browser* browser) override;
bool IsPromoShowing(Browser* browser) override;
bool MaybeShowPromo(Browser* browser) override;
void HidePromo(Browser* browser) override;
void OnNoticeClosed(
base::Time showed_when,
user_education::FeaturePromoController* promo_controller) override;
};
// Indicates if the notice is needed to be displayed.
bool IsNoticeNeeded();
// This is called internally when the service should start observing the tab
// strip model across all eligible browsers. Browser eligibility is determined
// by the "ShouldTrackBrowser" below.
void InitializeTabStripTracker();
// This is called internally when the service should no longer observe changes
// to the tab strip model.
void ResetTabStripTracker();
// TabStripModelObserver
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
// BrowserTabStripTrackerDelegate
bool ShouldTrackBrowser(Browser* browser) override;
// TrackingProtectionOnboarding::Observer
void OnShouldShowNoticeUpdated() override;
// Runs the Hats Logic, which means could either Register the profile for a
// group if eligible, or trigger a survey.
void RunHatsLogic();
// Whether or not the Hats logic is required for the current client/profile.
bool IsHatsLogicRequired();
raw_ptr<Profile> profile_;
raw_ptr<TrackingProtectionOnboarding> onboarding_service_;
std::unique_ptr<BaseIPHNotice> onboarding_notice_;
std::unique_ptr<BaseIPHNotice> offboarding_notice_;
std::unique_ptr<BaseIPHNotice> silent_onboarding_notice_;
std::unique_ptr<BrowserTabStripTracker> tab_strip_tracker_;
base::ScopedObservation<TrackingProtectionOnboarding,
TrackingProtectionOnboarding::Observer>
onboarding_observation_{this};
bool has_opened_first_ntp_ = false;
};
} // namespace privacy_sandbox
#endif // CHROME_BROWSER_PRIVACY_SANDBOX_TRACKING_PROTECTION_NOTICE_SERVICE_H_