Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
#include "ContentClassifierPrefMirror.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Preferences.h"
#include "mozilla/Span.h"
#include "MainThreadUtils.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
namespace mozilla {
StaticAutoPtr<ContentClassifierPrefMirror>
ContentClassifierPrefMirror::sInstance;
namespace {
constexpr char kMirrorEnabledPref[] =
"privacy.trackingprotection.content.mirror.enabled";
// Content classifier prefs the mirror owns while enabled.
constexpr char kProtectionEnabledPref[] =
"privacy.trackingprotection.content.protection.enabled";
constexpr char kProtectionEnginesPref[] =
"privacy.trackingprotection.content.protection.engines";
constexpr char kProtectionEnginesPBMPref[] =
"privacy.trackingprotection.content.protection.engines.pbmode";
constexpr char kAnnotationEnabledPref[] =
"privacy.trackingprotection.content.annotation.enabled";
constexpr char kAnnotationEnginesPref[] =
"privacy.trackingprotection.content.annotation.engines";
constexpr char kAnnotationEnginesPBMPref[] =
"privacy.trackingprotection.content.annotation.engines.pbmode";
// Maps a content classifier engine name (see ContentClassifierService.cpp,
// kFeatures) onto the ETP prefs that gate it in normal and private-browsing
// contexts. An engine joins the derived list when its gating pref is true.
struct EngineMapping {
const char* mEngine;
const char* mNormalPref;
const char* mPBMPref;
};
constexpr EngineMapping kProtectionMappings[] = {
{"trackers", "privacy.trackingprotection.enabled",
"privacy.trackingprotection.pbmode.enabled"},
{"fingerprinters", "privacy.trackingprotection.fingerprinting.enabled",
"privacy.trackingprotection.fingerprinting.enabled"},
{"cryptominers", "privacy.trackingprotection.cryptomining.enabled",
"privacy.trackingprotection.cryptomining.enabled"},
{"social-trackers", "privacy.trackingprotection.socialtracking.enabled",
"privacy.trackingprotection.socialtracking.enabled"},
{"email-trackers", "privacy.trackingprotection.emailtracking.enabled",
"privacy.trackingprotection.emailtracking.pbmode.enabled"},
};
constexpr EngineMapping kAnnotationMappings[] = {
{"trackers", "privacy.trackingprotection.annotate_channels",
"privacy.trackingprotection.annotate_channels"},
// Content trackers are annotated only when the strict list is in use.
{"trackers-content", "privacy.annotate_channels.strict_list.enabled",
"privacy.annotate_channels.strict_list.pbmode.enabled"},
{"fingerprinters", "privacy.trackingprotection.annotate_channels",
"privacy.trackingprotection.annotate_channels"},
{"cryptominers", "privacy.trackingprotection.annotate_channels",
"privacy.trackingprotection.annotate_channels"},
{"social-trackers", "privacy.trackingprotection.annotate_channels",
"privacy.trackingprotection.annotate_channels"},
};
// ETP source prefs the mirror observes; a change to any of these recomputes
// the derived content prefs. Must cover every gating pref referenced above.
constexpr const char* kWatchedPrefs[] = {
"privacy.trackingprotection.enabled",
"privacy.trackingprotection.pbmode.enabled",
"privacy.trackingprotection.annotate_channels",
"privacy.annotate_channels.strict_list.enabled",
"privacy.annotate_channels.strict_list.pbmode.enabled",
"privacy.trackingprotection.fingerprinting.enabled",
"privacy.trackingprotection.cryptomining.enabled",
"privacy.trackingprotection.socialtracking.enabled",
"privacy.trackingprotection.emailtracking.enabled",
"privacy.trackingprotection.emailtracking.pbmode.enabled",
};
// Build a comma-separated engine list, selecting each mapping's normal or
// PBM gating pref according to aPrivateBrowsing.
void BuildEngineList(Span<const EngineMapping> aMappings, bool aPrivateBrowsing,
nsACString& aOut) {
aOut.Truncate();
for (const auto& mapping : aMappings) {
const char* gatingPref =
aPrivateBrowsing ? mapping.mPBMPref : mapping.mNormalPref;
if (Preferences::GetBool(gatingPref, false)) {
if (!aOut.IsEmpty()) {
aOut.Append(',');
}
aOut.Append(nsDependentCString(mapping.mEngine));
}
}
}
} // namespace
// static
void ContentClassifierPrefMirror::Init() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
static bool sRegistered = false;
if (sRegistered) {
return;
}
sRegistered = true;
// Tear the singleton down at shutdown through the same path as a
// pref-disable.
RunOnShutdown([] { Shutdown(); });
Preferences::RegisterCallbackAndCall(
&ContentClassifierPrefMirror::OnMirrorPrefChange,
nsDependentCString(kMirrorEnabledPref));
}
ContentClassifierPrefMirror::ContentClassifierPrefMirror() {
for (const char* pref : kWatchedPrefs) {
Preferences::RegisterCallback(&ContentClassifierPrefMirror::OnPrefChange,
nsDependentCString(pref));
}
}
ContentClassifierPrefMirror::~ContentClassifierPrefMirror() {
for (const char* pref : kWatchedPrefs) {
Preferences::UnregisterCallback(&ContentClassifierPrefMirror::OnPrefChange,
nsDependentCString(pref));
}
}
// static
void ContentClassifierPrefMirror::OnMirrorPrefChange(const char* aPref,
void* aData) {
MOZ_ASSERT(NS_IsMainThread());
bool enabled = Preferences::GetBool(kMirrorEnabledPref, false);
if (enabled == !!sInstance) {
// The master pref changed but the up/down state didn't flip.
return;
}
if (!enabled) {
Shutdown();
return;
}
sInstance = new ContentClassifierPrefMirror();
sInstance->ScheduleSync();
}
// static
void ContentClassifierPrefMirror::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
sInstance = nullptr;
}
// static
void ContentClassifierPrefMirror::OnPrefChange(const char* aPref, void* aData) {
MOZ_ASSERT(NS_IsMainThread());
if (sInstance) {
sInstance->ScheduleSync();
}
}
void ContentClassifierPrefMirror::ScheduleSync() {
MOZ_ASSERT(NS_IsMainThread());
if (mSyncScheduled) {
return;
}
mSyncScheduled = true;
NS_DispatchToMainThread(
NS_NewRunnableFunction("ContentClassifierPrefMirror::Sync", [] {
if (sInstance) {
sInstance->mSyncScheduled = false;
sInstance->Sync();
}
}));
}
void ContentClassifierPrefMirror::Sync() {
MOZ_ASSERT(NS_IsMainThread());
if (!Preferences::GetBool(kMirrorEnabledPref, false)) {
// Disabled: leave the content prefs as they are. Any values previously
// derived by the mirror remain in place.
return;
}
// Compute the ContentClassifier engine lists from the ETP source prefs for
// the normal and private prefs.
nsAutoCString protectionEngines;
nsAutoCString protectionEnginesPBM;
nsAutoCString annotationEngines;
nsAutoCString annotationEnginesPBM;
BuildEngineList(Span(kProtectionMappings), false, protectionEngines);
BuildEngineList(Span(kProtectionMappings), true, protectionEnginesPBM);
BuildEngineList(Span(kAnnotationMappings), false, annotationEngines);
BuildEngineList(Span(kAnnotationMappings), true, annotationEnginesPBM);
Preferences::SetCString(kProtectionEnginesPref, protectionEngines);
Preferences::SetCString(kProtectionEnginesPBMPref, protectionEnginesPBM);
Preferences::SetCString(kAnnotationEnginesPref, annotationEngines);
Preferences::SetCString(kAnnotationEnginesPBMPref, annotationEnginesPBM);
Preferences::SetBool(
kProtectionEnabledPref,
!protectionEngines.IsEmpty() || !protectionEnginesPBM.IsEmpty());
Preferences::SetBool(
kAnnotationEnabledPref,
!annotationEngines.IsEmpty() || !annotationEnginesPBM.IsEmpty());
}
} // namespace mozilla