blob: e55ee8fad0bc6d13dacc5a2bc5c80324b7338c55 [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/privacy_sandbox/privacy_sandbox_service.h"
#include <algorithm>
#include <iterator>
#include <numeric>
#include "base/feature_list.h"
#include "base/i18n/time_formatting.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/types/optional_util.h"
#include "build/branding_buildflags.h"
#include "chrome/common/webui_url_constants.h"
#include "components/browsing_topics/browsing_topics_service.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/privacy_sandbox_prefs.h"
#include "components/strings/grit/components_strings.h"
#include "content/public/browser/browsing_data_filter_builder.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/first_party_sets_handler.h"
#include "content/public/browser/interest_group_manager.h"
#include "content/public/common/content_features.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/schemeful_site.h"
#include "net/first_party_sets/first_party_set_entry.h"
#include "net/first_party_sets/global_first_party_sets.h"
#include "third_party/blink/public/common/features.h"
#include "ui/base/l10n/l10n_util.h"
#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/hats/trust_safety_sentiment_service.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/profiles/profiles_state.h"
#endif
namespace {
constexpr char kBlockedTopicsTopicKey[] = "topic";
bool g_prompt_disabled_for_tests = false;
// Returns whether 3P cookies are blocked by |cookie_settings|. This can be
// either through blocking 3P cookies directly, or blocking all cookies.
bool AreThirdPartyCookiesBlocked(
content_settings::CookieSettings* cookie_settings) {
const auto default_content_setting =
cookie_settings->GetDefaultCookieSetting(/*provider_id=*/nullptr);
return cookie_settings->ShouldBlockThirdPartyCookies() ||
default_content_setting == ContentSetting::CONTENT_SETTING_BLOCK;
}
// Sorts |topics| alphabetically by topic display name for display.
void SortTopicsForDisplay(
std::vector<privacy_sandbox::CanonicalTopic>& topics) {
std::sort(topics.begin(), topics.end(),
[](const privacy_sandbox::CanonicalTopic& a,
const privacy_sandbox::CanonicalTopic& b) {
return a.GetLocalizedRepresentation() <
b.GetLocalizedRepresentation();
});
}
// Returns whether |profile_type|, and the current browser session on CrOS,
// represent a regular (e.g. non guest, non system, non kiosk) profile.
bool IsRegularProfile(profile_metrics::BrowserProfileType profile_type) {
if (profile_type != profile_metrics::BrowserProfileType::kRegular) {
return false;
}
#if BUILDFLAG(IS_CHROMEOS)
// Any Device Local account, which is a CrOS concept powering things like
// Kiosks and Managed Guest Sessions, is not considered regular.
return !profiles::IsPublicSession() && !profiles::IsKioskSession() &&
!profiles::IsChromeAppKioskSession();
#else
return true;
#endif
}
// Returns the text contents of the Topics Consent dialog.
std::string GetTopicsConfirmationText() {
std::vector<int> string_ids = {
IDS_PRIVACY_SANDBOX_M1_CONSENT_TITLE,
IDS_PRIVACY_SANDBOX_M1_CONSENT_DESCRIPTION_1,
IDS_PRIVACY_SANDBOX_M1_CONSENT_DESCRIPTION_2,
IDS_PRIVACY_SANDBOX_M1_CONSENT_DESCRIPTION_3,
IDS_PRIVACY_SANDBOX_M1_CONSENT_DESCRIPTION_4,
IDS_PRIVACY_SANDBOX_M1_CONSENT_LEARN_MORE_EXPAND_LABEL,
IDS_PRIVACY_SANDBOX_M1_CONSENT_LEARN_MORE_BULLET_1,
IDS_PRIVACY_SANDBOX_M1_CONSENT_LEARN_MORE_BULLET_2,
IDS_PRIVACY_SANDBOX_M1_CONSENT_LEARN_MORE_BULLET_3,
IDS_PRIVACY_SANDBOX_M1_CONSENT_LEARN_MORE_LINK};
return std::accumulate(
string_ids.begin(), string_ids.end(), std::string(),
[](const std::string& previous_result, int next_id) {
auto next_string = l10n_util::GetStringUTF8(next_id);
// Remove bold tags present in some strings.
base::ReplaceSubstringsAfterOffset(&next_string, 0, "<b>", "");
base::ReplaceSubstringsAfterOffset(&next_string, 0, "</b>", "");
return previous_result + (!previous_result.empty() ? " " : "") +
next_string;
}
);
}
// Returns the text contents of the Topics settings page.
std::string GetTopicsSettingsText(bool did_consent,
bool has_current_topics,
bool has_blocked_topics) {
// `did_consent` refers to the _updated_ state, and so the previous state,
// e.g. when the user clicked the toggle, will be the opposite.
auto topics_prev_enabled = !did_consent;
// A user should only have current topics while topics is enabled. Old topics
// will not appear when the user enables, as they will have been cleared when
// topics was previously disabled, or never generated at all.
DCHECK(topics_prev_enabled || !has_current_topics);
int blocked_topics_description =
has_blocked_topics
? IDS_SETTINGS_TOPICS_PAGE_BLOCKED_TOPICS_DESCRIPTION
: IDS_SETTINGS_TOPICS_PAGE_BLOCKED_TOPICS_DESCRIPTION_EMPTY;
std::vector<int> string_ids = {
IDS_SETTINGS_TOPICS_PAGE_TITLE,
IDS_SETTINGS_TOPICS_PAGE_TOGGLE_LABEL,
IDS_SETTINGS_TOPICS_PAGE_TOGGLE_SUB_LABEL,
IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_HEADING,
IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_CANONICAL,
IDS_SETTINGS_TOPICS_PAGE_LEARN_MORE_HEADING,
IDS_SETTINGS_TOPICS_PAGE_LEARN_MORE_BULLET_1,
IDS_SETTINGS_TOPICS_PAGE_LEARN_MORE_BULLET_2,
IDS_SETTINGS_TOPICS_PAGE_LEARN_MORE_BULLET_3,
IDS_SETTINGS_TOPICS_PAGE_BLOCKED_TOPICS_HEADING,
blocked_topics_description,
IDS_SETTINGS_TOPICS_PAGE_FOOTER_CANONICAL};
// Additional strings are displayed if there were no current topics, either
// because they were empty, or because Topics was disabled. These will have
// appeared after the current topics description.
if (!topics_prev_enabled) {
string_ids.insert(
string_ids.begin() + 5,
IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_DISABLED);
} else if (!has_current_topics) {
string_ids.insert(
string_ids.begin() + 5,
IDS_SETTINGS_TOPICS_PAGE_CURRENT_TOPICS_DESCRIPTION_EMPTY);
}
return std::accumulate(string_ids.begin(), string_ids.end(), std::string(),
[](const std::string& previous_result, int next_id) {
auto next_string = l10n_util::GetStringUTF8(next_id);
return previous_result +
(!previous_result.empty() ? " " : "") +
next_string;
});
}
// Returns whether this is a Google Chrome-branded build.
bool IsChromeBuild() {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
return true;
#else
return false;
#endif
}
} // namespace
PrivacySandboxService::PrivacySandboxService() = default;
PrivacySandboxService::PrivacySandboxService(
privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
content_settings::CookieSettings* cookie_settings,
PrefService* pref_service,
content::InterestGroupManager* interest_group_manager,
profile_metrics::BrowserProfileType profile_type,
content::BrowsingDataRemover* browsing_data_remover,
#if !BUILDFLAG(IS_ANDROID)
TrustSafetySentimentService* sentiment_service,
#endif
browsing_topics::BrowsingTopicsService* browsing_topics_service,
first_party_sets::FirstPartySetsPolicyService* first_party_sets_service)
: privacy_sandbox_settings_(privacy_sandbox_settings),
cookie_settings_(cookie_settings),
pref_service_(pref_service),
interest_group_manager_(interest_group_manager),
profile_type_(profile_type),
browsing_data_remover_(browsing_data_remover),
#if !BUILDFLAG(IS_ANDROID)
sentiment_service_(sentiment_service),
#endif
browsing_topics_service_(browsing_topics_service),
first_party_sets_policy_service_(first_party_sets_service) {
DCHECK(privacy_sandbox_settings_);
DCHECK(pref_service_);
DCHECK(cookie_settings_);
// Register observers for the Privacy Sandbox preferences.
user_prefs_registrar_.Init(pref_service_);
user_prefs_registrar_.Add(
prefs::kPrivacySandboxApisEnabledV2,
base::BindRepeating(&PrivacySandboxService::OnPrivacySandboxV2PrefChanged,
base::Unretained(this)));
user_prefs_registrar_.Add(
prefs::kPrivacySandboxM1TopicsEnabled,
base::BindRepeating(&PrivacySandboxService::OnTopicsPrefChanged,
base::Unretained(this)));
user_prefs_registrar_.Add(
prefs::kPrivacySandboxM1FledgeEnabled,
base::BindRepeating(&PrivacySandboxService::OnFledgePrefChanged,
base::Unretained(this)));
user_prefs_registrar_.Add(
prefs::kPrivacySandboxM1AdMeasurementEnabled,
base::BindRepeating(&PrivacySandboxService::OnAdMeasurementPrefChanged,
base::Unretained(this)));
// If the Sandbox is currently restricted, disable the V2 preference. The user
// must manually enable the sandbox if they stop being restricted.
if (IsPrivacySandboxRestricted()) {
pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, false);
}
// Check for FPS pref init at each startup.
// TODO(crbug.com/1351327): Remove this logic when most users have run init.
MaybeInitializeFirstPartySetsPref();
// Record preference state for UMA at each startup.
LogPrivacySandboxState();
}
PrivacySandboxService::~PrivacySandboxService() = default;
PrivacySandboxService::PromptType
PrivacySandboxService::GetRequiredPromptType() {
const auto third_party_cookies_blocked =
AreThirdPartyCookiesBlocked(cookie_settings_);
if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
return GetRequiredPromptTypeInternalM1(
pref_service_, profile_type_, privacy_sandbox_settings_,
third_party_cookies_blocked,
force_chrome_build_for_tests_ || IsChromeBuild());
}
return GetRequiredPromptTypeInternal(pref_service_, profile_type_,
privacy_sandbox_settings_,
third_party_cookies_blocked);
}
void PrivacySandboxService::PromptActionOccurred(
PrivacySandboxService::PromptAction action) {
InformSentimentService(action);
RecordPromptActionMetrics(action);
if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
PromptActionOccurredM1(action);
return;
}
if (PromptAction::kNoticeShown == action &&
PromptType::kNotice == GetRequiredPromptType()) {
// The Privacy Sandbox pref can be enabled when the notice has been
// shown. Note that a notice will not have been shown if the user
// disabled the old Privacy Sandbox pref.
pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, true);
pref_service_->SetBoolean(prefs::kPrivacySandboxNoticeDisplayed, true);
} else if (PromptAction::kConsentAccepted == action) {
pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, true);
pref_service_->SetBoolean(prefs::kPrivacySandboxConsentDecisionMade, true);
} else if (PromptAction::kConsentDeclined == action) {
pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, false);
pref_service_->SetBoolean(prefs::kPrivacySandboxConsentDecisionMade, true);
}
}
void PrivacySandboxService::PromptActionOccurredM1(
PrivacySandboxService::PromptAction action) {
if (PromptAction::kNoticeAcknowledge == action ||
PromptAction::kNoticeOpenSettings == action) {
if (privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
pref_service_->SetBoolean(prefs::kPrivacySandboxM1EEANoticeAcknowledged,
true);
} else {
DCHECK(privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get());
pref_service_->SetBoolean(prefs::kPrivacySandboxM1RowNoticeAcknowledged,
true);
pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
}
pref_service_->SetBoolean(prefs::kPrivacySandboxM1FledgeEnabled, true);
pref_service_->SetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled,
true);
} else if (PromptAction::kConsentAccepted == action) {
DCHECK(privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get());
pref_service_->SetBoolean(prefs::kPrivacySandboxM1ConsentDecisionMade,
true);
pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, true);
} else if (PromptAction::kConsentDeclined == action) {
DCHECK(privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get());
pref_service_->SetBoolean(prefs::kPrivacySandboxM1ConsentDecisionMade,
true);
pref_service_->SetBoolean(prefs::kPrivacySandboxM1TopicsEnabled, false);
}
}
// static
bool PrivacySandboxService::IsUrlSuitableForPrompt(const GURL& url) {
// The prompt should be shown on a limited list of pages:
// about:blank is valid.
if (url.IsAboutBlank()) {
return true;
}
// Chrome settings page is valid. The subpages aren't as most of them are not
// related to the prompt.
if (url == GURL(chrome::kChromeUISettingsURL)) {
return true;
}
// Chrome history is valid as the prompt mentions history.
if (url == GURL(chrome::kChromeUIHistoryURL)) {
return true;
}
// Only a Chrome controlled New Tab Page is valid. Third party NTP is still
// Chrome controlled, but is without Google branding.
if (url == GURL(chrome::kChromeUINewTabPageURL) ||
url == GURL(chrome::kChromeUINewTabPageThirdPartyURL)) {
return true;
}
return false;
}
void PrivacySandboxService::PromptOpenedForBrowser(Browser* browser) {
DCHECK(!browsers_with_open_prompts_.count(browser));
browsers_with_open_prompts_.insert(browser);
}
void PrivacySandboxService::PromptClosedForBrowser(Browser* browser) {
DCHECK(browsers_with_open_prompts_.count(browser));
browsers_with_open_prompts_.erase(browser);
}
bool PrivacySandboxService::IsPromptOpenForBrowser(Browser* browser) {
return browsers_with_open_prompts_.count(browser);
}
void PrivacySandboxService::SetPromptDisabledForTests(bool disabled) {
g_prompt_disabled_for_tests = disabled;
}
void PrivacySandboxService::ForceChromeBuildForTests(bool force_chrome_build) {
force_chrome_build_for_tests_ = force_chrome_build;
}
bool PrivacySandboxService::IsPrivacySandboxEnabled() {
return pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2);
}
bool PrivacySandboxService::IsPrivacySandboxManaged() {
return pref_service_->IsManagedPreference(
prefs::kPrivacySandboxApisEnabledV2);
}
bool PrivacySandboxService::IsPrivacySandboxRestricted() {
return privacy_sandbox_settings_->IsPrivacySandboxRestricted();
}
void PrivacySandboxService::SetPrivacySandboxEnabled(bool enabled) {
pref_service_->SetBoolean(prefs::kPrivacySandboxManuallyControlledV2, true);
privacy_sandbox_settings_->SetPrivacySandboxEnabled(enabled);
}
void PrivacySandboxService::OnPrivacySandboxV2PrefChanged() {
// If the user has disabled the Privacy Sandbox, any data stored should be
// cleared.
if (pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2)) {
return;
}
if (browsing_data_remover_) {
browsing_data_remover_->Remove(
base::Time::Min(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_PRIVACY_SANDBOX,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB);
}
if (browsing_topics_service_) {
browsing_topics_service_->ClearAllTopicsData();
}
}
bool PrivacySandboxService::IsFirstPartySetsDataAccessEnabled() const {
return pref_service_->GetBoolean(prefs::kPrivacySandboxFirstPartySetsEnabled);
}
bool PrivacySandboxService::IsFirstPartySetsDataAccessManaged() const {
return pref_service_->IsManagedPreference(
prefs::kPrivacySandboxFirstPartySetsEnabled);
}
void PrivacySandboxService::SetFirstPartySetsDataAccessEnabled(bool enabled) {
pref_service_->SetBoolean(prefs::kPrivacySandboxFirstPartySetsEnabled,
enabled);
}
void PrivacySandboxService::GetFledgeJoiningEtldPlusOneForDisplay(
base::OnceCallback<void(std::vector<std::string>)> callback) {
if (!interest_group_manager_) {
std::move(callback).Run({});
return;
}
interest_group_manager_->GetAllInterestGroupDataKeys(base::BindOnce(
&PrivacySandboxService::ConvertInterestGroupDataKeysForDisplay,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
std::vector<std::string>
PrivacySandboxService::GetBlockedFledgeJoiningTopFramesForDisplay() const {
const base::Value::Dict& pref_value =
pref_service_->GetDict(prefs::kPrivacySandboxFledgeJoinBlocked);
std::vector<std::string> blocked_top_frames;
for (auto entry : pref_value) {
blocked_top_frames.emplace_back(entry.first);
}
// Apply a lexographic ordering to match other settings permission surfaces.
std::sort(blocked_top_frames.begin(), blocked_top_frames.end());
return blocked_top_frames;
}
void PrivacySandboxService::SetFledgeJoiningAllowed(
const std::string& top_frame_etld_plus1,
bool allowed) const {
privacy_sandbox_settings_->SetFledgeJoiningAllowed(top_frame_etld_plus1,
allowed);
if (!allowed && browsing_data_remover_) {
std::unique_ptr<content::BrowsingDataFilterBuilder> filter =
content::BrowsingDataFilterBuilder::Create(
content::BrowsingDataFilterBuilder::Mode::kDelete);
filter->AddRegisterableDomain(top_frame_etld_plus1);
browsing_data_remover_->RemoveWithFilter(
base::Time::Min(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_INTEREST_GROUPS,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB,
std::move(filter));
}
}
void PrivacySandboxService::RecordFirstPartySetsStateHistogram(
PrivacySandboxService::FirstPartySetsState state) {
base::UmaHistogramEnumeration("Settings.FirstPartySets.State", state);
}
void PrivacySandboxService::RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled state) {
base::UmaHistogramEnumeration("Settings.PrivacySandbox.Enabled", state);
}
void PrivacySandboxService::RecordPrivacySandbox3StartupMetrics() {
const std::string privacy_sandbox_startup_histogram =
"Settings.PrivacySandbox.StartupState";
const bool sandbox_v2_enabled =
pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2);
// Handle PS V1 prefs disabled.
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxDisabled)) {
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled ? PSStartupStates::kPromptOffV1OffEnabled
: PSStartupStates::kPromptOffV1OffDisabled);
return;
}
// Handle 3PC disabled.
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxNoConfirmationThirdPartyCookiesBlocked)) {
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled ? PSStartupStates::kPromptOff3PCOffEnabled
: PSStartupStates::kPromptOff3PCOffDisabled);
return;
}
// Handle managed.
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxManaged)) {
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled ? PSStartupStates::kPromptOffManagedEnabled
: PSStartupStates::kPromptOffManagedDisabled);
return;
}
// Handle restricted.
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxRestricted)) {
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
PSStartupStates::kPromptOffRestricted);
return;
}
// Handle manually controlled
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxNoConfirmationManuallyControlled)) {
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled
? PSStartupStates::kPromptOffManuallyControlledEnabled
: PSStartupStates::kPromptOffManuallyControlledDisabled);
return;
}
if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()) {
if (!pref_service_->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade)) {
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
PSStartupStates::kPromptWaiting);
return;
}
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
sandbox_v2_enabled
? PSStartupStates::kConsentShownEnabled
: PSStartupStates::kConsentShownDisabled);
} else if (privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get()) {
if (!pref_service_->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)) {
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
PSStartupStates::kPromptWaiting);
return;
}
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
sandbox_v2_enabled
? PSStartupStates::kNoticeShownEnabled
: PSStartupStates::kNoticeShownDisabled);
} else { // No prompt currently required.
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled ? PSStartupStates::kNoPromptRequiredEnabled
: PSStartupStates::kNoPromptRequiredDisabled);
}
}
void PrivacySandboxService::RecordPrivacySandbox4StartupMetrics() {
// Record the status of the APIs.
const bool topics_enabled =
pref_service_->GetBoolean(prefs::kPrivacySandboxM1TopicsEnabled);
base::UmaHistogramBoolean("Settings.PrivacySandbox.Topics.Enabled",
topics_enabled);
base::UmaHistogramBoolean(
"Settings.PrivacySandbox.Fledge.Enabled",
pref_service_->GetBoolean(prefs::kPrivacySandboxM1FledgeEnabled));
base::UmaHistogramBoolean(
"Settings.PrivacySandbox.AdMeasurement.Enabled",
pref_service_->GetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled));
const std::string privacy_sandbox_prompt_startup_histogram =
"Settings.PrivacySandbox.PromptStartupState";
// Prompt suppressed cases.
PromptSuppressedReason prompt_suppressed_reason =
static_cast<PromptSuppressedReason>(
pref_service_->GetInteger(prefs::kPrivacySandboxM1PromptSuppressed));
switch (prompt_suppressed_reason) {
// Prompt never suppressed.
case PromptSuppressedReason::kNone: {
break;
}
case PromptSuppressedReason::kRestricted: {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PromptStartupState::kPromptNotShownDueToPrivacySandboxRestricted);
return;
}
case PromptSuppressedReason::kThirdPartyCookiesBlocked: {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PromptStartupState::kPromptNotShownDueTo3PCBlocked);
return;
}
case PromptSuppressedReason::kTrialsConsentDeclined: {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PromptStartupState::kPromptNotShownDueToTrialConsentDeclined);
return;
}
case PromptSuppressedReason::kTrialsDisabledAfterNotice: {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PromptStartupState::
kPromptNotShownDueToTrialsDisabledAfterNoticeShown);
return;
}
case PromptSuppressedReason::kPolicy: {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PromptStartupState::kPromptNotShownDueToManagedState);
return;
}
}
// Prompt was not suppressed at this point.
// EEA
if (privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
// Consent decision not made
if (!pref_service_->GetBoolean(
prefs::kPrivacySandboxM1ConsentDecisionMade)) {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PromptStartupState::kEEAConsentPromptWaiting);
return;
}
// Consent decision made at this point.
// Notice Acknowledged
const bool notice_acknowledged = pref_service_->GetBoolean(
prefs::kPrivacySandboxM1EEANoticeAcknowledged);
if (notice_acknowledged) {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
topics_enabled
? PromptStartupState::kEEAFlowCompletedWithTopicsAccepted
: PromptStartupState::kEEAFlowCompletedWithTopicsDeclined);
} else {
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
PrivacySandboxService::PromptStartupState::kEEANoticePromptWaiting);
}
return;
}
// ROW
if (privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get()) {
const bool row_notice_acknowledged = pref_service_->GetBoolean(
prefs::kPrivacySandboxM1RowNoticeAcknowledged);
base::UmaHistogramEnumeration(
privacy_sandbox_prompt_startup_histogram,
row_notice_acknowledged ? PromptStartupState::kROWNoticeFlowCompleted
: PromptStartupState::kROWNoticePromptWaiting);
return;
}
}
void PrivacySandboxService::LogPrivacySandboxState() {
// Do not record metrics for non-regular profiles.
if (!IsRegularProfile(profile_type_)) {
return;
}
auto fps_status = FirstPartySetsState::kFpsNotRelevant;
if (cookie_settings_->ShouldBlockThirdPartyCookies() &&
cookie_settings_->GetDefaultCookieSetting(/*provider_id=*/nullptr) !=
CONTENT_SETTING_BLOCK) {
fps_status =
pref_service_->GetBoolean(prefs::kPrivacySandboxFirstPartySetsEnabled)
? FirstPartySetsState::kFpsEnabled
: FirstPartySetsState::kFpsDisabled;
}
RecordFirstPartySetsStateHistogram(fps_status);
// Start by recording any metrics for Privacy Sandbox.
if (base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4)) {
RecordPrivacySandbox4StartupMetrics();
return;
} else {
RecordPrivacySandbox3StartupMetrics();
}
// Check policy status first.
std::string default_cookie_setting_provider;
auto default_cookie_setting = cookie_settings_->GetDefaultCookieSetting(
&default_cookie_setting_provider);
auto default_cookie_setting_source =
HostContentSettingsMap::GetSettingSourceFromProviderName(
default_cookie_setting_provider);
if (default_cookie_setting_source ==
content_settings::SettingSource::SETTING_SOURCE_POLICY &&
default_cookie_setting == ContentSetting::CONTENT_SETTING_BLOCK) {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSDisabledPolicyBlockAll);
return;
}
auto* cookie_controls_mode_pref =
pref_service_->FindPreference(prefs::kCookieControlsMode);
auto cookie_controls_mode_value =
static_cast<content_settings::CookieControlsMode>(
cookie_controls_mode_pref->GetValue()->GetInt());
if (cookie_controls_mode_pref->IsManaged() &&
cookie_controls_mode_value ==
content_settings::CookieControlsMode::kBlockThirdParty) {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSDisabledPolicyBlock3P);
return;
}
if (privacy_sandbox_settings_->IsPrivacySandboxEnabled()) {
if (default_cookie_setting == ContentSetting::CONTENT_SETTING_BLOCK) {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSEnabledBlockAll);
} else if (cookie_controls_mode_value ==
content_settings::CookieControlsMode::kBlockThirdParty) {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSEnabledBlock3P);
} else {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSEnabledAllowAll);
}
} else {
if (default_cookie_setting == ContentSetting::CONTENT_SETTING_BLOCK) {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSDisabledBlockAll);
} else if (cookie_controls_mode_value ==
content_settings::CookieControlsMode::kBlockThirdParty) {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSDisabledBlock3P);
} else {
RecordPrivacySandboxHistogram(
PrivacySandboxService::SettingsPrivacySandboxEnabled::
kPSDisabledAllowAll);
}
}
}
void PrivacySandboxService::ConvertInterestGroupDataKeysForDisplay(
base::OnceCallback<void(std::vector<std::string>)> callback,
std::vector<content::InterestGroupManager::InterestGroupDataKey>
data_keys) {
std::set<std::string> display_entries;
for (const auto& data_key : data_keys) {
// When displaying interest group information in settings, the joining
// origin is the relevant origin.
const auto& origin = data_key.joining_origin;
// Prefer to display the associated eTLD+1, if there is one.
auto etld_plus_one = net::registry_controlled_domains::GetDomainAndRegistry(
origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (etld_plus_one.length() > 0) {
display_entries.emplace(std::move(etld_plus_one));
continue;
}
// The next best option is a host, which may be an IP address or an eTLD
// itself (e.g. github.io).
if (origin.host().length() > 0) {
display_entries.emplace(origin.host());
continue;
}
// Other types of top-frame origins (file, opaque) do not support FLEDGE.
NOTREACHED();
}
// Entries should be displayed alphabetically, as |display_entries| is a
// std::set<std::string>, entries are already ordered correctly.
std::move(callback).Run(
std::vector<std::string>{display_entries.begin(), display_entries.end()});
}
std::vector<privacy_sandbox::CanonicalTopic>
PrivacySandboxService::GetCurrentTopTopics() const {
if (privacy_sandbox::kPrivacySandboxSettings3ShowSampleDataForTesting.Get() ||
privacy_sandbox::kPrivacySandboxSettings4ShowSampleDataForTesting.Get()) {
return {fake_current_topics_.begin(), fake_current_topics_.end()};
}
if (!browsing_topics_service_) {
return {};
}
auto topics = browsing_topics_service_->GetTopTopicsForDisplay();
// Topics returned by the backend may include duplicates. Sort into display
// order before removing them.
SortTopicsForDisplay(topics);
topics.erase(std::unique(topics.begin(), topics.end()), topics.end());
return topics;
}
std::vector<privacy_sandbox::CanonicalTopic>
PrivacySandboxService::GetBlockedTopics() const {
if (privacy_sandbox::kPrivacySandboxSettings3ShowSampleDataForTesting.Get() ||
privacy_sandbox::kPrivacySandboxSettings4ShowSampleDataForTesting.Get()) {
return {fake_blocked_topics_.begin(), fake_blocked_topics_.end()};
}
const base::Value::List& pref_value =
pref_service_->GetList(prefs::kPrivacySandboxBlockedTopics);
std::vector<privacy_sandbox::CanonicalTopic> blocked_topics;
for (const auto& entry : pref_value) {
auto blocked_topic = privacy_sandbox::CanonicalTopic::FromValue(
*entry.GetDict().Find(kBlockedTopicsTopicKey));
if (blocked_topic) {
blocked_topics.emplace_back(*blocked_topic);
}
}
SortTopicsForDisplay(blocked_topics);
return blocked_topics;
}
void PrivacySandboxService::SetTopicAllowed(
privacy_sandbox::CanonicalTopic topic,
bool allowed) {
if (privacy_sandbox::kPrivacySandboxSettings3ShowSampleDataForTesting.Get() ||
privacy_sandbox::kPrivacySandboxSettings4ShowSampleDataForTesting.Get()) {
if (allowed) {
fake_current_topics_.insert(topic);
fake_blocked_topics_.erase(topic);
} else {
fake_current_topics_.erase(topic);
fake_blocked_topics_.insert(topic);
}
return;
}
if (!allowed && browsing_topics_service_) {
browsing_topics_service_->ClearTopic(topic);
}
privacy_sandbox_settings_->SetTopicAllowed(topic, allowed);
}
base::flat_map<net::SchemefulSite, net::SchemefulSite>
PrivacySandboxService::GetSampleFirstPartySets() const {
if (privacy_sandbox::kPrivacySandboxFirstPartySetsUISampleSets.Get() &&
IsFirstPartySetsDataAccessEnabled()) {
return {{net::SchemefulSite(GURL("https://youtube.com")),
net::SchemefulSite(GURL("https://google.com"))},
{net::SchemefulSite(GURL("https://google.com")),
net::SchemefulSite(GURL("https://google.com"))},
{net::SchemefulSite(GURL("https://google.com.au")),
net::SchemefulSite(GURL("https://google.com"))},
{net::SchemefulSite(GURL("https://google.de")),
net::SchemefulSite(GURL("https://google.com"))},
{net::SchemefulSite(GURL("https://chromium.org")),
net::SchemefulSite(GURL("https://chromium.org"))},
{net::SchemefulSite(GURL("https://googlesource.com")),
net::SchemefulSite(GURL("https://chromium.org"))}};
}
return {};
}
absl::optional<net::SchemefulSite> PrivacySandboxService::GetFirstPartySetOwner(
const GURL& site_url) const {
// If FPS is not affecting cookie access, then there are effectively no
// first party sets.
if (!(cookie_settings_->ShouldBlockThirdPartyCookies() &&
cookie_settings_->GetDefaultCookieSetting(/*provider_id=*/nullptr) !=
CONTENT_SETTING_BLOCK &&
base::FeatureList::IsEnabled(
privacy_sandbox::kPrivacySandboxFirstPartySetsUI))) {
return absl::nullopt;
}
// Return the owner according to the sample sets if they're provided.
if (privacy_sandbox::kPrivacySandboxFirstPartySetsUISampleSets.Get()) {
const base::flat_map<net::SchemefulSite, net::SchemefulSite> sets =
GetSampleFirstPartySets();
net::SchemefulSite schemeful_site(site_url);
base::flat_map<net::SchemefulSite, net::SchemefulSite>::const_iterator
site_entry = sets.find(schemeful_site);
if (site_entry == sets.end()) {
return absl::nullopt;
}
return site_entry->second;
}
absl::optional<net::FirstPartySetEntry> site_entry =
first_party_sets_policy_service_->FindEntry(net::SchemefulSite(site_url));
if (!site_entry.has_value()) {
return absl::nullopt;
}
return site_entry->primary();
}
absl::optional<std::u16string>
PrivacySandboxService::GetFirstPartySetOwnerForDisplay(
const GURL& site_url) const {
absl::optional<net::SchemefulSite> site_owner =
GetFirstPartySetOwner(site_url);
if (!site_owner.has_value()) {
return absl::nullopt;
}
// TODO(crbug.com/1332513): Apply formatting that correctly displays unicode
// domains.
return base::UTF8ToUTF16(site_owner->GetURL().host());
}
bool PrivacySandboxService::IsPartOfManagedFirstPartySet(
const net::SchemefulSite& site) const {
if (privacy_sandbox::kPrivacySandboxFirstPartySetsUISampleSets.Get()) {
return IsFirstPartySetsDataAccessManaged() ||
GetSampleFirstPartySets()[site] ==
net::SchemefulSite(GURL("https://chromium.org"));
}
return first_party_sets_policy_service_->IsSiteInManagedSet(site);
}
void PrivacySandboxService::TopicsConfirmationDecisionMade(
bool confirmed) const {
RecordUpdatedTopicsConsent(
privacy_sandbox::TopicsConsentUpdateSource::kConfirmation, confirmed);
}
void PrivacySandboxService::TopicsToggleChanged(bool new_value) const {
RecordUpdatedTopicsConsent(
privacy_sandbox::TopicsConsentUpdateSource::kSettings, new_value);
}
void PrivacySandboxService::TopicsConsentRequired() const {
// TODO(crbug.com/1332513): Implement + Test.
NOTIMPLEMENTED();
}
bool PrivacySandboxService::TopicsHasActiveConsent() const {
return pref_service_->GetBoolean(prefs::kPrivacySandboxTopicsConsentGiven);
}
privacy_sandbox::TopicsConsentUpdateSource
PrivacySandboxService::TopicsConsentLastUpdateSource() const {
return static_cast<privacy_sandbox::TopicsConsentUpdateSource>(
pref_service_->GetInteger(
prefs::kPrivacySandboxTopicsConsentLastUpdateReason));
}
base::Time PrivacySandboxService::TopicsConsentLastUpdateTime() const {
return pref_service_->GetTime(
prefs::kPrivacySandboxTopicsConsentLastUpdateTime);
}
std::string PrivacySandboxService::TopicsConsentLastUpdateText() const {
return pref_service_->GetString(
prefs::kPrivacySandboxTopicsConsentTextAtLastUpdate);
}
/*static*/ PrivacySandboxService::PromptType
PrivacySandboxService::GetRequiredPromptTypeInternal(
PrefService* pref_service,
profile_metrics::BrowserProfileType profile_type,
privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
bool third_party_cookies_blocked) {
DCHECK(
!base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4));
// If the prompt is disabled for testing, never show it.
if (g_prompt_disabled_for_tests) {
return PromptType::kNone;
}
// If the profile isn't a regular profile, no prompt should ever be shown.
if (!IsRegularProfile(profile_type)) {
return PromptType::kNone;
}
// Forced testing feature parameters override everything.
if (base::FeatureList::IsEnabled(
privacy_sandbox::kDisablePrivacySandboxPrompts)) {
return PromptType::kNone;
}
if (privacy_sandbox::kPrivacySandboxSettings3DisablePromptForTesting.Get()) {
return PromptType::kNone;
}
if (privacy_sandbox::kPrivacySandboxSettings3ForceShowConsentForTesting
.Get()) {
return PromptType::kConsent;
}
if (privacy_sandbox::kPrivacySandboxSettings3ForceShowNoticeForTesting
.Get()) {
return PromptType::kNotice;
}
// If neither consent or notice is required, no prompt is required.
if (!privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() &&
!privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get()) {
return PromptType::kNone;
}
// Only one of the consent or notice should be required by Finch parameters.
DCHECK(!privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() ||
!privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get());
// Start by checking for any previous decision about the prompt, such as
// it already having been shown, or not having been shown for some reason.
// These checks for previous decisions occur in advance of their corresponding
// decisions later in this function, so that changes to profile state to not
// appear to impact previous decisions.
// If a user wasn't shown a confirmation because they previously turned the
// Privacy Sandbox off, we do not attempt to re-show one.
if (pref_service->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxDisabled)) {
return PromptType::kNone;
}
// If a consent decision has already been made, no prompt is required.
if (pref_service->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade)) {
return PromptType::kNone;
}
// If only a notice is required, and has been shown, no prompt is required.
if (!privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() &&
pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)) {
return PromptType::kNone;
}
// If a user wasn't shown a confirmation because the sandbox was previously
// restricted, do not attempt to show them one. The user will be able to
// enable the Sandbox on the settings page.
if (pref_service->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxRestricted)) {
return PromptType::kNone;
}
// If a user wasn't shown a prompt previously because the Privacy Sandbox
// was managed, do not show them one.
if (pref_service->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxManaged)) {
return PromptType::kNone;
}
// If a user wasn't shown a confirmation because they block third party
// cookies, we do not attempt to re-show one.
if (pref_service->GetBoolean(
prefs::kPrivacySandboxNoConfirmationThirdPartyCookiesBlocked)) {
return PromptType::kNone;
}
// If the user wasn't shown a confirmation because they are already manually
// controlling the sandbox, do not attempt to show one.
if (pref_service->GetBoolean(
prefs::kPrivacySandboxNoConfirmationManuallyControlled)) {
return PromptType::kNone;
}
// If the Privacy Sandbox is restricted, no prompt is shown.
if (privacy_sandbox_settings->IsPrivacySandboxRestricted()) {
pref_service->SetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxRestricted, true);
return PromptType::kNone;
}
// If the Privacy Sandbox is managed, no prompt is shown.
if (pref_service->FindPreference(prefs::kPrivacySandboxApisEnabledV2)
->IsManaged()) {
pref_service->SetBoolean(prefs::kPrivacySandboxNoConfirmationSandboxManaged,
true);
return PromptType::kNone;
}
// If the user blocks third party cookies, then no prompt is shown.
if (third_party_cookies_blocked) {
pref_service->SetBoolean(
prefs::kPrivacySandboxNoConfirmationThirdPartyCookiesBlocked, true);
return PromptType::kNone;
}
// If the Privacy Sandbox has been manually controlled by the user, no prompt
// is shown.
if (pref_service->GetBoolean(prefs::kPrivacySandboxManuallyControlledV2)) {
pref_service->SetBoolean(
prefs::kPrivacySandboxNoConfirmationManuallyControlled, true);
return PromptType::kNone;
}
// If a user now requires consent, but has previously seen a notice, whether
// a consent is shown depends on their current Privacy Sandbox setting.
if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() &&
pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)) {
DCHECK(
!pref_service->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade));
// As the user has not yet consented, the V2 pref must be disabled.
// However, this may not be the first time that this function is being
// called. The API for this service guarantees, and clients depend, on
// successive calls to this function returning the same value. Browser
// restarts & updates via PromptActionOccurred() notwithstanding. To achieve
// this, we need to distinguish between the case where the user themselves
// previously disabled the APIs, and when this logic disabled them
// previously due to having insufficient confirmation.
if (pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabledV2)) {
pref_service->SetBoolean(
prefs::kPrivacySandboxDisabledInsufficientConfirmation, true);
pref_service->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, false);
}
if (pref_service->GetBoolean(
prefs::kPrivacySandboxDisabledInsufficientConfirmation)) {
return PromptType::kConsent;
} else {
DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));
pref_service->SetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxDisabled, true);
return PromptType::kNone;
}
}
// At this point, no previous decision should have been made.
DCHECK(!pref_service->GetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxDisabled));
DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed));
DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade));
// If the user had previously disabled the Privacy Sandbox, no confirmation
// will be shown.
if (!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabled)) {
pref_service->SetBoolean(
prefs::kPrivacySandboxNoConfirmationSandboxDisabled, true);
return PromptType::kNone;
}
// Check if the users requires a consent. This information is provided by
// feature parameter to allow Finch based geo-targeting.
if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()) {
return PromptType::kConsent;
}
// Finally a notice is required.
DCHECK(privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get());
return PromptType::kNotice;
}
/*static*/ PrivacySandboxService::PromptType
PrivacySandboxService::GetRequiredPromptTypeInternalM1(
PrefService* pref_service,
profile_metrics::BrowserProfileType profile_type,
privacy_sandbox::PrivacySandboxSettings* privacy_sandbox_settings,
bool third_party_cookies_blocked,
bool is_chrome_build) {
DCHECK(
base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings4));
// If the prompt is disabled for testing, never show it.
if (g_prompt_disabled_for_tests) {
return PromptType::kNone;
}
// If the profile isn't a regular profile, no prompt should ever be shown.
if (!IsRegularProfile(profile_type)) {
return PromptType::kNone;
}
// Forced testing feature parameters override everything.
if (base::FeatureList::IsEnabled(
privacy_sandbox::kDisablePrivacySandboxPrompts)) {
return PromptType::kNone;
}
if (privacy_sandbox::kPrivacySandboxSettings4ForceShowConsentForTesting
.Get()) {
return PromptType::kM1Consent;
}
if (privacy_sandbox::kPrivacySandboxSettings4ForceShowNoticeRowForTesting
.Get()) {
return PromptType::kM1NoticeROW;
}
if (privacy_sandbox::kPrivacySandboxSettings4ForceShowNoticeEeaForTesting
.Get()) {
return PromptType::kM1NoticeEEA;
}
// If this a non-Chrome build, do not show a prompt.
if (!is_chrome_build) {
return PromptType::kNone;
}
// If neither a notice nor a consent is required, do not show a prompt.
if (!privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get() &&
!privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
return PromptType::kNone;
}
// Only one of the consent or notice should be required by Finch parameters.
DCHECK(!privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get() ||
!privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get());
// If a prompt was suppressed once, for any reason, it will forever remain
// suppressed and a prompt will not be shown.
if (static_cast<PromptSuppressedReason>(
pref_service->GetInteger(prefs::kPrivacySandboxM1PromptSuppressed)) !=
PromptSuppressedReason::kNone) {
return PromptType::kNone;
}
if (pref_service->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade) ||
pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)) {
// If during the trials a previous consent decision was made, or the notice
// was already acknowledged, and the privacy sandbox is disabled, set the
// PromptSuppressedReason as appropriate and do not show a prompt.
if (!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabledV2)) {
int suppresed_reason =
pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)
? static_cast<int>(
PromptSuppressedReason::kTrialsDisabledAfterNotice)
: static_cast<int>(
PromptSuppressedReason::kTrialsConsentDeclined);
pref_service->SetInteger(prefs::kPrivacySandboxM1PromptSuppressed,
suppresed_reason);
return PromptType::kNone;
}
}
// If third party cookies are blocked, set the suppression reason as such, and
// do not show a prompt.
if (third_party_cookies_blocked) {
pref_service->SetInteger(
prefs::kPrivacySandboxM1PromptSuppressed,
static_cast<int>(PromptSuppressedReason::kThirdPartyCookiesBlocked));
return PromptType::kNone;
}
// If the Privacy Sandbox is restricted, set the suppression reason as such,
// and do not show a prompt.
if (privacy_sandbox_settings->IsPrivacySandboxRestricted()) {
pref_service->SetInteger(
prefs::kPrivacySandboxM1PromptSuppressed,
static_cast<int>(PromptSuppressedReason::kRestricted));
return PromptType::kNone;
}
if (privacy_sandbox::kPrivacySandboxSettings4ConsentRequired.Get()) {
if (pref_service->GetBoolean(prefs::kPrivacySandboxM1ConsentDecisionMade)) {
// Since a consent decision has been made, if the eea notice has already
// been acknowledged, do not show a prompt; else, show the eea notice.
if (pref_service->GetBoolean(
prefs::kPrivacySandboxM1EEANoticeAcknowledged)) {
return PromptType::kNone;
} else {
return PromptType::kM1NoticeEEA;
}
} else {
// A consent decision has not yet been made, so show the consent prompt.
return PromptType::kM1Consent;
}
}
DCHECK(privacy_sandbox::kPrivacySandboxSettings4NoticeRequired.Get());
// If the notice has already been acknowledged, do not show a prompt.
// Else, show the row notice prompt.
if (pref_service->GetBoolean(prefs::kPrivacySandboxM1RowNoticeAcknowledged)) {
return PromptType::kNone;
} else {
return PromptType::kM1NoticeROW;
}
}
void PrivacySandboxService::MaybeInitializeFirstPartySetsPref() {
// If initialization has already run, it is not required.
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxFirstPartySetsDataAccessAllowedInitialized)) {
return;
}
// If the FPS UI is not available, no initialization is required.
if (!base::FeatureList::IsEnabled(
privacy_sandbox::kPrivacySandboxFirstPartySetsUI)) {
return;
}
// If the user blocks 3P cookies, disable the FPS data access preference.
// As this logic relies on checking synced preference state, it is possible
// that synced state is available when this decision is made. To err on the
// side of privacy, this init logic is run per-device (the pref recording that
// init has been run is not synced). If any of the user's devices local state
// would disable the pref, it is disabled across all devices.
if (AreThirdPartyCookiesBlocked(cookie_settings_)) {
pref_service_->SetBoolean(prefs::kPrivacySandboxFirstPartySetsEnabled,
false);
}
pref_service_->SetBoolean(
prefs::kPrivacySandboxFirstPartySetsDataAccessAllowedInitialized, true);
}
void PrivacySandboxService::RecordUpdatedTopicsConsent(
privacy_sandbox::TopicsConsentUpdateSource source,
bool did_consent) const {
std::string consent_text;
switch (source) {
case (privacy_sandbox::TopicsConsentUpdateSource::kDefaultValue): {
NOTREACHED();
break;
}
case (privacy_sandbox::TopicsConsentUpdateSource::kConfirmation): {
consent_text = GetTopicsConfirmationText();
break;
}
case (privacy_sandbox::TopicsConsentUpdateSource::kSettings): {
int current_topics_count = GetCurrentTopTopics().size();
int blocked_topics_count = GetBlockedTopics().size();
consent_text = GetTopicsSettingsText(
did_consent, current_topics_count > 0, blocked_topics_count > 0);
break;
}
default:
NOTREACHED();
}
pref_service_->SetBoolean(prefs::kPrivacySandboxTopicsConsentGiven,
did_consent);
pref_service_->SetTime(prefs::kPrivacySandboxTopicsConsentLastUpdateTime,
base::Time::Now());
pref_service_->SetInteger(prefs::kPrivacySandboxTopicsConsentLastUpdateReason,
static_cast<int>(source));
pref_service_->SetString(prefs::kPrivacySandboxTopicsConsentTextAtLastUpdate,
consent_text);
}
void PrivacySandboxService::InformSentimentService(
PrivacySandboxService::PromptAction action) {
#if !BUILDFLAG(IS_ANDROID)
if (!sentiment_service_) {
return;
}
TrustSafetySentimentService::FeatureArea area;
switch (action) {
case PromptAction::kNoticeOpenSettings:
area = TrustSafetySentimentService::FeatureArea::
kPrivacySandbox3NoticeSettings;
break;
case PromptAction::kNoticeAcknowledge:
area = TrustSafetySentimentService::FeatureArea::kPrivacySandbox3NoticeOk;
break;
case PromptAction::kNoticeDismiss:
area = TrustSafetySentimentService::FeatureArea::
kPrivacySandbox3NoticeDismiss;
break;
case PromptAction::kNoticeLearnMore:
area = TrustSafetySentimentService::FeatureArea::
kPrivacySandbox3NoticeLearnMore;
break;
case PromptAction::kConsentAccepted:
area = TrustSafetySentimentService::FeatureArea::
kPrivacySandbox3ConsentAccept;
break;
case PromptAction::kConsentDeclined:
area = TrustSafetySentimentService::FeatureArea::
kPrivacySandbox3ConsentDecline;
break;
default:
return;
}
sentiment_service_->InteractedWithPrivacySandbox3(area);
#endif
}
void PrivacySandboxService::RecordPromptActionMetrics(
PrivacySandboxService::PromptAction action) {
switch (action) {
case (PromptAction::kNoticeShown): {
base::RecordAction(
base::UserMetricsAction("Settings.PrivacySandbox.Notice.Shown"));
break;
}
case (PromptAction::kNoticeOpenSettings): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Notice.OpenedSettings"));
break;
}
case (PromptAction::kNoticeAcknowledge): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Notice.Acknowledged"));
break;
}
case (PromptAction::kNoticeDismiss): {
base::RecordAction(
base::UserMetricsAction("Settings.PrivacySandbox.Notice.Dismissed"));
break;
}
case (PromptAction::kNoticeClosedNoInteraction): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Notice.ClosedNoInteraction"));
break;
}
case (PromptAction::kConsentShown): {
base::RecordAction(
base::UserMetricsAction("Settings.PrivacySandbox.Consent.Shown"));
break;
}
case (PromptAction::kConsentAccepted): {
base::RecordAction(
base::UserMetricsAction("Settings.PrivacySandbox.Consent.Accepted"));
break;
}
case (PromptAction::kConsentDeclined): {
base::RecordAction(
base::UserMetricsAction("Settings.PrivacySandbox.Consent.Declined"));
break;
}
case (PromptAction::kConsentMoreInfoOpened): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Consent.LearnMoreExpanded"));
break;
}
case (PromptAction::kConsentMoreInfoClosed): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Consent.LearnMoreClosed"));
break;
}
case (PromptAction::kConsentClosedNoDecision): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Consent.ClosedNoInteraction"));
break;
}
case (PromptAction::kNoticeLearnMore): {
base::RecordAction(
base::UserMetricsAction("Settings.PrivacySandbox.Notice.LearnMore"));
break;
}
case (PromptAction::kNoticeMoreInfoOpened): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Notice.LearnMoreExpanded"));
break;
}
case (PromptAction::kNoticeMoreInfoClosed): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Notice.LearnMoreClosed"));
break;
}
case (PromptAction::kConsentMoreButtonClicked): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Consent.MoreButtonClicked"));
break;
}
case (PromptAction::kNoticeMoreButtonClicked): {
base::RecordAction(base::UserMetricsAction(
"Settings.PrivacySandbox.Notice.MoreButtonClicked"));
break;
}
}
}
void PrivacySandboxService::OnTopicsPrefChanged() {
// If the user has disabled the preference, any related data stored should be
// cleared.
if (pref_service_->GetBoolean(prefs::kPrivacySandboxM1TopicsEnabled)) {
return;
}
if (browsing_topics_service_) {
browsing_topics_service_->ClearAllTopicsData();
}
}
void PrivacySandboxService::OnFledgePrefChanged() {
// If the user has disabled the preference, any related data stored should be
// cleared.
if (pref_service_->GetBoolean(prefs::kPrivacySandboxM1FledgeEnabled)) {
return;
}
if (browsing_data_remover_) {
browsing_data_remover_->Remove(
base::Time::Min(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_INTEREST_GROUPS |
content::BrowsingDataRemover::DATA_TYPE_SHARED_STORAGE |
content::BrowsingDataRemover::DATA_TYPE_INTEREST_GROUPS_INTERNAL,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB);
}
}
void PrivacySandboxService::OnAdMeasurementPrefChanged() {
// If the user has disabled the preference, any related data stored should be
// cleared.
if (pref_service_->GetBoolean(prefs::kPrivacySandboxM1AdMeasurementEnabled)) {
return;
}
if (browsing_data_remover_) {
browsing_data_remover_->Remove(
base::Time::Min(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_ATTRIBUTION_REPORTING |
content::BrowsingDataRemover::DATA_TYPE_AGGREGATION_SERVICE |
content::BrowsingDataRemover::
DATA_TYPE_PRIVATE_AGGREGATION_INTERNAL,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB);
}
}