summaryrefslogtreecommitdiff
path: root/chromium/components/unified_consent
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/unified_consent')
-rw-r--r--chromium/components/unified_consent/BUILD.gn65
-rw-r--r--chromium/components/unified_consent/DEPS9
-rw-r--r--chromium/components/unified_consent/OWNERS5
-rw-r--r--chromium/components/unified_consent/README.md8
-rw-r--r--chromium/components/unified_consent/feature.cc38
-rw-r--r--chromium/components/unified_consent/feature.h38
-rw-r--r--chromium/components/unified_consent/feature_unittest.cc53
-rw-r--r--chromium/components/unified_consent/pref_names.cc26
-rw-r--r--chromium/components/unified_consent/pref_names.h19
-rw-r--r--chromium/components/unified_consent/scoped_unified_consent.cc43
-rw-r--r--chromium/components/unified_consent/scoped_unified_consent.h34
-rw-r--r--chromium/components/unified_consent/unified_consent_service.cc486
-rw-r--r--chromium/components/unified_consent/unified_consent_service.h168
-rw-r--r--chromium/components/unified_consent/unified_consent_service_client.cc50
-rw-r--r--chromium/components/unified_consent/unified_consent_service_client.h94
-rw-r--r--chromium/components/unified_consent/unified_consent_service_unittest.cc645
-rw-r--r--chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc191
-rw-r--r--chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h88
-rw-r--r--chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc220
19 files changed, 2280 insertions, 0 deletions
diff --git a/chromium/components/unified_consent/BUILD.gn b/chromium/components/unified_consent/BUILD.gn
new file mode 100644
index 00000000000..419332e8d31
--- /dev/null
+++ b/chromium/components/unified_consent/BUILD.gn
@@ -0,0 +1,65 @@
+# Copyright 2018 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+static_library("unified_consent") {
+ sources = [
+ "feature.cc",
+ "feature.h",
+ "pref_names.cc",
+ "pref_names.h",
+ "unified_consent_service.cc",
+ "unified_consent_service.h",
+ "unified_consent_service_client.cc",
+ "unified_consent_service_client.h",
+ "url_keyed_data_collection_consent_helper.cc",
+ "url_keyed_data_collection_consent_helper.h",
+ ]
+ deps = [
+ "//base",
+ "//components/autofill/core/common",
+ "//components/browser_sync",
+ "//components/pref_registry",
+ "//components/signin/core/browser",
+ "//components/sync",
+ "//services/identity/public/cpp",
+ ]
+}
+
+static_library("test_support") {
+ testonly = true
+ sources = [
+ "scoped_unified_consent.cc",
+ "scoped_unified_consent.h",
+ ]
+
+ deps = [
+ "//base/test:test_support",
+ ]
+
+ public_deps = [
+ ":unified_consent",
+ "//base",
+ "//components/sync",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "feature_unittest.cc",
+ "unified_consent_service_unittest.cc",
+ "url_keyed_data_collection_consent_helper_unittest.cc",
+ ]
+ deps = [
+ ":test_support",
+ ":unified_consent",
+ "//base/test:test_support",
+ "//components/autofill/core/common",
+ "//components/sync",
+ "//components/sync:test_support_driver",
+ "//components/sync_preferences:test_support",
+ "//services/identity/public/cpp:test_support",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/components/unified_consent/DEPS b/chromium/components/unified_consent/DEPS
new file mode 100644
index 00000000000..38bd56db868
--- /dev/null
+++ b/chromium/components/unified_consent/DEPS
@@ -0,0 +1,9 @@
+include_rules = [
+ "+components/autofill/core/common",
+ "+components/keyed_service/core",
+ "+components/pref_registry",
+ "+components/prefs",
+ "+components/sync",
+ "+components/sync_preferences",
+ "+services/identity/public/cpp",
+]
diff --git a/chromium/components/unified_consent/OWNERS b/chromium/components/unified_consent/OWNERS
new file mode 100644
index 00000000000..8a23481825c
--- /dev/null
+++ b/chromium/components/unified_consent/OWNERS
@@ -0,0 +1,5 @@
+msarda@chromium.org
+tangltom@chromium.org
+
+# TEAM: chrome-signin@chromium.org
+# COMPONENT: Services>SignIn
diff --git a/chromium/components/unified_consent/README.md b/chromium/components/unified_consent/README.md
new file mode 100644
index 00000000000..e37be17082a
--- /dev/null
+++ b/chromium/components/unified_consent/README.md
@@ -0,0 +1,8 @@
+The Unified Consent component contains the browser keyed service that
+manages user consent when the Unified Consent feature is enabled. It also
+holds the prefs and the APIs allowing the various Chromium features to verify if
+the user has given consent for a given feature.
+
+This component is currently in development.
+
+The component is used on all platforms (desktop, ChromeOS, Android and iOS).
diff --git a/chromium/components/unified_consent/feature.cc b/chromium/components/unified_consent/feature.cc
new file mode 100644
index 00000000000..b9c1feb616c
--- /dev/null
+++ b/chromium/components/unified_consent/feature.cc
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/feature.h"
+
+#include "base/feature_list.h"
+#include "base/metrics/field_trial_params.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace unified_consent {
+
+// base::Feature definitions.
+const base::Feature kUnifiedConsent{"UnifiedConsent",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+const char kUnifiedConsentShowBumpParameter[] = "show_consent_bump";
+
+namespace internal {
+UnifiedConsentFeatureState GetUnifiedConsentFeatureState() {
+ // Unified consent requires user consent to be recorded via its own
+ // sync model type.The reason is that when unified consent is enabled,
+ // |USER_EVENTS| sync model type is configurable and the user may disable it.
+ // Chromium needs to continue to record user consent even if the user
+ // manually disables |USER_EVENTS|.
+ if (!base::FeatureList::IsEnabled(switches::kSyncUserConsentSeparateType))
+ return UnifiedConsentFeatureState::kDisabled;
+
+ if (!base::FeatureList::IsEnabled(kUnifiedConsent))
+ return UnifiedConsentFeatureState::kDisabled;
+
+ std::string show_bump = base::GetFieldTrialParamValueByFeature(
+ kUnifiedConsent, kUnifiedConsentShowBumpParameter);
+ return show_bump.empty() ? UnifiedConsentFeatureState::kEnabledNoBump
+ : UnifiedConsentFeatureState::kEnabledWithBump;
+}
+} // namespace internal
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/feature.h b/chromium/components/unified_consent/feature.h
new file mode 100644
index 00000000000..e602df3665c
--- /dev/null
+++ b/chromium/components/unified_consent/feature.h
@@ -0,0 +1,38 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNIFIED_CONSENT_FEATURE_H_
+#define COMPONENTS_UNIFIED_CONSENT_FEATURE_H_
+
+#include "base/feature_list.h"
+
+namespace unified_consent {
+
+// State of the "Unified Consent" feature.
+enum class UnifiedConsentFeatureState {
+ // Unified consent is disabled.
+ kDisabled,
+ // Unified consent is enabled, but the bump is not shown.
+ kEnabledNoBump,
+ // Unified consent is enabled and the bump is shown.
+ kEnabledWithBump
+};
+
+// Improved and unified consent for privacy-related features.
+extern const base::Feature kUnifiedConsent;
+extern const char kUnifiedConsentShowBumpParameter[];
+
+namespace internal {
+// Returns the state of the "Unified Consent" feature.
+//
+// WARNING: Do not call this method directly to check whether unfied consent
+// is enabled. Please use the per-platfome functions defined in
+// * chrome/browser/signin/unified_consent_helper.h
+// * ios/chrome/browser/unified_consent/feature.h
+unified_consent::UnifiedConsentFeatureState GetUnifiedConsentFeatureState();
+} // namespace internal
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_FEATURE_H_
diff --git a/chromium/components/unified_consent/feature_unittest.cc b/chromium/components/unified_consent/feature_unittest.cc
new file mode 100644
index 00000000000..e66297f99c5
--- /dev/null
+++ b/chromium/components/unified_consent/feature_unittest.cc
@@ -0,0 +1,53 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/feature.h"
+
+#include <memory>
+
+#include "components/sync/driver/sync_driver_switches.h"
+#include "components/unified_consent/scoped_unified_consent.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unified_consent {
+
+TEST(UnifiedConsentFeatureTest, UnifiedConsent) {
+ // Unified consent is disabled by default.
+ EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
+ internal::GetUnifiedConsentFeatureState());
+
+ for (UnifiedConsentFeatureState state :
+ {UnifiedConsentFeatureState::kDisabled,
+ UnifiedConsentFeatureState::kEnabledNoBump,
+ UnifiedConsentFeatureState::kEnabledWithBump}) {
+ ScopedUnifiedConsent scoped_state(state);
+ EXPECT_EQ(state, internal::GetUnifiedConsentFeatureState());
+ }
+}
+
+TEST(UnifiedConsentFeatureTest, SyncUserConsentSeparateTypeDisabled) {
+ // Enable kSyncUserConsentSeparateType
+ base::test::ScopedFeatureList scoped_sync_user_consent_separate_type_feature;
+ scoped_sync_user_consent_separate_type_feature.InitAndDisableFeature(
+ switches::kSyncUserConsentSeparateType);
+
+ {
+ base::test::ScopedFeatureList unified_consent_feature_list_;
+ unified_consent_feature_list_.InitAndEnableFeature(kUnifiedConsent);
+ EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
+ internal::GetUnifiedConsentFeatureState());
+ }
+
+ {
+ std::map<std::string, std::string> feature_params;
+ feature_params[kUnifiedConsentShowBumpParameter] = "true";
+ base::test::ScopedFeatureList unified_consent_feature_list_;
+ unified_consent_feature_list_.InitAndEnableFeatureWithParameters(
+ kUnifiedConsent, feature_params);
+ EXPECT_EQ(UnifiedConsentFeatureState::kDisabled,
+ internal::GetUnifiedConsentFeatureState());
+ }
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/pref_names.cc b/chromium/components/unified_consent/pref_names.cc
new file mode 100644
index 00000000000..19cdb2bc1fc
--- /dev/null
+++ b/chromium/components/unified_consent/pref_names.cc
@@ -0,0 +1,26 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/pref_names.h"
+
+namespace unified_consent {
+namespace prefs {
+
+// Boolean indicating whether all criteria is met for the consent bump to be
+// shown.
+const char kShouldShowUnifiedConsentBump[] =
+ "unified_consent.consent_bump.should_show";
+
+// Boolean that is true when the user opted into unified consent.
+const char kUnifiedConsentGiven[] = "unified_consent_given";
+
+// Integer indicating the migration state of unified consent, defined in
+// unified_consent::MigrationState.
+const char kUnifiedConsentMigrationState[] = "unified_consent.migration_state";
+
+const char kUrlKeyedAnonymizedDataCollectionEnabled[] =
+ "url_keyed_anonymized_data_collection.enabled";
+
+} // namespace prefs
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/pref_names.h b/chromium/components/unified_consent/pref_names.h
new file mode 100644
index 00000000000..91ebf362a3a
--- /dev/null
+++ b/chromium/components/unified_consent/pref_names.h
@@ -0,0 +1,19 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_
+#define COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_
+
+namespace unified_consent {
+namespace prefs {
+
+extern const char kShouldShowUnifiedConsentBump[];
+extern const char kUnifiedConsentGiven[];
+extern const char kUnifiedConsentMigrationState[];
+extern const char kUrlKeyedAnonymizedDataCollectionEnabled[];
+
+} // namespace prefs
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_PREF_NAMES_H_
diff --git a/chromium/components/unified_consent/scoped_unified_consent.cc b/chromium/components/unified_consent/scoped_unified_consent.cc
new file mode 100644
index 00000000000..dc27b9f0e21
--- /dev/null
+++ b/chromium/components/unified_consent/scoped_unified_consent.cc
@@ -0,0 +1,43 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/scoped_unified_consent.h"
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/sync/driver/sync_driver_switches.h"
+
+namespace unified_consent {
+
+ScopedUnifiedConsent::ScopedUnifiedConsent(UnifiedConsentFeatureState state) {
+ sync_user_consent_separate_type_feature_list_.InitAndEnableFeature(
+ switches::kSyncUserConsentSeparateType);
+ switch (state) {
+ case UnifiedConsentFeatureState::kDisabled:
+ unified_consent_feature_list_.InitAndDisableFeature(kUnifiedConsent);
+ break;
+ case UnifiedConsentFeatureState::kEnabledNoBump:
+ unified_consent_feature_list_.InitAndEnableFeature(kUnifiedConsent);
+ break;
+ case UnifiedConsentFeatureState::kEnabledWithBump: {
+ std::map<std::string, std::string> feature_params;
+ feature_params[kUnifiedConsentShowBumpParameter] = "true";
+ unified_consent_feature_list_.InitAndEnableFeatureWithParameters(
+ kUnifiedConsent, feature_params);
+ break;
+ }
+ }
+
+ DCHECK_EQ(state, internal::GetUnifiedConsentFeatureState());
+}
+
+ScopedUnifiedConsent::~ScopedUnifiedConsent() {}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/scoped_unified_consent.h b/chromium/components/unified_consent/scoped_unified_consent.h
new file mode 100644
index 00000000000..a280cdc2244
--- /dev/null
+++ b/chromium/components/unified_consent/scoped_unified_consent.h
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNIFIED_CONSENT_SCOPED_UNIFIED_CONSENT_H_
+#define COMPONENTS_UNIFIED_CONSENT_SCOPED_UNIFIED_CONSENT_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/test/scoped_feature_list.h"
+#include "components/unified_consent/feature.h"
+
+namespace unified_consent {
+
+// Changes the unified consent feature state while it is in scope. Useful for
+// tests.
+// Also enables the feature |switches::kSyncUserConsentSeparateType| as
+// unified consent depends on its.
+class ScopedUnifiedConsent {
+ public:
+ explicit ScopedUnifiedConsent(UnifiedConsentFeatureState state);
+ ~ScopedUnifiedConsent();
+
+ private:
+ base::test::ScopedFeatureList sync_user_consent_separate_type_feature_list_;
+ base::test::ScopedFeatureList unified_consent_feature_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUnifiedConsent);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_SCOPED_UNIFIED_CONSENT_H_
diff --git a/chromium/components/unified_consent/unified_consent_service.cc b/chromium/components/unified_consent/unified_consent_service.cc
new file mode 100644
index 00000000000..9b9dbc133f6
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service.cc
@@ -0,0 +1,486 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/unified_consent_service.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/scoped_observer.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/unified_consent/unified_consent_service_client.h"
+
+namespace unified_consent {
+
+namespace {
+
+// Histogram recorded at startup to log which Google services are enabled.
+const char kSyncAndGoogleServicesSettingsHistogram[] =
+ "UnifiedConsent.SyncAndGoogleServicesSettings";
+
+// Records a sample in the kSyncAndGoogleServicesSettingsHistogram. Wrapped in a
+// function to avoid code size issues caused by histogram macros.
+void RecordSettingsHistogramSample(SettingsHistogramValue value) {
+ UMA_HISTOGRAM_ENUMERATION(kSyncAndGoogleServicesSettingsHistogram, value);
+}
+
+// Checks if a pref is enabled and if so, records a sample in the
+// kSyncAndGoogleServicesSettingsHistogram. Returns true if a sample was
+// recorded.
+bool RecordSettingsHistogramFromPref(const char* pref_name,
+ PrefService* pref_service,
+ SettingsHistogramValue value) {
+ if (!pref_service->GetBoolean(pref_name))
+ return false;
+ RecordSettingsHistogramSample(value);
+ return true;
+}
+
+// Checks if a service is enabled and if so, records a sample in the
+// kSyncAndGoogleServicesSettingsHistogram. Returns true if a sample was
+// recorded.
+bool RecordSettingsHistogramFromService(
+ UnifiedConsentServiceClient* client,
+ UnifiedConsentServiceClient::Service service,
+ SettingsHistogramValue value) {
+ if (client->GetServiceState(service) !=
+ UnifiedConsentServiceClient::ServiceState::kEnabled) {
+ return false;
+ }
+
+ RecordSettingsHistogramSample(value);
+ return true;
+}
+
+// Used for observing the sync service and finishing the rollback once the sync
+// engine is initialized.
+// Note: This object is suicidal - it will kill itself after it finishes the
+// rollback.
+class RollbackHelper : public syncer::SyncServiceObserver {
+ public:
+ explicit RollbackHelper(syncer::SyncService* sync_service);
+ ~RollbackHelper() override = default;
+
+ private:
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync_service) override;
+
+ void DoRollbackIfPossibleAndDie(syncer::SyncService* sync_service);
+
+ ScopedObserver<syncer::SyncService, RollbackHelper> scoped_sync_observer_;
+};
+
+RollbackHelper::RollbackHelper(syncer::SyncService* sync_service)
+ : scoped_sync_observer_(this) {
+ if (sync_service->IsEngineInitialized())
+ DoRollbackIfPossibleAndDie(sync_service);
+ else
+ scoped_sync_observer_.Add(sync_service);
+}
+
+void RollbackHelper::OnStateChanged(syncer::SyncService* sync_service) {
+ if (!sync_service->IsEngineInitialized())
+ return;
+
+ scoped_sync_observer_.RemoveAll();
+ DoRollbackIfPossibleAndDie(sync_service);
+}
+
+void RollbackHelper::DoRollbackIfPossibleAndDie(
+ syncer::SyncService* sync_service) {
+ DCHECK(!scoped_sync_observer_.IsObservingSources());
+
+ syncer::ModelTypeSet user_types_without_user_events =
+ syncer::UserSelectableTypes();
+ user_types_without_user_events.Remove(syncer::USER_EVENTS);
+
+ if (sync_service->GetPreferredDataTypes().HasAll(
+ user_types_without_user_events)) {
+ // As part of the migration of a profile to Unified Consent, sync everything
+ // is disabled but sync continues to be enabled for all data types except
+ // USER_EVENTS. Therefore it is desired to restore sync everything when
+ // rolling back unified consent to leave sync in the same state as the one
+ // before migration.
+ sync_service->OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ }
+
+ base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
+}
+
+} // namespace
+
+UnifiedConsentService::UnifiedConsentService(
+ std::unique_ptr<UnifiedConsentServiceClient> service_client,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ syncer::SyncService* sync_service)
+ : service_client_(std::move(service_client)),
+ pref_service_(pref_service),
+ identity_manager_(identity_manager),
+ sync_service_(sync_service) {
+ DCHECK(service_client_);
+ DCHECK(pref_service_);
+ DCHECK(identity_manager_);
+ DCHECK(sync_service_);
+
+ if (GetMigrationState() == MigrationState::kNotInitialized)
+ MigrateProfileToUnifiedConsent();
+
+ service_client_->AddObserver(this);
+ identity_manager_->AddObserver(this);
+ sync_service_->AddObserver(this);
+
+ pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
+ pref_change_registrar_->Init(pref_service);
+ pref_change_registrar_->Add(
+ prefs::kUnifiedConsentGiven,
+ base::BindRepeating(
+ &UnifiedConsentService::OnUnifiedConsentGivenPrefChanged,
+ base::Unretained(this)));
+
+ // If somebody disabled any of the non-personalized services while Chrome
+ // wasn't running, disable unified consent.
+ if (!AreAllNonPersonalizedServicesEnabled() && IsUnifiedConsentGiven()) {
+ SetUnifiedConsentGiven(false);
+ }
+
+ RecordSettingsHistogram();
+}
+
+UnifiedConsentService::~UnifiedConsentService() {}
+
+// static
+void UnifiedConsentService::RegisterPrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterBooleanPref(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ false);
+ registry->RegisterBooleanPref(prefs::kUnifiedConsentGiven, false);
+ registry->RegisterIntegerPref(
+ prefs::kUnifiedConsentMigrationState,
+ static_cast<int>(MigrationState::kNotInitialized));
+ registry->RegisterBooleanPref(prefs::kShouldShowUnifiedConsentBump, false);
+}
+
+// static
+void UnifiedConsentService::RollbackIfNeeded(
+ PrefService* user_pref_service,
+ syncer::SyncService* sync_service) {
+ DCHECK(user_pref_service);
+
+ if (user_pref_service->GetInteger(prefs::kUnifiedConsentMigrationState) ==
+ static_cast<int>(MigrationState::kNotInitialized)) {
+ // If there was no migration yet, nothing has to be rolled back.
+ return;
+ }
+
+ if (user_pref_service->GetBoolean(prefs::kShouldShowUnifiedConsentBump) &&
+ sync_service &&
+ sync_service->GetDisableReasons() ==
+ syncer::SyncService::DISABLE_REASON_NONE) {
+ // This will wait until the sync engine is initialized and then enables the
+ // sync-everything pref in case the user is syncing all data types.
+ new RollbackHelper(sync_service);
+ }
+
+ // Clear all unified consent prefs.
+ user_pref_service->ClearPref(prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
+ user_pref_service->ClearPref(prefs::kUnifiedConsentGiven);
+ user_pref_service->ClearPref(prefs::kUnifiedConsentMigrationState);
+ user_pref_service->ClearPref(prefs::kShouldShowUnifiedConsentBump);
+}
+
+void UnifiedConsentService::SetUnifiedConsentGiven(bool unified_consent_given) {
+ // Unified consent cannot be enabled if the user is not signed in.
+ DCHECK(!unified_consent_given || identity_manager_->HasPrimaryAccount());
+ pref_service_->SetBoolean(prefs::kUnifiedConsentGiven, unified_consent_given);
+}
+
+bool UnifiedConsentService::IsUnifiedConsentGiven() {
+ return pref_service_->GetBoolean(prefs::kUnifiedConsentGiven);
+}
+
+bool UnifiedConsentService::ShouldShowConsentBump() {
+ return pref_service_->GetBoolean(prefs::kShouldShowUnifiedConsentBump);
+}
+
+void UnifiedConsentService::MarkConsentBumpShown() {
+ // Record suppress reason kNone, which means that it was shown. This also sets
+ // the |kShouldShowConsentBump| pref to false.
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kNone);
+}
+
+void UnifiedConsentService::RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason suppress_reason) {
+ UMA_HISTOGRAM_ENUMERATION("UnifiedConsent.ConsentBump.SuppressReason",
+ suppress_reason);
+
+ switch (suppress_reason) {
+ case ConsentBumpSuppressReason::kNone:
+ case ConsentBumpSuppressReason::kNotSignedIn:
+ case ConsentBumpSuppressReason::kSyncEverythingOff:
+ case ConsentBumpSuppressReason::kPrivacySettingOff:
+ case ConsentBumpSuppressReason::kSettingsOptIn:
+ case ConsentBumpSuppressReason::kUserSignedOut:
+ pref_service_->SetBoolean(prefs::kShouldShowUnifiedConsentBump, false);
+ break;
+ case ConsentBumpSuppressReason::kSyncPaused:
+ // Consent bump should be shown when sync is active again.
+ DCHECK(ShouldShowConsentBump());
+ break;
+ }
+}
+
+void UnifiedConsentService::Shutdown() {
+ service_client_->RemoveObserver(this);
+ identity_manager_->RemoveObserver(this);
+ sync_service_->RemoveObserver(this);
+}
+
+void UnifiedConsentService::OnServiceStateChanged(Service service) {
+ // Unified consent is disabled when any of its dependent services gets
+ // disabled.
+ if (service_client_->GetServiceState(service) == ServiceState::kDisabled)
+ SetUnifiedConsentGiven(false);
+}
+
+void UnifiedConsentService::OnPrimaryAccountCleared(
+ const AccountInfo& account_info) {
+ // When signing out, the unfied consent is revoked.
+ pref_service_->SetBoolean(prefs::kUnifiedConsentGiven, false);
+
+ // By design, signing out of Chrome automatically disables off-by-default
+ // services.
+ pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ false);
+ service_client_->SetServiceEnabled(Service::kSafeBrowsingExtendedReporting,
+ false);
+ service_client_->SetServiceEnabled(Service::kSpellCheck, false);
+
+ if (GetMigrationState() != MigrationState::kCompleted) {
+ // When the user signs out, the migration is complete.
+ SetMigrationState(MigrationState::kCompleted);
+ }
+
+ if (ShouldShowConsentBump())
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kUserSignedOut);
+}
+
+void UnifiedConsentService::OnStateChanged(syncer::SyncService* sync) {
+ if (sync_service_->GetDisableReasons() !=
+ syncer::SyncService::DISABLE_REASON_NONE ||
+ !sync_service_->IsEngineInitialized()) {
+ return;
+ }
+
+ if (GetMigrationState() == MigrationState::kInProgressWaitForSyncInit)
+ UpdateSettingsForMigration();
+
+ if (sync_service_->IsUsingSecondaryPassphrase() && IsUnifiedConsentGiven()) {
+ // Force off unified consent given when the user sets a custom passphrase.
+ SetUnifiedConsentGiven(false);
+ }
+
+ syncer::SyncPrefs sync_prefs(pref_service_);
+ if (IsUnifiedConsentGiven() != sync_prefs.HasKeepEverythingSynced()) {
+ // Make sync-everything consistent with the |kUnifiedConsentGiven| pref.
+ SetSyncEverythingIfPossible(IsUnifiedConsentGiven());
+ }
+}
+
+void UnifiedConsentService::OnUnifiedConsentGivenPrefChanged() {
+ bool enabled = pref_service_->GetBoolean(prefs::kUnifiedConsentGiven);
+
+ if (!enabled) {
+ if (identity_manager_->HasPrimaryAccount()) {
+ // Sync-everything is set to false, so the user can select individual
+ // sync data types.
+ SetSyncEverythingIfPossible(false);
+ }
+ return;
+ }
+
+ DCHECK(sync_service_->IsSyncAllowed());
+ DCHECK(identity_manager_->HasPrimaryAccount());
+ DCHECK_LT(MigrationState::kNotInitialized, GetMigrationState());
+
+ if (GetMigrationState() != MigrationState::kCompleted) {
+ // If the user opted into unified consent, the migration is completed.
+ SetMigrationState(MigrationState::kCompleted);
+ }
+
+ if (ShouldShowConsentBump())
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kSettingsOptIn);
+
+ // Enable all sync data types if possible, otherwise they will be enabled with
+ // |OnStateChanged| once sync is active;
+ SetSyncEverythingIfPossible(true);
+
+ // Enable all non-personalized services.
+ pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ true);
+ // Inform client to enable non-personalized services.
+ for (int i = 0; i <= static_cast<int>(Service::kLast); ++i) {
+ Service service = static_cast<Service>(i);
+ if (service_client_->GetServiceState(service) !=
+ ServiceState::kNotSupported) {
+ service_client_->SetServiceEnabled(service, true);
+ }
+ }
+}
+
+void UnifiedConsentService::SetSyncEverythingIfPossible(bool sync_everything) {
+ syncer::SyncPrefs sync_prefs(pref_service_);
+ if (sync_everything == sync_prefs.HasKeepEverythingSynced())
+ return;
+
+ if (!IsSyncConfigurable())
+ return;
+
+ if (sync_everything) {
+ pref_service_->SetBoolean(autofill::prefs::kAutofillWalletImportEnabled,
+ true);
+ sync_service_->OnUserChoseDatatypes(sync_everything,
+ syncer::UserSelectableTypes());
+ } else {
+ syncer::ModelTypeSet preferred = sync_service_->GetPreferredDataTypes();
+ preferred.RetainAll(syncer::UserSelectableTypes());
+ sync_service_->OnUserChoseDatatypes(false, preferred);
+ }
+}
+
+MigrationState UnifiedConsentService::GetMigrationState() {
+ int migration_state_int =
+ pref_service_->GetInteger(prefs::kUnifiedConsentMigrationState);
+ DCHECK_LE(static_cast<int>(MigrationState::kNotInitialized),
+ migration_state_int);
+ DCHECK_GE(static_cast<int>(MigrationState::kCompleted), migration_state_int);
+ return static_cast<MigrationState>(migration_state_int);
+}
+
+void UnifiedConsentService::SetMigrationState(MigrationState migration_state) {
+ pref_service_->SetInteger(prefs::kUnifiedConsentMigrationState,
+ static_cast<int>(migration_state));
+}
+
+void UnifiedConsentService::MigrateProfileToUnifiedConsent() {
+ DCHECK_EQ(GetMigrationState(), MigrationState::kNotInitialized);
+ DCHECK(!IsUnifiedConsentGiven());
+
+ if (!identity_manager_->HasPrimaryAccount()) {
+ RecordConsentBumpSuppressReason(ConsentBumpSuppressReason::kNotSignedIn);
+ SetMigrationState(MigrationState::kCompleted);
+ return;
+ }
+
+ if (!syncer::SyncPrefs(pref_service_).HasKeepEverythingSynced()) {
+ RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason::kSyncEverythingOff);
+ } else if (!AreAllOnByDefaultPrivacySettingsOn()) {
+ RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason::kPrivacySettingOff);
+ } else {
+ // When the user was syncing everything, and all on-by-default privacy
+ // settings were on, the consent bump should be shown.
+ pref_service_->SetBoolean(prefs::kShouldShowUnifiedConsentBump, true);
+ }
+
+ UpdateSettingsForMigration();
+}
+
+void UnifiedConsentService::UpdateSettingsForMigration() {
+ if (!IsSyncConfigurable()) {
+ SetMigrationState(MigrationState::kInProgressWaitForSyncInit);
+ return;
+ }
+
+ if (IsUnifiedConsentGiven()) {
+ // When the user opted into unified consent through the consent bump or the
+ // settings page while waiting for sync initialization, the migration is
+ // completed.
+ SetMigrationState(MigrationState::kCompleted);
+ return;
+ }
+
+ // Set URL-keyed anonymized metrics to the state it had before unified
+ // consent.
+ bool url_keyed_metrics_enabled = sync_service_->GetPreferredDataTypes().Has(
+ syncer::HISTORY_DELETE_DIRECTIVES) &&
+ !sync_service_->IsUsingSecondaryPassphrase();
+ pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ url_keyed_metrics_enabled);
+
+ // Disable the datatype user events for newly migrated users. Also set
+ // sync-everything to false, so it matches unified consent given.
+ syncer::ModelTypeSet preferred_types_without_user_events =
+ sync_service_->GetPreferredDataTypes();
+ preferred_types_without_user_events.RetainAll(syncer::UserSelectableTypes());
+ preferred_types_without_user_events.Remove(syncer::USER_EVENTS);
+ sync_service_->OnUserChoseDatatypes(false /*sync everything */,
+ preferred_types_without_user_events);
+
+ SetMigrationState(MigrationState::kCompleted);
+}
+
+bool UnifiedConsentService::AreAllNonPersonalizedServicesEnabled() {
+ for (int i = 0; i <= static_cast<int>(Service::kLast); ++i) {
+ Service service = static_cast<Service>(i);
+ if (service_client_->GetServiceState(service) == ServiceState::kDisabled)
+ return false;
+ }
+ if (!pref_service_->GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled))
+ return false;
+
+ return true;
+}
+
+bool UnifiedConsentService::AreAllOnByDefaultPrivacySettingsOn() {
+ for (auto service : {Service::kAlternateErrorPages,
+ Service::kMetricsReporting, Service::kNetworkPrediction,
+ Service::kSafeBrowsing, Service::kSearchSuggest}) {
+ if (service_client_->GetServiceState(service) == ServiceState::kDisabled)
+ return false;
+ }
+ return true;
+}
+
+bool UnifiedConsentService::IsSyncConfigurable() {
+ return sync_service_->GetState() == syncer::SyncService::State::ACTIVE;
+}
+
+void UnifiedConsentService::RecordSettingsHistogram() {
+ bool metric_recorded = false;
+
+ if (IsUnifiedConsentGiven()) {
+ RecordSettingsHistogramSample(SettingsHistogramValue::kUnifiedConsentGiven);
+ metric_recorded = true;
+ }
+ if (identity_manager_->HasPrimaryAccount() &&
+ sync_service_->GetPreferredDataTypes().Has(syncer::USER_EVENTS)) {
+ RecordSettingsHistogramSample(SettingsHistogramValue::kUserEvents);
+ metric_recorded = true;
+ }
+ metric_recorded |= RecordSettingsHistogramFromPref(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled, pref_service_,
+ SettingsHistogramValue::kUrlKeyedAnonymizedDataCollection);
+ metric_recorded |= RecordSettingsHistogramFromService(
+ service_client_.get(),
+ UnifiedConsentServiceClient::Service::kSafeBrowsingExtendedReporting,
+ SettingsHistogramValue::kSafeBrowsingExtendedReporting);
+ metric_recorded |= RecordSettingsHistogramFromService(
+ service_client_.get(), UnifiedConsentServiceClient::Service::kSpellCheck,
+ SettingsHistogramValue::kSpellCheck);
+
+ if (!metric_recorded)
+ RecordSettingsHistogramSample(SettingsHistogramValue::kNone);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/unified_consent_service.h b/chromium/components/unified_consent/unified_consent_service.h
new file mode 100644
index 00000000000..f1a85b8bf4a
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service.h
@@ -0,0 +1,168 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
+#define COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "components/unified_consent/unified_consent_service_client.h"
+#include "services/identity/public/cpp/identity_manager.h"
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class PrefChangeRegistrar;
+class PrefService;
+
+namespace syncer {
+class SyncService;
+}
+
+namespace unified_consent {
+
+using Service = UnifiedConsentServiceClient::Service;
+using ServiceState = UnifiedConsentServiceClient::ServiceState;
+
+enum class MigrationState : int {
+ kNotInitialized = 0,
+ kInProgressWaitForSyncInit = 1,
+ // Reserve space for other kInProgress* entries to be added here.
+ kCompleted = 10,
+};
+
+// Used in histograms. Do not change existing values, append new values at the
+// end.
+enum class ConsentBumpSuppressReason {
+ kNone,
+ kNotSignedIn,
+ kSyncEverythingOff,
+ kPrivacySettingOff,
+ kSettingsOptIn,
+ kUserSignedOut,
+ kSyncPaused,
+
+ kMaxValue = kSyncPaused
+};
+
+// Google services that can be toggled in user settings.
+// Used in histograms. Do not change existing values, append new values at the
+// end.
+enum class SettingsHistogramValue {
+ kNone = 0,
+ kUnifiedConsentGiven = 1,
+ kUserEvents = 2,
+ kUrlKeyedAnonymizedDataCollection = 3,
+ kSafeBrowsingExtendedReporting = 4,
+ kSpellCheck = 5,
+
+ kMaxValue = kSpellCheck
+};
+
+// A browser-context keyed service that is used to manage the user consent
+// when UnifiedConsent feature is enabled.
+class UnifiedConsentService : public KeyedService,
+ public UnifiedConsentServiceClient::Observer,
+ public identity::IdentityManager::Observer,
+ public syncer::SyncServiceObserver {
+ public:
+ UnifiedConsentService(
+ std::unique_ptr<UnifiedConsentServiceClient> service_client,
+ PrefService* pref_service,
+ identity::IdentityManager* identity_manager,
+ syncer::SyncService* sync_service);
+ ~UnifiedConsentService() override;
+
+ // Register the prefs used by this UnifiedConsentService.
+ static void RegisterPrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Rolls back changes made during migration. This method does nothing if the
+ // user hasn't migrated to unified consent yet.
+ static void RollbackIfNeeded(PrefService* user_pref_service,
+ syncer::SyncService* sync_service);
+
+ // This updates the consent pref and if |unified_consent_given| is true, all
+ // unified consent services are enabled.
+ void SetUnifiedConsentGiven(bool unified_consent_given);
+ bool IsUnifiedConsentGiven();
+
+ // Returns true if all criteria is met to show the consent bump.
+ bool ShouldShowConsentBump();
+ // Marks the consent bump as shown. Any future calls to
+ // |ShouldShowConsentBump| are guaranteed to return false.
+ void MarkConsentBumpShown();
+ // Records the consent bump suppress reason and updates the state whether the
+ // consent bump should be shown. Note: In some cases, e.g. sync paused,
+ // |ShouldShowConsentBump| will still return true.
+ void RecordConsentBumpSuppressReason(
+ ConsentBumpSuppressReason suppress_reason);
+
+ // KeyedService:
+ void Shutdown() override;
+
+ // UnifiedConsentServiceClient::Observer:
+ void OnServiceStateChanged(Service service) override;
+
+ // IdentityManager::Observer:
+ void OnPrimaryAccountCleared(
+ const AccountInfo& previous_primary_account_info) override;
+
+ private:
+ friend class UnifiedConsentServiceTest;
+
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync) override;
+
+ // Called when |prefs::kUnifiedConsentGiven| pref value changes.
+ // When set to true, it enables syncing of all data types and it enables all
+ // non-personalized services. Otherwise it does nothing.
+ void OnUnifiedConsentGivenPrefChanged();
+
+ // Enables/disables syncing everything if the sync engine is initialized.
+ void SetSyncEverythingIfPossible(bool sync_everything);
+
+ // Migration helpers.
+ MigrationState GetMigrationState();
+ void SetMigrationState(MigrationState migration_state);
+ // Called when the unified consent service is created. This sets the
+ // |kShouldShowUnifiedConsentBump| pref to true if the user is eligible and
+ // calls |UpdateSettingsForMigration| at the end.
+ void MigrateProfileToUnifiedConsent();
+ // Updates the settings preferences for the migration when the sync engine is
+ // initialized. When it is not, this function will be called again from
+ // |OnStateChanged| when the sync engine is initialized.
+ void UpdateSettingsForMigration();
+
+ // Returns true if all non-personalized services are enabled.
+ bool AreAllNonPersonalizedServicesEnabled();
+
+ // Checks if all on-by-default non-personalized services are on.
+ bool AreAllOnByDefaultPrivacySettingsOn();
+
+ // Helper that checks whether it's okay to call
+ // |SyncService::OnUserChoseDatatypes|.
+ bool IsSyncConfigurable();
+
+ // Records a sample for each bucket enabled by the user (except kNone).
+ // kNone is recorded when none of the other buckets are recorded.
+ void RecordSettingsHistogram();
+
+ std::unique_ptr<UnifiedConsentServiceClient> service_client_;
+ PrefService* pref_service_;
+ identity::IdentityManager* identity_manager_;
+ syncer::SyncService* sync_service_;
+
+ std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnifiedConsentService);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
diff --git a/chromium/components/unified_consent/unified_consent_service_client.cc b/chromium/components/unified_consent/unified_consent_service_client.cc
new file mode 100644
index 00000000000..1bd56d205eb
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service_client.cc
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/unified_consent_service_client.h"
+
+#include "base/bind.h"
+
+namespace unified_consent {
+
+UnifiedConsentServiceClient::UnifiedConsentServiceClient() {}
+UnifiedConsentServiceClient::~UnifiedConsentServiceClient() {}
+
+void UnifiedConsentServiceClient::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void UnifiedConsentServiceClient::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+void UnifiedConsentServiceClient::ObserveServicePrefChange(
+ Service service,
+ const std::string& pref_name,
+ PrefService* pref_service) {
+ service_prefs_[pref_name] = service;
+
+ // First access to the pref registrar of |pref_service| in the map
+ // automatically creates an entry for it.
+ PrefChangeRegistrar* pref_change_registrar =
+ &(pref_change_registrars_[pref_service]);
+ if (!pref_change_registrar->prefs())
+ pref_change_registrar->Init(pref_service);
+ pref_change_registrar->Add(
+ pref_name,
+ base::BindRepeating(&UnifiedConsentServiceClient::OnPrefChanged,
+ base::Unretained(this)));
+}
+
+void UnifiedConsentServiceClient::FireOnServiceStateChanged(Service service) {
+ for (auto& observer : observer_list_)
+ observer.OnServiceStateChanged(service);
+}
+
+void UnifiedConsentServiceClient::OnPrefChanged(const std::string& pref_name) {
+ DCHECK(service_prefs_.count(pref_name));
+ FireOnServiceStateChanged(service_prefs_[pref_name]);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/unified_consent_service_client.h b/chromium/components/unified_consent/unified_consent_service_client.h
new file mode 100644
index 00000000000..cb31490d0db
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service_client.h
@@ -0,0 +1,94 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
+#define COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
+
+#include <map>
+#include <string>
+
+#include "base/observer_list.h"
+#include "components/prefs/pref_change_registrar.h"
+
+class PrefService;
+
+namespace unified_consent {
+
+class UnifiedConsentServiceClient {
+ public:
+ enum class Service {
+ // Link Doctor error pages.
+ kAlternateErrorPages,
+ // Metrics reporting.
+ kMetricsReporting,
+ // Prediction of network actions.
+ kNetworkPrediction,
+ // Safe browsing.
+ kSafeBrowsing,
+ // Extended safe browsing.
+ kSafeBrowsingExtendedReporting,
+ // Search suggestions.
+ kSearchSuggest,
+ // Spell checking.
+ kSpellCheck,
+
+ // Last element of the enum, used for iteration.
+ kLast = kSpellCheck,
+ };
+
+ enum class ServiceState {
+ // The service is not supported on this platform.
+ kNotSupported,
+ // The service is supported, but disabled.
+ kDisabled,
+ // The service is enabled.
+ kEnabled
+ };
+
+ class Observer {
+ public:
+ // Called when the service state of |service| changes.
+ virtual void OnServiceStateChanged(Service service) = 0;
+ };
+
+ UnifiedConsentServiceClient();
+ virtual ~UnifiedConsentServiceClient();
+
+ // Returns the ServiceState for |service|.
+ virtual ServiceState GetServiceState(Service service) = 0;
+ // Sets |service| enabled if it is supported on this platform.
+ virtual void SetServiceEnabled(Service service, bool enabled) = 0;
+
+ // Methods to register or remove observers.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ protected:
+ // This method adds |pref_name| to the list of prefs that will be observed for
+ // changes. Whenever there's a change in the pref, all
+ // |UnifiedConsentServiceClient::Observer|s are fired.
+ void ObserveServicePrefChange(Service service,
+ const std::string& pref_name,
+ PrefService* pref_service);
+
+ // Fires |OnServiceStateChanged| on all observers.
+ void FireOnServiceStateChanged(Service service);
+
+ private:
+ // Callback for the pref change registrars.
+ void OnPrefChanged(const std::string& pref_name);
+
+ base::ObserverList<Observer, true> observer_list_;
+
+ // Matches the pref name to it's service.
+ std::map<std::string, Service> service_prefs_;
+ // Matches pref service to it's change registrar.
+ std::map<PrefService*, PrefChangeRegistrar> pref_change_registrars_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnifiedConsentServiceClient);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_CLIENT_H_
diff --git a/chromium/components/unified_consent/unified_consent_service_unittest.cc b/chromium/components/unified_consent/unified_consent_service_unittest.cc
new file mode 100644
index 00000000000..332351a1e53
--- /dev/null
+++ b/chromium/components/unified_consent/unified_consent_service_unittest.cc
@@ -0,0 +1,645 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/unified_consent/unified_consent_service.h"
+
+#include <map>
+#include <memory>
+
+#include "base/message_loop/message_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "components/autofill/core/common/autofill_pref_names.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/unified_consent/scoped_unified_consent.h"
+#include "components/unified_consent/unified_consent_service_client.h"
+#include "services/identity/public/cpp/identity_test_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unified_consent {
+namespace {
+
+class TestSyncService : public syncer::FakeSyncService {
+ public:
+ explicit TestSyncService(PrefService* pref_service)
+ : pref_service_(pref_service) {}
+
+ int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+ bool IsFirstSetupComplete() const override { return true; }
+ bool IsEngineInitialized() const override { return engine_initialized_; }
+ void AddObserver(syncer::SyncServiceObserver* observer) override {
+ observer_ = observer;
+ }
+ void OnUserChoseDatatypes(bool sync_everything,
+ syncer::ModelTypeSet chosen_types) override {
+ syncer::SyncPrefs(pref_service_).SetKeepEverythingSynced(sync_everything);
+ chosen_types_ = chosen_types;
+ }
+ syncer::ModelTypeSet GetPreferredDataTypes() const override {
+ syncer::ModelTypeSet preferred = chosen_types_;
+ // Add this for the Migration_UpdateSettings test.
+ preferred.Put(syncer::HISTORY_DELETE_DIRECTIVES);
+ return preferred;
+ }
+ bool IsUsingSecondaryPassphrase() const override {
+ return is_using_passphrase_;
+ }
+ SyncService::State GetState() const override { return state_; }
+
+ void SetEngineInitialized(bool engine_initialized) {
+ engine_initialized_ = engine_initialized;
+ }
+ void SetIsUsingPassphrase(bool using_passphrase) {
+ is_using_passphrase_ = using_passphrase;
+ }
+ void SetState(SyncService::State state) { state_ = state; }
+
+ void FireStateChanged() {
+ if (observer_)
+ observer_->OnStateChanged(this);
+ }
+
+ private:
+ syncer::SyncServiceObserver* observer_ = nullptr;
+ bool engine_initialized_ = true;
+ syncer::ModelTypeSet chosen_types_ = syncer::UserSelectableTypes();
+ SyncService::State state_ = SyncService::State::ACTIVE;
+ bool is_using_passphrase_ = false;
+ PrefService* pref_service_;
+};
+
+const char kSpellCheckDummyEnabled[] = "spell_check_dummy.enabled";
+
+class FakeUnifiedConsentServiceClient : public UnifiedConsentServiceClient {
+ public:
+ FakeUnifiedConsentServiceClient(PrefService* pref_service)
+ : pref_service_(pref_service) {
+ // When the |kSpellCheckDummyEnabled| pref is changed, all observers should
+ // be fired.
+ ObserveServicePrefChange(Service::kSpellCheck, kSpellCheckDummyEnabled,
+ pref_service);
+ }
+ ~FakeUnifiedConsentServiceClient() override = default;
+
+ // UnifiedConsentServiceClient:
+ ServiceState GetServiceState(Service service) override {
+ if (is_not_supported_[service])
+ return ServiceState::kNotSupported;
+ bool enabled;
+ // Special treatment for spell check.
+ if (service == Service::kSpellCheck) {
+ enabled = pref_service_->GetBoolean(kSpellCheckDummyEnabled);
+ } else {
+ enabled = service_enabled_[service];
+ }
+ return enabled ? ServiceState::kEnabled : ServiceState::kDisabled;
+ }
+ void SetServiceEnabled(Service service, bool enabled) override {
+ if (is_not_supported_[service])
+ return;
+ // Special treatment for spell check.
+ if (service == Service::kSpellCheck) {
+ pref_service_->SetBoolean(kSpellCheckDummyEnabled, enabled);
+ return;
+ }
+ bool should_notify_observers = service_enabled_[service] != enabled;
+ service_enabled_[service] = enabled;
+ if (should_notify_observers)
+ FireOnServiceStateChanged(service);
+ }
+
+ void SetServiceNotSupported(Service service) {
+ is_not_supported_[service] = true;
+ }
+
+ private:
+ std::map<Service, bool> service_enabled_;
+ std::map<Service, bool> is_not_supported_;
+
+ PrefService* pref_service_;
+};
+
+} // namespace
+
+class UnifiedConsentServiceTest : public testing::Test {
+ public:
+ UnifiedConsentServiceTest() : sync_service_(&pref_service_) {}
+
+ // testing::Test:
+ void SetUp() override {
+ pref_service_.registry()->RegisterBooleanPref(
+ autofill::prefs::kAutofillWalletImportEnabled, false);
+ UnifiedConsentService::RegisterPrefs(pref_service_.registry());
+ syncer::SyncPrefs::RegisterProfilePrefs(pref_service_.registry());
+ pref_service_.registry()->RegisterBooleanPref(kSpellCheckDummyEnabled,
+ false);
+ }
+
+ void TearDown() override {
+ if (consent_service_)
+ consent_service_->Shutdown();
+ }
+
+ void CreateConsentService(bool client_services_on_by_default = false) {
+ if (!scoped_unified_consent_) {
+ SetUnifiedConsentFeatureState(
+ unified_consent::UnifiedConsentFeatureState::kEnabledWithBump);
+ }
+
+ auto client =
+ std::make_unique<FakeUnifiedConsentServiceClient>(&pref_service_);
+ if (client_services_on_by_default) {
+ for (int i = 0; i <= static_cast<int>(Service::kLast); ++i) {
+ Service service = static_cast<Service>(i);
+ client->SetServiceEnabled(service, true);
+ }
+ pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ true);
+ }
+ consent_service_ = std::make_unique<UnifiedConsentService>(
+ std::move(client), &pref_service_,
+ identity_test_environment_.identity_manager(), &sync_service_);
+ service_client_ = (FakeUnifiedConsentServiceClient*)
+ consent_service_->service_client_.get();
+ }
+
+ void SetUnifiedConsentFeatureState(
+ unified_consent::UnifiedConsentFeatureState feature_state) {
+ // First reset |scoped_unified_consent_| to nullptr in case it was set
+ // before and then initialize it with the new value. This makes sure that
+ // the old scoped object is deleted before the new one is created.
+ scoped_unified_consent_.reset();
+ scoped_unified_consent_.reset(
+ new unified_consent::ScopedUnifiedConsent(feature_state));
+ }
+
+ bool AreAllNonPersonalizedServicesEnabled() {
+ return consent_service_->AreAllNonPersonalizedServicesEnabled();
+ }
+
+ bool AreAllOnByDefaultPrivacySettingsOn() {
+ return consent_service_->AreAllOnByDefaultPrivacySettingsOn();
+ }
+
+ unified_consent::MigrationState GetMigrationState() {
+ int migration_state_int =
+ pref_service_.GetInteger(prefs::kUnifiedConsentMigrationState);
+ return static_cast<unified_consent::MigrationState>(migration_state_int);
+ }
+
+ protected:
+ base::MessageLoop message_loop_;
+ sync_preferences::TestingPrefServiceSyncable pref_service_;
+ identity::IdentityTestEnvironment identity_test_environment_;
+ TestSyncService sync_service_;
+ std::unique_ptr<UnifiedConsentService> consent_service_;
+ FakeUnifiedConsentServiceClient* service_client_ = nullptr;
+
+ std::unique_ptr<ScopedUnifiedConsent> scoped_unified_consent_;
+};
+
+TEST_F(UnifiedConsentServiceTest, DefaultValuesWhenSignedOut) {
+ CreateConsentService();
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all non-personaized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Disable unified consent does not disable any of the non-personalized
+ // features.
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, false);
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent_WithUnsupportedService) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ service_client_->SetServiceNotSupported(Service::kSpellCheck);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSpellCheck),
+ ServiceState::kNotSupported);
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all supported non-personalized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Disable unified consent does not disable any of the supported
+ // non-personalized features.
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, false);
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent_SyncNotActive) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ sync_service_.OnUserChoseDatatypes(false, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(consent_service_->IsUnifiedConsentGiven());
+
+ // Make sure sync is not active.
+ sync_service_.SetEngineInitialized(false);
+ EXPECT_FALSE(sync_service_.IsEngineInitialized());
+ sync_service_.SetState(syncer::SyncService::State::INITIALIZING);
+ EXPECT_NE(sync_service_.GetState(), syncer::SyncService::State::ACTIVE);
+
+ // Opt into unified consent.
+ consent_service_->SetUnifiedConsentGiven(true);
+ EXPECT_TRUE(consent_service_->IsUnifiedConsentGiven());
+
+ // Couldn't sync everything because sync is not active.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ // Initalize sync engine and therefore activate sync.
+ sync_service_.SetEngineInitialized(true);
+ sync_service_.SetState(syncer::SyncService::State::ACTIVE);
+ EXPECT_EQ(sync_service_.GetState(), syncer::SyncService::State::ACTIVE);
+ sync_service_.FireStateChanged();
+
+ // UnifiedConsentService starts syncing everything.
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+}
+
+TEST_F(UnifiedConsentServiceTest, EnableUnfiedConsent_WithCustomPassphrase) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(consent_service_->IsUnifiedConsentGiven());
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent.
+ consent_service_->SetUnifiedConsentGiven(true);
+ EXPECT_TRUE(consent_service_->IsUnifiedConsentGiven());
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Set custom passphrase.
+ sync_service_.SetIsUsingPassphrase(true);
+ sync_service_.FireStateChanged();
+
+ // Setting a custom passphrase forces off unified consent given.
+ EXPECT_FALSE(consent_service_->IsUnifiedConsentGiven());
+}
+
+// Test whether unified consent is disabled when any of its dependent services
+// gets disabled.
+TEST_F(UnifiedConsentServiceTest, DisableUnfiedConsentWhenServiceIsDisabled) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all supported non-personalized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Disabling child service disables unified consent.
+ pref_service_.SetBoolean(kSpellCheckDummyEnabled, false);
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+}
+
+// Test whether unified consent is disabled when any of its dependent services
+// gets disabled before startup.
+TEST_F(UnifiedConsentServiceTest,
+ DisableUnfiedConsentWhenServiceIsDisabled_OnStartup) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+
+ // Enable Unified Consent enables all supported non-personalized features
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Simulate shutdown.
+ consent_service_->Shutdown();
+ consent_service_.reset();
+
+ // Disable child service.
+ pref_service_.SetBoolean(kSpellCheckDummyEnabled, false);
+
+ // Unified Consent is disabled during creation of the consent service because
+ // not all non-personalized services are enabled.
+ CreateConsentService();
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(UnifiedConsentServiceTest, Migration_SyncingEverythingAndAllServicesOn) {
+ base::HistogramTester histogram_tester;
+
+ // Create inconsistent state.
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ sync_service_.SetState(
+ syncer::SyncService::State::PENDING_DESIRED_CONFIGURATION);
+ EXPECT_FALSE(sync_service_.IsSyncActive());
+
+ CreateConsentService(true /* client services on by default */);
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+ // After the creation of the consent service, the profile started to migrate
+ // (but waiting for sync init) and |ShouldShowConsentBump| should return true.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(GetMigrationState(),
+ unified_consent::MigrationState::kInProgressWaitForSyncInit);
+ EXPECT_TRUE(consent_service_->ShouldShowConsentBump());
+ // Sync-everything is still on because sync is not active yet.
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+
+ // When sync is active, the migration should continue and finish.
+ sync_service_.SetState(syncer::SyncService::State::ACTIVE);
+ sync_service_.FireStateChanged();
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ // No metric for the consent bump suppress reason should have been recorded at
+ // this point.
+ histogram_tester.ExpectTotalCount("UnifiedConsent.ConsentBump.SuppressReason",
+ 0);
+
+ // When the user signs out, the migration state changes to completed and the
+ // consent bump doesn't need to be shown anymore.
+ identity_test_environment_.ClearPrimaryAccount();
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ EXPECT_FALSE(consent_service_->ShouldShowConsentBump());
+ // A metric for the consent bump suppress reason should have been recorded at
+ // this point.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kUserSignedOut, 1);
+}
+
+TEST_F(UnifiedConsentServiceTest, Migration_SyncingEverythingAndServicesOff) {
+ base::HistogramTester histogram_tester;
+
+ // Create inconsistent state.
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_TRUE(sync_service_.IsSyncActive());
+
+ CreateConsentService();
+ EXPECT_FALSE(AreAllOnByDefaultPrivacySettingsOn());
+ // After the creation of the consent service, the profile is migrated and
+ // |ShouldShowConsentBump| should return false.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ EXPECT_FALSE(consent_service_->ShouldShowConsentBump());
+
+ // A metric for the consent bump suppress reason should have been recorded at
+ // this point.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kPrivacySettingOff, 1);
+}
+#endif // !defined(OS_CHROMEOS)
+
+TEST_F(UnifiedConsentServiceTest, Migration_NotSyncingEverything) {
+ base::HistogramTester histogram_tester;
+
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(false, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ CreateConsentService();
+ // When the user is not syncing everything the migration is completed after
+ // the creation of the consent service.
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ // The suppress reason for not showing the consent bump should be recorded.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kSyncEverythingOff, 1);
+}
+
+TEST_F(UnifiedConsentServiceTest, Migration_UpdateSettings) {
+ // Create user that syncs everything
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_TRUE(sync_service_.IsSyncActive());
+ EXPECT_TRUE(sync_service_.GetPreferredDataTypes().Has(syncer::USER_EVENTS));
+ // Url keyed data collection is off before the migration.
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+
+ CreateConsentService();
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ // During the migration USER_EVENTS is disabled and Url keyed data collection
+ // is enabled.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(sync_service_.GetPreferredDataTypes().Has(syncer::USER_EVENTS));
+ EXPECT_TRUE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+}
+
+#if !defined(OS_CHROMEOS)
+TEST_F(UnifiedConsentServiceTest, ClearPrimaryAccountDisablesSomeServices) {
+ CreateConsentService();
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+
+ // Precondition: Enable unified consent.
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ EXPECT_TRUE(AreAllNonPersonalizedServicesEnabled());
+
+ // Clearing primary account revokes unfied consent and a couple of other
+ // non-personalized services.
+ identity_test_environment_.ClearPrimaryAccount();
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_FALSE(AreAllNonPersonalizedServicesEnabled());
+ EXPECT_FALSE(pref_service_.GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled));
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSpellCheck),
+ ServiceState::kDisabled);
+ EXPECT_EQ(
+ service_client_->GetServiceState(Service::kSafeBrowsingExtendedReporting),
+ ServiceState::kDisabled);
+
+ // Consent is not revoked for the following services.
+ EXPECT_EQ(service_client_->GetServiceState(Service::kAlternateErrorPages),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kMetricsReporting),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kNetworkPrediction),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSearchSuggest),
+ ServiceState::kEnabled);
+ EXPECT_EQ(service_client_->GetServiceState(Service::kSafeBrowsing),
+ ServiceState::kEnabled);
+}
+
+TEST_F(UnifiedConsentServiceTest, Migration_NotSignedIn) {
+ base::HistogramTester histogram_tester;
+
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+
+ CreateConsentService();
+ // Since there were not inconsistencies, the migration is completed after the
+ // creation of the consent service.
+ EXPECT_EQ(GetMigrationState(), unified_consent::MigrationState::kCompleted);
+ // The suppress reason for not showing the consent bump should be recorded.
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.ConsentBump.SuppressReason",
+ unified_consent::ConsentBumpSuppressReason::kNotSignedIn, 1);
+}
+#endif // !defined(OS_CHROMEOS)
+
+TEST_F(UnifiedConsentServiceTest, Rollback_WasSyncingEverything) {
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ sync_service_.OnUserChoseDatatypes(true, syncer::UserSelectableTypes());
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+
+ // Migrate
+ CreateConsentService(true /* client services on by default */);
+ // Check expectations after migration.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kCompleted, GetMigrationState());
+ EXPECT_TRUE(consent_service_->ShouldShowConsentBump());
+
+ consent_service_->Shutdown();
+ consent_service_.reset();
+ SetUnifiedConsentFeatureState(UnifiedConsentFeatureState::kDisabled);
+
+ // Rollback
+ UnifiedConsentService::RollbackIfNeeded(&pref_service_, &sync_service_);
+ // Unified consent prefs should be cleared.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kNotInitialized,
+ GetMigrationState());
+ // Sync everything should be back on.
+ EXPECT_TRUE(sync_prefs.HasKeepEverythingSynced());
+
+ // Run until idle so the RollbackHelper is deleted.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(UnifiedConsentServiceTest, Rollback_WasNotSyncingEverything) {
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ syncer::SyncPrefs sync_prefs(&pref_service_);
+ syncer::ModelTypeSet chosen_data_types = syncer::UserSelectableTypes();
+ chosen_data_types.Remove(syncer::BOOKMARKS);
+ sync_service_.OnUserChoseDatatypes(false, chosen_data_types);
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(sync_service_.GetPreferredDataTypes().HasAll(
+ syncer::UserSelectableTypes()));
+
+ // Migrate
+ CreateConsentService();
+ // Check expectations after migration.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kCompleted, GetMigrationState());
+
+ consent_service_->Shutdown();
+ consent_service_.reset();
+
+ // Rollback
+ UnifiedConsentService::RollbackIfNeeded(&pref_service_, &sync_service_);
+ // Unified consent prefs should be cleared.
+ EXPECT_FALSE(pref_service_.GetBoolean(prefs::kUnifiedConsentGiven));
+ EXPECT_EQ(unified_consent::MigrationState::kNotInitialized,
+ GetMigrationState());
+
+ // Sync everything should be off because not all user types were on.
+ EXPECT_FALSE(sync_prefs.HasKeepEverythingSynced());
+
+ // Run until idle so the RollbackHelper is deleted.
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(UnifiedConsentServiceTest, SettingsHistogram_None) {
+ base::HistogramTester histogram_tester;
+ // Disable all services.
+ sync_service_.OnUserChoseDatatypes(false, syncer::ModelTypeSet());
+ CreateConsentService();
+
+ histogram_tester.ExpectUniqueSample(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kNone, 1);
+}
+
+TEST_F(UnifiedConsentServiceTest, SettingsHistogram_UnifiedConsentGiven) {
+ base::HistogramTester histogram_tester;
+ // Unified consent is given.
+ identity_test_environment_.SetPrimaryAccount("testaccount");
+ pref_service_.SetInteger(
+ prefs::kUnifiedConsentMigrationState,
+ static_cast<int>(unified_consent::MigrationState::kCompleted));
+ pref_service_.SetBoolean(prefs::kUnifiedConsentGiven, true);
+ CreateConsentService(true);
+
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kNone, 0);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kUnifiedConsentGiven, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kUserEvents, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kUrlKeyedAnonymizedDataCollection, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kSafeBrowsingExtendedReporting, 1);
+ histogram_tester.ExpectBucketCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kSpellCheck, 1);
+ histogram_tester.ExpectTotalCount(
+ "UnifiedConsent.SyncAndGoogleServicesSettings", 5);
+}
+
+TEST_F(UnifiedConsentServiceTest, SettingsHistogram_NoUnifiedConsentGiven) {
+ base::HistogramTester histogram_tester;
+ // Unified consent is not given. Only spellcheck is enabled.
+ pref_service_.SetBoolean(kSpellCheckDummyEnabled, true);
+ CreateConsentService();
+
+ // kUserEvents should have no sample even though the sync preference is set,
+ // because the user is not signed in.
+ histogram_tester.ExpectUniqueSample(
+ "UnifiedConsent.SyncAndGoogleServicesSettings",
+ SettingsHistogramValue::kSpellCheck, 1);
+}
+
+} // namespace unified_consent
diff --git a/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc
new file mode 100644
index 00000000000..17f2e88e7a2
--- /dev/null
+++ b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.cc
@@ -0,0 +1,191 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// 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 "base/bind.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_service.h"
+#include "components/sync/driver/sync_service_observer.h"
+#include "components/sync/driver/sync_service_utils.h"
+#include "components/unified_consent/pref_names.h"
+
+#include <map>
+#include <set>
+
+namespace unified_consent {
+
+namespace {
+
+class PrefBasedUrlKeyedDataCollectionConsentHelper
+ : public UrlKeyedDataCollectionConsentHelper {
+ public:
+ explicit PrefBasedUrlKeyedDataCollectionConsentHelper(
+ PrefService* pref_service);
+ ~PrefBasedUrlKeyedDataCollectionConsentHelper() override = default;
+
+ // UrlKeyedDataCollectionConsentHelper:
+ bool IsEnabled() override;
+
+ private:
+ void OnPrefChanged();
+ PrefService* pref_service_; // weak (must outlive this)
+ PrefChangeRegistrar pref_change_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrefBasedUrlKeyedDataCollectionConsentHelper);
+};
+
+class SyncBasedUrlKeyedDataCollectionConsentHelper
+ : public UrlKeyedDataCollectionConsentHelper,
+ syncer::SyncServiceObserver {
+ public:
+ SyncBasedUrlKeyedDataCollectionConsentHelper(
+ syncer::SyncService* sync_service,
+ std::set<syncer::ModelType> sync_data_types);
+ ~SyncBasedUrlKeyedDataCollectionConsentHelper() override;
+
+ // UrlKeyedDataCollectionConsentHelper:
+ bool IsEnabled() override;
+
+ // syncer::SyncServiceObserver:
+ void OnStateChanged(syncer::SyncService* sync) override;
+ void OnSyncShutdown(syncer::SyncService* sync) override;
+
+ private:
+ void UpdateSyncDataTypeStates();
+
+ syncer::SyncService* sync_service_;
+ std::map<syncer::ModelType, syncer::UploadState> sync_data_type_states_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncBasedUrlKeyedDataCollectionConsentHelper);
+};
+
+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)));
+}
+
+bool PrefBasedUrlKeyedDataCollectionConsentHelper::IsEnabled() {
+ return pref_service_->GetBoolean(
+ prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
+}
+
+void PrefBasedUrlKeyedDataCollectionConsentHelper::OnPrefChanged() {
+ FireOnStateChanged();
+}
+
+SyncBasedUrlKeyedDataCollectionConsentHelper::
+ SyncBasedUrlKeyedDataCollectionConsentHelper(
+ syncer::SyncService* sync_service,
+ std::set<syncer::ModelType> sync_data_types)
+ : 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);
+}
+
+bool SyncBasedUrlKeyedDataCollectionConsentHelper::IsEnabled() {
+ for (const auto& sync_data_type_states : sync_data_type_states_) {
+ if (sync_data_type_states.second != syncer::UploadState::ACTIVE)
+ return false;
+ }
+ return true;
+}
+
+void SyncBasedUrlKeyedDataCollectionConsentHelper::OnStateChanged(
+ syncer::SyncService* sync_service) {
+ DCHECK_EQ(sync_service_, sync_service);
+ bool enabled_before_state_updated = IsEnabled();
+ UpdateSyncDataTypeStates();
+ if (enabled_before_state_updated != IsEnabled())
+ FireOnStateChanged();
+}
+
+void SyncBasedUrlKeyedDataCollectionConsentHelper::OnSyncShutdown(
+ syncer::SyncService* sync_service) {
+ DCHECK_EQ(sync_service_, sync_service);
+ sync_service_->RemoveObserver(this);
+ sync_service_ = nullptr;
+}
+
+void SyncBasedUrlKeyedDataCollectionConsentHelper::UpdateSyncDataTypeStates() {
+ for (auto iter = sync_data_type_states_.begin();
+ iter != sync_data_type_states_.end(); ++iter) {
+ iter->second = syncer::GetUploadToGoogleState(sync_service_, iter->first);
+ }
+}
+
+} // namespace
+
+UrlKeyedDataCollectionConsentHelper::UrlKeyedDataCollectionConsentHelper() =
+ default;
+UrlKeyedDataCollectionConsentHelper::~UrlKeyedDataCollectionConsentHelper() =
+ default;
+
+// static
+std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+UrlKeyedDataCollectionConsentHelper::NewAnonymizedDataCollectionConsentHelper(
+ bool is_unified_consent_enabled,
+ PrefService* pref_service,
+ syncer::SyncService* sync_service) {
+ if (is_unified_consent_enabled) {
+ return std::make_unique<PrefBasedUrlKeyedDataCollectionConsentHelper>(
+ pref_service);
+ }
+
+ return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
+ sync_service, std::set<syncer::ModelType>(
+ {syncer::ModelType::HISTORY_DELETE_DIRECTIVES}));
+}
+
+// static
+std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+UrlKeyedDataCollectionConsentHelper::NewPersonalizedDataCollectionConsentHelper(
+ bool is_unified_consent_enabled,
+ syncer::SyncService* sync_service) {
+ if (is_unified_consent_enabled) {
+ return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
+ sync_service, std::set<syncer::ModelType>(
+ {syncer::ModelType::HISTORY_DELETE_DIRECTIVES,
+ syncer::ModelType::USER_EVENTS}));
+ } else {
+ return std::make_unique<SyncBasedUrlKeyedDataCollectionConsentHelper>(
+ sync_service, std::set<syncer::ModelType>(
+ {syncer::ModelType::HISTORY_DELETE_DIRECTIVES}));
+ }
+}
+
+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
diff --git a/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h
new file mode 100644
index 00000000000..f4d8486ab18
--- /dev/null
+++ b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper.h
@@ -0,0 +1,88 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
+#define COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
+
+#include <memory>
+
+#include "base/observer_list.h"
+
+class PrefService;
+namespace syncer {
+class SyncService;
+}
+
+namespace unified_consent {
+
+// Helper class that allows clients to check whether the user has consented
+// for URL-keyed data collection.
+class UrlKeyedDataCollectionConsentHelper {
+ public:
+ class Observer {
+ public:
+ // Called when the state of the URL-keyed data collection changes.
+ virtual void OnUrlKeyedDataCollectionConsentStateChanged(
+ UrlKeyedDataCollectionConsentHelper* consent_helper) = 0;
+ };
+
+ // Creates a new |UrlKeyedDataCollectionConsentHelper| instance that checks
+ // whether *anonymized* data collection is enabled. This should be used when
+ // the client needs to check whether the user has granted consent for
+ // *anonymized* URL-keyed data collection.
+ //
+ // Implementation-wise we distinguish the following cases:
+ // 1. If |is_unified_consent_enabled| true, then the instance is backed by
+ // |pref_service|. Url-keyed data collection is enabled if the preference
+ // |prefs::kUrlKeyedAnonymizedDataCollectionEnabled| is set to true.
+ //
+ // 2. If |is_unified_consent_enabled| is false, then the instance is backed by
+ // the sync service. Url-keyed data collection is enabled if sync is active
+ // and if sync history is enabled.
+ //
+ // Note: |pref_service| must outlive the retuned instance.
+ static std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+ NewAnonymizedDataCollectionConsentHelper(bool is_unified_consent_enabled,
+ PrefService* pref_service,
+ syncer::SyncService* sync_service);
+
+ // Creates a new |UrlKeyedDataCollectionConsentHelper| instance that checks
+ // whether *personalized* data collection is enabled. This should be used when
+ // the client needs to check whether the user has granted consent for
+ // URL-keyed data collection keyed by their Google account.
+ //
+ // Implementation-wise we distinguish the following cases:
+ // 1. If |is_unified_consent_enabled| is true then URL-keyed data collection
+ // is enabled if sync is active and if sync event logger is enabled.
+ // 2. If |is_unified_consent_enabled| is false then URL-keyed data collection
+ // is enabled if sync is active and if sync history is enabled.
+ static std::unique_ptr<UrlKeyedDataCollectionConsentHelper>
+ NewPersonalizedDataCollectionConsentHelper(bool is_unified_consent_enabled,
+ syncer::SyncService* sync_service);
+
+ virtual ~UrlKeyedDataCollectionConsentHelper();
+
+ // Returns true if the user has consented for URL keyed anonymized data
+ // collection.
+ virtual bool IsEnabled() = 0;
+
+ // Methods to register or remove observers.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ protected:
+ UrlKeyedDataCollectionConsentHelper();
+
+ // Fires |OnUrlKeyedDataCollectionConsentStateChanged| on all the observers.
+ void FireOnStateChanged();
+
+ private:
+ base::ObserverList<Observer, true> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(UrlKeyedDataCollectionConsentHelper);
+};
+
+} // namespace unified_consent
+
+#endif // COMPONENTS_UNIFIED_CONSENT_URL_KEYED_DATA_COLLECTION_CONSENT_HELPER_H_
diff --git a/chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
new file mode 100644
index 00000000000..6359b80f460
--- /dev/null
+++ b/chromium/components/unified_consent/url_keyed_data_collection_consent_helper_unittest.cc
@@ -0,0 +1,220 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// 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 <vector>
+
+#include "components/sync/driver/fake_sync_service.h"
+#include "components/sync/engine/cycle/sync_cycle_snapshot.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "components/unified_consent/pref_names.h"
+#include "components/unified_consent/unified_consent_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace unified_consent {
+namespace {
+
+class TestSyncService : public syncer::FakeSyncService {
+ public:
+ void set_sync_initialized(bool sync_initialized) {
+ sync_initialized_ = sync_initialized;
+ }
+ void AddActiveDataType(syncer::ModelType type) {
+ sync_active_data_types_.Put(type);
+ }
+ void ClearActiveDataTypes() { sync_active_data_types_.Clear(); }
+ void FireOnStateChangeOnAllObservers() {
+ for (auto& observer : observers_)
+ observer.OnStateChanged(this);
+ }
+
+ // syncer::FakeSyncService:
+ int GetDisableReasons() const override { return DISABLE_REASON_NONE; }
+ syncer::ModelTypeSet GetPreferredDataTypes() const override {
+ return syncer::ModelTypeSet(syncer::ModelType::HISTORY_DELETE_DIRECTIVES,
+ syncer::ModelType::USER_EVENTS,
+ syncer::ModelType::EXTENSIONS);
+ }
+ bool IsFirstSetupComplete() const override { return true; }
+ bool IsEngineInitialized() const override { return true; }
+
+ syncer::SyncCycleSnapshot GetLastCycleSnapshot() const override {
+ if (!sync_initialized_)
+ return syncer::SyncCycleSnapshot();
+ return syncer::SyncCycleSnapshot(
+ syncer::ModelNeutralState(), syncer::ProgressMarkerMap(), false, 5, 2,
+ 7, false, 0, base::Time::Now(), base::Time::Now(),
+ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
+ std::vector<int>(syncer::MODEL_TYPE_COUNT, 0),
+ sync_pb::SyncEnums::UNKNOWN_ORIGIN,
+ /*short_poll_interval=*/base::TimeDelta::FromMinutes(30),
+ /*long_poll_interval=*/base::TimeDelta::FromMinutes(180),
+ /*has_remaining_local_changes=*/false);
+ }
+
+ syncer::ModelTypeSet GetActiveDataTypes() const override {
+ return sync_active_data_types_;
+ }
+
+ void AddObserver(syncer::SyncServiceObserver* observer) override {
+ observers_.AddObserver(observer);
+ }
+ void RemoveObserver(syncer::SyncServiceObserver* observer) override {
+ observers_.RemoveObserver(observer);
+ }
+
+ private:
+ bool sync_initialized_ = false;
+ syncer::ModelTypeSet sync_active_data_types_;
+ base::ObserverList<syncer::SyncServiceObserver> observers_;
+};
+
+class UrlKeyedDataCollectionConsentHelperTest
+ : public testing::Test,
+ public UrlKeyedDataCollectionConsentHelper::Observer {
+ public:
+ // testing::Test:
+ void SetUp() override {
+ UnifiedConsentService::RegisterPrefs(pref_service_.registry());
+ }
+
+ void OnUrlKeyedDataCollectionConsentStateChanged(
+ UrlKeyedDataCollectionConsentHelper* consent_helper) override {
+ state_changed_notifications.push_back(consent_helper->IsEnabled());
+ }
+
+ protected:
+ sync_preferences::TestingPrefServiceSyncable pref_service_;
+ std::vector<bool> state_changed_notifications;
+ TestSyncService sync_service_;
+};
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ AnonymizedDataCollection_UnifiedConsentEnabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(true, &pref_service_,
+ &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ true);
+ EXPECT_TRUE(helper->IsEnabled());
+ ASSERT_EQ(1U, state_changed_notifications.size());
+ EXPECT_TRUE(state_changed_notifications[0]);
+
+ state_changed_notifications.clear();
+ pref_service_.SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
+ false);
+ EXPECT_FALSE(helper->IsEnabled());
+ ASSERT_EQ(1U, state_changed_notifications.size());
+ EXPECT_FALSE(state_changed_notifications[0]);
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ AnonymizedDataCollection_UnifiedConsentDisabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(false, &pref_service_,
+ &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ sync_service_.set_sync_initialized(true);
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_TRUE(helper->IsEnabled());
+ EXPECT_EQ(1U, state_changed_notifications.size());
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ AnonymizedDataCollection_UnifiedConsentDisabled_NullSyncService) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewAnonymizedDataCollectionConsentHelper(
+ false /* is_unified_consent_enabled */, &pref_service_,
+ nullptr /* sync_service */);
+ EXPECT_FALSE(helper->IsEnabled());
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ PersonalizeddDataCollection_UnifiedConsentEnabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(true, &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+ sync_service_.set_sync_initialized(true);
+
+ // Peronalized data collection is disabled when only USER_EVENTS are enabled.
+ sync_service_.AddActiveDataType(syncer::ModelType::USER_EVENTS);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ // Peronalized data collection is disabled when only HISTORY_DELETE_DIRECTIVES
+ // are enabled.
+ sync_service_.ClearActiveDataTypes();
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ // Personalized data collection is enabled iff USER_EVENTS and
+ // HISTORY_DELETE_DIRECTIVES are enabled.
+ sync_service_.ClearActiveDataTypes();
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.AddActiveDataType(syncer::ModelType::USER_EVENTS);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_TRUE(helper->IsEnabled());
+ EXPECT_EQ(1U, state_changed_notifications.size());
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ PersonalizedDataCollection_UnifiedConsentDisabled) {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(false, &sync_service_);
+ helper->AddObserver(this);
+ EXPECT_FALSE(helper->IsEnabled());
+ EXPECT_TRUE(state_changed_notifications.empty());
+
+ sync_service_.set_sync_initialized(true);
+ sync_service_.AddActiveDataType(syncer::ModelType::HISTORY_DELETE_DIRECTIVES);
+ sync_service_.FireOnStateChangeOnAllObservers();
+ EXPECT_TRUE(helper->IsEnabled());
+ EXPECT_EQ(1U, state_changed_notifications.size());
+ helper->RemoveObserver(this);
+}
+
+TEST_F(UrlKeyedDataCollectionConsentHelperTest,
+ PersonalizedDataCollection_NullSyncService) {
+ {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(
+ false /* is_unified_consent_enabled */,
+ nullptr /* sync_service */);
+ EXPECT_FALSE(helper->IsEnabled());
+ }
+ {
+ std::unique_ptr<UrlKeyedDataCollectionConsentHelper> helper =
+ UrlKeyedDataCollectionConsentHelper::
+ NewPersonalizedDataCollectionConsentHelper(
+ true /* is_unified_consent_enabled */,
+ nullptr /* sync_service */);
+ EXPECT_FALSE(helper->IsEnabled());
+ }
+}
+
+} // namespace
+} // namespace unified_consent