From 84e1fa37e0eb0030428ead54c6f3b0d530c5c8fc Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Thu, 22 Jan 2026 09:08:50 +0100 Subject: [PATCH] implement custom tracking policy --- app/lib/core/routing/routes.dart | 7 +- app/lib/core/routing/routes.g.dart | 27 ++ app/lib/core/routing/routes.settings.dart | 12 + .../services/engine_settings_replication.dart | 49 ++- .../screens/custom_tracking_protection.dart | 398 ++++++++++++++++++ .../screens/privacy_security_settings.dart | 27 +- .../user/data/models/engine_settings.dart | 71 ++++ .../user/data/models/engine_settings.g.dart | 189 +++++++++ .../domain/repositories/engine_settings.dart | 41 ++ .../repositories/engine_settings.g.dart | 2 +- .../api/GeckoEngineSettingsApiImpl.kt | 131 ++++-- .../pigeons/Gecko.g.kt | 388 +++++++++++------ .../lib/flutter_mozilla_components.dart | 2 + .../services/gecko_engine_settings.dart | 34 ++ .../lib/src/pigeons/gecko.g.dart | 372 ++++++++++------ .../pigeons/gecko.dart | 78 ++++ 16 files changed, 1513 insertions(+), 315 deletions(-) create mode 100644 app/lib/features/settings/presentation/screens/custom_tracking_protection.dart diff --git a/app/lib/core/routing/routes.dart b/app/lib/core/routing/routes.dart index 258e61b2..f4225b2a 100644 --- a/app/lib/core/routing/routes.dart +++ b/app/lib/core/routing/routes.dart @@ -56,6 +56,7 @@ import 'package:weblibre/features/settings/presentation/screens/addon_collection import 'package:weblibre/features/settings/presentation/screens/advanced_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/appearance_display_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/bang_settings.dart'; +import 'package:weblibre/features/settings/presentation/screens/custom_tracking_protection.dart'; import 'package:weblibre/features/settings/presentation/screens/doh_settings.dart'; import 'package:weblibre/features/settings/presentation/screens/error_logs_screen.dart'; import 'package:weblibre/features/settings/presentation/screens/fingerprint_settings.dart'; @@ -82,11 +83,11 @@ import 'package:weblibre/features/web_feed/presentation/screens/feed_edit.dart'; import 'package:weblibre/features/web_feed/presentation/screens/feed_list.dart'; import 'package:weblibre/features/web_feed/presentation/select_feed_dialog.dart'; +part 'routes.bangs.dart'; +part 'routes.browser.dart'; +part 'routes.feeds.dart'; part 'routes.g.dart'; part 'routes.settings.dart'; -part 'routes.browser.dart'; -part 'routes.bangs.dart'; -part 'routes.feeds.dart'; @TypedGoRoute(name: 'AboutRoute', path: '/about') class AboutRoute extends GoRouteData with $AboutRoute { diff --git a/app/lib/core/routing/routes.g.dart b/app/lib/core/routing/routes.g.dart index 349dfbbf..5fdf0547 100644 --- a/app/lib/core/routing/routes.g.dart +++ b/app/lib/core/routing/routes.g.dart @@ -151,6 +151,11 @@ RouteBase get $settingsRoute => GoRouteData.$route( name: 'TrackingProtectionExceptionsRoute', factory: $TrackingProtectionExceptionsRoute._fromState, ), + GoRouteData.$route( + path: 'custom_tracking_protection', + name: 'CustomTrackingProtectionRoute', + factory: $CustomTrackingProtectionRoute._fromState, + ), GoRouteData.$route( path: 'error_logs', name: 'ErrorLogsRoute', @@ -478,6 +483,28 @@ mixin $TrackingProtectionExceptionsRoute on GoRouteData { void replace(BuildContext context) => context.replace(location); } +mixin $CustomTrackingProtectionRoute on GoRouteData { + static CustomTrackingProtectionRoute _fromState(GoRouterState state) => + CustomTrackingProtectionRoute(); + + @override + String get location => + GoRouteData.$location('/settings/custom_tracking_protection'); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + mixin $ErrorLogsRoute on GoRouteData { static ErrorLogsRoute _fromState(GoRouterState state) => ErrorLogsRoute(); diff --git a/app/lib/core/routing/routes.settings.dart b/app/lib/core/routing/routes.settings.dart index f49d95ee..ee6978fb 100644 --- a/app/lib/core/routing/routes.settings.dart +++ b/app/lib/core/routing/routes.settings.dart @@ -75,6 +75,10 @@ part of 'routes.dart'; name: 'TrackingProtectionExceptionsRoute', path: 'tracking_protection_exceptions', ), + TypedGoRoute( + name: 'CustomTrackingProtectionRoute', + path: 'custom_tracking_protection', + ), TypedGoRoute(name: 'ErrorLogsRoute', path: 'error_logs'), ], ) @@ -202,3 +206,11 @@ class ErrorLogsRoute extends GoRouteData with $ErrorLogsRoute { return const ErrorLogsScreen(); } } + +class CustomTrackingProtectionRoute extends GoRouteData + with $CustomTrackingProtectionRoute { + @override + Widget build(BuildContext context, GoRouterState state) { + return const CustomTrackingProtectionScreen(); + } +} diff --git a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart index 3e4c168f..d5e1fbd7 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.dart @@ -29,6 +29,22 @@ import 'package:weblibre/features/user/domain/repositories/general_settings.dart part 'engine_settings_replication.g.dart'; +/// Checks if any Custom ETP setting changed between two EngineSettings instances. +bool _customEtpSettingsChanged(GeckoEngineSettings? previous, GeckoEngineSettings current) { + if (previous == null) return true; + return previous.blockCookies != current.blockCookies || + previous.customCookiePolicy != current.customCookiePolicy || + previous.blockTrackingContent != current.blockTrackingContent || + previous.trackingContentScope != current.trackingContentScope || + previous.blockCryptominers != current.blockCryptominers || + previous.blockFingerprinters != current.blockFingerprinters || + previous.blockRedirectTrackers != current.blockRedirectTrackers || + previous.blockSuspectedFingerprinters != current.blockSuspectedFingerprinters || + previous.suspectedFingerprintersScope != current.suspectedFingerprintersScope || + previous.allowListBaseline != current.allowListBaseline || + previous.allowListConvenience != current.allowListConvenience; +} + @Riverpod(keepAlive: true) class EngineSettingsReplicationService extends _$EngineSettingsReplicationService { @@ -90,11 +106,34 @@ class EngineSettingsReplicationService settings.javascriptEnabled) { await _service.javascriptEnabled(settings.javascriptEnabled); } - if (previous.value?.trackingProtectionPolicy != - settings.trackingProtectionPolicy) { - await _service.trackingProtectionPolicy( - settings.trackingProtectionPolicy, - ); + // Check if tracking protection policy mode changed OR any custom ETP setting changed + final policyModeChanged = previous.value?.trackingProtectionPolicy != + settings.trackingProtectionPolicy; + final customSettingsChanged = settings.trackingProtectionPolicy == TrackingProtectionPolicy.custom && + _customEtpSettingsChanged(previous.value, settings); + + if (policyModeChanged || customSettingsChanged) { + // Always send full custom settings when in CUSTOM mode + if (settings.trackingProtectionPolicy == TrackingProtectionPolicy.custom) { + await _service.customTrackingProtectionPolicy( + trackingProtectionPolicy: settings.trackingProtectionPolicy, + blockCookies: settings.blockCookies, + customCookiePolicy: settings.customCookiePolicy, + blockTrackingContent: settings.blockTrackingContent, + trackingContentScope: settings.trackingContentScope, + blockCryptominers: settings.blockCryptominers, + blockFingerprinters: settings.blockFingerprinters, + blockRedirectTrackers: settings.blockRedirectTrackers, + blockSuspectedFingerprinters: settings.blockSuspectedFingerprinters, + suspectedFingerprintersScope: settings.suspectedFingerprintersScope, + allowListBaseline: settings.allowListBaseline, + allowListConvenience: settings.allowListConvenience, + ); + } else { + await _service.trackingProtectionPolicy( + settings.trackingProtectionPolicy, + ); + } } if (previous.value?.httpsOnlyMode != settings.httpsOnlyMode) { await _service.httpsOnlyMode(settings.httpsOnlyMode); diff --git a/app/lib/features/settings/presentation/screens/custom_tracking_protection.dart b/app/lib/features/settings/presentation/screens/custom_tracking_protection.dart new file mode 100644 index 00000000..ca2c09fd --- /dev/null +++ b/app/lib/features/settings/presentation/screens/custom_tracking_protection.dart @@ -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 . + */ +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( + 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( + 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( + 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, + ), + ); + }, + ), + ), + ], + ), + ), + ], + ); + } +} diff --git a/app/lib/features/settings/presentation/screens/privacy_security_settings.dart b/app/lib/features/settings/presentation/screens/privacy_security_settings.dart index aa60a13a..ca8762fb 100644 --- a/app/lib/features/settings/presentation/screens/privacy_security_settings.dart +++ b/app/lib/features/settings/presentation/screens/privacy_security_settings.dart @@ -512,12 +512,25 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget { RadioGroup( groupValue: trackingProtectionPolicy, onChanged: (value) async { - await ref - .read(saveEngineSettingsControllerProvider.notifier) - .save( - (currentSettings) => currentSettings.copyWith - .trackingProtectionPolicy(value), - ); + if (value != null) { + // Save the policy change + await ref + .read(saveEngineSettingsControllerProvider.notifier) + .save( + (currentSettings) => currentSettings.copyWith + .trackingProtectionPolicy(value), + ); + } + + // Navigate to custom settings screen when Custom is selected + if (value == TrackingProtectionPolicy.custom || + (value == null && + trackingProtectionPolicy == + TrackingProtectionPolicy.custom)) { + if (context.mounted) { + await CustomTrackingProtectionRoute().push(context); + } + } }, child: const Column( children: [ @@ -541,8 +554,10 @@ class _EnhancedTrackingProtectionSection extends HookConsumerWidget { ), RadioListTile.adaptive( value: TrackingProtectionPolicy.custom, + toggleable: true, title: Text('Custom'), subtitle: Text('Choose which trackers and scripts to block.'), + secondary: Icon(Icons.chevron_right), ), ], ), diff --git a/app/lib/features/user/data/models/engine_settings.dart b/app/lib/features/user/data/models/engine_settings.dart index cf6f27ba..a672020c 100644 --- a/app/lib/features/user/data/models/engine_settings.dart +++ b/app/lib/features/user/data/models/engine_settings.dart @@ -79,6 +79,31 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { @override List get locales => super.locales!; + // Custom Tracking Protection overrides + @override + bool get blockCookies => super.blockCookies!; + @override + CustomCookiePolicy get customCookiePolicy => super.customCookiePolicy!; + @override + bool get blockTrackingContent => super.blockTrackingContent!; + @override + TrackingScope get trackingContentScope => super.trackingContentScope!; + @override + bool get blockCryptominers => super.blockCryptominers!; + @override + bool get blockFingerprinters => super.blockFingerprinters!; + @override + bool get blockRedirectTrackers => super.blockRedirectTrackers!; + @override + bool get blockSuspectedFingerprinters => super.blockSuspectedFingerprinters!; + @override + TrackingScope get suspectedFingerprintersScope => + super.suspectedFingerprintersScope!; + @override + bool get allowListBaseline => super.allowListBaseline!; + @override + bool get allowListConvenience => super.allowListConvenience!; + final QueryParameterStripping queryParameterStripping; final BounceTrackingProtectionMode bounceTrackingProtectionMode; @@ -135,6 +160,17 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { required super.fingerprintingProtectionOverrides, required this.enablePdfJs, required super.locales, + required super.blockCookies, + required super.customCookiePolicy, + required super.blockTrackingContent, + required super.trackingContentScope, + required super.blockCryptominers, + required super.blockFingerprinters, + required super.blockRedirectTrackers, + required super.blockSuspectedFingerprinters, + required super.suspectedFingerprintersScope, + required super.allowListBaseline, + required super.allowListConvenience, }); EngineSettings.withDefaults({ @@ -160,6 +196,17 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { String? fingerprintingProtectionOverrides, bool? enablePdfJs, List? locales, + bool? blockCookies, + CustomCookiePolicy? customCookiePolicy, + bool? blockTrackingContent, + TrackingScope? trackingContentScope, + bool? blockCryptominers, + bool? blockFingerprinters, + bool? blockRedirectTrackers, + bool? blockSuspectedFingerprinters, + TrackingScope? suspectedFingerprintersScope, + bool? allowListBaseline, + bool? allowListConvenience, }) : queryParameterStripping = queryParameterStripping ?? QueryParameterStripping.disabled, bounceTrackingProtectionMode = @@ -199,6 +246,19 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { WidgetsBinding.instance.platformDispatcher.locales .map((x) => x.toLanguageTag()) .toList(), + blockCookies: blockCookies ?? true, + customCookiePolicy: + customCookiePolicy ?? CustomCookiePolicy.totalProtection, + blockTrackingContent: blockTrackingContent ?? true, + trackingContentScope: trackingContentScope ?? TrackingScope.all, + blockCryptominers: blockCryptominers ?? true, + blockFingerprinters: blockFingerprinters ?? true, + blockRedirectTrackers: blockRedirectTrackers ?? true, + blockSuspectedFingerprinters: blockSuspectedFingerprinters ?? true, + suspectedFingerprintersScope: + suspectedFingerprintersScope ?? TrackingScope.all, + allowListBaseline: allowListBaseline ?? true, + allowListConvenience: allowListConvenience ?? false, ); static AddonCollection? _addonCollectionFromJson(String? json) => @@ -238,5 +298,16 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { fingerprintingProtectionOverrides, enablePdfJs, locales, + blockCookies, + customCookiePolicy, + blockTrackingContent, + trackingContentScope, + blockCryptominers, + blockFingerprinters, + blockRedirectTrackers, + blockSuspectedFingerprinters, + suspectedFingerprintersScope, + allowListBaseline, + allowListConvenience, ]; } diff --git a/app/lib/features/user/data/models/engine_settings.g.dart b/app/lib/features/user/data/models/engine_settings.g.dart index 1fb3bdad..75047221 100644 --- a/app/lib/features/user/data/models/engine_settings.g.dart +++ b/app/lib/features/user/data/models/engine_settings.g.dart @@ -69,6 +69,32 @@ abstract class _$EngineSettingsCWProxy { EngineSettings locales(List? locales); + EngineSettings blockCookies(bool? blockCookies); + + EngineSettings customCookiePolicy(CustomCookiePolicy? customCookiePolicy); + + EngineSettings blockTrackingContent(bool? blockTrackingContent); + + EngineSettings trackingContentScope(TrackingScope? trackingContentScope); + + EngineSettings blockCryptominers(bool? blockCryptominers); + + EngineSettings blockFingerprinters(bool? blockFingerprinters); + + EngineSettings blockRedirectTrackers(bool? blockRedirectTrackers); + + EngineSettings blockSuspectedFingerprinters( + bool? blockSuspectedFingerprinters, + ); + + EngineSettings suspectedFingerprintersScope( + TrackingScope? suspectedFingerprintersScope, + ); + + EngineSettings allowListBaseline(bool? allowListBaseline); + + EngineSettings allowListConvenience(bool? allowListConvenience); + /// Creates a new instance with the provided field values. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`. /// @@ -99,6 +125,17 @@ abstract class _$EngineSettingsCWProxy { String? fingerprintingProtectionOverrides, bool enablePdfJs, List? locales, + bool? blockCookies, + CustomCookiePolicy? customCookiePolicy, + bool? blockTrackingContent, + TrackingScope? trackingContentScope, + bool? blockCryptominers, + bool? blockFingerprinters, + bool? blockRedirectTrackers, + bool? blockSuspectedFingerprinters, + TrackingScope? suspectedFingerprintersScope, + bool? allowListBaseline, + bool? allowListConvenience, }); } @@ -213,6 +250,52 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { @override EngineSettings locales(List? locales) => call(locales: locales); + @override + EngineSettings blockCookies(bool? blockCookies) => + call(blockCookies: blockCookies); + + @override + EngineSettings customCookiePolicy(CustomCookiePolicy? customCookiePolicy) => + call(customCookiePolicy: customCookiePolicy); + + @override + EngineSettings blockTrackingContent(bool? blockTrackingContent) => + call(blockTrackingContent: blockTrackingContent); + + @override + EngineSettings trackingContentScope(TrackingScope? trackingContentScope) => + call(trackingContentScope: trackingContentScope); + + @override + EngineSettings blockCryptominers(bool? blockCryptominers) => + call(blockCryptominers: blockCryptominers); + + @override + EngineSettings blockFingerprinters(bool? blockFingerprinters) => + call(blockFingerprinters: blockFingerprinters); + + @override + EngineSettings blockRedirectTrackers(bool? blockRedirectTrackers) => + call(blockRedirectTrackers: blockRedirectTrackers); + + @override + EngineSettings blockSuspectedFingerprinters( + bool? blockSuspectedFingerprinters, + ) => call(blockSuspectedFingerprinters: blockSuspectedFingerprinters); + + @override + EngineSettings suspectedFingerprintersScope( + TrackingScope? suspectedFingerprintersScope, + ) => call(suspectedFingerprintersScope: suspectedFingerprintersScope); + + @override + EngineSettings allowListBaseline(bool? allowListBaseline) => + call(allowListBaseline: allowListBaseline); + + @override + EngineSettings allowListConvenience(bool? allowListConvenience) => + call(allowListConvenience: allowListConvenience); + @override /// Creates a new instance with the provided field values. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `EngineSettings(...).copyWith.fieldName(value)`. @@ -246,6 +329,17 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { Object? fingerprintingProtectionOverrides = const $CopyWithPlaceholder(), Object? enablePdfJs = const $CopyWithPlaceholder(), Object? locales = const $CopyWithPlaceholder(), + Object? blockCookies = const $CopyWithPlaceholder(), + Object? customCookiePolicy = const $CopyWithPlaceholder(), + Object? blockTrackingContent = const $CopyWithPlaceholder(), + Object? trackingContentScope = const $CopyWithPlaceholder(), + Object? blockCryptominers = const $CopyWithPlaceholder(), + Object? blockFingerprinters = const $CopyWithPlaceholder(), + Object? blockRedirectTrackers = const $CopyWithPlaceholder(), + Object? blockSuspectedFingerprinters = const $CopyWithPlaceholder(), + Object? suspectedFingerprintersScope = const $CopyWithPlaceholder(), + Object? allowListBaseline = const $CopyWithPlaceholder(), + Object? allowListConvenience = const $CopyWithPlaceholder(), }) { return EngineSettings( javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder() @@ -361,6 +455,53 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { ? _value.locales // ignore: cast_nullable_to_non_nullable : locales as List?, + 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 json) => locales: (json['locales'] as List?) ?.map((e) => e as String) .toList(), + blockCookies: json['blockCookies'] as bool?, + customCookiePolicy: $enumDecodeNullable( + _$CustomCookiePolicyEnumMap, + json['customCookiePolicy'], + ), + blockTrackingContent: json['blockTrackingContent'] as bool?, + trackingContentScope: $enumDecodeNullable( + _$TrackingScopeEnumMap, + json['trackingContentScope'], + ), + blockCryptominers: json['blockCryptominers'] as bool?, + blockFingerprinters: json['blockFingerprinters'] as bool?, + blockRedirectTrackers: json['blockRedirectTrackers'] as bool?, + blockSuspectedFingerprinters: + json['blockSuspectedFingerprinters'] as bool?, + suspectedFingerprintersScope: $enumDecodeNullable( + _$TrackingScopeEnumMap, + json['suspectedFingerprintersScope'], + ), + allowListBaseline: json['allowListBaseline'] as bool?, + allowListConvenience: json['allowListConvenience'] as bool?, ); Map _$EngineSettingsToJson( @@ -463,6 +625,20 @@ Map _$EngineSettingsToJson( .webContentIsolationStrategy]!, 'enterpriseRootsEnabled': instance.enterpriseRootsEnabled, 'locales': instance.locales, + 'blockCookies': instance.blockCookies, + 'customCookiePolicy': + _$CustomCookiePolicyEnumMap[instance.customCookiePolicy]!, + 'blockTrackingContent': instance.blockTrackingContent, + 'trackingContentScope': + _$TrackingScopeEnumMap[instance.trackingContentScope]!, + 'blockCryptominers': instance.blockCryptominers, + 'blockFingerprinters': instance.blockFingerprinters, + 'blockRedirectTrackers': instance.blockRedirectTrackers, + 'blockSuspectedFingerprinters': instance.blockSuspectedFingerprinters, + 'suspectedFingerprintersScope': + _$TrackingScopeEnumMap[instance.suspectedFingerprintersScope]!, + 'allowListBaseline': instance.allowListBaseline, + 'allowListConvenience': instance.allowListConvenience, 'queryParameterStripping': _$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!, 'bounceTrackingProtectionMode': @@ -528,3 +704,16 @@ const _$DohSettingsModeEnumMap = { DohSettingsMode.max: 'max', DohSettingsMode.off: 'off', }; + +const _$CustomCookiePolicyEnumMap = { + CustomCookiePolicy.totalProtection: 'totalProtection', + CustomCookiePolicy.crossSiteTrackers: 'crossSiteTrackers', + CustomCookiePolicy.unvisited: 'unvisited', + CustomCookiePolicy.thirdParty: 'thirdParty', + CustomCookiePolicy.allCookies: 'allCookies', +}; + +const _$TrackingScopeEnumMap = { + TrackingScope.all: 'all', + TrackingScope.privateOnly: 'privateOnly', +}; diff --git a/app/lib/features/user/domain/repositories/engine_settings.dart b/app/lib/features/user/domain/repositories/engine_settings.dart index 51778d4d..99acb18c 100644 --- a/app/lib/features/user/domain/repositories/engine_settings.dart +++ b/app/lib/features/user/domain/repositories/engine_settings.dart @@ -127,6 +127,47 @@ class EngineSettingsRepository extends _$EngineSettingsRepository { 'locales': settings['locales'] ?.readAs(DriftSqlType.string, db.typeMapping) .mapNotNull(jsonDecode), + // Custom Tracking Protection + 'blockCookies': settings['blockCookies']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'customCookiePolicy': settings['customCookiePolicy']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'blockTrackingContent': settings['blockTrackingContent']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'trackingContentScope': settings['trackingContentScope']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'blockCryptominers': settings['blockCryptominers']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'blockFingerprinters': settings['blockFingerprinters']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'blockRedirectTrackers': settings['blockRedirectTrackers']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'blockSuspectedFingerprinters': settings['blockSuspectedFingerprinters'] + ?.readAs(DriftSqlType.bool, db.typeMapping), + 'suspectedFingerprintersScope': settings['suspectedFingerprintersScope'] + ?.readAs(DriftSqlType.string, db.typeMapping), + 'allowListBaseline': settings['allowListBaseline']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'allowListConvenience': settings['allowListConvenience']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), }); } diff --git a/app/lib/features/user/domain/repositories/engine_settings.g.dart b/app/lib/features/user/domain/repositories/engine_settings.g.dart index 15df5821..cd6a2c8f 100644 --- a/app/lib/features/user/domain/repositories/engine_settings.g.dart +++ b/app/lib/features/user/domain/repositories/engine_settings.g.dart @@ -34,7 +34,7 @@ final class EngineSettingsRepositoryProvider } String _$engineSettingsRepositoryHash() => - r'916abcd932fc3b3b1e060bc93a46c1a3a38bb984'; + r'a3bcd3a82d251ce6fd0adc4922e512533d70a10a'; abstract class _$EngineSettingsRepository extends $StreamNotifier { diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoEngineSettingsApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoEngineSettingsApiImpl.kt index 7463b037..d855615f 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoEngineSettingsApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoEngineSettingsApiImpl.kt @@ -9,15 +9,19 @@ package eu.weblibre.flutter_mozilla_components.api import eu.weblibre.flutter_mozilla_components.GlobalComponents import eu.weblibre.flutter_mozilla_components.pigeons.ColorScheme 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.GeckoEngineSettings import eu.weblibre.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi import eu.weblibre.flutter_mozilla_components.pigeons.HttpsOnlyMode 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 mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.EngineSession 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.feature.addons.logger import mozilla.components.feature.session.SettingsUseCases @@ -35,7 +39,14 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi { 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) { eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> { components.core.engineSettings.fingerprintingProtection = true @@ -45,14 +56,90 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi { components.core.engineSettings.fingerprintingProtection = false 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 -> { 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) { if(settings.javascriptEnabled != null) { 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.RECOMMENDED -> TrackingProtectionPolicy.recommended() 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) { components.core.engineSettings.httpsOnlyMode = when(settings.httpsOnlyMode) { @@ -202,21 +289,10 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi { reloadSession = true } if(settings.contentBlocking != null) { - components.core.engine.settings.queryParameterStripping = when(settings.contentBlocking.queryParameterStripping) { - QueryParameterStripping.ENABLED -> true - QueryParameterStripping.DISABLED -> false - QueryParameterStripping.PRIVATE_ONLY -> false - } - 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 - + components.core.engine.settings.queryParameterStripping = components.core.engineSettings.queryParameterStripping + components.core.engine.settings.queryParameterStrippingPrivateBrowsing = components.core.engineSettings.queryParameterStrippingPrivateBrowsing + components.core.engine.settings.queryParameterStrippingAllowList = components.core.engineSettings.queryParameterStrippingAllowList + components.core.engine.settings.queryParameterStrippingStripList = components.core.engineSettings.queryParameterStrippingStripList reloadSession = true } if(settings.enterpriseRootsEnabled != null) { @@ -224,18 +300,13 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi { reloadSession = true } if(settings.dohSettings != null) { - components.core.engine.settings.dohSettingsMode = when(settings.dohSettings.dohSettingsMode) { - DohSettingsMode.GECKO_DEFAULT -> Engine.DohSettingsMode.DEFAULT - DohSettingsMode.INCREASED -> Engine.DohSettingsMode.INCREASED - DohSettingsMode.MAX -> Engine.DohSettingsMode.MAX - 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 + components.core.engine.settings.dohSettingsMode = components.core.engineSettings.dohSettingsMode + components.core.engine.settings.dohProviderUrl = components.core.engineSettings.dohProviderUrl + components.core.engine.settings.dohDefaultProviderUrl = components.core.engineSettings.dohDefaultProviderUrl + components.core.engine.settings.dohExceptionsList = components.core.engineSettings.dohExceptionsList } if(settings.fingerprintingProtectionOverrides != null) { - components.core.engine.settings.fingerprintingProtectionOverrides = settings.fingerprintingProtectionOverrides + components.core.engine.settings.fingerprintingProtectionOverrides = components.core.engineSettings.fingerprintingProtectionOverrides } if(reloadSession) { diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt index fb274509..7f185a17 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt @@ -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) { GECKO_DEFAULT(0), INCREASED(1), @@ -1865,7 +1914,32 @@ data class GeckoEngineSettings ( val enterpriseRootsEnabled: Boolean? = null, val dohSettings: DohSettings? = null, val fingerprintingProtectionOverrides: String? = null, - val locales: List? = null + val locales: List? = 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 { @@ -1886,7 +1960,18 @@ data class GeckoEngineSettings ( val dohSettings = pigeonVar_list[13] as DohSettings? val fingerprintingProtectionOverrides = pigeonVar_list[14] as String? val locales = pigeonVar_list[15] as List? - 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 { @@ -1907,6 +1992,17 @@ data class GeckoEngineSettings ( dohSettings, fingerprintingProtectionOverrides, locales, + blockCookies, + customCookiePolicy, + blockTrackingContent, + trackingContentScope, + blockCryptominers, + blockFingerprinters, + blockRedirectTrackers, + blockSuspectedFingerprinters, + suspectedFingerprintersScope, + allowListBaseline, + allowListConvenience, ) } override fun equals(other: Any?): Boolean { @@ -2945,330 +3041,340 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { } 145.toByte() -> { return (readValue(buffer) as Long?)?.let { - DohSettingsMode.ofRaw(it.toInt()) + CustomCookiePolicy.ofRaw(it.toInt()) } } 146.toByte() -> { return (readValue(buffer) as Long?)?.let { - DownloadStatus.ofRaw(it.toInt()) + TrackingScope.ofRaw(it.toInt()) } } 147.toByte() -> { return (readValue(buffer) as Long?)?.let { - LogLevel.ofRaw(it.toInt()) + DohSettingsMode.ofRaw(it.toInt()) } } 148.toByte() -> { return (readValue(buffer) as Long?)?.let { - MlProgressType.ofRaw(it.toInt()) + DownloadStatus.ofRaw(it.toInt()) } } 149.toByte() -> { return (readValue(buffer) as Long?)?.let { - MlProgressStatus.ofRaw(it.toInt()) + LogLevel.ofRaw(it.toInt()) } } 150.toByte() -> { return (readValue(buffer) as Long?)?.let { - ClearDataType.ofRaw(it.toInt()) + MlProgressType.ofRaw(it.toInt()) } } 151.toByte() -> { return (readValue(buffer) as Long?)?.let { - GeckoFetchMethod.ofRaw(it.toInt()) + MlProgressStatus.ofRaw(it.toInt()) } } 152.toByte() -> { return (readValue(buffer) as Long?)?.let { - GeckoFetchRedircet.ofRaw(it.toInt()) + ClearDataType.ofRaw(it.toInt()) } } 153.toByte() -> { return (readValue(buffer) as Long?)?.let { - GeckoFetchCookiePolicy.ofRaw(it.toInt()) + GeckoFetchMethod.ofRaw(it.toInt()) } } 154.toByte() -> { return (readValue(buffer) as Long?)?.let { - BookmarkNodeType.ofRaw(it.toInt()) + GeckoFetchRedircet.ofRaw(it.toInt()) } } 155.toByte() -> { return (readValue(buffer) as Long?)?.let { - SitePermissionStatus.ofRaw(it.toInt()) + GeckoFetchCookiePolicy.ofRaw(it.toInt()) } } 156.toByte() -> { return (readValue(buffer) as Long?)?.let { - AutoplayStatus.ofRaw(it.toInt()) + BookmarkNodeType.ofRaw(it.toInt()) } } 157.toByte() -> { - return (readValue(buffer) as? List)?.let { - TranslationOptions.fromList(it) + return (readValue(buffer) as Long?)?.let { + SitePermissionStatus.ofRaw(it.toInt()) } } 158.toByte() -> { - return (readValue(buffer) as? List)?.let { - ReaderState.fromList(it) + return (readValue(buffer) as Long?)?.let { + AutoplayStatus.ofRaw(it.toInt()) } } 159.toByte() -> { return (readValue(buffer) as? List)?.let { - AddTabParams.fromList(it) + TranslationOptions.fromList(it) } } 160.toByte() -> { return (readValue(buffer) as? List)?.let { - LastMediaAccessState.fromList(it) + ReaderState.fromList(it) } } 161.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryMetadataKey.fromList(it) + AddTabParams.fromList(it) } } 162.toByte() -> { return (readValue(buffer) as? List)?.let { - PackageCategoryValue.fromList(it) + LastMediaAccessState.fromList(it) } } 163.toByte() -> { return (readValue(buffer) as? List)?.let { - ExternalPackage.fromList(it) + HistoryMetadataKey.fromList(it) } } 164.toByte() -> { return (readValue(buffer) as? List)?.let { - LoadUrlFlagsValue.fromList(it) + PackageCategoryValue.fromList(it) } } 165.toByte() -> { return (readValue(buffer) as? List)?.let { - SourceValue.fromList(it) + ExternalPackage.fromList(it) } } 166.toByte() -> { return (readValue(buffer) as? List)?.let { - TabState.fromList(it) + LoadUrlFlagsValue.fromList(it) } } 167.toByte() -> { return (readValue(buffer) as? List)?.let { - RecoverableTab.fromList(it) + SourceValue.fromList(it) } } 168.toByte() -> { return (readValue(buffer) as? List)?.let { - RecoverableBrowserState.fromList(it) + TabState.fromList(it) } } 169.toByte() -> { return (readValue(buffer) as? List)?.let { - IconRequest.fromList(it) + RecoverableTab.fromList(it) } } 170.toByte() -> { return (readValue(buffer) as? List)?.let { - ResourceSize.fromList(it) + RecoverableBrowserState.fromList(it) } } 171.toByte() -> { return (readValue(buffer) as? List)?.let { - Resource.fromList(it) + IconRequest.fromList(it) } } 172.toByte() -> { return (readValue(buffer) as? List)?.let { - IconResult.fromList(it) + ResourceSize.fromList(it) } } 173.toByte() -> { return (readValue(buffer) as? List)?.let { - CookiePartitionKey.fromList(it) + Resource.fromList(it) } } 174.toByte() -> { return (readValue(buffer) as? List)?.let { - Cookie.fromList(it) + IconResult.fromList(it) } } 175.toByte() -> { return (readValue(buffer) as? List)?.let { - VisitInfo.fromList(it) + CookiePartitionKey.fromList(it) } } 176.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryItem.fromList(it) + Cookie.fromList(it) } } 177.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryState.fromList(it) + VisitInfo.fromList(it) } } 178.toByte() -> { return (readValue(buffer) as? List)?.let { - ReaderableState.fromList(it) + HistoryItem.fromList(it) } } 179.toByte() -> { return (readValue(buffer) as? List)?.let { - SecurityInfoState.fromList(it) + HistoryState.fromList(it) } } 180.toByte() -> { return (readValue(buffer) as? List)?.let { - TabContentState.fromList(it) + ReaderableState.fromList(it) } } 181.toByte() -> { return (readValue(buffer) as? List)?.let { - FindResultState.fromList(it) + SecurityInfoState.fromList(it) } } 182.toByte() -> { return (readValue(buffer) as? List)?.let { - CustomSelectionAction.fromList(it) + TabContentState.fromList(it) } } 183.toByte() -> { return (readValue(buffer) as? List)?.let { - WebExtensionData.fromList(it) + FindResultState.fromList(it) } } 184.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoSuggestion.fromList(it) + CustomSelectionAction.fromList(it) } } 185.toByte() -> { return (readValue(buffer) as? List)?.let { - TabContent.fromList(it) + WebExtensionData.fromList(it) } } 186.toByte() -> { return (readValue(buffer) as? List)?.let { - ContentBlocking.fromList(it) + GeckoSuggestion.fromList(it) } } 187.toByte() -> { return (readValue(buffer) as? List)?.let { - DohSettings.fromList(it) + TabContent.fromList(it) } } 188.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoEngineSettings.fromList(it) + ContentBlocking.fromList(it) } } 189.toByte() -> { return (readValue(buffer) as? List)?.let { - AutocompleteResult.fromList(it) + DohSettings.fromList(it) } } 190.toByte() -> { return (readValue(buffer) as? List)?.let { - UnknownHitResult.fromList(it) + GeckoEngineSettings.fromList(it) } } 191.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageHitResult.fromList(it) + AutocompleteResult.fromList(it) } } 192.toByte() -> { return (readValue(buffer) as? List)?.let { - VideoHitResult.fromList(it) + UnknownHitResult.fromList(it) } } 193.toByte() -> { return (readValue(buffer) as? List)?.let { - AudioHitResult.fromList(it) + ImageHitResult.fromList(it) } } 194.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageSrcHitResult.fromList(it) + VideoHitResult.fromList(it) } } 195.toByte() -> { return (readValue(buffer) as? List)?.let { - PhoneHitResult.fromList(it) + AudioHitResult.fromList(it) } } 196.toByte() -> { return (readValue(buffer) as? List)?.let { - EmailHitResult.fromList(it) + ImageSrcHitResult.fromList(it) } } 197.toByte() -> { return (readValue(buffer) as? List)?.let { - GeoHitResult.fromList(it) + PhoneHitResult.fromList(it) } } 198.toByte() -> { return (readValue(buffer) as? List)?.let { - DownloadState.fromList(it) + EmailHitResult.fromList(it) } } 199.toByte() -> { return (readValue(buffer) as? List)?.let { - ShareInternetResourceState.fromList(it) + GeoHitResult.fromList(it) } } 200.toByte() -> { return (readValue(buffer) as? List)?.let { - AddonCollection.fromList(it) + DownloadState.fromList(it) } } 201.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoPref.fromList(it) + ShareInternetResourceState.fromList(it) } } 202.toByte() -> { return (readValue(buffer) as? List)?.let { - MlProgressData.fromList(it) + AddonCollection.fromList(it) } } 203.toByte() -> { return (readValue(buffer) as? List)?.let { - ContainerSiteAssignment.fromList(it) + GeckoPref.fromList(it) } } 204.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoHeader.fromList(it) + MlProgressData.fromList(it) } } 205.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoFetchRequest.fromList(it) + ContainerSiteAssignment.fromList(it) } } 206.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoFetchResponse.fromList(it) + GeckoHeader.fromList(it) } } 207.toByte() -> { return (readValue(buffer) as? List)?.let { - BookmarkNode.fromList(it) + GeckoFetchRequest.fromList(it) } } 208.toByte() -> { return (readValue(buffer) as? List)?.let { - BookmarkInfo.fromList(it) + GeckoFetchResponse.fromList(it) } } 209.toByte() -> { return (readValue(buffer) as? List)?.let { - SitePermissions.fromList(it) + BookmarkNode.fromList(it) } } 210.toByte() -> { + return (readValue(buffer) as? List)?.let { + BookmarkInfo.fromList(it) + } + } + 211.toByte() -> { + return (readValue(buffer) as? List)?.let { + SitePermissions.fromList(it) + } + } + 212.toByte() -> { return (readValue(buffer) as? List)?.let { TrackingProtectionException.fromList(it) } @@ -3342,270 +3448,278 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { stream.write(144) writeValue(stream, value.raw.toLong()) } - is DohSettingsMode -> { + is CustomCookiePolicy -> { stream.write(145) writeValue(stream, value.raw.toLong()) } - is DownloadStatus -> { + is TrackingScope -> { stream.write(146) writeValue(stream, value.raw.toLong()) } - is LogLevel -> { + is DohSettingsMode -> { stream.write(147) writeValue(stream, value.raw.toLong()) } - is MlProgressType -> { + is DownloadStatus -> { stream.write(148) writeValue(stream, value.raw.toLong()) } - is MlProgressStatus -> { + is LogLevel -> { stream.write(149) writeValue(stream, value.raw.toLong()) } - is ClearDataType -> { + is MlProgressType -> { stream.write(150) writeValue(stream, value.raw.toLong()) } - is GeckoFetchMethod -> { + is MlProgressStatus -> { stream.write(151) writeValue(stream, value.raw.toLong()) } - is GeckoFetchRedircet -> { + is ClearDataType -> { stream.write(152) writeValue(stream, value.raw.toLong()) } - is GeckoFetchCookiePolicy -> { + is GeckoFetchMethod -> { stream.write(153) writeValue(stream, value.raw.toLong()) } - is BookmarkNodeType -> { + is GeckoFetchRedircet -> { stream.write(154) writeValue(stream, value.raw.toLong()) } - is SitePermissionStatus -> { + is GeckoFetchCookiePolicy -> { stream.write(155) writeValue(stream, value.raw.toLong()) } - is AutoplayStatus -> { + is BookmarkNodeType -> { stream.write(156) writeValue(stream, value.raw.toLong()) } - is TranslationOptions -> { + is SitePermissionStatus -> { stream.write(157) - writeValue(stream, value.toList()) + writeValue(stream, value.raw.toLong()) } - is ReaderState -> { + is AutoplayStatus -> { stream.write(158) - writeValue(stream, value.toList()) + writeValue(stream, value.raw.toLong()) } - is AddTabParams -> { + is TranslationOptions -> { stream.write(159) writeValue(stream, value.toList()) } - is LastMediaAccessState -> { + is ReaderState -> { stream.write(160) writeValue(stream, value.toList()) } - is HistoryMetadataKey -> { + is AddTabParams -> { stream.write(161) writeValue(stream, value.toList()) } - is PackageCategoryValue -> { + is LastMediaAccessState -> { stream.write(162) writeValue(stream, value.toList()) } - is ExternalPackage -> { + is HistoryMetadataKey -> { stream.write(163) writeValue(stream, value.toList()) } - is LoadUrlFlagsValue -> { + is PackageCategoryValue -> { stream.write(164) writeValue(stream, value.toList()) } - is SourceValue -> { + is ExternalPackage -> { stream.write(165) writeValue(stream, value.toList()) } - is TabState -> { + is LoadUrlFlagsValue -> { stream.write(166) writeValue(stream, value.toList()) } - is RecoverableTab -> { + is SourceValue -> { stream.write(167) writeValue(stream, value.toList()) } - is RecoverableBrowserState -> { + is TabState -> { stream.write(168) writeValue(stream, value.toList()) } - is IconRequest -> { + is RecoverableTab -> { stream.write(169) writeValue(stream, value.toList()) } - is ResourceSize -> { + is RecoverableBrowserState -> { stream.write(170) writeValue(stream, value.toList()) } - is Resource -> { + is IconRequest -> { stream.write(171) writeValue(stream, value.toList()) } - is IconResult -> { + is ResourceSize -> { stream.write(172) writeValue(stream, value.toList()) } - is CookiePartitionKey -> { + is Resource -> { stream.write(173) writeValue(stream, value.toList()) } - is Cookie -> { + is IconResult -> { stream.write(174) writeValue(stream, value.toList()) } - is VisitInfo -> { + is CookiePartitionKey -> { stream.write(175) writeValue(stream, value.toList()) } - is HistoryItem -> { + is Cookie -> { stream.write(176) writeValue(stream, value.toList()) } - is HistoryState -> { + is VisitInfo -> { stream.write(177) writeValue(stream, value.toList()) } - is ReaderableState -> { + is HistoryItem -> { stream.write(178) writeValue(stream, value.toList()) } - is SecurityInfoState -> { + is HistoryState -> { stream.write(179) writeValue(stream, value.toList()) } - is TabContentState -> { + is ReaderableState -> { stream.write(180) writeValue(stream, value.toList()) } - is FindResultState -> { + is SecurityInfoState -> { stream.write(181) writeValue(stream, value.toList()) } - is CustomSelectionAction -> { + is TabContentState -> { stream.write(182) writeValue(stream, value.toList()) } - is WebExtensionData -> { + is FindResultState -> { stream.write(183) writeValue(stream, value.toList()) } - is GeckoSuggestion -> { + is CustomSelectionAction -> { stream.write(184) writeValue(stream, value.toList()) } - is TabContent -> { + is WebExtensionData -> { stream.write(185) writeValue(stream, value.toList()) } - is ContentBlocking -> { + is GeckoSuggestion -> { stream.write(186) writeValue(stream, value.toList()) } - is DohSettings -> { + is TabContent -> { stream.write(187) writeValue(stream, value.toList()) } - is GeckoEngineSettings -> { + is ContentBlocking -> { stream.write(188) writeValue(stream, value.toList()) } - is AutocompleteResult -> { + is DohSettings -> { stream.write(189) writeValue(stream, value.toList()) } - is UnknownHitResult -> { + is GeckoEngineSettings -> { stream.write(190) writeValue(stream, value.toList()) } - is ImageHitResult -> { + is AutocompleteResult -> { stream.write(191) writeValue(stream, value.toList()) } - is VideoHitResult -> { + is UnknownHitResult -> { stream.write(192) writeValue(stream, value.toList()) } - is AudioHitResult -> { + is ImageHitResult -> { stream.write(193) writeValue(stream, value.toList()) } - is ImageSrcHitResult -> { + is VideoHitResult -> { stream.write(194) writeValue(stream, value.toList()) } - is PhoneHitResult -> { + is AudioHitResult -> { stream.write(195) writeValue(stream, value.toList()) } - is EmailHitResult -> { + is ImageSrcHitResult -> { stream.write(196) writeValue(stream, value.toList()) } - is GeoHitResult -> { + is PhoneHitResult -> { stream.write(197) writeValue(stream, value.toList()) } - is DownloadState -> { + is EmailHitResult -> { stream.write(198) writeValue(stream, value.toList()) } - is ShareInternetResourceState -> { + is GeoHitResult -> { stream.write(199) writeValue(stream, value.toList()) } - is AddonCollection -> { + is DownloadState -> { stream.write(200) writeValue(stream, value.toList()) } - is GeckoPref -> { + is ShareInternetResourceState -> { stream.write(201) writeValue(stream, value.toList()) } - is MlProgressData -> { + is AddonCollection -> { stream.write(202) writeValue(stream, value.toList()) } - is ContainerSiteAssignment -> { + is GeckoPref -> { stream.write(203) writeValue(stream, value.toList()) } - is GeckoHeader -> { + is MlProgressData -> { stream.write(204) writeValue(stream, value.toList()) } - is GeckoFetchRequest -> { + is ContainerSiteAssignment -> { stream.write(205) writeValue(stream, value.toList()) } - is GeckoFetchResponse -> { + is GeckoHeader -> { stream.write(206) writeValue(stream, value.toList()) } - is BookmarkNode -> { + is GeckoFetchRequest -> { stream.write(207) writeValue(stream, value.toList()) } - is BookmarkInfo -> { + is GeckoFetchResponse -> { stream.write(208) writeValue(stream, value.toList()) } - is SitePermissions -> { + is BookmarkNode -> { stream.write(209) writeValue(stream, value.toList()) } - is TrackingProtectionException -> { + is BookmarkInfo -> { stream.write(210) 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) } } diff --git a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart index 6319804c..78d49cc2 100644 --- a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart +++ b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart @@ -47,6 +47,7 @@ export 'src/pigeons/gecko.g.dart' ContentBlocking, CookieBannerHandlingMode, CookieSameSiteStatus, + CustomCookiePolicy, DohSettings, DohSettingsMode, EmailHitResult, @@ -82,6 +83,7 @@ export 'src/pigeons/gecko.g.dart' TabContentState, TrackingProtectionException, TrackingProtectionPolicy, + TrackingScope, UnknownHitResult, VideoHitResult, VisitInfo, diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_engine_settings.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_engine_settings.dart index acb7cd1f..1c67d8eb 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_engine_settings.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_engine_settings.dart @@ -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 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 httpsOnlyMode(HttpsOnlyMode state) { return _api.updateRuntimeSettings( GeckoEngineSettings(httpsOnlyMode: state), diff --git a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart index 813e1502..89d3b5c9 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -186,6 +186,33 @@ enum WebContentIsolationStrategy { 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 { geckoDefault, increased, @@ -2203,6 +2230,17 @@ class GeckoEngineSettings { this.dohSettings, this.fingerprintingProtectionOverrides, 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; @@ -2237,6 +2275,40 @@ class GeckoEngineSettings { List? 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 _toList() { return [ javascriptEnabled, @@ -2255,6 +2327,17 @@ class GeckoEngineSettings { dohSettings, fingerprintingProtectionOverrides, locales, + blockCookies, + customCookiePolicy, + blockTrackingContent, + trackingContentScope, + blockCryptominers, + blockFingerprinters, + blockRedirectTrackers, + blockSuspectedFingerprinters, + suspectedFingerprintersScope, + allowListBaseline, + allowListConvenience, ]; } @@ -2280,6 +2363,17 @@ class GeckoEngineSettings { dohSettings: result[13] as DohSettings?, fingerprintingProtectionOverrides: result[14] as String?, locales: (result[15] as List?)?.cast(), + 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) { buffer.putUint8(144); writeValue(buffer, value.index); - } else if (value is DohSettingsMode) { + } else if (value is CustomCookiePolicy) { buffer.putUint8(145); writeValue(buffer, value.index); - } else if (value is DownloadStatus) { + } else if (value is TrackingScope) { buffer.putUint8(146); writeValue(buffer, value.index); - } else if (value is LogLevel) { + } else if (value is DohSettingsMode) { buffer.putUint8(147); writeValue(buffer, value.index); - } else if (value is MlProgressType) { + } else if (value is DownloadStatus) { buffer.putUint8(148); writeValue(buffer, value.index); - } else if (value is MlProgressStatus) { + } else if (value is LogLevel) { buffer.putUint8(149); writeValue(buffer, value.index); - } else if (value is ClearDataType) { + } else if (value is MlProgressType) { buffer.putUint8(150); writeValue(buffer, value.index); - } else if (value is GeckoFetchMethod) { + } else if (value is MlProgressStatus) { buffer.putUint8(151); writeValue(buffer, value.index); - } else if (value is GeckoFetchRedircet) { + } else if (value is ClearDataType) { buffer.putUint8(152); writeValue(buffer, value.index); - } else if (value is GeckoFetchCookiePolicy) { + } else if (value is GeckoFetchMethod) { buffer.putUint8(153); writeValue(buffer, value.index); - } else if (value is BookmarkNodeType) { + } else if (value is GeckoFetchRedircet) { buffer.putUint8(154); writeValue(buffer, value.index); - } else if (value is SitePermissionStatus) { + } else if (value is GeckoFetchCookiePolicy) { buffer.putUint8(155); writeValue(buffer, value.index); - } else if (value is AutoplayStatus) { + } else if (value is BookmarkNodeType) { buffer.putUint8(156); writeValue(buffer, value.index); - } else if (value is TranslationOptions) { + } else if (value is SitePermissionStatus) { buffer.putUint8(157); - writeValue(buffer, value.encode()); - } else if (value is ReaderState) { + writeValue(buffer, value.index); + } else if (value is AutoplayStatus) { buffer.putUint8(158); - writeValue(buffer, value.encode()); - } else if (value is AddTabParams) { + writeValue(buffer, value.index); + } else if (value is TranslationOptions) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is LastMediaAccessState) { + } else if (value is ReaderState) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is HistoryMetadataKey) { + } else if (value is AddTabParams) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is PackageCategoryValue) { + } else if (value is LastMediaAccessState) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is ExternalPackage) { + } else if (value is HistoryMetadataKey) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is LoadUrlFlagsValue) { + } else if (value is PackageCategoryValue) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is SourceValue) { + } else if (value is ExternalPackage) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is TabState) { + } else if (value is LoadUrlFlagsValue) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is RecoverableTab) { + } else if (value is SourceValue) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is RecoverableBrowserState) { + } else if (value is TabState) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is IconRequest) { + } else if (value is RecoverableTab) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is ResourceSize) { + } else if (value is RecoverableBrowserState) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is Resource) { + } else if (value is IconRequest) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is IconResult) { + } else if (value is ResourceSize) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is CookiePartitionKey) { + } else if (value is Resource) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is Cookie) { + } else if (value is IconResult) { buffer.putUint8(174); writeValue(buffer, value.encode()); - } else if (value is VisitInfo) { + } else if (value is CookiePartitionKey) { buffer.putUint8(175); writeValue(buffer, value.encode()); - } else if (value is HistoryItem) { + } else if (value is Cookie) { buffer.putUint8(176); writeValue(buffer, value.encode()); - } else if (value is HistoryState) { + } else if (value is VisitInfo) { buffer.putUint8(177); writeValue(buffer, value.encode()); - } else if (value is ReaderableState) { + } else if (value is HistoryItem) { buffer.putUint8(178); writeValue(buffer, value.encode()); - } else if (value is SecurityInfoState) { + } else if (value is HistoryState) { buffer.putUint8(179); writeValue(buffer, value.encode()); - } else if (value is TabContentState) { + } else if (value is ReaderableState) { buffer.putUint8(180); writeValue(buffer, value.encode()); - } else if (value is FindResultState) { + } else if (value is SecurityInfoState) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is CustomSelectionAction) { + } else if (value is TabContentState) { buffer.putUint8(182); writeValue(buffer, value.encode()); - } else if (value is WebExtensionData) { + } else if (value is FindResultState) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is GeckoSuggestion) { + } else if (value is CustomSelectionAction) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is TabContent) { + } else if (value is WebExtensionData) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is ContentBlocking) { + } else if (value is GeckoSuggestion) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is DohSettings) { + } else if (value is TabContent) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is GeckoEngineSettings) { + } else if (value is ContentBlocking) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is AutocompleteResult) { + } else if (value is DohSettings) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is UnknownHitResult) { + } else if (value is GeckoEngineSettings) { buffer.putUint8(190); writeValue(buffer, value.encode()); - } else if (value is ImageHitResult) { + } else if (value is AutocompleteResult) { buffer.putUint8(191); writeValue(buffer, value.encode()); - } else if (value is VideoHitResult) { + } else if (value is UnknownHitResult) { buffer.putUint8(192); writeValue(buffer, value.encode()); - } else if (value is AudioHitResult) { + } else if (value is ImageHitResult) { buffer.putUint8(193); writeValue(buffer, value.encode()); - } else if (value is ImageSrcHitResult) { + } else if (value is VideoHitResult) { buffer.putUint8(194); writeValue(buffer, value.encode()); - } else if (value is PhoneHitResult) { + } else if (value is AudioHitResult) { buffer.putUint8(195); writeValue(buffer, value.encode()); - } else if (value is EmailHitResult) { + } else if (value is ImageSrcHitResult) { buffer.putUint8(196); writeValue(buffer, value.encode()); - } else if (value is GeoHitResult) { + } else if (value is PhoneHitResult) { buffer.putUint8(197); writeValue(buffer, value.encode()); - } else if (value is DownloadState) { + } else if (value is EmailHitResult) { buffer.putUint8(198); writeValue(buffer, value.encode()); - } else if (value is ShareInternetResourceState) { + } else if (value is GeoHitResult) { buffer.putUint8(199); writeValue(buffer, value.encode()); - } else if (value is AddonCollection) { + } else if (value is DownloadState) { buffer.putUint8(200); writeValue(buffer, value.encode()); - } else if (value is GeckoPref) { + } else if (value is ShareInternetResourceState) { buffer.putUint8(201); writeValue(buffer, value.encode()); - } else if (value is MlProgressData) { + } else if (value is AddonCollection) { buffer.putUint8(202); writeValue(buffer, value.encode()); - } else if (value is ContainerSiteAssignment) { + } else if (value is GeckoPref) { buffer.putUint8(203); writeValue(buffer, value.encode()); - } else if (value is GeckoHeader) { + } else if (value is MlProgressData) { buffer.putUint8(204); writeValue(buffer, value.encode()); - } else if (value is GeckoFetchRequest) { + } else if (value is ContainerSiteAssignment) { buffer.putUint8(205); writeValue(buffer, value.encode()); - } else if (value is GeckoFetchResponse) { + } else if (value is GeckoHeader) { buffer.putUint8(206); writeValue(buffer, value.encode()); - } else if (value is BookmarkNode) { + } else if (value is GeckoFetchRequest) { buffer.putUint8(207); writeValue(buffer, value.encode()); - } else if (value is BookmarkInfo) { + } else if (value is GeckoFetchResponse) { buffer.putUint8(208); writeValue(buffer, value.encode()); - } else if (value is SitePermissions) { + } else if (value is BookmarkNode) { buffer.putUint8(209); writeValue(buffer, value.encode()); - } else if (value is TrackingProtectionException) { + } else if (value is BookmarkInfo) { buffer.putUint8(210); 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 { super.writeValue(buffer, value); } @@ -3970,147 +4070,153 @@ class _PigeonCodec extends StandardMessageCodec { return value == null ? null : WebContentIsolationStrategy.values[value]; case 145: final value = readValue(buffer) as int?; - return value == null ? null : DohSettingsMode.values[value]; + return value == null ? null : CustomCookiePolicy.values[value]; case 146: final value = readValue(buffer) as int?; - return value == null ? null : DownloadStatus.values[value]; + return value == null ? null : TrackingScope.values[value]; case 147: final value = readValue(buffer) as int?; - return value == null ? null : LogLevel.values[value]; + return value == null ? null : DohSettingsMode.values[value]; case 148: final value = readValue(buffer) as int?; - return value == null ? null : MlProgressType.values[value]; + return value == null ? null : DownloadStatus.values[value]; case 149: final value = readValue(buffer) as int?; - return value == null ? null : MlProgressStatus.values[value]; + return value == null ? null : LogLevel.values[value]; case 150: final value = readValue(buffer) as int?; - return value == null ? null : ClearDataType.values[value]; + return value == null ? null : MlProgressType.values[value]; case 151: final value = readValue(buffer) as int?; - return value == null ? null : GeckoFetchMethod.values[value]; + return value == null ? null : MlProgressStatus.values[value]; case 152: final value = readValue(buffer) as int?; - return value == null ? null : GeckoFetchRedircet.values[value]; + return value == null ? null : ClearDataType.values[value]; case 153: final value = readValue(buffer) as int?; - return value == null ? null : GeckoFetchCookiePolicy.values[value]; + return value == null ? null : GeckoFetchMethod.values[value]; case 154: final value = readValue(buffer) as int?; - return value == null ? null : BookmarkNodeType.values[value]; + return value == null ? null : GeckoFetchRedircet.values[value]; case 155: final value = readValue(buffer) as int?; - return value == null ? null : SitePermissionStatus.values[value]; + return value == null ? null : GeckoFetchCookiePolicy.values[value]; case 156: final value = readValue(buffer) as int?; - return value == null ? null : AutoplayStatus.values[value]; + return value == null ? null : BookmarkNodeType.values[value]; case 157: - return TranslationOptions.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : SitePermissionStatus.values[value]; case 158: - return ReaderState.decode(readValue(buffer)!); + final value = readValue(buffer) as int?; + return value == null ? null : AutoplayStatus.values[value]; case 159: - return AddTabParams.decode(readValue(buffer)!); + return TranslationOptions.decode(readValue(buffer)!); case 160: - return LastMediaAccessState.decode(readValue(buffer)!); + return ReaderState.decode(readValue(buffer)!); case 161: - return HistoryMetadataKey.decode(readValue(buffer)!); + return AddTabParams.decode(readValue(buffer)!); case 162: - return PackageCategoryValue.decode(readValue(buffer)!); + return LastMediaAccessState.decode(readValue(buffer)!); case 163: - return ExternalPackage.decode(readValue(buffer)!); + return HistoryMetadataKey.decode(readValue(buffer)!); case 164: - return LoadUrlFlagsValue.decode(readValue(buffer)!); + return PackageCategoryValue.decode(readValue(buffer)!); case 165: - return SourceValue.decode(readValue(buffer)!); + return ExternalPackage.decode(readValue(buffer)!); case 166: - return TabState.decode(readValue(buffer)!); + return LoadUrlFlagsValue.decode(readValue(buffer)!); case 167: - return RecoverableTab.decode(readValue(buffer)!); + return SourceValue.decode(readValue(buffer)!); case 168: - return RecoverableBrowserState.decode(readValue(buffer)!); + return TabState.decode(readValue(buffer)!); case 169: - return IconRequest.decode(readValue(buffer)!); + return RecoverableTab.decode(readValue(buffer)!); case 170: - return ResourceSize.decode(readValue(buffer)!); + return RecoverableBrowserState.decode(readValue(buffer)!); case 171: - return Resource.decode(readValue(buffer)!); + return IconRequest.decode(readValue(buffer)!); case 172: - return IconResult.decode(readValue(buffer)!); + return ResourceSize.decode(readValue(buffer)!); case 173: - return CookiePartitionKey.decode(readValue(buffer)!); + return Resource.decode(readValue(buffer)!); case 174: - return Cookie.decode(readValue(buffer)!); + return IconResult.decode(readValue(buffer)!); case 175: - return VisitInfo.decode(readValue(buffer)!); + return CookiePartitionKey.decode(readValue(buffer)!); case 176: - return HistoryItem.decode(readValue(buffer)!); + return Cookie.decode(readValue(buffer)!); case 177: - return HistoryState.decode(readValue(buffer)!); + return VisitInfo.decode(readValue(buffer)!); case 178: - return ReaderableState.decode(readValue(buffer)!); + return HistoryItem.decode(readValue(buffer)!); case 179: - return SecurityInfoState.decode(readValue(buffer)!); + return HistoryState.decode(readValue(buffer)!); case 180: - return TabContentState.decode(readValue(buffer)!); + return ReaderableState.decode(readValue(buffer)!); case 181: - return FindResultState.decode(readValue(buffer)!); + return SecurityInfoState.decode(readValue(buffer)!); case 182: - return CustomSelectionAction.decode(readValue(buffer)!); + return TabContentState.decode(readValue(buffer)!); case 183: - return WebExtensionData.decode(readValue(buffer)!); + return FindResultState.decode(readValue(buffer)!); case 184: - return GeckoSuggestion.decode(readValue(buffer)!); + return CustomSelectionAction.decode(readValue(buffer)!); case 185: - return TabContent.decode(readValue(buffer)!); + return WebExtensionData.decode(readValue(buffer)!); case 186: - return ContentBlocking.decode(readValue(buffer)!); + return GeckoSuggestion.decode(readValue(buffer)!); case 187: - return DohSettings.decode(readValue(buffer)!); + return TabContent.decode(readValue(buffer)!); case 188: - return GeckoEngineSettings.decode(readValue(buffer)!); + return ContentBlocking.decode(readValue(buffer)!); case 189: - return AutocompleteResult.decode(readValue(buffer)!); + return DohSettings.decode(readValue(buffer)!); case 190: - return UnknownHitResult.decode(readValue(buffer)!); + return GeckoEngineSettings.decode(readValue(buffer)!); case 191: - return ImageHitResult.decode(readValue(buffer)!); + return AutocompleteResult.decode(readValue(buffer)!); case 192: - return VideoHitResult.decode(readValue(buffer)!); + return UnknownHitResult.decode(readValue(buffer)!); case 193: - return AudioHitResult.decode(readValue(buffer)!); + return ImageHitResult.decode(readValue(buffer)!); case 194: - return ImageSrcHitResult.decode(readValue(buffer)!); + return VideoHitResult.decode(readValue(buffer)!); case 195: - return PhoneHitResult.decode(readValue(buffer)!); + return AudioHitResult.decode(readValue(buffer)!); case 196: - return EmailHitResult.decode(readValue(buffer)!); + return ImageSrcHitResult.decode(readValue(buffer)!); case 197: - return GeoHitResult.decode(readValue(buffer)!); + return PhoneHitResult.decode(readValue(buffer)!); case 198: - return DownloadState.decode(readValue(buffer)!); + return EmailHitResult.decode(readValue(buffer)!); case 199: - return ShareInternetResourceState.decode(readValue(buffer)!); + return GeoHitResult.decode(readValue(buffer)!); case 200: - return AddonCollection.decode(readValue(buffer)!); + return DownloadState.decode(readValue(buffer)!); case 201: - return GeckoPref.decode(readValue(buffer)!); + return ShareInternetResourceState.decode(readValue(buffer)!); case 202: - return MlProgressData.decode(readValue(buffer)!); + return AddonCollection.decode(readValue(buffer)!); case 203: - return ContainerSiteAssignment.decode(readValue(buffer)!); + return GeckoPref.decode(readValue(buffer)!); case 204: - return GeckoHeader.decode(readValue(buffer)!); + return MlProgressData.decode(readValue(buffer)!); case 205: - return GeckoFetchRequest.decode(readValue(buffer)!); + return ContainerSiteAssignment.decode(readValue(buffer)!); case 206: - return GeckoFetchResponse.decode(readValue(buffer)!); + return GeckoHeader.decode(readValue(buffer)!); case 207: - return BookmarkNode.decode(readValue(buffer)!); + return GeckoFetchRequest.decode(readValue(buffer)!); case 208: - return BookmarkInfo.decode(readValue(buffer)!); + return GeckoFetchResponse.decode(readValue(buffer)!); case 209: - return SitePermissions.decode(readValue(buffer)!); + return BookmarkNode.decode(readValue(buffer)!); case 210: + return BookmarkInfo.decode(readValue(buffer)!); + case 211: + return SitePermissions.decode(readValue(buffer)!); + case 212: return TrackingProtectionException.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index ed6147f6..ebf6d6e8 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -646,6 +646,38 @@ enum WebContentIsolationStrategy { 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 { QueryParameterStripping queryParameterStripping; String queryParameterStrippingAllowList; @@ -694,6 +726,41 @@ class GeckoEngineSettings { final String? fingerprintingProtectionOverrides; final List? 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( this.javascriptEnabled, this.trackingProtectionPolicy, @@ -711,6 +778,17 @@ class GeckoEngineSettings { this.dohSettings, this.fingerprintingProtectionOverrides, this.locales, + this.blockCookies, + this.customCookiePolicy, + this.blockTrackingContent, + this.trackingContentScope, + this.blockCryptominers, + this.blockFingerprinters, + this.blockRedirectTrackers, + this.blockSuspectedFingerprinters, + this.suspectedFingerprintersScope, + this.allowListBaseline, + this.allowListConvenience, ); }