blob: 0c9b4aaf1c07589a68bc5965fe57f4451797544a [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/unified_consent/url_keyed_data_collection_consent_helper.h"
#include <algorithm>
#include <map>
#include <set>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/observer_list.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/sync/base/data_type.h"
#include "components/sync/service/sync_service.h"
#include "components/sync/service/sync_service_observer.h"
#include "components/sync/service/sync_service_utils.h"
#include "components/unified_consent/pref_names.h"
namespace unified_consent {
namespace {
class PrefBasedUrlKeyedDataCollectionConsentHelper
: public UrlKeyedDataCollectionConsentHelper {
public:
explicit PrefBasedUrlKeyedDataCollectionConsentHelper(
PrefService* pref_service);
PrefBasedUrlKeyedDataCollectionConsentHelper(
const PrefBasedUrlKeyedDataCollectionConsentHelper&) = delete;
PrefBasedUrlKeyedDataCollectionConsentHelper& operator=(
const PrefBasedUrlKeyedDataCollectionConsentHelper&) = delete;
~PrefBasedUrlKeyedDataCollectionConsentHelper() override = default;
// UrlKeyedDataCollectionConsentHelper:
State GetConsentState() override;
private:
void OnPrefChanged();
raw_ptr<PrefService> pref_service_; // weak (must outlive this)
PrefChangeRegistrar pref_change_registrar_;
};
class SyncBasedUrlKeyedDataCollectionConsentHelper
: public UrlKeyedDataCollectionConsentHelper,
syncer::SyncServiceObserver {
public:
SyncBasedUrlKeyedDataCollectionConsentHelper(
syncer::SyncService* sync_service,
std::set<syncer::DataType> sync_data_types,
bool require_sync_feature_enabled);
SyncBasedUrlKeyedDataCollectionConsentHelper(
const SyncBasedUrlKeyedDataCollectionConsentHelper&) = delete;
SyncBasedUrlKeyedDataCollectionConsentHelper& operator=(
const SyncBasedUrlKeyedDataCollectionConsentHelper&) = delete;
~SyncBasedUrlKeyedDataCollectionConsentHelper() override;
// UrlKeyedDataCollectionConsentHelper:
State GetConsentState() override;
// syncer::SyncServiceObserver:
void OnStateChanged(syncer::SyncService* sync) override;
void OnSyncShutdown(syncer::SyncService* sync) override;
private:
void UpdateSyncDataTypeStates();
const bool require_sync_feature_enabled_;
raw_ptr<syncer::SyncService> sync_service_;
std::map<syncer::DataType, syncer::UploadState> sync_data_type_states_;
bool sync_feature_state_ = false;
};
PrefBasedUrlKeyedDataCollectionConsentHelper::
PrefBasedUrlKeyedDataCollectionConsentHelper(PrefService* pref_service)
: pref_service_(pref_service) {
pref_change_registrar_.Init(pref_service_);
pref_change_registrar_.Add(
prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
base::BindRepeating(
&PrefBasedUrlKeyedDataCollectionConsentHelper::OnPrefChanged,
base::Unretained(this)));
}
UrlKeyedDataCollectionConsentHelper::State
PrefBasedUrlKeyedDataCollectionConsentHelper::GetConsentState() {
// There's no initializing state for pref-based helpers.
return pref_service_->GetBoolean(
prefs::kUrlKeyedAnonymizedDataCollectionEnabled)
? State::kEnabled
: State::kDisabled;
}
void PrefBasedUrlKeyedDataCollectionConsentHelper::OnPrefChanged() {
FireOnStateChanged();
}
SyncBasedUrlKeyedDataCollectionConsentHelper::
SyncBasedUrlKeyedDataCollectionConsentHelper(
syncer::SyncService* sync_service,
std::set<syncer::DataType> sync_data_types,
bool require_sync_feature_enabled)
: require_sync_feature_enabled_(require_sync_feature_enabled),
sync_service_(sync_service) {
DCHECK(!sync_data_types.empty());
for (const auto& sync_data_type : sync_data_types) {
sync_data_type_states_[sync_data_type] = syncer::UploadState::NOT_ACTIVE;
}
UpdateSyncDataTypeStates();
if (sync_service_)
sync_service_->AddObserver(this);
}
SyncBasedUrlKeyedDataCollectionConsentHelper::
~SyncBasedUrlKeyedDataCollectionConsentHelper() {
if (sync_service_)
sync_service_->RemoveObserver(this);
}
// Note: This method must only consume cached state (not query anything from
// SyncService), to ensure that the state-change detection in OnStateChanged()
// works correctly.
UrlKeyedDataCollectionConsentHelper::State
SyncBasedUrlKeyedDataCollectionConsentHelper::GetConsentState() {
if (require_sync_feature_enabled_ && !sync_feature_state_) {
return State::kDisabled;
}
// Any sync type that's NOT_ACTIVE makes the whole consent kDisabled.
for (const auto& sync_data_type_states : sync_data_type_states_) {
if (sync_data_type_states.second == syncer::UploadState::NOT_ACTIVE) {
return State::kDisabled;
}
}
// If no sync type is NOT_ACTIVE, any sync type still INITIALIZING makes the
// whole consent kInitializing.
for (const auto& sync_data_type_states : sync_data_type_states_) {
if (sync_data_type_states.second == syncer::UploadState::INITIALIZING) {
return State::kInitializing;
}
}
DCHECK(std::ranges::all_of(sync_data_type_states_, [](auto& state) {
return state.second == syncer::UploadState::ACTIVE;
})) << "Nothing is NOT_ACTIVE or INITIALIZING, so all must be ACTIVE.";
return State::kEnabled;
}
void SyncBasedUrlKeyedDataCollectionConsentHelper::OnStateChanged(
syncer::SyncService* sync_service) {
DCHECK_EQ(sync_service_, sync_service);
auto old_state = GetConsentState();
UpdateSyncDataTypeStates();
if (old_state != GetConsentState()) {
FireOnStateChanged();
}
}
void SyncBasedUrlKeyedDataCollectionConsentHelper::OnSyncShutdown(
syncer::SyncService* sync_service) {
DCHECK_EQ(sync_service_, sync_service);
sync_service_->RemoveObserver(this);
sync_service_ = nullptr;
}
void SyncBasedUrlKeyedDataCollectionConsentHelper::UpdateSyncDataTypeStates() {
// TODO(crbug.com/40067025): Remove the `require_sync_feature_enabled` param
// once kReplaceSyncPromosWithSignInPromos is launched.
if (require_sync_feature_enabled_) {
sync_feature_state_ =
sync_service_ && sync_service_->IsSyncFeatureEnabled();
}
for (auto& [data_type, upload_state] : sync_data_type_states_) {
upload_state = syncer::GetUploadToGoogleState(sync_service_, data_type);
}
}
} // namespace
UrlKeyedDataCollectionConsentHelper::UrlKeyedDataCollectionConsentHelper() =
default;
UrlKeyedDataCollectionConsentHelper::~UrlKeyedDataCollectionConsentHelper() =
default;
// static
std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
UrlKeyedDataCollectionConsentHelper::NewAnonymizedDataCollectionConsentHelper(
PrefService* pref_service) {
return std::make_unique<PrefBasedUrlKeyedDataCollectionConsentHelper>(
pref_service);
}
// static
std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
UrlKeyedDataCollectionConsentHelper::NewPersonalizedDataCollectionConsentHelper(
syncer::SyncService* sync_service) {
return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
sync_service,
std::set<syncer::DataType>({syncer::DataType::HISTORY_DELETE_DIRECTIVES}),
/*require_sync_feature_enabled=*/false);
}
// static
std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
UrlKeyedDataCollectionConsentHelper::
NewPersonalizedBookmarksDataCollectionConsentHelper(
syncer::SyncService* sync_service,
bool require_sync_feature_enabled) {
return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
sync_service, std::set<syncer::DataType>({syncer::DataType::BOOKMARKS}),
require_sync_feature_enabled);
}
bool UrlKeyedDataCollectionConsentHelper::IsEnabled() {
return GetConsentState() == State::kEnabled;
}
void UrlKeyedDataCollectionConsentHelper::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void UrlKeyedDataCollectionConsentHelper::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void UrlKeyedDataCollectionConsentHelper::FireOnStateChanged() {
for (auto& observer : observer_list_)
observer.OnUrlKeyedDataCollectionConsentStateChanged(this);
}
} // namespace unified_consent