Skip to content

Commit

Permalink
Privacy Sandbox Settings: Adjust confirmation dialog logic
Browse files Browse the repository at this point in the history
CL introduces the option for no notice or consent to be shown to the
user, but to still receive the Privacy Sandbox release 3 updated
settings.

The default value of the Privacy Sandbox V2 preference is set based on
a combination of finch parameters and local state, such that users
which will eventually require consent have the user facing control off,
while users who will eventually require notice have the user facing
control on.

Privacy Sandbox APIs cannot run if the user has not confirmed them,
either through the notice / consent, or by manually controlling them on
the updated settings page.

If a user manually controls the Privacy Sandbox toggle, any future
notice or consent will be suppressed.

Bug: 1309805
Change-Id: Ib17882d6fdc1bd6cb76e28308d4814cecf0bb86c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3546056
Reviewed-by: Christian Dullweber <dullweber@chromium.org>
Reviewed-by: Matt Menke <mmenke@chromium.org>
Reviewed-by: Yao Xiao <yaoxia@chromium.org>
Commit-Queue: Theodore Olsauskas-Warren <sauski@google.com>
Cr-Commit-Position: refs/heads/main@{#985217}
  • Loading branch information
sauski-alternative authored and Chromium LUCI CQ committed Mar 25, 2022
1 parent c66d654 commit 610624d
Show file tree
Hide file tree
Showing 17 changed files with 511 additions and 62 deletions.
Expand Up @@ -51,6 +51,7 @@
#include "components/prefs/pref_service.h"
#include "components/privacy_sandbox/privacy_sandbox_features.h"
#include "components/privacy_sandbox/privacy_sandbox_settings.h"
#include "components/privacy_sandbox/privacy_sandbox_test_util.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_features.h"
Expand Down Expand Up @@ -804,6 +805,12 @@ IN_PROC_BROWSER_TEST_F(ProfileNetworkContextTrustTokensBrowsertest,
ProvideRequestHandlerKeyCommitmentsToNetworkService("a.test");
auto* privacy_sandbox_settings =
PrivacySandboxSettingsFactory::GetForProfile(browser()->profile());
auto privacy_sandbox_delegate = std::make_unique<
privacy_sandbox_test_util::MockPrivacySandboxSettingsDelegate>();
privacy_sandbox_delegate->SetupDefaultResponse(/*restricted=*/false,
/*confirmed=*/true);
privacy_sandbox_settings->SetDelegateForTesting(
std::move(privacy_sandbox_delegate));
privacy_sandbox_settings->SetPrivacySandboxEnabled(true);
browser()->profile()->GetPrefs()->SetInteger(
prefs::kCookieControlsMode,
Expand Down
117 changes: 101 additions & 16 deletions chrome/browser/privacy_sandbox/privacy_sandbox_service.cc
Expand Up @@ -111,6 +111,16 @@ int GetNumberOfDaysRoundedAboveOne(base::TimeDelta time) {
return number_of_days;
}

// 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;
}

} // namespace

PrivacySandboxService::PrivacySandboxService() = default;
Expand Down Expand Up @@ -163,6 +173,11 @@ PrivacySandboxService::PrivacySandboxService(
// further separated from cookie controls.
MaybeReconcilePrivacySandboxPref();

// When the user enters the Privacy Sandbox 3 experiment, the default value
// of their V2 pref must be set. This is a one time operation that is checked
// here to ensure it runs on profile startup.
InitializePrivacySandboxV2Pref();

// If the Sandbox is currently restricted, disable the V2 preference. The user
// must manually enable the sandbox if they stop being restricted.
if (IsPrivacySandboxRestricted())
Expand All @@ -173,15 +188,8 @@ PrivacySandboxService::~PrivacySandboxService() = default;

PrivacySandboxService::DialogType
PrivacySandboxService::GetRequiredDialogType() {
const auto cookie_controls_mode =
static_cast<content_settings::CookieControlsMode>(
pref_service_->GetInteger(prefs::kCookieControlsMode));
const auto default_content_setting =
cookie_settings_->GetDefaultCookieSetting(/*provider_id=*/nullptr);
const auto third_party_cookies_blocked =
default_content_setting == ContentSetting::CONTENT_SETTING_BLOCK ||
cookie_controls_mode ==
content_settings::CookieControlsMode::kBlockThirdParty;
AreThirdPartyCookiesBlocked(cookie_settings_);
return GetRequiredDialogTypeInternal(pref_service_, profile_type_,
privacy_sandbox_settings_,
third_party_cookies_blocked);
Expand Down Expand Up @@ -356,7 +364,9 @@ void PrivacySandboxService::SetFlocPrefEnabled(bool enabled) const {
}

bool PrivacySandboxService::IsPrivacySandboxEnabled() {
return privacy_sandbox_settings_->IsPrivacySandboxEnabled();
return base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3)
? pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2)
: pref_service_->GetBoolean(prefs::kPrivacySandboxFlocEnabled);
}

bool PrivacySandboxService::IsPrivacySandboxManaged() {
Expand All @@ -374,6 +384,11 @@ bool PrivacySandboxService::IsPrivacySandboxRestricted() {
}

void PrivacySandboxService::SetPrivacySandboxEnabled(bool enabled) {
pref_service_->SetBoolean(
base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3)
? prefs::kPrivacySandboxManuallyControlledV2
: prefs::kPrivacySandboxManuallyControlled,
true);
privacy_sandbox_settings_->SetPrivacySandboxEnabled(enabled);
}

Expand Down Expand Up @@ -574,6 +589,39 @@ void PrivacySandboxService::ReconcilePrivacySandboxPref() {
LogPrivacySandboxState();
}

void PrivacySandboxService::InitializePrivacySandboxV2Pref() {
if (!base::FeatureList::IsEnabled(privacy_sandbox::kPrivacySandboxSettings3))
return;

// The initialization process may turn a preference which is otherwise default
// off, on. The default setting for the user is provided by Finch and may
// change over time (e.g. location change). This init logic is however only
// ever performed once per profile, and so will not attempt to enable if the
// user changes location.
if (pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabledV2Init))
return;

pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2Init, true);

// This logic should run before the user has had an opporunity to interact
// with the Privacy Sandbox controls.
DCHECK(
!pref_service_->GetBoolean(prefs::kPrivacySandboxManuallyControlledV2));

// Users must have the V1 sandbox enabled, 3P cookies enabled, and the
// appropriate feature parameter for the V2 pref to be default enabled.
if (!pref_service_->GetBoolean(prefs::kPrivacySandboxApisEnabled))
return;

if (AreThirdPartyCookiesBlocked(cookie_settings_))
return;

if (!privacy_sandbox::kPrivacySandboxSettings3DefaultOn.Get())
return;

pref_service_->SetBoolean(prefs::kPrivacySandboxApisEnabledV2, true);
}

void PrivacySandboxService::StopObserving() {
// Removing a non-observing observer is a no-op.
sync_service_observer_.Reset();
Expand Down Expand Up @@ -629,6 +677,16 @@ void PrivacySandboxService::RecordPrivacySandbox3StartupMetrics() {
PSStartupStates::kDialogOffRestricted);
return;
}
// Handle manually controlled
if (pref_service_->GetBoolean(
prefs::kPrivacySandboxNoConfirmationManuallyControlled)) {
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled
? PSStartupStates::kDialogOffManuallyControlledEnabled
: PSStartupStates::kDialogOffManuallyControlledDisabled);
return;
}
if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()) {
if (!pref_service_->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade)) {
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
Expand All @@ -639,7 +697,7 @@ void PrivacySandboxService::RecordPrivacySandbox3StartupMetrics() {
sandbox_v2_enabled
? PSStartupStates::kConsentShownEnabled
: PSStartupStates::kConsentShownDisabled);
} else { // Notice required.
} else if (privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get()) {
if (!pref_service_->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed)) {
base::UmaHistogramEnumeration(privacy_sandbox_startup_histogram,
PSStartupStates::kDialogWaiting);
Expand All @@ -649,6 +707,11 @@ void PrivacySandboxService::RecordPrivacySandbox3StartupMetrics() {
sandbox_v2_enabled
? PSStartupStates::kNoticeShownEnabled
: PSStartupStates::kNoticeShownDisabled);
} else { // No dialog currently required.
base::UmaHistogramEnumeration(
privacy_sandbox_startup_histogram,
sandbox_v2_enabled ? PSStartupStates::kNoDialogRequiredEnabled
: PSStartupStates::kNoDialogRequiredDisabled);
}
}

Expand Down Expand Up @@ -824,6 +887,16 @@ PrivacySandboxService::GetRequiredDialogTypeInternal(
if (privacy_sandbox::kPrivacySandboxSettings3ForceShowNoticeForTesting.Get())
return DialogType::kNotice;

// If neither consent or notice is required, no dialog is required.
if (!privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get() &&
!privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get()) {
return DialogType::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 dialog, 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
Expand Down Expand Up @@ -869,6 +942,13 @@ PrivacySandboxService::GetRequiredDialogTypeInternal(
return DialogType::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 DialogType::kNone;
}

// If the Privacy Sandbox is restricted, no dialog is shown.
if (privacy_sandbox_settings->IsPrivacySandboxRestricted()) {
pref_service->SetBoolean(
Expand All @@ -891,6 +971,14 @@ PrivacySandboxService::GetRequiredDialogTypeInternal(
return DialogType::kNone;
}

// If the Privacy Sandbox has been manually controlled by the user, no dialog
// is shown.
if (pref_service->GetBoolean(prefs::kPrivacySandboxManuallyControlledV2)) {
pref_service->SetBoolean(
prefs::kPrivacySandboxNoConfirmationManuallyControlled, true);
return DialogType::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() &&
Expand Down Expand Up @@ -929,11 +1017,6 @@ PrivacySandboxService::GetRequiredDialogTypeInternal(
DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxNoticeDisplayed));
DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxConsentDecisionMade));

// The user should not have been able to enable the Sandbox without a
// previous decision having been made. The exception to this is through test
// only feature parameters, which will have let the user skip confirmation.
DCHECK(!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabledV2));

// If the user had previously disabled the Privacy Sandbox, no confirmation
// will be shown.
if (!pref_service->GetBoolean(prefs::kPrivacySandboxApisEnabled)) {
Expand All @@ -944,9 +1027,11 @@ PrivacySandboxService::GetRequiredDialogTypeInternal(

// 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())
if (privacy_sandbox::kPrivacySandboxSettings3ConsentRequired.Get()) {
return DialogType::kConsent;
}

// Finally a notice is required.
DCHECK(privacy_sandbox::kPrivacySandboxSettings3NoticeRequired.Get());
return DialogType::kNotice;
}
37 changes: 31 additions & 6 deletions chrome/browser/privacy_sandbox/privacy_sandbox_service.h
Expand Up @@ -177,13 +177,18 @@ class PrivacySandboxService : public KeyedService,
// Sets the FLoC preference to |enabled|.
void SetFlocPrefEnabled(bool enabled) const;

// Disables the Privacy Sandbox completely if |enabled| is false, if |enabled|
// is true, more granular checks will still be performed to determine if
// specific APIs are available in specific contexts.
// Disables the Privacy Sandbox completely if |enabled| is false. If |enabled|
// is true, context specific as well as restriction/confirmation checks
// will still be performed to determine if specific APIs are available in
// specific contexts.
void SetPrivacySandboxEnabled(bool enabled);

// Used by the UI to check if the API is enabled. Checks the primary
// pref directly.
// Used by the UI to check if the API is enabled. This is a UI function ONLY.
// Checks the primary pref directly, and _only_ the primary pref. There are
// many other reasons that API access may be denied that are not checked by
// this function. All decisions for allowing access to APIs should be routed
// through the PrivacySandboxSettings class.
// TODO(crbug.com/1310157): Rename this function to better reflect this.
bool IsPrivacySandboxEnabled();

// Returns whether the state of the API is managed.
Expand Down Expand Up @@ -288,6 +293,9 @@ class PrivacySandboxService : public KeyedService,
NoMetricsRecorded);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest, RestrictedDialog);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest, ManagedNoDialog);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest,
ManuallyControlledNoDialog);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDialogTest, NoParamNoDialog);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceDeathTest,
GetRequiredDialogType);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
Expand All @@ -314,6 +322,15 @@ class PrivacySandboxService : public KeyedService,
PrivacySandboxManagedEnabled);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
PrivacySandboxManagedDisabled);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
PrivacySandboxManuallyControlledEnabled);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
PrivacySandboxManuallyControlledDisabled);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
PrivacySandboxNoDialogDisabled);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest,
PrivacySandboxNoDialogEnabled);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest, InitializeV2Pref);
FRIEND_TEST_ALL_PREFIXES(PrivacySandboxServiceTest, PrivacySandboxRestricted);

// Should be used only for tests when mocking the service.
Expand Down Expand Up @@ -357,10 +374,14 @@ class PrivacySandboxService : public KeyedService,
kDialogOffManagedEnabled = 9,
kDialogOffManagedDisabled = 10,
kDialogOffRestricted = 11,
kDialogOffManuallyControlledEnabled = 12,
kDialogOffManuallyControlledDisabled = 13,
kNoDialogRequiredEnabled = 14,
kNoDialogRequiredDisabled = 15,

// Add values above this line with a corresponding label in
// tools/metrics/histograms/enums.xml
kMaxValue = kDialogOffRestricted,
kMaxValue = kNoDialogRequiredDisabled,
};

// Inspects the current sync state and settings to determine if the Privacy
Expand All @@ -375,6 +396,10 @@ class PrivacySandboxService : public KeyedService,
// user out of the sandbox.
void ReconcilePrivacySandboxPref();

// Potentially enables the Privacy Sandbox V2 pref if required based on
// feature parameters and the profiles current state.
void InitializePrivacySandboxV2Pref();

// Stops any observation of services being performed by this class.
void StopObserving();

Expand Down

0 comments on commit 610624d

Please sign in to comment.