implement custom tracking policy
This commit is contained in:
@@ -56,6 +56,7 @@ import 'package:weblibre/features/settings/presentation/screens/addon_collection
|
||||
import 'package:weblibre/features/settings/presentation/screens/advanced_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/screens/appearance_display_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/screens/custom_tracking_protection.dart';
|
||||
import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/screens/error_logs_screen.dart';
|
||||
import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.dart';
|
||||
@@ -82,11 +83,11 @@ import 'package:weblibre/features/web_feed/presentation/screens/feed_edit.dart';
|
||||
import 'package:weblibre/features/web_feed/presentation/screens/feed_list.dart';
|
||||
import 'package:weblibre/features/web_feed/presentation/select_feed_dialog.dart';
|
||||
|
||||
part 'routes.bangs.dart';
|
||||
part 'routes.browser.dart';
|
||||
part 'routes.feeds.dart';
|
||||
part 'routes.g.dart';
|
||||
part 'routes.settings.dart';
|
||||
part 'routes.browser.dart';
|
||||
part 'routes.bangs.dart';
|
||||
part 'routes.feeds.dart';
|
||||
|
||||
@TypedGoRoute<AboutRoute>(name: 'AboutRoute', path: '/about')
|
||||
class AboutRoute extends GoRouteData with $AboutRoute {
|
||||
|
||||
@@ -151,6 +151,11 @@ RouteBase get $settingsRoute => GoRouteData.$route(
|
||||
name: 'TrackingProtectionExceptionsRoute',
|
||||
factory: $TrackingProtectionExceptionsRoute._fromState,
|
||||
),
|
||||
GoRouteData.$route(
|
||||
path: 'custom_tracking_protection',
|
||||
name: 'CustomTrackingProtectionRoute',
|
||||
factory: $CustomTrackingProtectionRoute._fromState,
|
||||
),
|
||||
GoRouteData.$route(
|
||||
path: 'error_logs',
|
||||
name: 'ErrorLogsRoute',
|
||||
@@ -478,6 +483,28 @@ mixin $TrackingProtectionExceptionsRoute on GoRouteData {
|
||||
void replace(BuildContext context) => context.replace(location);
|
||||
}
|
||||
|
||||
mixin $CustomTrackingProtectionRoute on GoRouteData {
|
||||
static CustomTrackingProtectionRoute _fromState(GoRouterState state) =>
|
||||
CustomTrackingProtectionRoute();
|
||||
|
||||
@override
|
||||
String get location =>
|
||||
GoRouteData.$location('/settings/custom_tracking_protection');
|
||||
|
||||
@override
|
||||
void go(BuildContext context) => context.go(location);
|
||||
|
||||
@override
|
||||
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||
|
||||
@override
|
||||
void pushReplacement(BuildContext context) =>
|
||||
context.pushReplacement(location);
|
||||
|
||||
@override
|
||||
void replace(BuildContext context) => context.replace(location);
|
||||
}
|
||||
|
||||
mixin $ErrorLogsRoute on GoRouteData {
|
||||
static ErrorLogsRoute _fromState(GoRouterState state) => ErrorLogsRoute();
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ part of 'routes.dart';
|
||||
name: 'TrackingProtectionExceptionsRoute',
|
||||
path: 'tracking_protection_exceptions',
|
||||
),
|
||||
TypedGoRoute<CustomTrackingProtectionRoute>(
|
||||
name: 'CustomTrackingProtectionRoute',
|
||||
path: 'custom_tracking_protection',
|
||||
),
|
||||
TypedGoRoute<ErrorLogsRoute>(name: 'ErrorLogsRoute', path: 'error_logs'),
|
||||
],
|
||||
)
|
||||
@@ -202,3 +206,11 @@ class ErrorLogsRoute extends GoRouteData with $ErrorLogsRoute {
|
||||
return const ErrorLogsScreen();
|
||||
}
|
||||
}
|
||||
|
||||
class CustomTrackingProtectionRoute extends GoRouteData
|
||||
with $CustomTrackingProtectionRoute {
|
||||
@override
|
||||
Widget build(BuildContext context, GoRouterState state) {
|
||||
return const CustomTrackingProtectionScreen();
|
||||
}
|
||||
}
|
||||
|
||||
+44
-5
@@ -29,6 +29,22 @@ import 'package:weblibre/features/user/domain/repositories/general_settings.dart
|
||||
|
||||
part 'engine_settings_replication.g.dart';
|
||||
|
||||
/// Checks if any Custom ETP setting changed between two EngineSettings instances.
|
||||
bool _customEtpSettingsChanged(GeckoEngineSettings? previous, GeckoEngineSettings current) {
|
||||
if (previous == null) return true;
|
||||
return previous.blockCookies != current.blockCookies ||
|
||||
previous.customCookiePolicy != current.customCookiePolicy ||
|
||||
previous.blockTrackingContent != current.blockTrackingContent ||
|
||||
previous.trackingContentScope != current.trackingContentScope ||
|
||||
previous.blockCryptominers != current.blockCryptominers ||
|
||||
previous.blockFingerprinters != current.blockFingerprinters ||
|
||||
previous.blockRedirectTrackers != current.blockRedirectTrackers ||
|
||||
previous.blockSuspectedFingerprinters != current.blockSuspectedFingerprinters ||
|
||||
previous.suspectedFingerprintersScope != current.suspectedFingerprintersScope ||
|
||||
previous.allowListBaseline != current.allowListBaseline ||
|
||||
previous.allowListConvenience != current.allowListConvenience;
|
||||
}
|
||||
|
||||
@Riverpod(keepAlive: true)
|
||||
class EngineSettingsReplicationService
|
||||
extends _$EngineSettingsReplicationService {
|
||||
@@ -90,11 +106,34 @@ class EngineSettingsReplicationService
|
||||
settings.javascriptEnabled) {
|
||||
await _service.javascriptEnabled(settings.javascriptEnabled);
|
||||
}
|
||||
if (previous.value?.trackingProtectionPolicy !=
|
||||
settings.trackingProtectionPolicy) {
|
||||
await _service.trackingProtectionPolicy(
|
||||
settings.trackingProtectionPolicy,
|
||||
);
|
||||
// Check if tracking protection policy mode changed OR any custom ETP setting changed
|
||||
final policyModeChanged = previous.value?.trackingProtectionPolicy !=
|
||||
settings.trackingProtectionPolicy;
|
||||
final customSettingsChanged = settings.trackingProtectionPolicy == TrackingProtectionPolicy.custom &&
|
||||
_customEtpSettingsChanged(previous.value, settings);
|
||||
|
||||
if (policyModeChanged || customSettingsChanged) {
|
||||
// Always send full custom settings when in CUSTOM mode
|
||||
if (settings.trackingProtectionPolicy == TrackingProtectionPolicy.custom) {
|
||||
await _service.customTrackingProtectionPolicy(
|
||||
trackingProtectionPolicy: settings.trackingProtectionPolicy,
|
||||
blockCookies: settings.blockCookies,
|
||||
customCookiePolicy: settings.customCookiePolicy,
|
||||
blockTrackingContent: settings.blockTrackingContent,
|
||||
trackingContentScope: settings.trackingContentScope,
|
||||
blockCryptominers: settings.blockCryptominers,
|
||||
blockFingerprinters: settings.blockFingerprinters,
|
||||
blockRedirectTrackers: settings.blockRedirectTrackers,
|
||||
blockSuspectedFingerprinters: settings.blockSuspectedFingerprinters,
|
||||
suspectedFingerprintersScope: settings.suspectedFingerprintersScope,
|
||||
allowListBaseline: settings.allowListBaseline,
|
||||
allowListConvenience: settings.allowListConvenience,
|
||||
);
|
||||
} else {
|
||||
await _service.trackingProtectionPolicy(
|
||||
settings.trackingProtectionPolicy,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (previous.value?.httpsOnlyMode != settings.httpsOnlyMode) {
|
||||
await _service.httpsOnlyMode(settings.httpsOnlyMode);
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Fabian Freund.
|
||||
*
|
||||
* This file is part of WebLibre
|
||||
* (see https://weblibre.eu).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import 'package:fading_scroll/fading_scroll.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart';
|
||||
import 'package:weblibre/features/settings/presentation/widgets/sections.dart';
|
||||
import 'package:weblibre/features/user/data/models/engine_settings.dart';
|
||||
import 'package:weblibre/features/user/domain/repositories/engine_settings.dart';
|
||||
|
||||
class CustomTrackingProtectionScreen extends StatelessWidget {
|
||||
const CustomTrackingProtectionScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Custom Tracking Protection')),
|
||||
body: SafeArea(
|
||||
child: FadingScroll(
|
||||
fadingSize: 25,
|
||||
builder: (context, controller) {
|
||||
return ListView(
|
||||
controller: controller,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
children: const [
|
||||
_AllowlistSection(),
|
||||
_CookiesSection(),
|
||||
_TrackingContentSection(),
|
||||
_TrackersSection(),
|
||||
_AdvancedFingerprintingSection(),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AllowlistSection extends HookConsumerWidget {
|
||||
const _AllowlistSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final allowListBaseline = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.allowListBaseline),
|
||||
);
|
||||
final allowListConvenience = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.allowListConvenience),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SettingSection(name: 'Allowlist Exceptions'),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Fix website major issues'),
|
||||
subtitle: const Text(
|
||||
'Apply exceptions required to avoid major website breakage (recommended)',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.shieldCheck),
|
||||
value: allowListBaseline,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.allowListBaseline(value));
|
||||
},
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Fix website minor issues'),
|
||||
subtitle: const Text(
|
||||
'Apply exceptions to fix minor issues and enable convenience features',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.shieldHalfFull),
|
||||
value: allowListConvenience,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.allowListConvenience(value));
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CookiesSection extends HookConsumerWidget {
|
||||
const _CookiesSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final blockCookies = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.blockCookies),
|
||||
);
|
||||
final customCookiePolicy = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.customCookiePolicy),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SettingSection(name: 'Cookies'),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Block Cookies'),
|
||||
subtitle: const Text('Block cookies based on the policy below'),
|
||||
secondary: const Icon(MdiIcons.cookie),
|
||||
value: blockCookies,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.blockCookies(value));
|
||||
},
|
||||
),
|
||||
if (blockCookies)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text('Cookie Policy'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
DropdownMenu<CustomCookiePolicy>(
|
||||
initialSelection: customCookiePolicy,
|
||||
width: double.infinity,
|
||||
dropdownMenuEntries: const [
|
||||
DropdownMenuEntry(
|
||||
value: CustomCookiePolicy.totalProtection,
|
||||
label: 'Total Cookie Protection (Recommended)',
|
||||
leadingIcon: Icon(MdiIcons.shieldLock),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: CustomCookiePolicy.crossSiteTrackers,
|
||||
label: 'Cross-site and social media trackers',
|
||||
leadingIcon: Icon(MdiIcons.accountGroup),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: CustomCookiePolicy.unvisited,
|
||||
label: 'Unvisited sites',
|
||||
leadingIcon: Icon(MdiIcons.webOff),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: CustomCookiePolicy.thirdParty,
|
||||
label: 'All third-party cookies',
|
||||
leadingIcon: Icon(MdiIcons.cookieOff),
|
||||
),
|
||||
DropdownMenuEntry(
|
||||
value: CustomCookiePolicy.allCookies,
|
||||
label: 'All cookies (may break sites)',
|
||||
leadingIcon: Icon(MdiIcons.cookieRemove),
|
||||
),
|
||||
],
|
||||
onSelected: (value) async {
|
||||
if (value != null) {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.customCookiePolicy(value));
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TrackingContentSection extends HookConsumerWidget {
|
||||
const _TrackingContentSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final blockTrackingContent = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.blockTrackingContent),
|
||||
);
|
||||
final trackingContentScope = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.trackingContentScope),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SettingSection(name: 'Tracking Content'),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Block Tracking Content'),
|
||||
subtitle: const Text(
|
||||
'Block tracking scripts and resources embedded in websites',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.scriptTextOutline),
|
||||
value: blockTrackingContent,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.blockTrackingContent(value));
|
||||
},
|
||||
),
|
||||
if (blockTrackingContent)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text('Apply to'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: SegmentedButton<TrackingScope>(
|
||||
segments: const [
|
||||
ButtonSegment(
|
||||
value: TrackingScope.all,
|
||||
label: Text('All tabs'),
|
||||
),
|
||||
ButtonSegment(
|
||||
value: TrackingScope.privateOnly,
|
||||
label: Text('Private tabs only'),
|
||||
),
|
||||
],
|
||||
selected: {trackingContentScope},
|
||||
onSelectionChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(s) => s.copyWith.trackingContentScope(value.first),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TrackersSection extends HookConsumerWidget {
|
||||
const _TrackersSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final blockCryptominers = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.blockCryptominers),
|
||||
);
|
||||
final blockFingerprinters = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.blockFingerprinters),
|
||||
);
|
||||
final blockRedirectTrackers = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select((s) => s.blockRedirectTrackers),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SettingSection(name: 'Trackers'),
|
||||
const ListTile(
|
||||
title: Text('Always Blocked'),
|
||||
subtitle: Text(
|
||||
'Ads, analytics, social trackers, and Mozilla social trackers are always blocked in Custom mode.',
|
||||
),
|
||||
leading: Icon(MdiIcons.shieldLock),
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Cryptominers'),
|
||||
subtitle: const Text(
|
||||
'Block scripts that use your device to mine cryptocurrency',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.currencyBtc),
|
||||
value: blockCryptominers,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.blockCryptominers(value));
|
||||
},
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Known Fingerprinters'),
|
||||
subtitle: const Text(
|
||||
'Block scripts that collect information to uniquely identify your device',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.fingerprint),
|
||||
value: blockFingerprinters,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.blockFingerprinters(value));
|
||||
},
|
||||
),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Redirect Trackers'),
|
||||
subtitle: const Text(
|
||||
'Block trackers that collect data through intermediate URL redirects',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.routerNetwork),
|
||||
value: blockRedirectTrackers,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.blockRedirectTrackers(value));
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _AdvancedFingerprintingSection extends HookConsumerWidget {
|
||||
const _AdvancedFingerprintingSection();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final blockSuspectedFingerprinters = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select(
|
||||
(s) => s.blockSuspectedFingerprinters,
|
||||
),
|
||||
);
|
||||
final suspectedFingerprintersScope = ref.watch(
|
||||
engineSettingsWithDefaultsProvider.select(
|
||||
(s) => s.suspectedFingerprintersScope,
|
||||
),
|
||||
);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
const SettingSection(name: 'Advanced Fingerprinting Protection'),
|
||||
SwitchListTile.adaptive(
|
||||
title: const Text('Suspected Fingerprinters'),
|
||||
subtitle: const Text(
|
||||
'Block additional fingerprinting techniques that may be used to track you',
|
||||
),
|
||||
secondary: const Icon(MdiIcons.shieldSearch),
|
||||
value: blockSuspectedFingerprinters,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save((s) => s.copyWith.blockSuspectedFingerprinters(value));
|
||||
},
|
||||
),
|
||||
if (blockSuspectedFingerprinters)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const ListTile(
|
||||
title: Text('Apply to'),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: SegmentedButton<TrackingScope>(
|
||||
segments: const [
|
||||
ButtonSegment(
|
||||
value: TrackingScope.all,
|
||||
label: Text('All tabs'),
|
||||
),
|
||||
ButtonSegment(
|
||||
value: TrackingScope.privateOnly,
|
||||
label: Text('Private tabs only'),
|
||||
),
|
||||
],
|
||||
selected: {suspectedFingerprintersScope},
|
||||
onSelectionChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(s) => s.copyWith.suspectedFingerprintersScope(
|
||||
value.first,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -512,12 +512,25 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget {
|
||||
RadioGroup(
|
||||
groupValue: trackingProtectionPolicy,
|
||||
onChanged: (value) async {
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith
|
||||
.trackingProtectionPolicy(value),
|
||||
);
|
||||
if (value != null) {
|
||||
// Save the policy change
|
||||
await ref
|
||||
.read(saveEngineSettingsControllerProvider.notifier)
|
||||
.save(
|
||||
(currentSettings) => currentSettings.copyWith
|
||||
.trackingProtectionPolicy(value),
|
||||
);
|
||||
}
|
||||
|
||||
// Navigate to custom settings screen when Custom is selected
|
||||
if (value == TrackingProtectionPolicy.custom ||
|
||||
(value == null &&
|
||||
trackingProtectionPolicy ==
|
||||
TrackingProtectionPolicy.custom)) {
|
||||
if (context.mounted) {
|
||||
await CustomTrackingProtectionRoute().push(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: const Column(
|
||||
children: [
|
||||
@@ -541,8 +554,10 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget {
|
||||
),
|
||||
RadioListTile<TrackingProtectionPolicy>.adaptive(
|
||||
value: TrackingProtectionPolicy.custom,
|
||||
toggleable: true,
|
||||
title: Text('Custom'),
|
||||
subtitle: Text('Choose which trackers and scripts to block.'),
|
||||
secondary: Icon(Icons.chevron_right),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -79,6 +79,31 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
@override
|
||||
List<String> get locales => super.locales!;
|
||||
|
||||
// Custom Tracking Protection overrides
|
||||
@override
|
||||
bool get blockCookies => super.blockCookies!;
|
||||
@override
|
||||
CustomCookiePolicy get customCookiePolicy => super.customCookiePolicy!;
|
||||
@override
|
||||
bool get blockTrackingContent => super.blockTrackingContent!;
|
||||
@override
|
||||
TrackingScope get trackingContentScope => super.trackingContentScope!;
|
||||
@override
|
||||
bool get blockCryptominers => super.blockCryptominers!;
|
||||
@override
|
||||
bool get blockFingerprinters => super.blockFingerprinters!;
|
||||
@override
|
||||
bool get blockRedirectTrackers => super.blockRedirectTrackers!;
|
||||
@override
|
||||
bool get blockSuspectedFingerprinters => super.blockSuspectedFingerprinters!;
|
||||
@override
|
||||
TrackingScope get suspectedFingerprintersScope =>
|
||||
super.suspectedFingerprintersScope!;
|
||||
@override
|
||||
bool get allowListBaseline => super.allowListBaseline!;
|
||||
@override
|
||||
bool get allowListConvenience => super.allowListConvenience!;
|
||||
|
||||
final QueryParameterStripping queryParameterStripping;
|
||||
|
||||
final BounceTrackingProtectionMode bounceTrackingProtectionMode;
|
||||
@@ -135,6 +160,17 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
required super.fingerprintingProtectionOverrides,
|
||||
required this.enablePdfJs,
|
||||
required super.locales,
|
||||
required super.blockCookies,
|
||||
required super.customCookiePolicy,
|
||||
required super.blockTrackingContent,
|
||||
required super.trackingContentScope,
|
||||
required super.blockCryptominers,
|
||||
required super.blockFingerprinters,
|
||||
required super.blockRedirectTrackers,
|
||||
required super.blockSuspectedFingerprinters,
|
||||
required super.suspectedFingerprintersScope,
|
||||
required super.allowListBaseline,
|
||||
required super.allowListConvenience,
|
||||
});
|
||||
|
||||
EngineSettings.withDefaults({
|
||||
@@ -160,6 +196,17 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
String? fingerprintingProtectionOverrides,
|
||||
bool? enablePdfJs,
|
||||
List<String>? locales,
|
||||
bool? blockCookies,
|
||||
CustomCookiePolicy? customCookiePolicy,
|
||||
bool? blockTrackingContent,
|
||||
TrackingScope? trackingContentScope,
|
||||
bool? blockCryptominers,
|
||||
bool? blockFingerprinters,
|
||||
bool? blockRedirectTrackers,
|
||||
bool? blockSuspectedFingerprinters,
|
||||
TrackingScope? suspectedFingerprintersScope,
|
||||
bool? allowListBaseline,
|
||||
bool? allowListConvenience,
|
||||
}) : queryParameterStripping =
|
||||
queryParameterStripping ?? QueryParameterStripping.disabled,
|
||||
bounceTrackingProtectionMode =
|
||||
@@ -199,6 +246,19 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
WidgetsBinding.instance.platformDispatcher.locales
|
||||
.map((x) => x.toLanguageTag())
|
||||
.toList(),
|
||||
blockCookies: blockCookies ?? true,
|
||||
customCookiePolicy:
|
||||
customCookiePolicy ?? CustomCookiePolicy.totalProtection,
|
||||
blockTrackingContent: blockTrackingContent ?? true,
|
||||
trackingContentScope: trackingContentScope ?? TrackingScope.all,
|
||||
blockCryptominers: blockCryptominers ?? true,
|
||||
blockFingerprinters: blockFingerprinters ?? true,
|
||||
blockRedirectTrackers: blockRedirectTrackers ?? true,
|
||||
blockSuspectedFingerprinters: blockSuspectedFingerprinters ?? true,
|
||||
suspectedFingerprintersScope:
|
||||
suspectedFingerprintersScope ?? TrackingScope.all,
|
||||
allowListBaseline: allowListBaseline ?? true,
|
||||
allowListConvenience: allowListConvenience ?? false,
|
||||
);
|
||||
|
||||
static AddonCollection? _addonCollectionFromJson(String? json) =>
|
||||
@@ -238,5 +298,16 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
|
||||
fingerprintingProtectionOverrides,
|
||||
enablePdfJs,
|
||||
locales,
|
||||
blockCookies,
|
||||
customCookiePolicy,
|
||||
blockTrackingContent,
|
||||
trackingContentScope,
|
||||
blockCryptominers,
|
||||
blockFingerprinters,
|
||||
blockRedirectTrackers,
|
||||
blockSuspectedFingerprinters,
|
||||
suspectedFingerprintersScope,
|
||||
allowListBaseline,
|
||||
allowListConvenience,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -69,6 +69,32 @@ abstract class _$EngineSettingsCWProxy {
|
||||
|
||||
EngineSettings locales(List<String>? locales);
|
||||
|
||||
EngineSettings blockCookies(bool? blockCookies);
|
||||
|
||||
EngineSettings customCookiePolicy(CustomCookiePolicy? customCookiePolicy);
|
||||
|
||||
EngineSettings blockTrackingContent(bool? blockTrackingContent);
|
||||
|
||||
EngineSettings trackingContentScope(TrackingScope? trackingContentScope);
|
||||
|
||||
EngineSettings blockCryptominers(bool? blockCryptominers);
|
||||
|
||||
EngineSettings blockFingerprinters(bool? blockFingerprinters);
|
||||
|
||||
EngineSettings blockRedirectTrackers(bool? blockRedirectTrackers);
|
||||
|
||||
EngineSettings blockSuspectedFingerprinters(
|
||||
bool? blockSuspectedFingerprinters,
|
||||
);
|
||||
|
||||
EngineSettings suspectedFingerprintersScope(
|
||||
TrackingScope? suspectedFingerprintersScope,
|
||||
);
|
||||
|
||||
EngineSettings allowListBaseline(bool? allowListBaseline);
|
||||
|
||||
EngineSettings allowListConvenience(bool? allowListConvenience);
|
||||
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`.
|
||||
///
|
||||
@@ -99,6 +125,17 @@ abstract class _$EngineSettingsCWProxy {
|
||||
String? fingerprintingProtectionOverrides,
|
||||
bool enablePdfJs,
|
||||
List<String>? locales,
|
||||
bool? blockCookies,
|
||||
CustomCookiePolicy? customCookiePolicy,
|
||||
bool? blockTrackingContent,
|
||||
TrackingScope? trackingContentScope,
|
||||
bool? blockCryptominers,
|
||||
bool? blockFingerprinters,
|
||||
bool? blockRedirectTrackers,
|
||||
bool? blockSuspectedFingerprinters,
|
||||
TrackingScope? suspectedFingerprintersScope,
|
||||
bool? allowListBaseline,
|
||||
bool? allowListConvenience,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -213,6 +250,52 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
@override
|
||||
EngineSettings locales(List<String>? locales) => call(locales: locales);
|
||||
|
||||
@override
|
||||
EngineSettings blockCookies(bool? blockCookies) =>
|
||||
call(blockCookies: blockCookies);
|
||||
|
||||
@override
|
||||
EngineSettings customCookiePolicy(CustomCookiePolicy? customCookiePolicy) =>
|
||||
call(customCookiePolicy: customCookiePolicy);
|
||||
|
||||
@override
|
||||
EngineSettings blockTrackingContent(bool? blockTrackingContent) =>
|
||||
call(blockTrackingContent: blockTrackingContent);
|
||||
|
||||
@override
|
||||
EngineSettings trackingContentScope(TrackingScope? trackingContentScope) =>
|
||||
call(trackingContentScope: trackingContentScope);
|
||||
|
||||
@override
|
||||
EngineSettings blockCryptominers(bool? blockCryptominers) =>
|
||||
call(blockCryptominers: blockCryptominers);
|
||||
|
||||
@override
|
||||
EngineSettings blockFingerprinters(bool? blockFingerprinters) =>
|
||||
call(blockFingerprinters: blockFingerprinters);
|
||||
|
||||
@override
|
||||
EngineSettings blockRedirectTrackers(bool? blockRedirectTrackers) =>
|
||||
call(blockRedirectTrackers: blockRedirectTrackers);
|
||||
|
||||
@override
|
||||
EngineSettings blockSuspectedFingerprinters(
|
||||
bool? blockSuspectedFingerprinters,
|
||||
) => call(blockSuspectedFingerprinters: blockSuspectedFingerprinters);
|
||||
|
||||
@override
|
||||
EngineSettings suspectedFingerprintersScope(
|
||||
TrackingScope? suspectedFingerprintersScope,
|
||||
) => call(suspectedFingerprintersScope: suspectedFingerprintersScope);
|
||||
|
||||
@override
|
||||
EngineSettings allowListBaseline(bool? allowListBaseline) =>
|
||||
call(allowListBaseline: allowListBaseline);
|
||||
|
||||
@override
|
||||
EngineSettings allowListConvenience(bool? allowListConvenience) =>
|
||||
call(allowListConvenience: allowListConvenience);
|
||||
|
||||
@override
|
||||
/// Creates a new instance with the provided field values.
|
||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`.
|
||||
@@ -246,6 +329,17 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
Object? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(),
|
||||
Object? enablePdfJs = const $CopyWithPlaceholder(),
|
||||
Object? locales = const $CopyWithPlaceholder(),
|
||||
Object? blockCookies = const $CopyWithPlaceholder(),
|
||||
Object? customCookiePolicy = const $CopyWithPlaceholder(),
|
||||
Object? blockTrackingContent = const $CopyWithPlaceholder(),
|
||||
Object? trackingContentScope = const $CopyWithPlaceholder(),
|
||||
Object? blockCryptominers = const $CopyWithPlaceholder(),
|
||||
Object? blockFingerprinters = const $CopyWithPlaceholder(),
|
||||
Object? blockRedirectTrackers = const $CopyWithPlaceholder(),
|
||||
Object? blockSuspectedFingerprinters = const $CopyWithPlaceholder(),
|
||||
Object? suspectedFingerprintersScope = const $CopyWithPlaceholder(),
|
||||
Object? allowListBaseline = const $CopyWithPlaceholder(),
|
||||
Object? allowListConvenience = const $CopyWithPlaceholder(),
|
||||
}) {
|
||||
return EngineSettings(
|
||||
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
|
||||
@@ -361,6 +455,53 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
|
||||
? _value.locales
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: locales as List<String>?,
|
||||
blockCookies: blockCookies == const $CopyWithPlaceholder()
|
||||
? _value.blockCookies
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: blockCookies as bool?,
|
||||
customCookiePolicy: customCookiePolicy == const $CopyWithPlaceholder()
|
||||
? _value.customCookiePolicy
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: customCookiePolicy as CustomCookiePolicy?,
|
||||
blockTrackingContent: blockTrackingContent == const $CopyWithPlaceholder()
|
||||
? _value.blockTrackingContent
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: blockTrackingContent as bool?,
|
||||
trackingContentScope: trackingContentScope == const $CopyWithPlaceholder()
|
||||
? _value.trackingContentScope
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: trackingContentScope as TrackingScope?,
|
||||
blockCryptominers: blockCryptominers == const $CopyWithPlaceholder()
|
||||
? _value.blockCryptominers
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: blockCryptominers as bool?,
|
||||
blockFingerprinters: blockFingerprinters == const $CopyWithPlaceholder()
|
||||
? _value.blockFingerprinters
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: blockFingerprinters as bool?,
|
||||
blockRedirectTrackers:
|
||||
blockRedirectTrackers == const $CopyWithPlaceholder()
|
||||
? _value.blockRedirectTrackers
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: blockRedirectTrackers as bool?,
|
||||
blockSuspectedFingerprinters:
|
||||
blockSuspectedFingerprinters == const $CopyWithPlaceholder()
|
||||
? _value.blockSuspectedFingerprinters
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: blockSuspectedFingerprinters as bool?,
|
||||
suspectedFingerprintersScope:
|
||||
suspectedFingerprintersScope == const $CopyWithPlaceholder()
|
||||
? _value.suspectedFingerprintersScope
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: suspectedFingerprintersScope as TrackingScope?,
|
||||
allowListBaseline: allowListBaseline == const $CopyWithPlaceholder()
|
||||
? _value.allowListBaseline
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: allowListBaseline as bool?,
|
||||
allowListConvenience: allowListConvenience == const $CopyWithPlaceholder()
|
||||
? _value.allowListConvenience
|
||||
// ignore: cast_nullable_to_non_nullable
|
||||
: allowListConvenience as bool?,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -436,6 +577,27 @@ EngineSettings _$EngineSettingsFromJson(Map<String, dynamic> json) =>
|
||||
locales: (json['locales'] as List<dynamic>?)
|
||||
?.map((e) => e as String)
|
||||
.toList(),
|
||||
blockCookies: json['blockCookies'] as bool?,
|
||||
customCookiePolicy: $enumDecodeNullable(
|
||||
_$CustomCookiePolicyEnumMap,
|
||||
json['customCookiePolicy'],
|
||||
),
|
||||
blockTrackingContent: json['blockTrackingContent'] as bool?,
|
||||
trackingContentScope: $enumDecodeNullable(
|
||||
_$TrackingScopeEnumMap,
|
||||
json['trackingContentScope'],
|
||||
),
|
||||
blockCryptominers: json['blockCryptominers'] as bool?,
|
||||
blockFingerprinters: json['blockFingerprinters'] as bool?,
|
||||
blockRedirectTrackers: json['blockRedirectTrackers'] as bool?,
|
||||
blockSuspectedFingerprinters:
|
||||
json['blockSuspectedFingerprinters'] as bool?,
|
||||
suspectedFingerprintersScope: $enumDecodeNullable(
|
||||
_$TrackingScopeEnumMap,
|
||||
json['suspectedFingerprintersScope'],
|
||||
),
|
||||
allowListBaseline: json['allowListBaseline'] as bool?,
|
||||
allowListConvenience: json['allowListConvenience'] as bool?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$EngineSettingsToJson(
|
||||
@@ -463,6 +625,20 @@ Map<String, dynamic> _$EngineSettingsToJson(
|
||||
.webContentIsolationStrategy]!,
|
||||
'enterpriseRootsEnabled': instance.enterpriseRootsEnabled,
|
||||
'locales': instance.locales,
|
||||
'blockCookies': instance.blockCookies,
|
||||
'customCookiePolicy':
|
||||
_$CustomCookiePolicyEnumMap[instance.customCookiePolicy]!,
|
||||
'blockTrackingContent': instance.blockTrackingContent,
|
||||
'trackingContentScope':
|
||||
_$TrackingScopeEnumMap[instance.trackingContentScope]!,
|
||||
'blockCryptominers': instance.blockCryptominers,
|
||||
'blockFingerprinters': instance.blockFingerprinters,
|
||||
'blockRedirectTrackers': instance.blockRedirectTrackers,
|
||||
'blockSuspectedFingerprinters': instance.blockSuspectedFingerprinters,
|
||||
'suspectedFingerprintersScope':
|
||||
_$TrackingScopeEnumMap[instance.suspectedFingerprintersScope]!,
|
||||
'allowListBaseline': instance.allowListBaseline,
|
||||
'allowListConvenience': instance.allowListConvenience,
|
||||
'queryParameterStripping':
|
||||
_$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!,
|
||||
'bounceTrackingProtectionMode':
|
||||
@@ -528,3 +704,16 @@ const _$DohSettingsModeEnumMap = {
|
||||
DohSettingsMode.max: 'max',
|
||||
DohSettingsMode.off: 'off',
|
||||
};
|
||||
|
||||
const _$CustomCookiePolicyEnumMap = {
|
||||
CustomCookiePolicy.totalProtection: 'totalProtection',
|
||||
CustomCookiePolicy.crossSiteTrackers: 'crossSiteTrackers',
|
||||
CustomCookiePolicy.unvisited: 'unvisited',
|
||||
CustomCookiePolicy.thirdParty: 'thirdParty',
|
||||
CustomCookiePolicy.allCookies: 'allCookies',
|
||||
};
|
||||
|
||||
const _$TrackingScopeEnumMap = {
|
||||
TrackingScope.all: 'all',
|
||||
TrackingScope.privateOnly: 'privateOnly',
|
||||
};
|
||||
|
||||
@@ -127,6 +127,47 @@ class EngineSettingsRepository extends _$EngineSettingsRepository {
|
||||
'locales': settings['locales']
|
||||
?.readAs(DriftSqlType.string, db.typeMapping)
|
||||
.mapNotNull(jsonDecode),
|
||||
// Custom Tracking Protection
|
||||
'blockCookies': settings['blockCookies']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'customCookiePolicy': settings['customCookiePolicy']?.readAs(
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
),
|
||||
'blockTrackingContent': settings['blockTrackingContent']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'trackingContentScope': settings['trackingContentScope']?.readAs(
|
||||
DriftSqlType.string,
|
||||
db.typeMapping,
|
||||
),
|
||||
'blockCryptominers': settings['blockCryptominers']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'blockFingerprinters': settings['blockFingerprinters']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'blockRedirectTrackers': settings['blockRedirectTrackers']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'blockSuspectedFingerprinters': settings['blockSuspectedFingerprinters']
|
||||
?.readAs(DriftSqlType.bool, db.typeMapping),
|
||||
'suspectedFingerprintersScope': settings['suspectedFingerprintersScope']
|
||||
?.readAs(DriftSqlType.string, db.typeMapping),
|
||||
'allowListBaseline': settings['allowListBaseline']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
'allowListConvenience': settings['allowListConvenience']?.readAs(
|
||||
DriftSqlType.bool,
|
||||
db.typeMapping,
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider
|
||||
}
|
||||
|
||||
String _$engineSettingsRepositoryHash() =>
|
||||
r'916abcd932fc3b3b1e060bc93a46c1a3a38bb984';
|
||||
r'a3bcd3a82d251ce6fd0adc4922e512533d70a10a';
|
||||
|
||||
abstract class _$EngineSettingsRepository
|
||||
extends $StreamNotifier<EngineSettings> {
|
||||
|
||||
Reference in New Issue
Block a user