Commit 7969bbd3 authored by Thomas Tangl's avatar Thomas Tangl Committed by Commit Bot

[unified-consent] Revoke unified consent when a service is disabled

When a unified consent service is disabled while unified consent is
given, unified consent is revoked.

This can happen when a synced preference is turned off on another
machine or MetricsReporting is disabled on another profile.

Bug: 867480
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I2dfe0127a86ccb718f4f6414f42c7c22eb8dc117
Reviewed-on: https://chromium-review.googlesource.com/1151324
Commit-Queue: Thomas Tangl <tangltom@chromium.org>
Reviewed-by: default avatarMihai Sardarescu <msarda@chromium.org>
Reviewed-by: default avatarRobert Kaplow (slow) <rkaplow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#580190}
parent f85f820a
......@@ -139,6 +139,7 @@ class ChromeMetricsServiceAccessor : public metrics::MetricsServiceAccessor {
friend class ChromeMetricsServiceClient;
friend class ChromePasswordManagerClient;
friend class NavigationMetricsRecorder;
friend class ChromeUnifiedConsentServiceClient;
// Testing related friends.
friend class MetricsReportingStateTest;
......
......@@ -4,49 +4,101 @@
#include "chrome/browser/unified_consent/chrome_unified_consent_service_client.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/metrics/metrics_reporting_state.h"
#include "chrome/browser/net/prediction_options.h"
#include "chrome/common/pref_names.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/common/safe_browsing_prefs.h"
#include "components/spellcheck/browser/pref_names.h"
ChromeUnifiedConsentServiceClient::ChromeUnifiedConsentServiceClient(
PrefService* pref_service)
: pref_service_(pref_service) {}
void ChromeUnifiedConsentServiceClient::SetAlternateErrorPagesEnabled(
bool enabled) {
pref_service_->SetBoolean(prefs::kAlternateErrorPagesEnabled, enabled);
}
void ChromeUnifiedConsentServiceClient::SetMetricsReportingEnabled(
bool enabled) {
ChangeMetricsReportingState(enabled);
}
void ChromeUnifiedConsentServiceClient::SetSearchSuggestEnabled(bool enabled) {
pref_service_->SetBoolean(prefs::kSearchSuggestEnabled, enabled);
}
void ChromeUnifiedConsentServiceClient::SetSafeBrowsingEnabled(bool enabled) {
pref_service_->SetBoolean(prefs::kSafeBrowsingEnabled, enabled);
: pref_service_(pref_service) {
DCHECK(pref_service_);
ObserveServicePrefChange(Service::kAlternateErrorPages,
prefs::kAlternateErrorPagesEnabled, pref_service_);
ObserveServicePrefChange(Service::kMetricsReporting,
metrics::prefs::kMetricsReportingEnabled,
g_browser_process->local_state());
ObserveServicePrefChange(Service::kNetworkPrediction,
prefs::kNetworkPredictionOptions, pref_service_);
ObserveServicePrefChange(Service::kSafeBrowsing, prefs::kSafeBrowsingEnabled,
pref_service_);
ObserveServicePrefChange(
Service::kSafeBrowsingExtendedReporting,
safe_browsing::GetExtendedReportingPrefName(*pref_service_),
pref_service_);
ObserveServicePrefChange(Service::kSearchSuggest,
prefs::kSearchSuggestEnabled, pref_service_);
ObserveServicePrefChange(Service::kSpellCheck,
spellcheck::prefs::kSpellCheckUseSpellingService,
pref_service_);
}
void ChromeUnifiedConsentServiceClient::SetSafeBrowsingExtendedReportingEnabled(
bool enabled) {
safe_browsing::SetExtendedReportingPref(pref_service_, enabled);
}
ChromeUnifiedConsentServiceClient::~ChromeUnifiedConsentServiceClient() {}
void ChromeUnifiedConsentServiceClient::SetNetworkPredictionEnabled(
bool enabled) {
pref_service_->SetInteger(prefs::kNetworkPredictionOptions,
enabled
? chrome_browser_net::NETWORK_PREDICTION_DEFAULT
: chrome_browser_net::NETWORK_PREDICTION_NEVER);
ChromeUnifiedConsentServiceClient::ServiceState
ChromeUnifiedConsentServiceClient::GetServiceState(Service service) {
bool enabled;
switch (service) {
case Service::kAlternateErrorPages:
enabled = pref_service_->GetBoolean(prefs::kAlternateErrorPagesEnabled);
break;
case Service::kMetricsReporting:
enabled =
ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled();
break;
case Service::kNetworkPrediction:
enabled = pref_service_->GetInteger(prefs::kNetworkPredictionOptions) ==
chrome_browser_net::NETWORK_PREDICTION_DEFAULT;
break;
case Service::kSafeBrowsing:
enabled = pref_service_->GetBoolean(prefs::kSafeBrowsingEnabled);
break;
case Service::kSafeBrowsingExtendedReporting:
enabled = safe_browsing::IsExtendedReportingEnabled(*pref_service_);
break;
case Service::kSearchSuggest:
enabled = pref_service_->GetBoolean(prefs::kSearchSuggestEnabled);
break;
case Service::kSpellCheck:
enabled = pref_service_->GetBoolean(
spellcheck::prefs::kSpellCheckUseSpellingService);
break;
}
return enabled ? ServiceState::kEnabled : ServiceState::kDisabled;
}
void ChromeUnifiedConsentServiceClient::SetSpellCheckEnabled(bool enabled) {
pref_service_->SetBoolean(spellcheck::prefs::kSpellCheckUseSpellingService,
enabled);
void ChromeUnifiedConsentServiceClient::SetServiceEnabled(Service service,
bool enabled) {
switch (service) {
case Service::kAlternateErrorPages:
pref_service_->SetBoolean(prefs::kAlternateErrorPagesEnabled, enabled);
break;
case Service::kMetricsReporting:
ChangeMetricsReportingState(enabled);
break;
case Service::kNetworkPrediction:
pref_service_->SetInteger(
prefs::kNetworkPredictionOptions,
enabled ? chrome_browser_net::NETWORK_PREDICTION_DEFAULT
: chrome_browser_net::NETWORK_PREDICTION_NEVER);
break;
case Service::kSafeBrowsing:
pref_service_->SetBoolean(prefs::kSafeBrowsingEnabled, enabled);
break;
case Service::kSafeBrowsingExtendedReporting:
safe_browsing::SetExtendedReportingPref(pref_service_, enabled);
break;
case Service::kSearchSuggest:
pref_service_->SetBoolean(prefs::kSearchSuggestEnabled, enabled);
break;
case Service::kSpellCheck:
pref_service_->SetBoolean(
spellcheck::prefs::kSpellCheckUseSpellingService, enabled);
break;
}
}
......@@ -14,15 +14,11 @@ class ChromeUnifiedConsentServiceClient
: public unified_consent::UnifiedConsentServiceClient {
public:
explicit ChromeUnifiedConsentServiceClient(PrefService* pref_service);
~ChromeUnifiedConsentServiceClient() override = default;
void SetAlternateErrorPagesEnabled(bool enabled) override;
void SetMetricsReportingEnabled(bool enabled) override;
void SetSearchSuggestEnabled(bool enabled) override;
void SetSafeBrowsingEnabled(bool enabled) override;
void SetSafeBrowsingExtendedReportingEnabled(bool enabled) override;
void SetNetworkPredictionEnabled(bool enabled) override;
void SetSpellCheckEnabled(bool enabled) override;
~ChromeUnifiedConsentServiceClient() override;
// unified_consent::UnifiedConsentServiceClient:
ServiceState GetServiceState(Service service) override;
void SetServiceEnabled(Service service, bool enabled) override;
private:
PrefService* pref_service_;
......
......@@ -14,6 +14,9 @@
namespace {
using Service = unified_consent::UnifiedConsentServiceClient::Service;
using ServiceState = unified_consent::UnifiedConsentServiceClient::ServiceState;
class FakeUnifiedConsentServiceClient
: public unified_consent::UnifiedConsentServiceClient {
public:
......@@ -21,13 +24,10 @@ class FakeUnifiedConsentServiceClient
~FakeUnifiedConsentServiceClient() override = default;
// UnifiedConsentServiceClient:
void SetAlternateErrorPagesEnabled(bool enabled) override {}
void SetMetricsReportingEnabled(bool enabled) override {}
void SetSearchSuggestEnabled(bool enabled) override {}
void SetSafeBrowsingEnabled(bool enabled) override {}
void SetSafeBrowsingExtendedReportingEnabled(bool enabled) override {}
void SetNetworkPredictionEnabled(bool enabled) override {}
void SetSpellCheckEnabled(bool enabled) override {}
ServiceState GetServiceState(Service service) override {
return ServiceState::kNotSupported;
}
void SetServiceEnabled(Service service, bool enabled) override {}
};
} // namespace
......
......@@ -10,6 +10,7 @@ static_library("unified_consent") {
"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",
......
......@@ -26,15 +26,18 @@ UnifiedConsentService::UnifiedConsentService(
pref_service_(pref_service),
identity_manager_(identity_manager),
sync_service_(sync_service) {
DCHECK(service_client_);
DCHECK(pref_service_);
DCHECK(identity_manager_);
DCHECK(sync_service_);
identity_manager_->AddObserver(this);
sync_service_->AddObserver(this);
if (GetMigrationState() == MigrationState::NOT_INITIALIZED)
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(
......@@ -85,10 +88,18 @@ void UnifiedConsentService::MarkMigrationComplete() {
}
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.
......@@ -98,8 +109,9 @@ void UnifiedConsentService::OnPrimaryAccountCleared(
// services.
pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
false);
service_client_->SetSafeBrowsingExtendedReportingEnabled(false);
service_client_->SetSpellCheckEnabled(false);
service_client_->SetServiceEnabled(Service::kSafeBrowsingExtendedReporting,
false);
service_client_->SetServiceEnabled(Service::kSpellCheck, false);
switch (GetMigrationState()) {
case MigrationState::NOT_INITIALIZED:
......@@ -155,15 +167,14 @@ void UnifiedConsentService::OnUnifiedConsentGivenPrefChanged() {
// Enable all non-personalized services.
pref_service_->SetBoolean(prefs::kUrlKeyedAnonymizedDataCollectionEnabled,
true);
// Inform client to enable non-personalized services.
service_client_->SetAlternateErrorPagesEnabled(true);
service_client_->SetMetricsReportingEnabled(true);
service_client_->SetSearchSuggestEnabled(true);
service_client_->SetSafeBrowsingEnabled(true);
service_client_->SetSafeBrowsingExtendedReportingEnabled(true);
service_client_->SetNetworkPredictionEnabled(true);
service_client_->SetSpellCheckEnabled(true);
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) {
......
......@@ -6,12 +6,12 @@
#define COMPONENTS_UNIFIED_CONSENT_UNIFIED_CONSENT_SERVICE_H_
#include <memory>
#include <vector>
#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 {
......@@ -27,6 +27,9 @@ class SyncService;
namespace unified_consent {
using Service = UnifiedConsentServiceClient::Service;
using ServiceState = UnifiedConsentServiceClient::ServiceState;
enum class MigrationState : int {
NOT_INITIALIZED = 0,
IN_PROGRESS_SHOULD_SHOW_CONSENT_BUMP = 1,
......@@ -34,11 +37,10 @@ enum class MigrationState : int {
COMPLETED = 10,
};
class UnifiedConsentServiceClient;
// 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:
......@@ -70,11 +72,16 @@ class UnifiedConsentService : public KeyedService,
// 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;
......
// 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
......@@ -5,26 +5,88 @@
#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:
virtual ~UnifiedConsentServiceClient() {}
// Enables/disables Link Doctor error pages.
virtual void SetAlternateErrorPagesEnabled(bool enabled) = 0;
// Enables/disables metrics reporting.
virtual void SetMetricsReportingEnabled(bool enabled) = 0;
// Enables/disables search suggestions.
virtual void SetSearchSuggestEnabled(bool enabled) = 0;
// Enables/disables safe browsing.
virtual void SetSafeBrowsingEnabled(bool enabled) = 0;
// Enables/disables extended safe browsing.
virtual void SetSafeBrowsingExtendedReportingEnabled(bool enabled) = 0;
// Enables/disables prediction of network actions.
virtual void SetNetworkPredictionEnabled(bool enabled) = 0;
// Enables/disables spell check.
virtual void SetSpellCheckEnabled(bool enabled) = 0;
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
......
......@@ -11,49 +11,76 @@
UnifiedConsentServiceClientImpl::UnifiedConsentServiceClientImpl(
PrefService* pref_service)
: pref_service_(pref_service) {}
void UnifiedConsentServiceClientImpl::SetAlternateErrorPagesEnabled(
bool enabled) {
// Feature not available on iOS.
NOTIMPLEMENTED();
}
void UnifiedConsentServiceClientImpl::SetMetricsReportingEnabled(bool enabled) {
BooleanPrefMember basePreference;
basePreference.Init(metrics::prefs::kMetricsReportingEnabled, pref_service_);
basePreference.SetValue(enabled);
BooleanPrefMember wifiPreference;
wifiPreference.Init(prefs::kMetricsReportingWifiOnly, pref_service_);
wifiPreference.SetValue(enabled);
}
void UnifiedConsentServiceClientImpl::SetSearchSuggestEnabled(bool enabled) {
pref_service_->SetBoolean(prefs::kSearchSuggestEnabled, enabled);
}
void UnifiedConsentServiceClientImpl::SetSafeBrowsingEnabled(bool enabled) {
// Feature not available on iOS.
NOTIMPLEMENTED();
}
void UnifiedConsentServiceClientImpl::SetSafeBrowsingExtendedReportingEnabled(
bool enabled) {
// Feature not available on iOS.
NOTIMPLEMENTED();
: pref_service_(pref_service) {
DCHECK(pref_service_);
ObserveServicePrefChange(Service::kMetricsReporting,
metrics::prefs::kMetricsReportingEnabled,
pref_service_);
ObserveServicePrefChange(Service::kNetworkPrediction,
prefs::kNetworkPredictionEnabled, pref_service_);
ObserveServicePrefChange(Service::kSearchSuggest,
prefs::kSearchSuggestEnabled, pref_service_);
}
void UnifiedConsentServiceClientImpl::SetNetworkPredictionEnabled(
bool enabled) {
BooleanPrefMember basePreference;
basePreference.Init(prefs::kNetworkPredictionEnabled, pref_service_);
basePreference.SetValue(enabled);
BooleanPrefMember wifiPreference;
wifiPreference.Init(prefs::kNetworkPredictionWifiOnly, pref_service_);
wifiPreference.SetValue(enabled);
UnifiedConsentServiceClientImpl::ServiceState
UnifiedConsentServiceClientImpl::GetServiceState(Service service) {
bool enabled;
switch (service) {
case Service::kMetricsReporting: {
BooleanPrefMember metrics_pref;
metrics_pref.Init(metrics::prefs::kMetricsReportingEnabled,
pref_service_);
enabled = metrics_pref.GetValue();
break;
}
case Service::kNetworkPrediction: {
BooleanPrefMember network_pref;
network_pref.Init(prefs::kNetworkPredictionEnabled, pref_service_);
enabled = network_pref.GetValue();
break;
}
case Service::kSearchSuggest:
enabled = pref_service_->GetBoolean(prefs::kSearchSuggestEnabled);
break;
case Service::kAlternateErrorPages:
case Service::kSafeBrowsing:
case Service::kSafeBrowsingExtendedReporting:
case Service::kSpellCheck:
return ServiceState::kNotSupported;
}
return enabled ? ServiceState::kEnabled : ServiceState::kDisabled;
}
void UnifiedConsentServiceClientImpl::SetSpellCheckEnabled(bool enabled) {
// Feature not available on iOS.
NOTIMPLEMENTED();
void UnifiedConsentServiceClientImpl::SetServiceEnabled(Service service,
bool enabled) {
switch (service) {
case Service::kMetricsReporting: {
BooleanPrefMember metrics_pref;
metrics_pref.Init(metrics::prefs::kMetricsReportingEnabled,
pref_service_);
metrics_pref.SetValue(enabled);
BooleanPrefMember metrics_wifi_pref;
metrics_wifi_pref.Init(prefs::kMetricsReportingWifiOnly, pref_service_);
metrics_wifi_pref.SetValue(enabled);
break;
}
case Service::kNetworkPrediction: {
BooleanPrefMember network_pref;
network_pref.Init(prefs::kNetworkPredictionEnabled, pref_service_);
network_pref.SetValue(enabled);
BooleanPrefMember network_wifi_pref;
network_wifi_pref.Init(prefs::kNetworkPredictionWifiOnly, pref_service_);
network_wifi_pref.SetValue(enabled);
break;
}
case Service::kSearchSuggest:
pref_service_->SetBoolean(prefs::kSearchSuggestEnabled, enabled);
break;
case Service::kAlternateErrorPages:
case Service::kSafeBrowsing:
case Service::kSafeBrowsingExtendedReporting:
case Service::kSpellCheck:
NOTIMPLEMENTED() << "Feature not available on iOS";
break;
}
}
......@@ -17,13 +17,9 @@ class UnifiedConsentServiceClientImpl
explicit UnifiedConsentServiceClientImpl(PrefService* pref_service);
~UnifiedConsentServiceClientImpl() override = default;
void SetAlternateErrorPagesEnabled(bool enabled) override;
void SetMetricsReportingEnabled(bool enabled) override;
void SetSearchSuggestEnabled(bool enabled) override;
void SetSafeBrowsingEnabled(bool enabled) override;
void SetSafeBrowsingExtendedReportingEnabled(bool enabled) override;
void SetNetworkPredictionEnabled(bool enabled) override;
void SetSpellCheckEnabled(bool enabled) override;
// unified_consent::UnifiedConsentServiceClient overrides:
ServiceState GetServiceState(Service service) override;
void SetServiceEnabled(Service service, bool enabled) override;
private:
PrefService* pref_service_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment