implement custom tracking policy

This commit is contained in:
Fabian Freund
2026-01-22 09:08:50 +01:00
parent fe1b20af1b
commit 84e1fa37e0
16 changed files with 1513 additions and 315 deletions
+4 -3
View File
@@ -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/advanced_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/appearance_display_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/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/doh_settings.dart';
import 'package:weblibre/features/settings/presentation/screens/error_logs_screen.dart'; import 'package:weblibre/features/settings/presentation/screens/error_logs_screen.dart';
import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.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/screens/feed_list.dart';
import 'package:weblibre/features/web_feed/presentation/select_feed_dialog.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.g.dart';
part 'routes.settings.dart'; part 'routes.settings.dart';
part 'routes.browser.dart';
part 'routes.bangs.dart';
part 'routes.feeds.dart';
@TypedGoRoute<AboutRoute>(name: 'AboutRoute', path: '/about') @TypedGoRoute<AboutRoute>(name: 'AboutRoute', path: '/about')
class AboutRoute extends GoRouteData with $AboutRoute { class AboutRoute extends GoRouteData with $AboutRoute {
+27
View File
@@ -151,6 +151,11 @@ RouteBase get $settingsRoute => GoRouteData.$route(
name: 'TrackingProtectionExceptionsRoute', name: 'TrackingProtectionExceptionsRoute',
factory: $TrackingProtectionExceptionsRoute._fromState, factory: $TrackingProtectionExceptionsRoute._fromState,
), ),
GoRouteData.$route(
path: 'custom_tracking_protection',
name: 'CustomTrackingProtectionRoute',
factory: $CustomTrackingProtectionRoute._fromState,
),
GoRouteData.$route( GoRouteData.$route(
path: 'error_logs', path: 'error_logs',
name: 'ErrorLogsRoute', name: 'ErrorLogsRoute',
@@ -478,6 +483,28 @@ mixin $TrackingProtectionExceptionsRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location); 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 { mixin $ErrorLogsRoute on GoRouteData {
static ErrorLogsRoute _fromState(GoRouterState state) => ErrorLogsRoute(); static ErrorLogsRoute _fromState(GoRouterState state) => ErrorLogsRoute();
+12
View File
@@ -75,6 +75,10 @@ part of 'routes.dart';
name: 'TrackingProtectionExceptionsRoute', name: 'TrackingProtectionExceptionsRoute',
path: 'tracking_protection_exceptions', path: 'tracking_protection_exceptions',
), ),
TypedGoRoute<CustomTrackingProtectionRoute>(
name: 'CustomTrackingProtectionRoute',
path: 'custom_tracking_protection',
),
TypedGoRoute<ErrorLogsRoute>(name: 'ErrorLogsRoute', path: 'error_logs'), TypedGoRoute<ErrorLogsRoute>(name: 'ErrorLogsRoute', path: 'error_logs'),
], ],
) )
@@ -202,3 +206,11 @@ class ErrorLogsRoute extends GoRouteData with $ErrorLogsRoute {
return const ErrorLogsScreen(); return const ErrorLogsScreen();
} }
} }
class CustomTrackingProtectionRoute extends GoRouteData
with $CustomTrackingProtectionRoute {
@override
Widget build(BuildContext context, GoRouterState state) {
return const CustomTrackingProtectionScreen();
}
}
@@ -29,6 +29,22 @@ import 'package:weblibre/features/user/domain/repositories/general_settings.dart
part 'engine_settings_replication.g.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) @Riverpod(keepAlive: true)
class EngineSettingsReplicationService class EngineSettingsReplicationService
extends _$EngineSettingsReplicationService { extends _$EngineSettingsReplicationService {
@@ -90,11 +106,34 @@ class EngineSettingsReplicationService
settings.javascriptEnabled) { settings.javascriptEnabled) {
await _service.javascriptEnabled(settings.javascriptEnabled); await _service.javascriptEnabled(settings.javascriptEnabled);
} }
if (previous.value?.trackingProtectionPolicy != // Check if tracking protection policy mode changed OR any custom ETP setting changed
settings.trackingProtectionPolicy) { final policyModeChanged = previous.value?.trackingProtectionPolicy !=
await _service.trackingProtectionPolicy( settings.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) { if (previous.value?.httpsOnlyMode != settings.httpsOnlyMode) {
await _service.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( RadioGroup(
groupValue: trackingProtectionPolicy, groupValue: trackingProtectionPolicy,
onChanged: (value) async { onChanged: (value) async {
await ref if (value != null) {
.read(saveEngineSettingsControllerProvider.notifier) // Save the policy change
.save( await ref
(currentSettings) => currentSettings.copyWith .read(saveEngineSettingsControllerProvider.notifier)
.trackingProtectionPolicy(value), .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( child: const Column(
children: [ children: [
@@ -541,8 +554,10 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget {
), ),
RadioListTile<TrackingProtectionPolicy>.adaptive( RadioListTile<TrackingProtectionPolicy>.adaptive(
value: TrackingProtectionPolicy.custom, value: TrackingProtectionPolicy.custom,
toggleable: true,
title: Text('Custom'), title: Text('Custom'),
subtitle: Text('Choose which trackers and scripts to block.'), 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 @override
List<String> get locales => super.locales!; 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 QueryParameterStripping queryParameterStripping;
final BounceTrackingProtectionMode bounceTrackingProtectionMode; final BounceTrackingProtectionMode bounceTrackingProtectionMode;
@@ -135,6 +160,17 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
required super.fingerprintingProtectionOverrides, required super.fingerprintingProtectionOverrides,
required this.enablePdfJs, required this.enablePdfJs,
required super.locales, 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({ EngineSettings.withDefaults({
@@ -160,6 +196,17 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
String? fingerprintingProtectionOverrides, String? fingerprintingProtectionOverrides,
bool? enablePdfJs, bool? enablePdfJs,
List<String>? locales, 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 ?? QueryParameterStripping.disabled, queryParameterStripping ?? QueryParameterStripping.disabled,
bounceTrackingProtectionMode = bounceTrackingProtectionMode =
@@ -199,6 +246,19 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
WidgetsBinding.instance.platformDispatcher.locales WidgetsBinding.instance.platformDispatcher.locales
.map((x) => x.toLanguageTag()) .map((x) => x.toLanguageTag())
.toList(), .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) => static AddonCollection? _addonCollectionFromJson(String? json) =>
@@ -238,5 +298,16 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable {
fingerprintingProtectionOverrides, fingerprintingProtectionOverrides,
enablePdfJs, enablePdfJs,
locales, 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 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. /// 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)`. /// 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, String? fingerprintingProtectionOverrides,
bool enablePdfJs, bool enablePdfJs,
List<String>? locales, 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 @override
EngineSettings locales(List<String>? locales) => call(locales: locales); 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 @override
/// Creates a new instance with the provided field values. /// 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)`. /// 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? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(),
Object? enablePdfJs = const $CopyWithPlaceholder(), Object? enablePdfJs = const $CopyWithPlaceholder(),
Object? locales = 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( return EngineSettings(
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder() javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
@@ -361,6 +455,53 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
? _value.locales ? _value.locales
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: locales as List<String>?, : 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>?) locales: (json['locales'] as List<dynamic>?)
?.map((e) => e as String) ?.map((e) => e as String)
.toList(), .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( Map<String, dynamic> _$EngineSettingsToJson(
@@ -463,6 +625,20 @@ Map<String, dynamic> _$EngineSettingsToJson(
.webContentIsolationStrategy]!, .webContentIsolationStrategy]!,
'enterpriseRootsEnabled': instance.enterpriseRootsEnabled, 'enterpriseRootsEnabled': instance.enterpriseRootsEnabled,
'locales': instance.locales, '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': 'queryParameterStripping':
_$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!, _$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!,
'bounceTrackingProtectionMode': 'bounceTrackingProtectionMode':
@@ -528,3 +704,16 @@ const _$DohSettingsModeEnumMap = {
DohSettingsMode.max: 'max', DohSettingsMode.max: 'max',
DohSettingsMode.off: 'off', 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'] 'locales': settings['locales']
?.readAs(DriftSqlType.string, db.typeMapping) ?.readAs(DriftSqlType.string, db.typeMapping)
.mapNotNull(jsonDecode), .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() => String _$engineSettingsRepositoryHash() =>
r'916abcd932fc3b3b1e060bc93a46c1a3a38bb984'; r'a3bcd3a82d251ce6fd0adc4922e512533d70a10a';
abstract class _$EngineSettingsRepository abstract class _$EngineSettingsRepository
extends $StreamNotifier<EngineSettings> { extends $StreamNotifier<EngineSettings> {
@@ -9,15 +9,19 @@ package eu.weblibre.flutter_mozilla_components.api
import eu.weblibre.flutter_mozilla_components.GlobalComponents import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.pigeons.ColorScheme import eu.weblibre.flutter_mozilla_components.pigeons.ColorScheme
import eu.weblibre.flutter_mozilla_components.pigeons.CookieBannerHandlingMode import eu.weblibre.flutter_mozilla_components.pigeons.CookieBannerHandlingMode
import eu.weblibre.flutter_mozilla_components.pigeons.CustomCookiePolicy
import eu.weblibre.flutter_mozilla_components.pigeons.DohSettingsMode import eu.weblibre.flutter_mozilla_components.pigeons.DohSettingsMode
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoEngineSettings import eu.weblibre.flutter_mozilla_components.pigeons.GeckoEngineSettings
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi import eu.weblibre.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
import eu.weblibre.flutter_mozilla_components.pigeons.HttpsOnlyMode import eu.weblibre.flutter_mozilla_components.pigeons.HttpsOnlyMode
import eu.weblibre.flutter_mozilla_components.pigeons.QueryParameterStripping import eu.weblibre.flutter_mozilla_components.pigeons.QueryParameterStripping
import eu.weblibre.flutter_mozilla_components.pigeons.TrackingScope
import eu.weblibre.flutter_mozilla_components.pigeons.WebContentIsolationStrategy import eu.weblibre.flutter_mozilla_components.pigeons.WebContentIsolationStrategy
import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.CookiePolicy
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.feature.addons.logger import mozilla.components.feature.addons.logger
import mozilla.components.feature.session.SettingsUseCases import mozilla.components.feature.session.SettingsUseCases
@@ -35,7 +39,14 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
requireNotNull(GlobalComponents.components) { "Components not initialized" } requireNotNull(GlobalComponents.components) { "Components not initialized" }
} }
private fun updateFingerprintingProtection(trackingProtectionPolicy: eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy) { /**
* Updates fingerprinting protection settings based on the tracking protection policy.
* For CUSTOM mode, uses the settings from GeckoEngineSettings to determine the behavior.
*/
private fun updateFingerprintingProtection(
trackingProtectionPolicy: eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy,
settings: GeckoEngineSettings? = null
) {
when(trackingProtectionPolicy) { when(trackingProtectionPolicy) {
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> { eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> {
components.core.engineSettings.fingerprintingProtection = true components.core.engineSettings.fingerprintingProtection = true
@@ -45,14 +56,90 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
components.core.engineSettings.fingerprintingProtection = false components.core.engineSettings.fingerprintingProtection = false
components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = true components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = true
} }
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.CUSTOM -> TODO() eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.CUSTOM -> {
// Handle suspected fingerprinters (separate from FINGERPRINTING category)
if (settings?.blockSuspectedFingerprinters == true) {
when (settings.suspectedFingerprintersScope) {
TrackingScope.ALL -> {
components.core.engineSettings.fingerprintingProtection = true
components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = true
}
TrackingScope.PRIVATE_ONLY, null -> {
components.core.engineSettings.fingerprintingProtection = false
components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = true
}
}
} else {
components.core.engineSettings.fingerprintingProtection = false
components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = false
}
}
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.NONE -> { eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.NONE -> {
components.core.engineSettings.fingerprintingProtection = false components.core.engineSettings.fingerprintingProtection = false
components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = true components.core.engineSettings.fingerprintingProtectionPrivateBrowsing = false
} }
} }
} }
/**
* Creates a custom tracking protection policy based on user settings.
* Matches Fenix's implementation where AD, ANALYTICS, SOCIAL, and MOZILLA_SOCIAL
* are always blocked, while other categories are configurable.
*/
private fun createCustomTrackingProtectionPolicy(settings: GeckoEngineSettings): TrackingProtectionPolicy {
// Always include these categories (not user-configurable, matches Fenix)
val categories = mutableListOf(
TrackingCategory.AD,
TrackingCategory.ANALYTICS,
TrackingCategory.SOCIAL,
TrackingCategory.MOZILLA_SOCIAL,
)
// Add configurable categories
if (settings.blockTrackingContent == true) {
categories.add(TrackingCategory.SCRIPTS_AND_SUB_RESOURCES)
}
if (settings.blockFingerprinters == true) {
categories.add(TrackingCategory.FINGERPRINTING)
}
if (settings.blockCryptominers == true) {
categories.add(TrackingCategory.CRYPTOMINING)
}
// Determine cookie policy
val cookiePolicy = if (settings.blockCookies != true) {
CookiePolicy.ACCEPT_ALL
} else {
when (settings.customCookiePolicy) {
CustomCookiePolicy.TOTAL_PROTECTION -> CookiePolicy.ACCEPT_FIRST_PARTY_AND_ISOLATE_OTHERS
CustomCookiePolicy.CROSS_SITE_TRACKERS -> CookiePolicy.ACCEPT_NON_TRACKERS
CustomCookiePolicy.UNVISITED -> CookiePolicy.ACCEPT_VISITED
CustomCookiePolicy.THIRD_PARTY -> CookiePolicy.ACCEPT_ONLY_FIRST_PARTY
CustomCookiePolicy.ALL_COOKIES -> CookiePolicy.ACCEPT_NONE
null -> CookiePolicy.ACCEPT_FIRST_PARTY_AND_ISOLATE_OTHERS // default
}
}
// Build policy
val policy = TrackingProtectionPolicy.select(
trackingCategories = categories.toTypedArray(),
cookiePolicy = cookiePolicy,
cookiePurging = settings.blockRedirectTrackers ?: true,
strictSocialTrackingProtection = settings.blockTrackingContent ?: true,
allowListBaselineTrackingProtection = settings.allowListBaseline ?: true,
allowListConvenienceTrackingProtection = settings.allowListConvenience ?: false,
)
// Apply scope for tracking content
return if (settings.trackingContentScope == TrackingScope.PRIVATE_ONLY) {
policy.forPrivateSessionsOnly()
} else {
policy
}
}
override fun setDefaultSettings(settings: GeckoEngineSettings) { override fun setDefaultSettings(settings: GeckoEngineSettings) {
if(settings.javascriptEnabled != null) { if(settings.javascriptEnabled != null) {
components.core.engineSettings.javascriptEnabled = settings.javascriptEnabled; components.core.engineSettings.javascriptEnabled = settings.javascriptEnabled;
@@ -62,10 +149,10 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.NONE -> TrackingProtectionPolicy.none() eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.NONE -> TrackingProtectionPolicy.none()
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.RECOMMENDED -> TrackingProtectionPolicy.recommended() eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.RECOMMENDED -> TrackingProtectionPolicy.recommended()
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> TrackingProtectionPolicy.strict() eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> TrackingProtectionPolicy.strict()
eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.CUSTOM -> TODO() eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.CUSTOM -> createCustomTrackingProtectionPolicy(settings)
} }
updateFingerprintingProtection(settings.trackingProtectionPolicy) updateFingerprintingProtection(settings.trackingProtectionPolicy, settings)
} }
if(settings.httpsOnlyMode != null) { if(settings.httpsOnlyMode != null) {
components.core.engineSettings.httpsOnlyMode = when(settings.httpsOnlyMode) { components.core.engineSettings.httpsOnlyMode = when(settings.httpsOnlyMode) {
@@ -202,21 +289,10 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
reloadSession = true reloadSession = true
} }
if(settings.contentBlocking != null) { if(settings.contentBlocking != null) {
components.core.engine.settings.queryParameterStripping = when(settings.contentBlocking.queryParameterStripping) { components.core.engine.settings.queryParameterStripping = components.core.engineSettings.queryParameterStripping
QueryParameterStripping.ENABLED -> true components.core.engine.settings.queryParameterStrippingPrivateBrowsing = components.core.engineSettings.queryParameterStrippingPrivateBrowsing
QueryParameterStripping.DISABLED -> false components.core.engine.settings.queryParameterStrippingAllowList = components.core.engineSettings.queryParameterStrippingAllowList
QueryParameterStripping.PRIVATE_ONLY -> false components.core.engine.settings.queryParameterStrippingStripList = components.core.engineSettings.queryParameterStrippingStripList
}
components.core.engine.settings.queryParameterStrippingPrivateBrowsing = when(settings.contentBlocking.queryParameterStripping) {
QueryParameterStripping.ENABLED -> true
QueryParameterStripping.DISABLED -> false
QueryParameterStripping.PRIVATE_ONLY -> true
}
components.core.engine.settings.queryParameterStrippingAllowList = settings.contentBlocking.queryParameterStrippingAllowList
components.core.engine.settings.queryParameterStrippingStripList = settings.contentBlocking.queryParameterStrippingStripList
components.core.engine.settings
reloadSession = true reloadSession = true
} }
if(settings.enterpriseRootsEnabled != null) { if(settings.enterpriseRootsEnabled != null) {
@@ -224,18 +300,13 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
reloadSession = true reloadSession = true
} }
if(settings.dohSettings != null) { if(settings.dohSettings != null) {
components.core.engine.settings.dohSettingsMode = when(settings.dohSettings.dohSettingsMode) { components.core.engine.settings.dohSettingsMode = components.core.engineSettings.dohSettingsMode
DohSettingsMode.GECKO_DEFAULT -> Engine.DohSettingsMode.DEFAULT components.core.engine.settings.dohProviderUrl = components.core.engineSettings.dohProviderUrl
DohSettingsMode.INCREASED -> Engine.DohSettingsMode.INCREASED components.core.engine.settings.dohDefaultProviderUrl = components.core.engineSettings.dohDefaultProviderUrl
DohSettingsMode.MAX -> Engine.DohSettingsMode.MAX components.core.engine.settings.dohExceptionsList = components.core.engineSettings.dohExceptionsList
DohSettingsMode.OFF -> Engine.DohSettingsMode.OFF
}
components.core.engine.settings.dohProviderUrl = settings.dohSettings.dohProviderUrl
components.core.engine.settings.dohDefaultProviderUrl = settings.dohSettings.dohDefaultProviderUrl
components.core.engine.settings.dohExceptionsList = settings.dohSettings.dohExceptionsList
} }
if(settings.fingerprintingProtectionOverrides != null) { if(settings.fingerprintingProtectionOverrides != null) {
components.core.engine.settings.fingerprintingProtectionOverrides = settings.fingerprintingProtectionOverrides components.core.engine.settings.fingerprintingProtectionOverrides = components.core.engineSettings.fingerprintingProtectionOverrides
} }
if(reloadSession) { if(reloadSession) {
@@ -334,6 +334,55 @@ enum class WebContentIsolationStrategy(val raw: Int) {
} }
} }
/**
* Cookie blocking policy for Custom tracking protection mode.
* Note: These only apply when blockCookies is true.
*/
enum class CustomCookiePolicy(val raw: Int) {
/**
* Total Cookie Protection - Dynamic First-Party Isolation (dFPI)
* Most private option, isolates cookies per site
*/
TOTAL_PROTECTION(0),
/**
* Block cross-site and social media tracker cookies
* Allows most cookies but blocks tracking cookies
*/
CROSS_SITE_TRACKERS(1),
/**
* Block cookies from sites you haven't visited
* Balances privacy with functionality
*/
UNVISITED(2),
/**
* Block all third-party cookies
* Only allows first-party cookies
*/
THIRD_PARTY(3),
/** Block all cookies (may break many sites) */
ALL_COOKIES(4);
companion object {
fun ofRaw(raw: Int): CustomCookiePolicy? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** Scope for applying tracking protection features */
enum class TrackingScope(val raw: Int) {
/** Apply to all browsing (normal + private) */
ALL(0),
/** Apply only to private browsing tabs */
PRIVATE_ONLY(1);
companion object {
fun ofRaw(raw: Int): TrackingScope? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class DohSettingsMode(val raw: Int) { enum class DohSettingsMode(val raw: Int) {
GECKO_DEFAULT(0), GECKO_DEFAULT(0),
INCREASED(1), INCREASED(1),
@@ -1865,7 +1914,32 @@ data class GeckoEngineSettings (
val enterpriseRootsEnabled: Boolean? = null, val enterpriseRootsEnabled: Boolean? = null,
val dohSettings: DohSettings? = null, val dohSettings: DohSettings? = null,
val fingerprintingProtectionOverrides: String? = null, val fingerprintingProtectionOverrides: String? = null,
val locales: List<String>? = null val locales: List<String>? = null,
/** Master toggle for cookie blocking in Custom mode */
val blockCookies: Boolean? = null,
/** Cookie policy selection (only applies when blockCookies is true) */
val customCookiePolicy: CustomCookiePolicy? = null,
/** Block tracking scripts and content */
val blockTrackingContent: Boolean? = null,
/** Scope for tracking content blocking */
val trackingContentScope: TrackingScope? = null,
/** Block cryptomining scripts */
val blockCryptominers: Boolean? = null,
/** Block known fingerprinters (FINGERPRINTING tracking category) */
val blockFingerprinters: Boolean? = null,
/** Block redirect trackers via cookie purging */
val blockRedirectTrackers: Boolean? = null,
/**
* Block suspected fingerprinters (separate from FINGERPRINTING category)
* Controls GeckoView's fingerprintingProtection settings
*/
val blockSuspectedFingerprinters: Boolean? = null,
/** Scope for suspected fingerprinters blocking */
val suspectedFingerprintersScope: TrackingScope? = null,
/** Allow baseline tracking protection exceptions (prevents major site breakage) */
val allowListBaseline: Boolean? = null,
/** Allow convenience tracking protection exceptions (fixes minor issues) */
val allowListConvenience: Boolean? = null
) )
{ {
companion object { companion object {
@@ -1886,7 +1960,18 @@ data class GeckoEngineSettings (
val dohSettings = pigeonVar_list[13] as DohSettings? val dohSettings = pigeonVar_list[13] as DohSettings?
val fingerprintingProtectionOverrides = pigeonVar_list[14] as String? val fingerprintingProtectionOverrides = pigeonVar_list[14] as String?
val locales = pigeonVar_list[15] as List<String>? val locales = pigeonVar_list[15] as List<String>?
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking, enterpriseRootsEnabled, dohSettings, fingerprintingProtectionOverrides, locales) val blockCookies = pigeonVar_list[16] as Boolean?
val customCookiePolicy = pigeonVar_list[17] as CustomCookiePolicy?
val blockTrackingContent = pigeonVar_list[18] as Boolean?
val trackingContentScope = pigeonVar_list[19] as TrackingScope?
val blockCryptominers = pigeonVar_list[20] as Boolean?
val blockFingerprinters = pigeonVar_list[21] as Boolean?
val blockRedirectTrackers = pigeonVar_list[22] as Boolean?
val blockSuspectedFingerprinters = pigeonVar_list[23] as Boolean?
val suspectedFingerprintersScope = pigeonVar_list[24] as TrackingScope?
val allowListBaseline = pigeonVar_list[25] as Boolean?
val allowListConvenience = pigeonVar_list[26] as Boolean?
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking, enterpriseRootsEnabled, dohSettings, fingerprintingProtectionOverrides, locales, blockCookies, customCookiePolicy, blockTrackingContent, trackingContentScope, blockCryptominers, blockFingerprinters, blockRedirectTrackers, blockSuspectedFingerprinters, suspectedFingerprintersScope, allowListBaseline, allowListConvenience)
} }
} }
fun toList(): List<Any?> { fun toList(): List<Any?> {
@@ -1907,6 +1992,17 @@ data class GeckoEngineSettings (
dohSettings, dohSettings,
fingerprintingProtectionOverrides, fingerprintingProtectionOverrides,
locales, locales,
blockCookies,
customCookiePolicy,
blockTrackingContent,
trackingContentScope,
blockCryptominers,
blockFingerprinters,
blockRedirectTrackers,
blockSuspectedFingerprinters,
suspectedFingerprintersScope,
allowListBaseline,
allowListConvenience,
) )
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
@@ -2945,330 +3041,340 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
} }
145.toByte() -> { 145.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
DohSettingsMode.ofRaw(it.toInt()) CustomCookiePolicy.ofRaw(it.toInt())
} }
} }
146.toByte() -> { 146.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
DownloadStatus.ofRaw(it.toInt()) TrackingScope.ofRaw(it.toInt())
} }
} }
147.toByte() -> { 147.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
LogLevel.ofRaw(it.toInt()) DohSettingsMode.ofRaw(it.toInt())
} }
} }
148.toByte() -> { 148.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
MlProgressType.ofRaw(it.toInt()) DownloadStatus.ofRaw(it.toInt())
} }
} }
149.toByte() -> { 149.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
MlProgressStatus.ofRaw(it.toInt()) LogLevel.ofRaw(it.toInt())
} }
} }
150.toByte() -> { 150.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
ClearDataType.ofRaw(it.toInt()) MlProgressType.ofRaw(it.toInt())
} }
} }
151.toByte() -> { 151.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
GeckoFetchMethod.ofRaw(it.toInt()) MlProgressStatus.ofRaw(it.toInt())
} }
} }
152.toByte() -> { 152.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
GeckoFetchRedircet.ofRaw(it.toInt()) ClearDataType.ofRaw(it.toInt())
} }
} }
153.toByte() -> { 153.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
GeckoFetchCookiePolicy.ofRaw(it.toInt()) GeckoFetchMethod.ofRaw(it.toInt())
} }
} }
154.toByte() -> { 154.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
BookmarkNodeType.ofRaw(it.toInt()) GeckoFetchRedircet.ofRaw(it.toInt())
} }
} }
155.toByte() -> { 155.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
SitePermissionStatus.ofRaw(it.toInt()) GeckoFetchCookiePolicy.ofRaw(it.toInt())
} }
} }
156.toByte() -> { 156.toByte() -> {
return (readValue(buffer) as Long?)?.let { return (readValue(buffer) as Long?)?.let {
AutoplayStatus.ofRaw(it.toInt()) BookmarkNodeType.ofRaw(it.toInt())
} }
} }
157.toByte() -> { 157.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as Long?)?.let {
TranslationOptions.fromList(it) SitePermissionStatus.ofRaw(it.toInt())
} }
} }
158.toByte() -> { 158.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as Long?)?.let {
ReaderState.fromList(it) AutoplayStatus.ofRaw(it.toInt())
} }
} }
159.toByte() -> { 159.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AddTabParams.fromList(it) TranslationOptions.fromList(it)
} }
} }
160.toByte() -> { 160.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
LastMediaAccessState.fromList(it) ReaderState.fromList(it)
} }
} }
161.toByte() -> { 161.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryMetadataKey.fromList(it) AddTabParams.fromList(it)
} }
} }
162.toByte() -> { 162.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
PackageCategoryValue.fromList(it) LastMediaAccessState.fromList(it)
} }
} }
163.toByte() -> { 163.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ExternalPackage.fromList(it) HistoryMetadataKey.fromList(it)
} }
} }
164.toByte() -> { 164.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
LoadUrlFlagsValue.fromList(it) PackageCategoryValue.fromList(it)
} }
} }
165.toByte() -> { 165.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SourceValue.fromList(it) ExternalPackage.fromList(it)
} }
} }
166.toByte() -> { 166.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabState.fromList(it) LoadUrlFlagsValue.fromList(it)
} }
} }
167.toByte() -> { 167.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
RecoverableTab.fromList(it) SourceValue.fromList(it)
} }
} }
168.toByte() -> { 168.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
RecoverableBrowserState.fromList(it) TabState.fromList(it)
} }
} }
169.toByte() -> { 169.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
IconRequest.fromList(it) RecoverableTab.fromList(it)
} }
} }
170.toByte() -> { 170.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ResourceSize.fromList(it) RecoverableBrowserState.fromList(it)
} }
} }
171.toByte() -> { 171.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
Resource.fromList(it) IconRequest.fromList(it)
} }
} }
172.toByte() -> { 172.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
IconResult.fromList(it) ResourceSize.fromList(it)
} }
} }
173.toByte() -> { 173.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
CookiePartitionKey.fromList(it) Resource.fromList(it)
} }
} }
174.toByte() -> { 174.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
Cookie.fromList(it) IconResult.fromList(it)
} }
} }
175.toByte() -> { 175.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
VisitInfo.fromList(it) CookiePartitionKey.fromList(it)
} }
} }
176.toByte() -> { 176.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryItem.fromList(it) Cookie.fromList(it)
} }
} }
177.toByte() -> { 177.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
HistoryState.fromList(it) VisitInfo.fromList(it)
} }
} }
178.toByte() -> { 178.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ReaderableState.fromList(it) HistoryItem.fromList(it)
} }
} }
179.toByte() -> { 179.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SecurityInfoState.fromList(it) HistoryState.fromList(it)
} }
} }
180.toByte() -> { 180.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it) ReaderableState.fromList(it)
} }
} }
181.toByte() -> { 181.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it) SecurityInfoState.fromList(it)
} }
} }
182.toByte() -> { 182.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it) TabContentState.fromList(it)
} }
} }
183.toByte() -> { 183.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
WebExtensionData.fromList(it) FindResultState.fromList(it)
} }
} }
184.toByte() -> { 184.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoSuggestion.fromList(it) CustomSelectionAction.fromList(it)
} }
} }
185.toByte() -> { 185.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TabContent.fromList(it) WebExtensionData.fromList(it)
} }
} }
186.toByte() -> { 186.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ContentBlocking.fromList(it) GeckoSuggestion.fromList(it)
} }
} }
187.toByte() -> { 187.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
DohSettings.fromList(it) TabContent.fromList(it)
} }
} }
188.toByte() -> { 188.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoEngineSettings.fromList(it) ContentBlocking.fromList(it)
} }
} }
189.toByte() -> { 189.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AutocompleteResult.fromList(it) DohSettings.fromList(it)
} }
} }
190.toByte() -> { 190.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
UnknownHitResult.fromList(it) GeckoEngineSettings.fromList(it)
} }
} }
191.toByte() -> { 191.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ImageHitResult.fromList(it) AutocompleteResult.fromList(it)
} }
} }
192.toByte() -> { 192.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
VideoHitResult.fromList(it) UnknownHitResult.fromList(it)
} }
} }
193.toByte() -> { 193.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AudioHitResult.fromList(it) ImageHitResult.fromList(it)
} }
} }
194.toByte() -> { 194.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ImageSrcHitResult.fromList(it) VideoHitResult.fromList(it)
} }
} }
195.toByte() -> { 195.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
PhoneHitResult.fromList(it) AudioHitResult.fromList(it)
} }
} }
196.toByte() -> { 196.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
EmailHitResult.fromList(it) ImageSrcHitResult.fromList(it)
} }
} }
197.toByte() -> { 197.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeoHitResult.fromList(it) PhoneHitResult.fromList(it)
} }
} }
198.toByte() -> { 198.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
DownloadState.fromList(it) EmailHitResult.fromList(it)
} }
} }
199.toByte() -> { 199.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ShareInternetResourceState.fromList(it) GeoHitResult.fromList(it)
} }
} }
200.toByte() -> { 200.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
AddonCollection.fromList(it) DownloadState.fromList(it)
} }
} }
201.toByte() -> { 201.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoPref.fromList(it) ShareInternetResourceState.fromList(it)
} }
} }
202.toByte() -> { 202.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
MlProgressData.fromList(it) AddonCollection.fromList(it)
} }
} }
203.toByte() -> { 203.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
ContainerSiteAssignment.fromList(it) GeckoPref.fromList(it)
} }
} }
204.toByte() -> { 204.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoHeader.fromList(it) MlProgressData.fromList(it)
} }
} }
205.toByte() -> { 205.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchRequest.fromList(it) ContainerSiteAssignment.fromList(it)
} }
} }
206.toByte() -> { 206.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchResponse.fromList(it) GeckoHeader.fromList(it)
} }
} }
207.toByte() -> { 207.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
BookmarkNode.fromList(it) GeckoFetchRequest.fromList(it)
} }
} }
208.toByte() -> { 208.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
BookmarkInfo.fromList(it) GeckoFetchResponse.fromList(it)
} }
} }
209.toByte() -> { 209.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
SitePermissions.fromList(it) BookmarkNode.fromList(it)
} }
} }
210.toByte() -> { 210.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
BookmarkInfo.fromList(it)
}
}
211.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SitePermissions.fromList(it)
}
}
212.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let { return (readValue(buffer) as? List<Any?>)?.let {
TrackingProtectionException.fromList(it) TrackingProtectionException.fromList(it)
} }
@@ -3342,270 +3448,278 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(144) stream.write(144)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is DohSettingsMode -> { is CustomCookiePolicy -> {
stream.write(145) stream.write(145)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is DownloadStatus -> { is TrackingScope -> {
stream.write(146) stream.write(146)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is LogLevel -> { is DohSettingsMode -> {
stream.write(147) stream.write(147)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is MlProgressType -> { is DownloadStatus -> {
stream.write(148) stream.write(148)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is MlProgressStatus -> { is LogLevel -> {
stream.write(149) stream.write(149)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is ClearDataType -> { is MlProgressType -> {
stream.write(150) stream.write(150)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is GeckoFetchMethod -> { is MlProgressStatus -> {
stream.write(151) stream.write(151)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is GeckoFetchRedircet -> { is ClearDataType -> {
stream.write(152) stream.write(152)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is GeckoFetchCookiePolicy -> { is GeckoFetchMethod -> {
stream.write(153) stream.write(153)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is BookmarkNodeType -> { is GeckoFetchRedircet -> {
stream.write(154) stream.write(154)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is SitePermissionStatus -> { is GeckoFetchCookiePolicy -> {
stream.write(155) stream.write(155)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is AutoplayStatus -> { is BookmarkNodeType -> {
stream.write(156) stream.write(156)
writeValue(stream, value.raw.toLong()) writeValue(stream, value.raw.toLong())
} }
is TranslationOptions -> { is SitePermissionStatus -> {
stream.write(157) stream.write(157)
writeValue(stream, value.toList()) writeValue(stream, value.raw.toLong())
} }
is ReaderState -> { is AutoplayStatus -> {
stream.write(158) stream.write(158)
writeValue(stream, value.toList()) writeValue(stream, value.raw.toLong())
} }
is AddTabParams -> { is TranslationOptions -> {
stream.write(159) stream.write(159)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is LastMediaAccessState -> { is ReaderState -> {
stream.write(160) stream.write(160)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryMetadataKey -> { is AddTabParams -> {
stream.write(161) stream.write(161)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is PackageCategoryValue -> { is LastMediaAccessState -> {
stream.write(162) stream.write(162)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ExternalPackage -> { is HistoryMetadataKey -> {
stream.write(163) stream.write(163)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is LoadUrlFlagsValue -> { is PackageCategoryValue -> {
stream.write(164) stream.write(164)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SourceValue -> { is ExternalPackage -> {
stream.write(165) stream.write(165)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabState -> { is LoadUrlFlagsValue -> {
stream.write(166) stream.write(166)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is RecoverableTab -> { is SourceValue -> {
stream.write(167) stream.write(167)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is RecoverableBrowserState -> { is TabState -> {
stream.write(168) stream.write(168)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is IconRequest -> { is RecoverableTab -> {
stream.write(169) stream.write(169)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ResourceSize -> { is RecoverableBrowserState -> {
stream.write(170) stream.write(170)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is Resource -> { is IconRequest -> {
stream.write(171) stream.write(171)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is IconResult -> { is ResourceSize -> {
stream.write(172) stream.write(172)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is CookiePartitionKey -> { is Resource -> {
stream.write(173) stream.write(173)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is Cookie -> { is IconResult -> {
stream.write(174) stream.write(174)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is VisitInfo -> { is CookiePartitionKey -> {
stream.write(175) stream.write(175)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryItem -> { is Cookie -> {
stream.write(176) stream.write(176)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is HistoryState -> { is VisitInfo -> {
stream.write(177) stream.write(177)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ReaderableState -> { is HistoryItem -> {
stream.write(178) stream.write(178)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SecurityInfoState -> { is HistoryState -> {
stream.write(179) stream.write(179)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabContentState -> { is ReaderableState -> {
stream.write(180) stream.write(180)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is FindResultState -> { is SecurityInfoState -> {
stream.write(181) stream.write(181)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is CustomSelectionAction -> { is TabContentState -> {
stream.write(182) stream.write(182)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is WebExtensionData -> { is FindResultState -> {
stream.write(183) stream.write(183)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoSuggestion -> { is CustomSelectionAction -> {
stream.write(184) stream.write(184)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TabContent -> { is WebExtensionData -> {
stream.write(185) stream.write(185)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ContentBlocking -> { is GeckoSuggestion -> {
stream.write(186) stream.write(186)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is DohSettings -> { is TabContent -> {
stream.write(187) stream.write(187)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoEngineSettings -> { is ContentBlocking -> {
stream.write(188) stream.write(188)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is AutocompleteResult -> { is DohSettings -> {
stream.write(189) stream.write(189)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is UnknownHitResult -> { is GeckoEngineSettings -> {
stream.write(190) stream.write(190)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ImageHitResult -> { is AutocompleteResult -> {
stream.write(191) stream.write(191)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is VideoHitResult -> { is UnknownHitResult -> {
stream.write(192) stream.write(192)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is AudioHitResult -> { is ImageHitResult -> {
stream.write(193) stream.write(193)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ImageSrcHitResult -> { is VideoHitResult -> {
stream.write(194) stream.write(194)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is PhoneHitResult -> { is AudioHitResult -> {
stream.write(195) stream.write(195)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is EmailHitResult -> { is ImageSrcHitResult -> {
stream.write(196) stream.write(196)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeoHitResult -> { is PhoneHitResult -> {
stream.write(197) stream.write(197)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is DownloadState -> { is EmailHitResult -> {
stream.write(198) stream.write(198)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ShareInternetResourceState -> { is GeoHitResult -> {
stream.write(199) stream.write(199)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is AddonCollection -> { is DownloadState -> {
stream.write(200) stream.write(200)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoPref -> { is ShareInternetResourceState -> {
stream.write(201) stream.write(201)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is MlProgressData -> { is AddonCollection -> {
stream.write(202) stream.write(202)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is ContainerSiteAssignment -> { is GeckoPref -> {
stream.write(203) stream.write(203)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoHeader -> { is MlProgressData -> {
stream.write(204) stream.write(204)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoFetchRequest -> { is ContainerSiteAssignment -> {
stream.write(205) stream.write(205)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is GeckoFetchResponse -> { is GeckoHeader -> {
stream.write(206) stream.write(206)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is BookmarkNode -> { is GeckoFetchRequest -> {
stream.write(207) stream.write(207)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is BookmarkInfo -> { is GeckoFetchResponse -> {
stream.write(208) stream.write(208)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SitePermissions -> { is BookmarkNode -> {
stream.write(209) stream.write(209)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is TrackingProtectionException -> { is BookmarkInfo -> {
stream.write(210) stream.write(210)
writeValue(stream, value.toList()) writeValue(stream, value.toList())
} }
is SitePermissions -> {
stream.write(211)
writeValue(stream, value.toList())
}
is TrackingProtectionException -> {
stream.write(212)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value) else -> super.writeValue(stream, value)
} }
} }
@@ -47,6 +47,7 @@ export 'src/pigeons/gecko.g.dart'
ContentBlocking, ContentBlocking,
CookieBannerHandlingMode, CookieBannerHandlingMode,
CookieSameSiteStatus, CookieSameSiteStatus,
CustomCookiePolicy,
DohSettings, DohSettings,
DohSettingsMode, DohSettingsMode,
EmailHitResult, EmailHitResult,
@@ -82,6 +83,7 @@ export 'src/pigeons/gecko.g.dart'
TabContentState, TabContentState,
TrackingProtectionException, TrackingProtectionException,
TrackingProtectionPolicy, TrackingProtectionPolicy,
TrackingScope,
UnknownHitResult, UnknownHitResult,
VideoHitResult, VideoHitResult,
VisitInfo, VisitInfo,
@@ -35,6 +35,40 @@ class GeckoEngineSettingsService {
); );
} }
/// Updates tracking protection policy with all custom settings.
/// Use this when in CUSTOM mode and any custom setting changes.
Future<void> customTrackingProtectionPolicy({
required TrackingProtectionPolicy trackingProtectionPolicy,
bool? blockCookies,
CustomCookiePolicy? customCookiePolicy,
bool? blockTrackingContent,
TrackingScope? trackingContentScope,
bool? blockCryptominers,
bool? blockFingerprinters,
bool? blockRedirectTrackers,
bool? blockSuspectedFingerprinters,
TrackingScope? suspectedFingerprintersScope,
bool? allowListBaseline,
bool? allowListConvenience,
}) {
return _api.updateRuntimeSettings(
GeckoEngineSettings(
trackingProtectionPolicy: trackingProtectionPolicy,
blockCookies: blockCookies,
customCookiePolicy: customCookiePolicy,
blockTrackingContent: blockTrackingContent,
trackingContentScope: trackingContentScope,
blockCryptominers: blockCryptominers,
blockFingerprinters: blockFingerprinters,
blockRedirectTrackers: blockRedirectTrackers,
blockSuspectedFingerprinters: blockSuspectedFingerprinters,
suspectedFingerprintersScope: suspectedFingerprintersScope,
allowListBaseline: allowListBaseline,
allowListConvenience: allowListConvenience,
),
);
}
Future<void> httpsOnlyMode(HttpsOnlyMode state) { Future<void> httpsOnlyMode(HttpsOnlyMode state) {
return _api.updateRuntimeSettings( return _api.updateRuntimeSettings(
GeckoEngineSettings(httpsOnlyMode: state), GeckoEngineSettings(httpsOnlyMode: state),
@@ -186,6 +186,33 @@ enum WebContentIsolationStrategy {
isolateHighValue, isolateHighValue,
} }
/// Cookie blocking policy for Custom tracking protection mode.
/// Note: These only apply when blockCookies is true.
enum CustomCookiePolicy {
/// Total Cookie Protection - Dynamic First-Party Isolation (dFPI)
/// Most private option, isolates cookies per site
totalProtection,
/// Block cross-site and social media tracker cookies
/// Allows most cookies but blocks tracking cookies
crossSiteTrackers,
/// Block cookies from sites you haven't visited
/// Balances privacy with functionality
unvisited,
/// Block all third-party cookies
/// Only allows first-party cookies
thirdParty,
/// Block all cookies (may break many sites)
allCookies,
}
/// Scope for applying tracking protection features
enum TrackingScope {
/// Apply to all browsing (normal + private)
all,
/// Apply only to private browsing tabs
privateOnly,
}
enum DohSettingsMode { enum DohSettingsMode {
geckoDefault, geckoDefault,
increased, increased,
@@ -2203,6 +2230,17 @@ class GeckoEngineSettings {
this.dohSettings, this.dohSettings,
this.fingerprintingProtectionOverrides, this.fingerprintingProtectionOverrides,
this.locales, this.locales,
this.blockCookies,
this.customCookiePolicy,
this.blockTrackingContent,
this.trackingContentScope,
this.blockCryptominers,
this.blockFingerprinters,
this.blockRedirectTrackers,
this.blockSuspectedFingerprinters,
this.suspectedFingerprintersScope,
this.allowListBaseline,
this.allowListConvenience,
}); });
bool? javascriptEnabled; bool? javascriptEnabled;
@@ -2237,6 +2275,40 @@ class GeckoEngineSettings {
List<String>? locales; List<String>? locales;
/// Master toggle for cookie blocking in Custom mode
bool? blockCookies;
/// Cookie policy selection (only applies when blockCookies is true)
CustomCookiePolicy? customCookiePolicy;
/// Block tracking scripts and content
bool? blockTrackingContent;
/// Scope for tracking content blocking
TrackingScope? trackingContentScope;
/// Block cryptomining scripts
bool? blockCryptominers;
/// Block known fingerprinters (FINGERPRINTING tracking category)
bool? blockFingerprinters;
/// Block redirect trackers via cookie purging
bool? blockRedirectTrackers;
/// Block suspected fingerprinters (separate from FINGERPRINTING category)
/// Controls GeckoView's fingerprintingProtection settings
bool? blockSuspectedFingerprinters;
/// Scope for suspected fingerprinters blocking
TrackingScope? suspectedFingerprintersScope;
/// Allow baseline tracking protection exceptions (prevents major site breakage)
bool? allowListBaseline;
/// Allow convenience tracking protection exceptions (fixes minor issues)
bool? allowListConvenience;
List<Object?> _toList() { List<Object?> _toList() {
return <Object?>[ return <Object?>[
javascriptEnabled, javascriptEnabled,
@@ -2255,6 +2327,17 @@ class GeckoEngineSettings {
dohSettings, dohSettings,
fingerprintingProtectionOverrides, fingerprintingProtectionOverrides,
locales, locales,
blockCookies,
customCookiePolicy,
blockTrackingContent,
trackingContentScope,
blockCryptominers,
blockFingerprinters,
blockRedirectTrackers,
blockSuspectedFingerprinters,
suspectedFingerprintersScope,
allowListBaseline,
allowListConvenience,
]; ];
} }
@@ -2280,6 +2363,17 @@ class GeckoEngineSettings {
dohSettings: result[13] as DohSettings?, dohSettings: result[13] as DohSettings?,
fingerprintingProtectionOverrides: result[14] as String?, fingerprintingProtectionOverrides: result[14] as String?,
locales: (result[15] as List<Object?>?)?.cast<String>(), locales: (result[15] as List<Object?>?)?.cast<String>(),
blockCookies: result[16] as bool?,
customCookiePolicy: result[17] as CustomCookiePolicy?,
blockTrackingContent: result[18] as bool?,
trackingContentScope: result[19] as TrackingScope?,
blockCryptominers: result[20] as bool?,
blockFingerprinters: result[21] as bool?,
blockRedirectTrackers: result[22] as bool?,
blockSuspectedFingerprinters: result[23] as bool?,
suspectedFingerprintersScope: result[24] as TrackingScope?,
allowListBaseline: result[25] as bool?,
allowListConvenience: result[26] as bool?,
); );
} }
@@ -3714,204 +3808,210 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is WebContentIsolationStrategy) { } else if (value is WebContentIsolationStrategy) {
buffer.putUint8(144); buffer.putUint8(144);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is DohSettingsMode) { } else if (value is CustomCookiePolicy) {
buffer.putUint8(145); buffer.putUint8(145);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is DownloadStatus) { } else if (value is TrackingScope) {
buffer.putUint8(146); buffer.putUint8(146);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is LogLevel) { } else if (value is DohSettingsMode) {
buffer.putUint8(147); buffer.putUint8(147);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is MlProgressType) { } else if (value is DownloadStatus) {
buffer.putUint8(148); buffer.putUint8(148);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is MlProgressStatus) { } else if (value is LogLevel) {
buffer.putUint8(149); buffer.putUint8(149);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is ClearDataType) { } else if (value is MlProgressType) {
buffer.putUint8(150); buffer.putUint8(150);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is GeckoFetchMethod) { } else if (value is MlProgressStatus) {
buffer.putUint8(151); buffer.putUint8(151);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is GeckoFetchRedircet) { } else if (value is ClearDataType) {
buffer.putUint8(152); buffer.putUint8(152);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is GeckoFetchCookiePolicy) { } else if (value is GeckoFetchMethod) {
buffer.putUint8(153); buffer.putUint8(153);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is BookmarkNodeType) { } else if (value is GeckoFetchRedircet) {
buffer.putUint8(154); buffer.putUint8(154);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is SitePermissionStatus) { } else if (value is GeckoFetchCookiePolicy) {
buffer.putUint8(155); buffer.putUint8(155);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is AutoplayStatus) { } else if (value is BookmarkNodeType) {
buffer.putUint8(156); buffer.putUint8(156);
writeValue(buffer, value.index); writeValue(buffer, value.index);
} else if (value is TranslationOptions) { } else if (value is SitePermissionStatus) {
buffer.putUint8(157); buffer.putUint8(157);
writeValue(buffer, value.encode()); writeValue(buffer, value.index);
} else if (value is ReaderState) { } else if (value is AutoplayStatus) {
buffer.putUint8(158); buffer.putUint8(158);
writeValue(buffer, value.encode()); writeValue(buffer, value.index);
} else if (value is AddTabParams) { } else if (value is TranslationOptions) {
buffer.putUint8(159); buffer.putUint8(159);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is LastMediaAccessState) { } else if (value is ReaderState) {
buffer.putUint8(160); buffer.putUint8(160);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryMetadataKey) { } else if (value is AddTabParams) {
buffer.putUint8(161); buffer.putUint8(161);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is PackageCategoryValue) { } else if (value is LastMediaAccessState) {
buffer.putUint8(162); buffer.putUint8(162);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ExternalPackage) { } else if (value is HistoryMetadataKey) {
buffer.putUint8(163); buffer.putUint8(163);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is LoadUrlFlagsValue) { } else if (value is PackageCategoryValue) {
buffer.putUint8(164); buffer.putUint8(164);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SourceValue) { } else if (value is ExternalPackage) {
buffer.putUint8(165); buffer.putUint8(165);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabState) { } else if (value is LoadUrlFlagsValue) {
buffer.putUint8(166); buffer.putUint8(166);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RecoverableTab) { } else if (value is SourceValue) {
buffer.putUint8(167); buffer.putUint8(167);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is RecoverableBrowserState) { } else if (value is TabState) {
buffer.putUint8(168); buffer.putUint8(168);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is IconRequest) { } else if (value is RecoverableTab) {
buffer.putUint8(169); buffer.putUint8(169);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ResourceSize) { } else if (value is RecoverableBrowserState) {
buffer.putUint8(170); buffer.putUint8(170);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is Resource) { } else if (value is IconRequest) {
buffer.putUint8(171); buffer.putUint8(171);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is IconResult) { } else if (value is ResourceSize) {
buffer.putUint8(172); buffer.putUint8(172);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is CookiePartitionKey) { } else if (value is Resource) {
buffer.putUint8(173); buffer.putUint8(173);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is Cookie) { } else if (value is IconResult) {
buffer.putUint8(174); buffer.putUint8(174);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is VisitInfo) { } else if (value is CookiePartitionKey) {
buffer.putUint8(175); buffer.putUint8(175);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryItem) { } else if (value is Cookie) {
buffer.putUint8(176); buffer.putUint8(176);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is HistoryState) { } else if (value is VisitInfo) {
buffer.putUint8(177); buffer.putUint8(177);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ReaderableState) { } else if (value is HistoryItem) {
buffer.putUint8(178); buffer.putUint8(178);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SecurityInfoState) { } else if (value is HistoryState) {
buffer.putUint8(179); buffer.putUint8(179);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabContentState) { } else if (value is ReaderableState) {
buffer.putUint8(180); buffer.putUint8(180);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is FindResultState) { } else if (value is SecurityInfoState) {
buffer.putUint8(181); buffer.putUint8(181);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) { } else if (value is TabContentState) {
buffer.putUint8(182); buffer.putUint8(182);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is WebExtensionData) { } else if (value is FindResultState) {
buffer.putUint8(183); buffer.putUint8(183);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoSuggestion) { } else if (value is CustomSelectionAction) {
buffer.putUint8(184); buffer.putUint8(184);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TabContent) { } else if (value is WebExtensionData) {
buffer.putUint8(185); buffer.putUint8(185);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ContentBlocking) { } else if (value is GeckoSuggestion) {
buffer.putUint8(186); buffer.putUint8(186);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is DohSettings) { } else if (value is TabContent) {
buffer.putUint8(187); buffer.putUint8(187);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoEngineSettings) { } else if (value is ContentBlocking) {
buffer.putUint8(188); buffer.putUint8(188);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is AutocompleteResult) { } else if (value is DohSettings) {
buffer.putUint8(189); buffer.putUint8(189);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is UnknownHitResult) { } else if (value is GeckoEngineSettings) {
buffer.putUint8(190); buffer.putUint8(190);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ImageHitResult) { } else if (value is AutocompleteResult) {
buffer.putUint8(191); buffer.putUint8(191);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is VideoHitResult) { } else if (value is UnknownHitResult) {
buffer.putUint8(192); buffer.putUint8(192);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is AudioHitResult) { } else if (value is ImageHitResult) {
buffer.putUint8(193); buffer.putUint8(193);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ImageSrcHitResult) { } else if (value is VideoHitResult) {
buffer.putUint8(194); buffer.putUint8(194);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is PhoneHitResult) { } else if (value is AudioHitResult) {
buffer.putUint8(195); buffer.putUint8(195);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is EmailHitResult) { } else if (value is ImageSrcHitResult) {
buffer.putUint8(196); buffer.putUint8(196);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeoHitResult) { } else if (value is PhoneHitResult) {
buffer.putUint8(197); buffer.putUint8(197);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is DownloadState) { } else if (value is EmailHitResult) {
buffer.putUint8(198); buffer.putUint8(198);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ShareInternetResourceState) { } else if (value is GeoHitResult) {
buffer.putUint8(199); buffer.putUint8(199);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is AddonCollection) { } else if (value is DownloadState) {
buffer.putUint8(200); buffer.putUint8(200);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoPref) { } else if (value is ShareInternetResourceState) {
buffer.putUint8(201); buffer.putUint8(201);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is MlProgressData) { } else if (value is AddonCollection) {
buffer.putUint8(202); buffer.putUint8(202);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is ContainerSiteAssignment) { } else if (value is GeckoPref) {
buffer.putUint8(203); buffer.putUint8(203);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoHeader) { } else if (value is MlProgressData) {
buffer.putUint8(204); buffer.putUint8(204);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoFetchRequest) { } else if (value is ContainerSiteAssignment) {
buffer.putUint8(205); buffer.putUint8(205);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is GeckoFetchResponse) { } else if (value is GeckoHeader) {
buffer.putUint8(206); buffer.putUint8(206);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is BookmarkNode) { } else if (value is GeckoFetchRequest) {
buffer.putUint8(207); buffer.putUint8(207);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is BookmarkInfo) { } else if (value is GeckoFetchResponse) {
buffer.putUint8(208); buffer.putUint8(208);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SitePermissions) { } else if (value is BookmarkNode) {
buffer.putUint8(209); buffer.putUint8(209);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is TrackingProtectionException) { } else if (value is BookmarkInfo) {
buffer.putUint8(210); buffer.putUint8(210);
writeValue(buffer, value.encode()); writeValue(buffer, value.encode());
} else if (value is SitePermissions) {
buffer.putUint8(211);
writeValue(buffer, value.encode());
} else if (value is TrackingProtectionException) {
buffer.putUint8(212);
writeValue(buffer, value.encode());
} else { } else {
super.writeValue(buffer, value); super.writeValue(buffer, value);
} }
@@ -3970,147 +4070,153 @@ class _PigeonCodec extends StandardMessageCodec {
return value == null ? null : WebContentIsolationStrategy.values[value]; return value == null ? null : WebContentIsolationStrategy.values[value];
case 145: case 145:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : DohSettingsMode.values[value]; return value == null ? null : CustomCookiePolicy.values[value];
case 146: case 146:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : DownloadStatus.values[value]; return value == null ? null : TrackingScope.values[value];
case 147: case 147:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : LogLevel.values[value]; return value == null ? null : DohSettingsMode.values[value];
case 148: case 148:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : MlProgressType.values[value]; return value == null ? null : DownloadStatus.values[value];
case 149: case 149:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : MlProgressStatus.values[value]; return value == null ? null : LogLevel.values[value];
case 150: case 150:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : ClearDataType.values[value]; return value == null ? null : MlProgressType.values[value];
case 151: case 151:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : GeckoFetchMethod.values[value]; return value == null ? null : MlProgressStatus.values[value];
case 152: case 152:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : GeckoFetchRedircet.values[value]; return value == null ? null : ClearDataType.values[value];
case 153: case 153:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : GeckoFetchCookiePolicy.values[value]; return value == null ? null : GeckoFetchMethod.values[value];
case 154: case 154:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : BookmarkNodeType.values[value]; return value == null ? null : GeckoFetchRedircet.values[value];
case 155: case 155:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : SitePermissionStatus.values[value]; return value == null ? null : GeckoFetchCookiePolicy.values[value];
case 156: case 156:
final value = readValue(buffer) as int?; final value = readValue(buffer) as int?;
return value == null ? null : AutoplayStatus.values[value]; return value == null ? null : BookmarkNodeType.values[value];
case 157: case 157:
return TranslationOptions.decode(readValue(buffer)!); final value = readValue(buffer) as int?;
return value == null ? null : SitePermissionStatus.values[value];
case 158: case 158:
return ReaderState.decode(readValue(buffer)!); final value = readValue(buffer) as int?;
return value == null ? null : AutoplayStatus.values[value];
case 159: case 159:
return AddTabParams.decode(readValue(buffer)!); return TranslationOptions.decode(readValue(buffer)!);
case 160: case 160:
return LastMediaAccessState.decode(readValue(buffer)!); return ReaderState.decode(readValue(buffer)!);
case 161: case 161:
return HistoryMetadataKey.decode(readValue(buffer)!); return AddTabParams.decode(readValue(buffer)!);
case 162: case 162:
return PackageCategoryValue.decode(readValue(buffer)!); return LastMediaAccessState.decode(readValue(buffer)!);
case 163: case 163:
return ExternalPackage.decode(readValue(buffer)!); return HistoryMetadataKey.decode(readValue(buffer)!);
case 164: case 164:
return LoadUrlFlagsValue.decode(readValue(buffer)!); return PackageCategoryValue.decode(readValue(buffer)!);
case 165: case 165:
return SourceValue.decode(readValue(buffer)!); return ExternalPackage.decode(readValue(buffer)!);
case 166: case 166:
return TabState.decode(readValue(buffer)!); return LoadUrlFlagsValue.decode(readValue(buffer)!);
case 167: case 167:
return RecoverableTab.decode(readValue(buffer)!); return SourceValue.decode(readValue(buffer)!);
case 168: case 168:
return RecoverableBrowserState.decode(readValue(buffer)!); return TabState.decode(readValue(buffer)!);
case 169: case 169:
return IconRequest.decode(readValue(buffer)!); return RecoverableTab.decode(readValue(buffer)!);
case 170: case 170:
return ResourceSize.decode(readValue(buffer)!); return RecoverableBrowserState.decode(readValue(buffer)!);
case 171: case 171:
return Resource.decode(readValue(buffer)!); return IconRequest.decode(readValue(buffer)!);
case 172: case 172:
return IconResult.decode(readValue(buffer)!); return ResourceSize.decode(readValue(buffer)!);
case 173: case 173:
return CookiePartitionKey.decode(readValue(buffer)!); return Resource.decode(readValue(buffer)!);
case 174: case 174:
return Cookie.decode(readValue(buffer)!); return IconResult.decode(readValue(buffer)!);
case 175: case 175:
return VisitInfo.decode(readValue(buffer)!); return CookiePartitionKey.decode(readValue(buffer)!);
case 176: case 176:
return HistoryItem.decode(readValue(buffer)!); return Cookie.decode(readValue(buffer)!);
case 177: case 177:
return HistoryState.decode(readValue(buffer)!); return VisitInfo.decode(readValue(buffer)!);
case 178: case 178:
return ReaderableState.decode(readValue(buffer)!); return HistoryItem.decode(readValue(buffer)!);
case 179: case 179:
return SecurityInfoState.decode(readValue(buffer)!); return HistoryState.decode(readValue(buffer)!);
case 180: case 180:
return TabContentState.decode(readValue(buffer)!); return ReaderableState.decode(readValue(buffer)!);
case 181: case 181:
return FindResultState.decode(readValue(buffer)!); return SecurityInfoState.decode(readValue(buffer)!);
case 182: case 182:
return CustomSelectionAction.decode(readValue(buffer)!); return TabContentState.decode(readValue(buffer)!);
case 183: case 183:
return WebExtensionData.decode(readValue(buffer)!); return FindResultState.decode(readValue(buffer)!);
case 184: case 184:
return GeckoSuggestion.decode(readValue(buffer)!); return CustomSelectionAction.decode(readValue(buffer)!);
case 185: case 185:
return TabContent.decode(readValue(buffer)!); return WebExtensionData.decode(readValue(buffer)!);
case 186: case 186:
return ContentBlocking.decode(readValue(buffer)!); return GeckoSuggestion.decode(readValue(buffer)!);
case 187: case 187:
return DohSettings.decode(readValue(buffer)!); return TabContent.decode(readValue(buffer)!);
case 188: case 188:
return GeckoEngineSettings.decode(readValue(buffer)!); return ContentBlocking.decode(readValue(buffer)!);
case 189: case 189:
return AutocompleteResult.decode(readValue(buffer)!); return DohSettings.decode(readValue(buffer)!);
case 190: case 190:
return UnknownHitResult.decode(readValue(buffer)!); return GeckoEngineSettings.decode(readValue(buffer)!);
case 191: case 191:
return ImageHitResult.decode(readValue(buffer)!); return AutocompleteResult.decode(readValue(buffer)!);
case 192: case 192:
return VideoHitResult.decode(readValue(buffer)!); return UnknownHitResult.decode(readValue(buffer)!);
case 193: case 193:
return AudioHitResult.decode(readValue(buffer)!); return ImageHitResult.decode(readValue(buffer)!);
case 194: case 194:
return ImageSrcHitResult.decode(readValue(buffer)!); return VideoHitResult.decode(readValue(buffer)!);
case 195: case 195:
return PhoneHitResult.decode(readValue(buffer)!); return AudioHitResult.decode(readValue(buffer)!);
case 196: case 196:
return EmailHitResult.decode(readValue(buffer)!); return ImageSrcHitResult.decode(readValue(buffer)!);
case 197: case 197:
return GeoHitResult.decode(readValue(buffer)!); return PhoneHitResult.decode(readValue(buffer)!);
case 198: case 198:
return DownloadState.decode(readValue(buffer)!); return EmailHitResult.decode(readValue(buffer)!);
case 199: case 199:
return ShareInternetResourceState.decode(readValue(buffer)!); return GeoHitResult.decode(readValue(buffer)!);
case 200: case 200:
return AddonCollection.decode(readValue(buffer)!); return DownloadState.decode(readValue(buffer)!);
case 201: case 201:
return GeckoPref.decode(readValue(buffer)!); return ShareInternetResourceState.decode(readValue(buffer)!);
case 202: case 202:
return MlProgressData.decode(readValue(buffer)!); return AddonCollection.decode(readValue(buffer)!);
case 203: case 203:
return ContainerSiteAssignment.decode(readValue(buffer)!); return GeckoPref.decode(readValue(buffer)!);
case 204: case 204:
return GeckoHeader.decode(readValue(buffer)!); return MlProgressData.decode(readValue(buffer)!);
case 205: case 205:
return GeckoFetchRequest.decode(readValue(buffer)!); return ContainerSiteAssignment.decode(readValue(buffer)!);
case 206: case 206:
return GeckoFetchResponse.decode(readValue(buffer)!); return GeckoHeader.decode(readValue(buffer)!);
case 207: case 207:
return BookmarkNode.decode(readValue(buffer)!); return GeckoFetchRequest.decode(readValue(buffer)!);
case 208: case 208:
return BookmarkInfo.decode(readValue(buffer)!); return GeckoFetchResponse.decode(readValue(buffer)!);
case 209: case 209:
return SitePermissions.decode(readValue(buffer)!); return BookmarkNode.decode(readValue(buffer)!);
case 210: case 210:
return BookmarkInfo.decode(readValue(buffer)!);
case 211:
return SitePermissions.decode(readValue(buffer)!);
case 212:
return TrackingProtectionException.decode(readValue(buffer)!); return TrackingProtectionException.decode(readValue(buffer)!);
default: default:
return super.readValueOfType(type, buffer); return super.readValueOfType(type, buffer);
@@ -646,6 +646,38 @@ enum WebContentIsolationStrategy {
isolateHighValue, isolateHighValue,
} }
/// Cookie blocking policy for Custom tracking protection mode.
/// Note: These only apply when blockCookies is true.
enum CustomCookiePolicy {
/// Total Cookie Protection - Dynamic First-Party Isolation (dFPI)
/// Most private option, isolates cookies per site
totalProtection,
/// Block cross-site and social media tracker cookies
/// Allows most cookies but blocks tracking cookies
crossSiteTrackers,
/// Block cookies from sites you haven't visited
/// Balances privacy with functionality
unvisited,
/// Block all third-party cookies
/// Only allows first-party cookies
thirdParty,
/// Block all cookies (may break many sites)
allCookies,
}
/// Scope for applying tracking protection features
enum TrackingScope {
/// Apply to all browsing (normal + private)
all,
/// Apply only to private browsing tabs
privateOnly,
}
class ContentBlocking { class ContentBlocking {
QueryParameterStripping queryParameterStripping; QueryParameterStripping queryParameterStripping;
String queryParameterStrippingAllowList; String queryParameterStrippingAllowList;
@@ -694,6 +726,41 @@ class GeckoEngineSettings {
final String? fingerprintingProtectionOverrides; final String? fingerprintingProtectionOverrides;
final List<String>? locales; final List<String>? locales;
// Custom Tracking Protection Settings
/// Master toggle for cookie blocking in Custom mode
final bool? blockCookies;
/// Cookie policy selection (only applies when blockCookies is true)
final CustomCookiePolicy? customCookiePolicy;
/// Block tracking scripts and content
final bool? blockTrackingContent;
/// Scope for tracking content blocking
final TrackingScope? trackingContentScope;
/// Block cryptomining scripts
final bool? blockCryptominers;
/// Block known fingerprinters (FINGERPRINTING tracking category)
final bool? blockFingerprinters;
/// Block redirect trackers via cookie purging
final bool? blockRedirectTrackers;
/// Block suspected fingerprinters (separate from FINGERPRINTING category)
/// Controls GeckoView's fingerprintingProtection settings
final bool? blockSuspectedFingerprinters;
/// Scope for suspected fingerprinters blocking
final TrackingScope? suspectedFingerprintersScope;
/// Allow baseline tracking protection exceptions (prevents major site breakage)
final bool? allowListBaseline;
/// Allow convenience tracking protection exceptions (fixes minor issues)
final bool? allowListConvenience;
GeckoEngineSettings( GeckoEngineSettings(
this.javascriptEnabled, this.javascriptEnabled,
this.trackingProtectionPolicy, this.trackingProtectionPolicy,
@@ -711,6 +778,17 @@ class GeckoEngineSettings {
this.dohSettings, this.dohSettings,
this.fingerprintingProtectionOverrides, this.fingerprintingProtectionOverrides,
this.locales, this.locales,
this.blockCookies,
this.customCookiePolicy,
this.blockTrackingContent,
this.trackingContentScope,
this.blockCryptominers,
this.blockFingerprinters,
this.blockRedirectTrackers,
this.blockSuspectedFingerprinters,
this.suspectedFingerprintersScope,
this.allowListBaseline,
this.allowListConvenience,
); );
} }