From ce657dd26aff68f37384afe608f42602cdf8566f Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Sat, 23 Aug 2025 06:30:25 +0200 Subject: [PATCH] refactor setting providers; include content blocking settings; --- app/assets/preferences/settings.json | 16 - .../bangs/domain/providers/bangs.dart | 2 +- .../bangs/domain/providers/bangs.g.dart | 2 +- .../bangs/presentation/screens/list.dart | 2 +- .../presentation/widgets/bang_details.dart | 2 +- .../presentation/widgets/site_search.dart | 2 +- .../features/geckoview/domain/providers.dart | 4 +- .../geckoview/domain/providers.g.dart | 2 +- .../geckoview/domain/repositories/tab.dart | 2 +- .../geckoview/domain/repositories/tab.g.dart | 2 +- .../services/engine_settings_replication.dart | 124 ++++---- .../engine_settings_replication.g.dart | 2 +- .../services/proxy_settings_replication.dart | 2 +- .../proxy_settings_replication.g.dart | 2 +- .../browser_modules/bottom_app_bar.dart | 6 +- .../widgets/browser_modules/browser_view.dart | 17 +- .../widgets/sheets/view_tabs.dart | 8 +- .../widgets/tab_creation_menu.dart | 2 +- .../candidates/open_image_new_tab.dart | 4 +- .../presentation/candidates/open_new_tab.dart | 4 +- .../presentation/widgets/reader_button.dart | 4 +- .../domain/providers/search_suggestions.dart | 2 +- .../providers/search_suggestions.g.dart | 2 +- .../search/presentation/screens/search.dart | 2 +- .../domain/repositories/gecko_inference.dart | 4 +- .../repositories/gecko_inference.g.dart | 2 +- .../presentation/pages/ai_configuration.dart | 2 +- .../presentation/pages/default_search.dart | 2 +- .../screens/developer_settings.dart | 2 +- .../screens/general_settings.dart | 2 +- .../screens/web_engine_settings.dart | 76 ++++- .../tor/presentation/screens/tor_proxy.dart | 2 +- .../user/data/models/engine_settings.dart | 27 +- .../user/data/models/engine_settings.g.dart | 58 ++++ app/lib/features/user/domain/providers.dart | 2 +- app/lib/features/user/domain/providers.g.dart | 2 +- .../domain/repositories/engine_settings.dart | 150 +++++---- .../repositories/engine_settings.g.dart | 25 +- .../domain/repositories/general_settings.dart | 36 ++- .../repositories/general_settings.g.dart | 26 +- .../presentation/screens/feed_article.dart | 4 +- app/lib/main.dart | 16 +- .../flutter_mozilla_components/Components.kt | 2 + .../EngineProvider.kt | 28 ++ .../GlobalComponents.kt | 8 +- .../api/GeckoBrowserApiImpl.kt | 10 +- .../api/GeckoEngineSettingsApiImpl.kt | 35 +++ .../pigeons/Gecko.g.kt | 294 ++++++++++++------ .../lib/flutter_mozilla_components.dart | 3 + .../src/domain/services/gecko_browser.dart | 4 +- .../services/gecko_engine_settings.dart | 6 + .../lib/src/pigeons/gecko.g.dart | 280 +++++++++++------ .../pigeons/gecko.dart | 37 ++- 53 files changed, 958 insertions(+), 404 deletions(-) diff --git a/app/assets/preferences/settings.json b/app/assets/preferences/settings.json index 88de035a..f5132869 100644 --- a/app/assets/preferences/settings.json +++ b/app/assets/preferences/settings.json @@ -90,10 +90,6 @@ "value": "", "title": "Disable GIO as a potential proxy bypass vector" }, - "media.peerconnection.ice.proxy_only_if_behind_proxy": { - "value": true, - "title": "Force WebRTC inside the proxy" - }, "media.peerconnection.enabled": { "value": false, "title": "Disable WebRTC PeerConnection", @@ -199,18 +195,6 @@ "title": "Disable automatic User Agent updates", "description": "This preference controls whether Firefox automatically updates its User-Agent string. When set to false, it prevents Firefox from automatically updating the browser's User-Agent string, which is a text identifier that tells websites which browser and version you're using." }, - "privacy.query_stripping.enabled": { - "value": true, - "title": "Strip known tracking query parameters from URLs" - }, - "privacy.query_stripping.enabled.pbmode": { - "value": true, - "title": "Strip known tracking query parameters from URLs (Private browsing)" - }, - "privacy.query_stripping.strip_list": { - "value": "__hsfp __hssc __hstc __s _bhlid _branch_match_id _branch_referrer _gl _hsenc _kx _openstat at_recipient_id at_recipient_list bbeml bsft_clkid bsft_uid dclid et_rid fb_action_ids fb_comment_id fbclid gbraid gclid guce_referrer guce_referrer_sig hsCtaTracking igshid irclickid mc_eid mkt_tok ml_subscriber ml_subscriber_hash msclkid mtm_cid oft_c oft_ck oft_d oft_id oft_ids oft_k oft_lk oft_sk oly_anon_id oly_enc_id pk_cid rb_clickid s_cid sc_customer sc_eh sc_uid srsltid ss_email_id twclid unicorn_click_id vero_conv vero_id vgo_ee wbraid wickedid yclid ymclid ysclid", - "title": "Improve built-in query stripping" - }, "dom.private-attribution.submission.enabled": { "value": false, "title": "Disable Private Attribution Submission Control", diff --git a/app/lib/features/bangs/domain/providers/bangs.dart b/app/lib/features/bangs/domain/providers/bangs.dart index 7ebef349..6e0c6d34 100644 --- a/app/lib/features/bangs/domain/providers/bangs.dart +++ b/app/lib/features/bangs/domain/providers/bangs.dart @@ -31,7 +31,7 @@ part 'bangs.g.dart'; @Riverpod(keepAlive: true) Stream defaultSearchBangData(Ref ref) { final trigger = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.defaultSearchProvider, ), ); diff --git a/app/lib/features/bangs/domain/providers/bangs.g.dart b/app/lib/features/bangs/domain/providers/bangs.g.dart index 2ae40d73..5e462632 100644 --- a/app/lib/features/bangs/domain/providers/bangs.g.dart +++ b/app/lib/features/bangs/domain/providers/bangs.g.dart @@ -7,7 +7,7 @@ part of 'bangs.dart'; // ************************************************************************** String _$defaultSearchBangDataHash() => - r'acce519559903505f9623f3631b6f4177a396d39'; + r'5fa3f588b57a82dd8fffb2082e495108a242ef72'; /// See also [defaultSearchBangData]. @ProviderFor(defaultSearchBangData) diff --git a/app/lib/features/bangs/presentation/screens/list.dart b/app/lib/features/bangs/presentation/screens/list.dart index 843b5e39..bce7ca3b 100644 --- a/app/lib/features/bangs/presentation/screens/list.dart +++ b/app/lib/features/bangs/presentation/screens/list.dart @@ -63,7 +63,7 @@ class BangListScreen extends HookConsumerWidget { .setTrigger(bang.trigger); final settings = ref.read( - generalSettingsRepositoryProvider, + generalSettingsWithDefaultsProvider, ); SearchRoute( diff --git a/app/lib/features/bangs/presentation/widgets/bang_details.dart b/app/lib/features/bangs/presentation/widgets/bang_details.dart index c31360c6..0ea5c28d 100644 --- a/app/lib/features/bangs/presentation/widgets/bang_details.dart +++ b/app/lib/features/bangs/presentation/widgets/bang_details.dart @@ -89,7 +89,7 @@ class BangDetails extends HookConsumerWidget { final url = Uri.parse(bangData.getTemplateUrl('').origin); final isPrivate = ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; diff --git a/app/lib/features/bangs/presentation/widgets/site_search.dart b/app/lib/features/bangs/presentation/widgets/site_search.dart index aab24a16..0579c8ad 100644 --- a/app/lib/features/bangs/presentation/widgets/site_search.dart +++ b/app/lib/features/bangs/presentation/widgets/site_search.dart @@ -86,7 +86,7 @@ class SiteSearch extends HookConsumerWidget { final isPrivate = ref.read(selectedTabStateProvider)?.isPrivate ?? ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; diff --git a/app/lib/features/geckoview/domain/providers.dart b/app/lib/features/geckoview/domain/providers.dart index 7ae7f526..1df607f7 100644 --- a/app/lib/features/geckoview/domain/providers.dart +++ b/app/lib/features/geckoview/domain/providers.dart @@ -44,7 +44,7 @@ GeckoSelectionActionService selectionActionService(Ref ref) { service.setActions([ SearchAction((text) async { final router = await ref.read(routerProvider.future); - final settings = ref.read(generalSettingsRepositoryProvider); + final settings = ref.read(generalSettingsWithDefaultsProvider); final route = SearchRoute( tabType: ref.read(selectedTabTypeProvider) ?? @@ -64,7 +64,7 @@ GeckoSelectionActionService selectionActionService(Ref ref) { final isPrivate = currentTab?.isPrivate ?? ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; diff --git a/app/lib/features/geckoview/domain/providers.g.dart b/app/lib/features/geckoview/domain/providers.g.dart index 354ab754..d62efd1f 100644 --- a/app/lib/features/geckoview/domain/providers.g.dart +++ b/app/lib/features/geckoview/domain/providers.g.dart @@ -7,7 +7,7 @@ part of 'providers.dart'; // ************************************************************************** String _$selectionActionServiceHash() => - r'f356434878b46fd443c8e3a8a48cf07c34a63b70'; + r'b1deac5e0566443b4729656c7082903b529bc269'; /// See also [selectionActionService]. @ProviderFor(selectionActionService) diff --git a/app/lib/features/geckoview/domain/repositories/tab.dart b/app/lib/features/geckoview/domain/repositories/tab.dart index 1420537e..f7025182 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.dart @@ -349,7 +349,7 @@ class TabRepository extends _$TabRepository { next.whenData((value) async { final isPrivate = ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultIntentTabType == TabType.private; diff --git a/app/lib/features/geckoview/domain/repositories/tab.g.dart b/app/lib/features/geckoview/domain/repositories/tab.g.dart index b964f5b6..e5ae6a17 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.g.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.g.dart @@ -6,7 +6,7 @@ part of 'tab.dart'; // RiverpodGenerator // ************************************************************************** -String _$tabRepositoryHash() => r'fccaa6521e1bad7e8b3f82e6d611469348dc8667'; +String _$tabRepositoryHash() => r'3465f351f1d36e0f21b4e8ef4ebe7fdc4fbc0b75'; /// See also [TabRepository]. @ProviderFor(TabRepository) 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 3873bbcd..fbd642ca 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 @@ -37,7 +37,7 @@ class EngineSettingsReplicationService ref.listen( fireImmediately: true, - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (settings) => settings.themeMode, ), (previous, next) async { @@ -62,61 +62,75 @@ class EngineSettingsReplicationService fireImmediately: true, engineSettingsRepositoryProvider, (previous, next) async { - if (initialSettingsSent && previous != null) { - if (previous.javascriptEnabled != next.javascriptEnabled) { - await _service.javascriptEnabled(next.javascriptEnabled); + final settings = next.valueOrNull; + + if (settings != null) { + if (initialSettingsSent && previous != null) { + if (previous.valueOrNull?.javascriptEnabled != + settings.javascriptEnabled) { + await _service.javascriptEnabled(settings.javascriptEnabled); + } + if (previous.valueOrNull?.trackingProtectionPolicy != + settings.trackingProtectionPolicy) { + await _service.trackingProtectionPolicy( + settings.trackingProtectionPolicy, + ); + } + if (previous.valueOrNull?.httpsOnlyMode != settings.httpsOnlyMode) { + await _service.httpsOnlyMode(settings.httpsOnlyMode); + } + if (previous.valueOrNull?.globalPrivacyControlEnabled != + settings.globalPrivacyControlEnabled) { + await _service.globalPrivacyControlEnabled( + settings.globalPrivacyControlEnabled, + ); + } + if (previous.valueOrNull?.preferredColorScheme != + settings.preferredColorScheme) { + await _service.preferredColorScheme( + settings.preferredColorScheme, + ); + } + if (previous.valueOrNull?.cookieBannerHandlingMode != + settings.cookieBannerHandlingMode) { + await _service.cookieBannerHandlingMode( + settings.cookieBannerHandlingMode, + ); + } + if (previous.valueOrNull?.cookieBannerHandlingModePrivateBrowsing != + settings.cookieBannerHandlingModePrivateBrowsing) { + await _service.cookieBannerHandlingModePrivateBrowsing( + settings.cookieBannerHandlingModePrivateBrowsing, + ); + } + if (previous.valueOrNull?.cookieBannerHandlingGlobalRules != + settings.cookieBannerHandlingGlobalRules) { + await _service.cookieBannerHandlingGlobalRules( + settings.cookieBannerHandlingGlobalRules, + ); + } + if (previous + .valueOrNull + ?.cookieBannerHandlingGlobalRulesSubFrames != + settings.cookieBannerHandlingGlobalRulesSubFrames) { + await _service.cookieBannerHandlingGlobalRulesSubFrames( + settings.cookieBannerHandlingGlobalRulesSubFrames, + ); + } + if (previous.valueOrNull?.webContentIsolationStrategy != + settings.webContentIsolationStrategy) { + await _service.webContentIsolationStrategy( + settings.webContentIsolationStrategy, + ); + } + if (previous.valueOrNull?.contentBlocking != + settings.contentBlocking) { + await _service.contentBlocking(settings.contentBlocking); + } + } else { + await _service.setDefaultSettings(settings); + initialSettingsSent = true; } - if (previous.trackingProtectionPolicy != - next.trackingProtectionPolicy) { - await _service.trackingProtectionPolicy( - next.trackingProtectionPolicy, - ); - } - if (previous.httpsOnlyMode != next.httpsOnlyMode) { - await _service.httpsOnlyMode(next.httpsOnlyMode); - } - if (previous.globalPrivacyControlEnabled != - next.globalPrivacyControlEnabled) { - await _service.globalPrivacyControlEnabled( - next.globalPrivacyControlEnabled, - ); - } - if (previous.preferredColorScheme != next.preferredColorScheme) { - await _service.preferredColorScheme(next.preferredColorScheme); - } - if (previous.cookieBannerHandlingMode != - next.cookieBannerHandlingMode) { - await _service.cookieBannerHandlingMode( - next.cookieBannerHandlingMode, - ); - } - if (previous.cookieBannerHandlingModePrivateBrowsing != - next.cookieBannerHandlingModePrivateBrowsing) { - await _service.cookieBannerHandlingModePrivateBrowsing( - next.cookieBannerHandlingModePrivateBrowsing, - ); - } - if (previous.cookieBannerHandlingGlobalRules != - next.cookieBannerHandlingGlobalRules) { - await _service.cookieBannerHandlingGlobalRules( - next.cookieBannerHandlingGlobalRules, - ); - } - if (previous.cookieBannerHandlingGlobalRulesSubFrames != - next.cookieBannerHandlingGlobalRulesSubFrames) { - await _service.cookieBannerHandlingGlobalRulesSubFrames( - next.cookieBannerHandlingGlobalRulesSubFrames, - ); - } - if (previous.webContentIsolationStrategy != - next.webContentIsolationStrategy) { - await _service.webContentIsolationStrategy( - next.webContentIsolationStrategy, - ); - } - } else { - await _service.setDefaultSettings(next); - initialSettingsSent = true; } }, onError: (error, stackTrace) { diff --git a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart index c2ba1006..8014e3f0 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/engine_settings_replication.g.dart @@ -7,7 +7,7 @@ part of 'engine_settings_replication.dart'; // ************************************************************************** String _$engineSettingsReplicationServiceHash() => - r'31b5b79e429bfedb9c6b57050cd2d99407914228'; + r'be3001c952dac6c7c6be3927f6834dae57280601'; /// See also [EngineSettingsReplicationService]. @ProviderFor(EngineSettingsReplicationService) diff --git a/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart b/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart index b383e212..2eb4e900 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart @@ -84,7 +84,7 @@ class ProxySettingsReplication extends _$ProxySettingsReplication { ref.listen( fireImmediately: true, - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.proxyPrivateTabsTor, ), (previous, next) async { diff --git a/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.g.dart b/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.g.dart index d8956c16..8377dc3b 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.g.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/proxy_settings_replication.g.dart @@ -7,7 +7,7 @@ part of 'proxy_settings_replication.dart'; // ************************************************************************** String _$proxySettingsReplicationHash() => - r'4476bab659f09de0866d0b3ae10d54ccb996335d'; + r'e9f3adc73349c6d33fc7009412984b1daa4cc8da'; /// See also [ProxySettingsReplication]. @ProviderFor(ProxySettingsReplication) diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart index de9df4d9..8a5027c9 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart @@ -65,7 +65,7 @@ class BrowserBottomAppBar extends HookConsumerWidget { ); final showExtensionShortcut = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.showExtensionShortcut, ), ); @@ -209,7 +209,7 @@ class BrowserBottomAppBar extends HookConsumerWidget { onPressed: () async { final isPrivate = ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; @@ -268,7 +268,7 @@ class BrowserBottomAppBar extends HookConsumerWidget { onPressed: () async { final isPrivate = ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart index 4dc89ad7..fb8cd506 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart @@ -85,13 +85,14 @@ class _BrowserViewState extends ConsumerState @override Widget build(BuildContext context) { useOnInitialization(() async { - await ref.read(generalSettingsRepositoryProvider.notifier).fetch().then(( - settings, - ) async { - await ref - .read(browserDataServiceProvider.notifier) - .deleteDataOnEngineStart(settings.deleteBrowsingDataOnQuit); - }); + await ref + .read(generalSettingsRepositoryProvider.notifier) + .fetchSettings() + .then((settings) async { + await ref + .read(browserDataServiceProvider.notifier) + .deleteDataOnEngineStart(settings.deleteBrowsingDataOnQuit); + }); }); final hasTab = ref.watch( @@ -313,7 +314,7 @@ class _BrowserViewState extends ConsumerState widget.suggestionTimeout) { //Don't do anything if a child route is active if (GoRouterState.of(context).topRoute?.name == BrowserRoute.name) { - final settings = ref.read(generalSettingsRepositoryProvider); + final settings = ref.read(generalSettingsWithDefaultsProvider); unawaited( showSuggestNewTabMessage( diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart index 1a394816..64bef7db 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart @@ -129,7 +129,7 @@ class _TabSheetHeader extends HookConsumerWidget { ); final enableAiFeatures = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (settings) => settings.enableLocalAiFeatures, ), ); @@ -415,7 +415,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget { tabSuggestionsControllerProvider, ); final enableAiFeatures = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (settings) => settings.enableLocalAiFeatures, ), ); @@ -605,7 +605,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget { ), child: FloatingActionButton.small( onPressed: () async { - final settings = ref.read(generalSettingsRepositoryProvider); + final settings = ref.read(generalSettingsWithDefaultsProvider); await SearchRoute( tabType: @@ -786,7 +786,7 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget { ), child: FloatingActionButton.small( onPressed: () async { - final settings = ref.read(generalSettingsRepositoryProvider); + final settings = ref.read(generalSettingsWithDefaultsProvider); await SearchRoute( tabType: diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart index dde0b345..adb26945 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart @@ -39,7 +39,7 @@ class TabCreationMenu extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final createChildTabsOption = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.createChildTabsOption, ), ); diff --git a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart index b2c7e95e..946bc989 100644 --- a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart +++ b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_image_new_tab.dart @@ -47,7 +47,9 @@ class OpenImageInNewTab extends HookConsumerWidget { final currentTab = ref.read(selectedTabStateProvider); final isPrivate = currentTab?.isPrivate ?? - ref.read(generalSettingsRepositoryProvider).defaultCreateTabType == + ref + .read(generalSettingsWithDefaultsProvider) + .defaultCreateTabType == TabType.private; final tabId = await ref diff --git a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart index 0cba7694..5fb309a7 100644 --- a/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart +++ b/app/lib/features/geckoview/features/contextmenu/presentation/candidates/open_new_tab.dart @@ -47,7 +47,9 @@ class OpenInNewTab extends HookConsumerWidget { final currentTab = ref.read(selectedTabStateProvider); final isPrivate = currentTab?.isPrivate ?? - ref.read(generalSettingsRepositoryProvider).defaultCreateTabType == + ref + .read(generalSettingsWithDefaultsProvider) + .defaultCreateTabType == TabType.private; final tabId = await ref diff --git a/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart b/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart index 10d94b1b..43668dd3 100644 --- a/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart +++ b/app/lib/features/geckoview/features/readerview/presentation/widgets/reader_button.dart @@ -34,13 +34,13 @@ class ReaderButton extends HookConsumerWidget { final readerChanging = ref.watch(readerableScreenControllerProvider); final enableReadability = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.enableReadability, ), ); final enforceReadability = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.enforceReadability, ), ); diff --git a/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.dart b/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.dart index 13bcb4b9..4eb910fa 100644 --- a/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.dart +++ b/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.dart @@ -33,7 +33,7 @@ part 'search_suggestions.g.dart'; @Riverpod(keepAlive: true) ISearchSuggestionProvider defaultSearchSuggestions(Ref ref) { final provider = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.defaultSearchSuggestionsProvider, ), ); diff --git a/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.g.dart b/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.g.dart index 0eb555c3..69a4e068 100644 --- a/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.g.dart +++ b/app/lib/features/geckoview/features/search/domain/providers/search_suggestions.g.dart @@ -7,7 +7,7 @@ part of 'search_suggestions.dart'; // ************************************************************************** String _$defaultSearchSuggestionsHash() => - r'eb96a5db465422558a26e22abb3f843a82c17452'; + r'6d7a387c7daeb790a4c9a5cdc105d2a2bdbacd2b'; /// See also [defaultSearchSuggestions]. @ProviderFor(defaultSearchSuggestions) diff --git a/app/lib/features/geckoview/features/search/presentation/screens/search.dart b/app/lib/features/geckoview/features/search/presentation/screens/search.dart index 3e0970e0..1605b863 100644 --- a/app/lib/features/geckoview/features/search/presentation/screens/search.dart +++ b/app/lib/features/geckoview/features/search/presentation/screens/search.dart @@ -52,7 +52,7 @@ class SearchScreen extends HookConsumerWidget { final formKey = useMemoized(() => GlobalKey()); final createChildTabsOption = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.createChildTabsOption, ), ); diff --git a/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart b/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart index c275332b..de0b6170 100644 --- a/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart +++ b/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart @@ -61,7 +61,7 @@ class GeckoInferenceRepository extends _$GeckoInferenceRepository { Future predictDocumentTopic(Set titles) async { if (!ref.read( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (settings) => settings.enableLocalAiFeatures, ), )) { @@ -97,7 +97,7 @@ class GeckoInferenceRepository extends _$GeckoInferenceRepository { required List unassignedDocumentsInput, }) async { if (!ref.read( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (settings) => settings.enableLocalAiFeatures, ), )) { diff --git a/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.g.dart b/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.g.dart index 133a424c..aea1ca37 100644 --- a/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.g.dart +++ b/app/lib/features/geckoview/features/tabs/domain/repositories/gecko_inference.g.dart @@ -276,7 +276,7 @@ class _ContainerTabSuggestionsProviderElement } String _$geckoInferenceRepositoryHash() => - r'170bd0b67e5f1edc43ed97d4535f41353fb8796e'; + r'6099998a3f49fc4faf650878ab4afd6268c593e2'; /// See also [GeckoInferenceRepository]. @ProviderFor(GeckoInferenceRepository) diff --git a/app/lib/features/onboarding/presentation/pages/ai_configuration.dart b/app/lib/features/onboarding/presentation/pages/ai_configuration.dart index d47978c3..19378b7b 100644 --- a/app/lib/features/onboarding/presentation/pages/ai_configuration.dart +++ b/app/lib/features/onboarding/presentation/pages/ai_configuration.dart @@ -32,7 +32,7 @@ class AiConfigurationPage extends HookConsumerWidget { final theme = Theme.of(context); final enableLocalAiFeatures = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (settings) => settings.enableLocalAiFeatures, ), ); diff --git a/app/lib/features/onboarding/presentation/pages/default_search.dart b/app/lib/features/onboarding/presentation/pages/default_search.dart index 9b32727f..521fa205 100644 --- a/app/lib/features/onboarding/presentation/pages/default_search.dart +++ b/app/lib/features/onboarding/presentation/pages/default_search.dart @@ -45,7 +45,7 @@ class DefaultSearchPage extends HookConsumerWidget { defaultSearchBangDataProvider.select((value) => value.valueOrNull), ); final activeAutosuggest = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.defaultSearchSuggestionsProvider, ), ); diff --git a/app/lib/features/settings/presentation/screens/developer_settings.dart b/app/lib/features/settings/presentation/screens/developer_settings.dart index 38afd48c..6804c453 100644 --- a/app/lib/features/settings/presentation/screens/developer_settings.dart +++ b/app/lib/features/settings/presentation/screens/developer_settings.dart @@ -37,7 +37,7 @@ class DeveloperSettingsScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final engineSettings = ref.watch(engineSettingsRepositoryProvider); + final engineSettings = ref.watch(engineSettingsWithDefaultsProvider); final userAgentTextController = useTextEditingController( text: engineSettings.userAgent, diff --git a/app/lib/features/settings/presentation/screens/general_settings.dart b/app/lib/features/settings/presentation/screens/general_settings.dart index 6dd191e5..86045552 100644 --- a/app/lib/features/settings/presentation/screens/general_settings.dart +++ b/app/lib/features/settings/presentation/screens/general_settings.dart @@ -39,7 +39,7 @@ class GeneralSettingsScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final generalSettings = ref.watch(generalSettingsRepositoryProvider); + final generalSettings = ref.watch(generalSettingsWithDefaultsProvider); return Scaffold( appBar: AppBar(title: const Text('General Settings')), diff --git a/app/lib/features/settings/presentation/screens/web_engine_settings.dart b/app/lib/features/settings/presentation/screens/web_engine_settings.dart index cb2dc3f6..66998b20 100644 --- a/app/lib/features/settings/presentation/screens/web_engine_settings.dart +++ b/app/lib/features/settings/presentation/screens/web_engine_settings.dart @@ -35,8 +35,8 @@ class WebEngineSettingsScreen extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final generalSettings = ref.watch(generalSettingsRepositoryProvider); - final engineSettings = ref.watch(engineSettingsRepositoryProvider); + final generalSettings = ref.watch(generalSettingsWithDefaultsProvider); + final engineSettings = ref.watch(engineSettingsWithDefaultsProvider); return Scaffold( appBar: AppBar(title: const Text('Web Engine Settings')), @@ -115,7 +115,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget { ), ), SwitchListTile.adaptive( - title: const Text('Enable Global Privacy Control (GPC)'), + title: const Text('Global Privacy Control (GPC)'), secondary: const Icon(MdiIcons.incognitoCircleOff), value: engineSettings.globalPrivacyControlEnabled, onChanged: (value) async { @@ -300,6 +300,76 @@ class WebEngineSettingsScreen extends HookConsumerWidget { // ], // ), // ), + SwitchListTile.adaptive( + title: const Text('Bounce Tracking Protection'), + secondary: const Icon(MdiIcons.securityNetwork), + value: switch (engineSettings + .contentBlocking + .bounceTrackingProtectionMode) { + BounceTrackingProtectionMode.disabled => false, + BounceTrackingProtectionMode.enabled => true, + BounceTrackingProtectionMode.enabledStandby => false, + BounceTrackingProtectionMode.enabledDryRun => false, + }, + onChanged: (value) async { + await ref + .read(saveEngineSettingsControllerProvider.notifier) + .save( + (currentSettings) => currentSettings.copyWith + .bounceTrackingProtectionMode( + value + ? BounceTrackingProtectionMode.enabled + : BounceTrackingProtectionMode.disabled, + ), + ); + }, + ), + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('Query Parameter Stripping'), + leading: Icon(MdiIcons.closeNetwork), + contentPadding: EdgeInsets.zero, + ), + Center( + child: SegmentedButton( + segments: const [ + ButtonSegment( + value: QueryParameterStripping.disabled, + label: Text('Disabled'), + ), + ButtonSegment( + value: QueryParameterStripping.enabled, + label: Text('Enabled'), + ), + ButtonSegment( + value: QueryParameterStripping.privateOnly, + label: Text('Private mode only'), + ), + ], + selected: {engineSettings.queryParameterStripping}, + onSelectionChanged: (value) async { + await ref + .read( + saveEngineSettingsControllerProvider.notifier, + ) + .save( + (currentSettings) => currentSettings.copyWith + .queryParameterStripping(value.first), + ); + }, + ), + ), + ], + ), + ), ListTile( title: const Text('Web Engine Hardening'), contentPadding: const EdgeInsets.symmetric( diff --git a/app/lib/features/tor/presentation/screens/tor_proxy.dart b/app/lib/features/tor/presentation/screens/tor_proxy.dart index 74c2ba21..4b47be3c 100644 --- a/app/lib/features/tor/presentation/screens/tor_proxy.dart +++ b/app/lib/features/tor/presentation/screens/tor_proxy.dart @@ -34,7 +34,7 @@ class TorProxyScreen extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final torProxyPort = ref.watch(torProxyServiceProvider); final proxyPrivateTabsTor = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.proxyPrivateTabsTor, ), ); diff --git a/app/lib/features/user/data/models/engine_settings.dart b/app/lib/features/user/data/models/engine_settings.dart index f03eeac9..a36ced0b 100644 --- a/app/lib/features/user/data/models/engine_settings.dart +++ b/app/lib/features/user/data/models/engine_settings.dart @@ -54,6 +54,20 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { WebContentIsolationStrategy get webContentIsolationStrategy => super.webContentIsolationStrategy!; + final QueryParameterStripping queryParameterStripping; + + final BounceTrackingProtectionMode bounceTrackingProtectionMode; + + @override + @JsonKey(includeFromJson: false, includeToJson: false) + ContentBlocking get contentBlocking => ContentBlocking( + queryParameterStripping: queryParameterStripping, + queryParameterStrippingAllowList: '', + queryParameterStrippingStripList: + '__hsfp __hssc __hstc __s _bhlid _branch_match_id _branch_referrer _gl _hsenc _kx _openstat at_recipient_id at_recipient_list bbeml bsft_clkid bsft_uid dclid et_rid fb_action_ids fb_comment_id fbclid gbraid gclid guce_referrer guce_referrer_sig hsCtaTracking igshid irclickid mc_eid mkt_tok ml_subscriber ml_subscriber_hash msclkid mtm_cid oft_c oft_ck oft_d oft_id oft_ids oft_k oft_lk oft_sk oly_anon_id oly_enc_id pk_cid rb_clickid s_cid sc_customer sc_eh sc_uid srsltid ss_email_id twclid unicorn_click_id vero_conv vero_id vgo_ee wbraid wickedid yclid ymclid ysclid', + bounceTrackingProtectionMode: bounceTrackingProtectionMode, + ); + EngineSettings({ required super.javascriptEnabled, required super.trackingProtectionPolicy, @@ -66,6 +80,8 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { required super.cookieBannerHandlingGlobalRulesSubFrames, required super.webContentIsolationStrategy, required super.userAgent, + required this.queryParameterStripping, + required this.bounceTrackingProtectionMode, }); EngineSettings.withDefaults({ @@ -79,8 +95,15 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { bool? cookieBannerHandlingGlobalRules, bool? cookieBannerHandlingGlobalRulesSubFrames, WebContentIsolationStrategy? webContentIsolationStrategy, + QueryParameterStripping? queryParameterStripping, + BounceTrackingProtectionMode? bounceTrackingProtectionMode, super.userAgent, - }) : super( + }) : queryParameterStripping = + queryParameterStripping ?? QueryParameterStripping.disabled, + bounceTrackingProtectionMode = + bounceTrackingProtectionMode ?? + BounceTrackingProtectionMode.disabled, + super( javascriptEnabled: javascriptEnabled ?? true, trackingProtectionPolicy: trackingProtectionPolicy ?? TrackingProtectionPolicy.strict, @@ -119,5 +142,7 @@ class EngineSettings extends GeckoEngineSettings with FastEquatable { super.cookieBannerHandlingGlobalRulesSubFrames, super.webContentIsolationStrategy, super.userAgent, + queryParameterStripping, + bounceTrackingProtectionMode, ]; } 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 62144558..d69e4d85 100644 --- a/app/lib/features/user/data/models/engine_settings.g.dart +++ b/app/lib/features/user/data/models/engine_settings.g.dart @@ -41,6 +41,14 @@ abstract class _$EngineSettingsCWProxy { EngineSettings userAgent(String? userAgent); + EngineSettings queryParameterStripping( + QueryParameterStripping queryParameterStripping, + ); + + EngineSettings bounceTrackingProtectionMode( + BounceTrackingProtectionMode bounceTrackingProtectionMode, + ); + /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `EngineSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. /// /// Usage @@ -59,6 +67,8 @@ abstract class _$EngineSettingsCWProxy { bool? cookieBannerHandlingGlobalRulesSubFrames, WebContentIsolationStrategy? webContentIsolationStrategy, String? userAgent, + QueryParameterStripping queryParameterStripping, + BounceTrackingProtectionMode bounceTrackingProtectionMode, }); } @@ -124,6 +134,16 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { @override EngineSettings userAgent(String? userAgent) => this(userAgent: userAgent); + @override + EngineSettings queryParameterStripping( + QueryParameterStripping queryParameterStripping, + ) => this(queryParameterStripping: queryParameterStripping); + + @override + EngineSettings bounceTrackingProtectionMode( + BounceTrackingProtectionMode bounceTrackingProtectionMode, + ) => this(bounceTrackingProtectionMode: bounceTrackingProtectionMode); + @override /// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `EngineSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support. /// @@ -145,6 +165,8 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { const $CopyWithPlaceholder(), Object? webContentIsolationStrategy = const $CopyWithPlaceholder(), Object? userAgent = const $CopyWithPlaceholder(), + Object? queryParameterStripping = const $CopyWithPlaceholder(), + Object? bounceTrackingProtectionMode = const $CopyWithPlaceholder(), }) { return EngineSettings( javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder() @@ -201,6 +223,16 @@ class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy { ? _value.userAgent // ignore: cast_nullable_to_non_nullable : userAgent as String?, + queryParameterStripping: + queryParameterStripping == const $CopyWithPlaceholder() + ? _value.queryParameterStripping + // ignore: cast_nullable_to_non_nullable + : queryParameterStripping as QueryParameterStripping, + bounceTrackingProtectionMode: + bounceTrackingProtectionMode == const $CopyWithPlaceholder() + ? _value.bounceTrackingProtectionMode + // ignore: cast_nullable_to_non_nullable + : bounceTrackingProtectionMode as BounceTrackingProtectionMode, ); } } @@ -247,6 +279,14 @@ EngineSettings _$EngineSettingsFromJson(Map json) => _$WebContentIsolationStrategyEnumMap, json['webContentIsolationStrategy'], ), + queryParameterStripping: $enumDecodeNullable( + _$QueryParameterStrippingEnumMap, + json['queryParameterStripping'], + ), + bounceTrackingProtectionMode: $enumDecodeNullable( + _$BounceTrackingProtectionModeEnumMap, + json['bounceTrackingProtectionMode'], + ), userAgent: json['userAgent'] as String?, ); @@ -271,6 +311,11 @@ Map _$EngineSettingsToJson( 'webContentIsolationStrategy': _$WebContentIsolationStrategyEnumMap[instance .webContentIsolationStrategy]!, + 'queryParameterStripping': + _$QueryParameterStrippingEnumMap[instance.queryParameterStripping]!, + 'bounceTrackingProtectionMode': + _$BounceTrackingProtectionModeEnumMap[instance + .bounceTrackingProtectionMode]!, }; const _$TrackingProtectionPolicyEnumMap = { @@ -303,3 +348,16 @@ const _$WebContentIsolationStrategyEnumMap = { WebContentIsolationStrategy.isolateEverything: 'isolateEverything', WebContentIsolationStrategy.isolateHighValue: 'isolateHighValue', }; + +const _$QueryParameterStrippingEnumMap = { + QueryParameterStripping.disabled: 'disabled', + QueryParameterStripping.privateOnly: 'privateOnly', + QueryParameterStripping.enabled: 'enabled', +}; + +const _$BounceTrackingProtectionModeEnumMap = { + BounceTrackingProtectionMode.disabled: 'disabled', + BounceTrackingProtectionMode.enabled: 'enabled', + BounceTrackingProtectionMode.enabledStandby: 'enabledStandby', + BounceTrackingProtectionMode.enabledDryRun: 'enabledDryRun', +}; diff --git a/app/lib/features/user/domain/providers.dart b/app/lib/features/user/domain/providers.dart index 235a0c5e..9d9e2d3b 100644 --- a/app/lib/features/user/domain/providers.dart +++ b/app/lib/features/user/domain/providers.dart @@ -42,7 +42,7 @@ Stream iconCacheSizeMegabytes(Ref ref) { @Riverpod() bool incognitoModeEnabled(Ref ref) { return ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.deleteBrowsingDataOnQuit != null, ), ); diff --git a/app/lib/features/user/domain/providers.g.dart b/app/lib/features/user/domain/providers.g.dart index 22ab44a7..a99d1fea 100644 --- a/app/lib/features/user/domain/providers.g.dart +++ b/app/lib/features/user/domain/providers.g.dart @@ -43,7 +43,7 @@ final iconCacheSizeMegabytesProvider = // ignore: unused_element typedef IconCacheSizeMegabytesRef = AutoDisposeStreamProviderRef; String _$incognitoModeEnabledHash() => - r'3968c2d4945be09a0bc08ce0d70b0390187c44e8'; + r'36957b70a5261f9d3ad228e07cc8dd5c8f616082'; /// See also [incognitoModeEnabled]. @ProviderFor(incognitoModeEnabled) diff --git a/app/lib/features/user/domain/repositories/engine_settings.dart b/app/lib/features/user/domain/repositories/engine_settings.dart index 39473b38..be8dbcf5 100644 --- a/app/lib/features/user/domain/repositories/engine_settings.dart +++ b/app/lib/features/user/domain/repositories/engine_settings.dart @@ -20,6 +20,7 @@ import 'dart:async'; import 'package:drift/drift.dart'; +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:weblibre/features/user/data/models/engine_settings.dart'; import 'package:weblibre/features/user/data/providers.dart'; @@ -33,9 +34,72 @@ typedef UpdateEngineSettingsFunc = class EngineSettingsRepository extends _$EngineSettingsRepository { final _partitionKey = 'engine'; - Future updateSettings(UpdateEngineSettingsFunc updateWithCurrent) { - final oldJson = state.toJson(); - final newJson = updateWithCurrent(state).toJson(); + EngineSettings _deserializeSettings( + List> entries, + ) { + final db = ref.read(userDatabaseProvider); + final settings = Map.fromEntries(entries); + + return EngineSettings.fromJson({ + 'incognitoMode': settings['incognitoMode']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'javascriptEnabled': settings['javascriptEnabled']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'trackingProtectionPolicy': settings['trackingProtectionPolicy']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'httpsOnlyMode': settings['httpsOnlyMode']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'globalPrivacyControlEnabled': settings['globalPrivacyControlEnabled'] + ?.readAs(DriftSqlType.bool, db.typeMapping), + 'cookieBannerHandlingMode': settings['cookieBannerHandlingMode']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'cookieBannerHandlingModePrivateBrowsing': + settings['cookieBannerHandlingModePrivateBrowsing']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'cookieBannerHandlingGlobalRules': + settings['cookieBannerHandlingGlobalRules']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'cookieBannerHandlingGlobalRulesSubFrames': + settings['cookieBannerHandlingGlobalRulesSubFrames']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), + 'webContentIsolationStrategy': settings['webContentIsolationStrategy'] + ?.readAs(DriftSqlType.string, db.typeMapping), + 'userAgent': settings['userAgent']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'queryParameterStripping': settings['queryParameterStripping']?.readAs( + DriftSqlType.string, + db.typeMapping, + ), + 'bounceTrackingProtectionMode': settings['bounceTrackingProtectionMode'] + ?.readAs(DriftSqlType.string, db.typeMapping), + }); + } + + Future updateSettings( + UpdateEngineSettingsFunc updateWithCurrent, + ) async { + final current = await fetchSettings(); + + final oldJson = current.toJson(); + final newJson = updateWithCurrent(current).toJson(); return ref.read(userDatabaseProvider).transaction(() async { for (final MapEntry(:key, :value) in newJson.entries) { @@ -49,69 +113,33 @@ class EngineSettingsRepository extends _$EngineSettingsRepository { }); } + Future fetchSettings() { + return ref + .read(userDatabaseProvider) + .settingDao + .getAllSettingsOfPartitionKey(_partitionKey) + .get() + .then(_deserializeSettings); + } + @override - EngineSettings build() { + Stream build() { final db = ref.watch(userDatabaseProvider); - final watchSub = db.settingDao + return db.settingDao .getAllSettingsOfPartitionKey(_partitionKey) .watch() - .listen((entries) { - final settings = Map.fromEntries(entries); - - state = EngineSettings.fromJson({ - 'incognitoMode': settings['incognitoMode']?.readAs( - DriftSqlType.bool, - db.typeMapping, - ), - 'javascriptEnabled': settings['javascriptEnabled']?.readAs( - DriftSqlType.bool, - db.typeMapping, - ), - 'trackingProtectionPolicy': settings['trackingProtectionPolicy'] - ?.readAs(DriftSqlType.string, db.typeMapping), - 'httpsOnlyMode': settings['httpsOnlyMode']?.readAs( - DriftSqlType.string, - db.typeMapping, - ), - 'globalPrivacyControlEnabled': - settings['globalPrivacyControlEnabled']?.readAs( - DriftSqlType.bool, - db.typeMapping, - ), - 'cookieBannerHandlingMode': settings['cookieBannerHandlingMode'] - ?.readAs(DriftSqlType.string, db.typeMapping), - 'cookieBannerHandlingModePrivateBrowsing': - settings['cookieBannerHandlingModePrivateBrowsing']?.readAs( - DriftSqlType.string, - db.typeMapping, - ), - 'cookieBannerHandlingGlobalRules': - settings['cookieBannerHandlingGlobalRules']?.readAs( - DriftSqlType.bool, - db.typeMapping, - ), - 'cookieBannerHandlingGlobalRulesSubFrames': - settings['cookieBannerHandlingGlobalRulesSubFrames']?.readAs( - DriftSqlType.bool, - db.typeMapping, - ), - 'webContentIsolationStrategy': - settings['webContentIsolationStrategy']?.readAs( - DriftSqlType.string, - db.typeMapping, - ), - 'userAgent': settings['userAgent']?.readAs( - DriftSqlType.string, - db.typeMapping, - ), - }); + .map((entries) { + return _deserializeSettings(entries); }); - - ref.onDispose(() async { - await watchSub.cancel(); - }); - - return EngineSettings.withDefaults(); } } + +@Riverpod() +EngineSettings engineSettingsWithDefaults(Ref ref) { + return ref.watch( + engineSettingsRepositoryProvider.select( + (value) => value.valueOrNull ?? EngineSettings.withDefaults(), + ), + ); +} 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 492bb0f7..a06c86dd 100644 --- a/app/lib/features/user/domain/repositories/engine_settings.g.dart +++ b/app/lib/features/user/domain/repositories/engine_settings.g.dart @@ -6,13 +6,32 @@ part of 'engine_settings.dart'; // RiverpodGenerator // ************************************************************************** +String _$engineSettingsWithDefaultsHash() => + r'018e37f31f893e203b86038e84b0ddf5e72454d4'; + +/// See also [engineSettingsWithDefaults]. +@ProviderFor(engineSettingsWithDefaults) +final engineSettingsWithDefaultsProvider = + AutoDisposeProvider.internal( + engineSettingsWithDefaults, + name: r'engineSettingsWithDefaultsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$engineSettingsWithDefaultsHash, + dependencies: null, + allTransitiveDependencies: null, + ); + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef EngineSettingsWithDefaultsRef = AutoDisposeProviderRef; String _$engineSettingsRepositoryHash() => - r'87e308c8bd4390d1d5c401532a0c35cd96f6176e'; + r'aa522c35377280c451f6ac9e5f7063eab7ba6efc'; /// See also [EngineSettingsRepository]. @ProviderFor(EngineSettingsRepository) final engineSettingsRepositoryProvider = - NotifierProvider.internal( + StreamNotifierProvider.internal( EngineSettingsRepository.new, name: r'engineSettingsRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,6 +41,6 @@ final engineSettingsRepositoryProvider = allTransitiveDependencies: null, ); -typedef _$EngineSettingsRepository = Notifier; +typedef _$EngineSettingsRepository = StreamNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/app/lib/features/user/domain/repositories/general_settings.dart b/app/lib/features/user/domain/repositories/general_settings.dart index b2e8657f..9ad915e7 100644 --- a/app/lib/features/user/domain/repositories/general_settings.dart +++ b/app/lib/features/user/domain/repositories/general_settings.dart @@ -22,6 +22,7 @@ import 'dart:convert'; import 'package:drift/drift.dart'; import 'package:nullability/nullability.dart'; +import 'package:riverpod/riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:weblibre/features/user/data/models/general_settings.dart'; import 'package:weblibre/features/user/data/providers.dart'; @@ -95,7 +96,7 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository { } //Eager fetch, when up to date settings are required - Future fetch() { + Future fetchSettings() { return ref .read(userDatabaseProvider) .settingDao @@ -104,9 +105,13 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository { .then(_deserializeSettings); } - Future updateSettings(UpdateGeneralSettingsFunc updateWithCurrent) { - final oldJson = state.toJson(); - final newJson = updateWithCurrent(state).toJson(); + Future updateSettings( + UpdateGeneralSettingsFunc updateWithCurrent, + ) async { + final current = await fetchSettings(); + + final oldJson = current.toJson(); + final newJson = updateWithCurrent(current).toJson(); final db = ref.read(userDatabaseProvider); @@ -120,20 +125,23 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository { } @override - GeneralSettings build() { + Stream build() { final db = ref.watch(userDatabaseProvider); - final watchSub = db.settingDao + return db.settingDao .getAllSettingsOfPartitionKey(_partitionKey) .watch() - .listen((event) { - state = _deserializeSettings(event); + .map((event) { + return _deserializeSettings(event); }); - - ref.onDispose(() async { - await watchSub.cancel(); - }); - - return GeneralSettings.withDefaults(); } } + +@Riverpod() +GeneralSettings generalSettingsWithDefaults(Ref ref) { + return ref.watch( + generalSettingsRepositoryProvider.select( + (value) => value.valueOrNull ?? GeneralSettings.withDefaults(), + ), + ); +} diff --git a/app/lib/features/user/domain/repositories/general_settings.g.dart b/app/lib/features/user/domain/repositories/general_settings.g.dart index 42ecc406..7997831e 100644 --- a/app/lib/features/user/domain/repositories/general_settings.g.dart +++ b/app/lib/features/user/domain/repositories/general_settings.g.dart @@ -6,13 +6,33 @@ part of 'general_settings.dart'; // RiverpodGenerator // ************************************************************************** +String _$generalSettingsWithDefaultsHash() => + r'd2a20cb527f8bf5a16e273933cf7ccc9d0851dc4'; + +/// See also [generalSettingsWithDefaults]. +@ProviderFor(generalSettingsWithDefaults) +final generalSettingsWithDefaultsProvider = + AutoDisposeProvider.internal( + generalSettingsWithDefaults, + name: r'generalSettingsWithDefaultsProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$generalSettingsWithDefaultsHash, + dependencies: null, + allTransitiveDependencies: null, + ); + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef GeneralSettingsWithDefaultsRef = + AutoDisposeProviderRef; String _$generalSettingsRepositoryHash() => - r'538e03e9eff8beefc0918614c86d8919224c3039'; + r'7e1c4d6051c4c3972b6998dee89d13b49be32e48'; /// See also [GeneralSettingsRepository]. @ProviderFor(GeneralSettingsRepository) final generalSettingsRepositoryProvider = - NotifierProvider.internal( + StreamNotifierProvider.internal( GeneralSettingsRepository.new, name: r'generalSettingsRepositoryProvider', debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') @@ -22,6 +42,6 @@ final generalSettingsRepositoryProvider = allTransitiveDependencies: null, ); -typedef _$GeneralSettingsRepository = Notifier; +typedef _$GeneralSettingsRepository = StreamNotifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/app/lib/features/web_feed/presentation/screens/feed_article.dart b/app/lib/features/web_feed/presentation/screens/feed_article.dart index b1cb6ff4..58293812 100644 --- a/app/lib/features/web_feed/presentation/screens/feed_article.dart +++ b/app/lib/features/web_feed/presentation/screens/feed_article.dart @@ -196,7 +196,7 @@ class FeedArticleScreen extends HookConsumerWidget { onPressed: () async { final isPrivate = ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; @@ -243,7 +243,7 @@ class FeedArticleScreen extends HookConsumerWidget { case final Uri url) { final isPrivate = ref - .read(generalSettingsRepositoryProvider) + .read(generalSettingsWithDefaultsProvider) .defaultCreateTabType == TabType.private; diff --git a/app/lib/main.dart b/app/lib/main.dart index b3ae9927..2fa279a0 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -32,6 +32,7 @@ import 'package:weblibre/core/error_observer.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/providers/defaults.dart'; import 'package:weblibre/domain/services/app_initialization.dart'; +import 'package:weblibre/features/user/domain/repositories/engine_settings.dart'; import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/features/web_feed/presentation/controllers/fetch_articles.dart'; import 'package:weblibre/features/web_feed/utils/fetch_entrypoint.dart'; @@ -69,10 +70,6 @@ void main() async { }, message); }); - await GeckoBrowserService().initialize( - kDebugMode ? LogLevel.debug : LogLevel.warn, - ); - await HomeWidget.setAppGroupId('weblibre'); runApp( @@ -81,12 +78,21 @@ void main() async { child: HookConsumer( builder: (context, ref, child) { final themeMode = ref.watch( - generalSettingsRepositoryProvider.select( + generalSettingsWithDefaultsProvider.select( (value) => value.themeMode, ), ); useOnInitialization(() async { + final engineSettings = await ref + .read(engineSettingsRepositoryProvider.notifier) + .fetchSettings(); + + await GeckoBrowserService().initialize( + kDebugMode ? LogLevel.debug : LogLevel.warn, + engineSettings.contentBlocking, + ); + await ref .read(appInitializationServiceProvider.notifier) .initialize(); diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt index d182286e..9a939427 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/Components.kt @@ -15,6 +15,7 @@ import eu.weblibre.flutter_mozilla_components.components.Search import eu.weblibre.flutter_mozilla_components.components.Services import eu.weblibre.flutter_mozilla_components.components.UseCases import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents +import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents import eu.weblibre.flutter_mozilla_components.pigeons.GeckoSuggestionEvents @@ -35,6 +36,7 @@ class Components(private val context: Context, val readerViewController: ReaderViewController, val selectionAction: SelectionActionDelegate, val logLevel: Log.Priority, + val contentBlocking: ContentBlocking, private val addonEvents: GeckoAddonEvents, private val tabContentEvents: GeckoTabContentEvents, private val extensionEvents: BrowserExtensionEvents diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/EngineProvider.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/EngineProvider.kt index 7f3eeea5..da4b0427 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/EngineProvider.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/EngineProvider.kt @@ -10,15 +10,19 @@ import eu.weblibre.flutter_mozilla_components.feature.CookieManagerFeature import eu.weblibre.flutter_mozilla_components.feature.PrefManagerFeature import eu.weblibre.flutter_mozilla_components.feature.BrowserExtensionFeature import eu.weblibre.flutter_mozilla_components.feature.MLEngineFeature +import eu.weblibre.flutter_mozilla_components.pigeons.BounceTrackingProtectionMode import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents +import eu.weblibre.flutter_mozilla_components.pigeons.QueryParameterStripping import mozilla.components.browser.engine.gecko.GeckoEngine import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient import mozilla.components.concept.engine.DefaultSettings import mozilla.components.concept.engine.Engine +import mozilla.components.concept.engine.EngineSession import mozilla.components.concept.fetch.Client import mozilla.components.feature.webcompat.WebCompatFeature import mozilla.components.support.base.log.Log import mozilla.components.support.base.log.logger.Logger +import org.mozilla.geckoview.ContentBlocking import org.mozilla.geckoview.GeckoRuntime import org.mozilla.geckoview.GeckoRuntimeSettings @@ -34,6 +38,29 @@ object EngineProvider { if (runtime == null) { Logger.debug("Creating Runtime") val builder = GeckoRuntimeSettings.Builder() + val contentBlocking = ContentBlocking.Settings.Builder(); + + contentBlocking.bounceTrackingProtectionMode(when(components.contentBlocking.bounceTrackingProtectionMode) { + BounceTrackingProtectionMode.ENABLED -> EngineSession.BounceTrackingProtectionMode.ENABLED.mode + BounceTrackingProtectionMode.DISABLED -> EngineSession.BounceTrackingProtectionMode.DISABLED.mode + BounceTrackingProtectionMode.ENABLED_STANDBY -> EngineSession.BounceTrackingProtectionMode.ENABLED_STANDBY.mode + BounceTrackingProtectionMode.ENABLED_DRY_RUN -> EngineSession.BounceTrackingProtectionMode.ENABLED_DRY_RUN.mode + }) + + contentBlocking.queryParameterStrippingEnabled(when(components.contentBlocking.queryParameterStripping) { + QueryParameterStripping.ENABLED -> true + QueryParameterStripping.DISABLED -> false + QueryParameterStripping.PRIVATE_ONLY -> false + }) + + contentBlocking.queryParameterStrippingPrivateBrowsingEnabled(when(components.contentBlocking.queryParameterStripping) { + QueryParameterStripping.ENABLED -> true + QueryParameterStripping.DISABLED -> false + QueryParameterStripping.PRIVATE_ONLY -> true + }) + + contentBlocking.queryParameterStrippingAllowList(components.contentBlocking.queryParameterStrippingAllowList) + contentBlocking.queryParameterStrippingStripList(components.contentBlocking.queryParameterStrippingStripList) // if (isCrashReportActive) { // builder.crashHandler(CrashHandlerService::class.java) @@ -45,6 +72,7 @@ object EngineProvider { builder.extensionsWebAPIEnabled(true) builder.debugLogging(components.logLevel == Log.Priority.DEBUG) builder.consoleOutput(components.logLevel == Log.Priority.DEBUG) + builder.contentBlocking(contentBlocking.build()) runtime = GeckoRuntime.create(context, builder.build()) } diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt index 8938088c..2143b5ee 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/GlobalComponents.kt @@ -8,6 +8,7 @@ package eu.weblibre.flutter_mozilla_components import android.content.Context import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents +import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents import eu.weblibre.flutter_mozilla_components.pigeons.GeckoSuggestionEvents @@ -53,7 +54,8 @@ object GlobalComponents { addonEvents: GeckoAddonEvents, tabContentEvents: GeckoTabContentEvents, extensionEvents: BrowserExtensionEvents, - logLevel: Log.Priority + logLevel: Log.Priority, + contentBlocking: ContentBlocking ) { Logger.debug("Creating new components") @@ -63,9 +65,11 @@ object GlobalComponents { readerViewController, selectionAction, logLevel, + contentBlocking, addonEvents, tabContentEvents, - extensionEvents + extensionEvents, + ) _components = newComponents diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt index 772c4d9b..f2f9b8ae 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoBrowserApiImpl.kt @@ -16,6 +16,7 @@ import eu.weblibre.flutter_mozilla_components.GlobalComponents import eu.weblibre.flutter_mozilla_components.activities.NotificationActivity import eu.weblibre.flutter_mozilla_components.feature.DefaultSelectionActionDelegate import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents +import eu.weblibre.flutter_mozilla_components.pigeons.ContentBlocking import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonEvents import eu.weblibre.flutter_mozilla_components.pigeons.GeckoAddonsApi import eu.weblibre.flutter_mozilla_components.pigeons.GeckoBrowserApi @@ -133,7 +134,7 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { isPlatformViewRegistered = false } - override fun initialize(logLevel: LogLevel) { + override fun initialize(logLevel: LogLevel, contentBlocking: ContentBlocking) { synchronized(this) { if(!isGeckoInitialized) { val geckoLogging = GeckoLogging(_flutterPluginBinding.binaryMessenger) @@ -147,7 +148,7 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { Log.addSink(PriorityAwareLogSink(level, geckoLogging)) - setupGeckoEngine(level) + setupGeckoEngine(level, contentBlocking) isGeckoInitialized = true } } @@ -163,7 +164,7 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { return false } - private fun setupGeckoEngine(logLevel: Log.Priority) { + private fun setupGeckoEngine(logLevel: Log.Priority, contentBlocking: ContentBlocking) { val selectionActionEvents = GeckoSelectionActionEvents(_flutterPluginBinding.binaryMessenger) val selectionActionDelegate = DefaultSelectionActionDelegate(selectionActionEvents) { actions -> @@ -193,7 +194,8 @@ class GeckoBrowserApiImpl : GeckoBrowserApi { addonEvents, tabContentEvents, extensionEvents, - logLevel + logLevel, + contentBlocking ) GeckoEngineSettingsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoEngineSettingsApiImpl()) 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 2bdb72a9..23ea5c35 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 @@ -12,6 +12,7 @@ import eu.weblibre.flutter_mozilla_components.pigeons.CookieBannerHandlingMode 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.WebContentIsolationStrategy import mozilla.components.concept.engine.Engine import mozilla.components.concept.engine.EngineSession @@ -112,6 +113,22 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi { if(settings.userAgent != null) { components.core.engineSettings.userAgentString = settings.userAgent; } + if(settings.contentBlocking != null) { + components.core.engineSettings.queryParameterStripping = when(settings.contentBlocking.queryParameterStripping) { + QueryParameterStripping.ENABLED -> true + QueryParameterStripping.DISABLED -> false + QueryParameterStripping.PRIVATE_ONLY -> false + } + components.core.engineSettings.queryParameterStrippingPrivateBrowsing = when(settings.contentBlocking.queryParameterStripping) { + QueryParameterStripping.ENABLED -> true + QueryParameterStripping.DISABLED -> false + QueryParameterStripping.PRIVATE_ONLY -> true + } + components.core.engineSettings.queryParameterStrippingAllowList = settings.contentBlocking.queryParameterStrippingAllowList + components.core.engineSettings.queryParameterStrippingStripList = settings.contentBlocking.queryParameterStrippingStripList + + //TODO: Add bounce tracking protection when available + } } override fun updateRuntimeSettings(settings: GeckoEngineSettings) { @@ -162,6 +179,24 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi { components.core.engine.settings.userAgentString = components.core.engineSettings.userAgentString 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 + + //TODO: Add bounce tracking protection when available + + reloadSession = true + } if(reloadSession) { components.useCases.sessionUseCases.reload() 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 22f4c780..4222bbe1 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 @@ -225,6 +225,42 @@ enum class HttpsOnlyMode(val raw: Int) { } } +enum class QueryParameterStripping(val raw: Int) { + DISABLED(0), + PRIVATE_ONLY(1), + ENABLED(2); + + companion object { + fun ofRaw(raw: Int): QueryParameterStripping? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class BounceTrackingProtectionMode(val raw: Int) { + /** Fully disabled. */ + DISABLED(0), + /** Fully enabled. */ + ENABLED(1), + /** + * Disabled, but collects user interaction data. Use this mode as the + * "disabled" state when the feature can be toggled on and off, e.g. via + * preferences. + */ + ENABLED_STANDBY(2), + /** + * Feature enabled, but tracker purging is only simulated. Used for + * testing and telemetry collection. + */ + ENABLED_DRY_RUN(3); + + companion object { + fun ofRaw(raw: Int): BounceTrackingProtectionMode? { + return values().firstOrNull { it.raw == raw } + } + } +} + enum class ColorScheme(val raw: Int) { SYSTEM(0), LIGHT(1), @@ -1456,6 +1492,43 @@ data class TabContent ( override fun hashCode(): Int = toList().hashCode() } +/** Generated class from Pigeon that represents data sent in messages. */ +data class ContentBlocking ( + val queryParameterStripping: QueryParameterStripping, + val queryParameterStrippingAllowList: String, + val queryParameterStrippingStripList: String, + val bounceTrackingProtectionMode: BounceTrackingProtectionMode +) + { + companion object { + fun fromList(pigeonVar_list: List): ContentBlocking { + val queryParameterStripping = pigeonVar_list[0] as QueryParameterStripping + val queryParameterStrippingAllowList = pigeonVar_list[1] as String + val queryParameterStrippingStripList = pigeonVar_list[2] as String + val bounceTrackingProtectionMode = pigeonVar_list[3] as BounceTrackingProtectionMode + return ContentBlocking(queryParameterStripping, queryParameterStrippingAllowList, queryParameterStrippingStripList, bounceTrackingProtectionMode) + } + } + fun toList(): List { + return listOf( + queryParameterStripping, + queryParameterStrippingAllowList, + queryParameterStrippingStripList, + bounceTrackingProtectionMode, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is ContentBlocking) { + return false + } + if (this === other) { + return true + } + return GeckoPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} + /** Generated class from Pigeon that represents data sent in messages. */ data class GeckoEngineSettings ( val javascriptEnabled: Boolean? = null, @@ -1468,7 +1541,8 @@ data class GeckoEngineSettings ( val cookieBannerHandlingGlobalRules: Boolean? = null, val cookieBannerHandlingGlobalRulesSubFrames: Boolean? = null, val webContentIsolationStrategy: WebContentIsolationStrategy? = null, - val userAgent: String? = null + val userAgent: String? = null, + val contentBlocking: ContentBlocking? = null ) { companion object { @@ -1484,7 +1558,8 @@ data class GeckoEngineSettings ( val cookieBannerHandlingGlobalRulesSubFrames = pigeonVar_list[8] as Boolean? val webContentIsolationStrategy = pigeonVar_list[9] as WebContentIsolationStrategy? val userAgent = pigeonVar_list[10] as String? - return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent) + val contentBlocking = pigeonVar_list[11] as ContentBlocking? + return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, contentBlocking) } } fun toList(): List { @@ -1500,6 +1575,7 @@ data class GeckoEngineSettings ( cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, + contentBlocking, ) } override fun equals(other: Any?): Boolean { @@ -1997,220 +2073,235 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { } 139.toByte() -> { return (readValue(buffer) as Long?)?.let { - ColorScheme.ofRaw(it.toInt()) + QueryParameterStripping.ofRaw(it.toInt()) } } 140.toByte() -> { return (readValue(buffer) as Long?)?.let { - CookieBannerHandlingMode.ofRaw(it.toInt()) + BounceTrackingProtectionMode.ofRaw(it.toInt()) } } 141.toByte() -> { return (readValue(buffer) as Long?)?.let { - WebContentIsolationStrategy.ofRaw(it.toInt()) + ColorScheme.ofRaw(it.toInt()) } } 142.toByte() -> { return (readValue(buffer) as Long?)?.let { - DownloadStatus.ofRaw(it.toInt()) + CookieBannerHandlingMode.ofRaw(it.toInt()) } } 143.toByte() -> { return (readValue(buffer) as Long?)?.let { - LogLevel.ofRaw(it.toInt()) + WebContentIsolationStrategy.ofRaw(it.toInt()) } } 144.toByte() -> { - return (readValue(buffer) as? List)?.let { - TranslationOptions.fromList(it) + return (readValue(buffer) as Long?)?.let { + DownloadStatus.ofRaw(it.toInt()) } } 145.toByte() -> { - return (readValue(buffer) as? List)?.let { - ReaderState.fromList(it) + return (readValue(buffer) as Long?)?.let { + LogLevel.ofRaw(it.toInt()) } } 146.toByte() -> { return (readValue(buffer) as? List)?.let { - LastMediaAccessState.fromList(it) + TranslationOptions.fromList(it) } } 147.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryMetadataKey.fromList(it) + ReaderState.fromList(it) } } 148.toByte() -> { return (readValue(buffer) as? List)?.let { - PackageCategoryValue.fromList(it) + LastMediaAccessState.fromList(it) } } 149.toByte() -> { return (readValue(buffer) as? List)?.let { - ExternalPackage.fromList(it) + HistoryMetadataKey.fromList(it) } } 150.toByte() -> { return (readValue(buffer) as? List)?.let { - LoadUrlFlagsValue.fromList(it) + PackageCategoryValue.fromList(it) } } 151.toByte() -> { return (readValue(buffer) as? List)?.let { - SourceValue.fromList(it) + ExternalPackage.fromList(it) } } 152.toByte() -> { return (readValue(buffer) as? List)?.let { - TabState.fromList(it) + LoadUrlFlagsValue.fromList(it) } } 153.toByte() -> { return (readValue(buffer) as? List)?.let { - RecoverableTab.fromList(it) + SourceValue.fromList(it) } } 154.toByte() -> { return (readValue(buffer) as? List)?.let { - RecoverableBrowserState.fromList(it) + TabState.fromList(it) } } 155.toByte() -> { return (readValue(buffer) as? List)?.let { - IconRequest.fromList(it) + RecoverableTab.fromList(it) } } 156.toByte() -> { return (readValue(buffer) as? List)?.let { - ResourceSize.fromList(it) + RecoverableBrowserState.fromList(it) } } 157.toByte() -> { return (readValue(buffer) as? List)?.let { - Resource.fromList(it) + IconRequest.fromList(it) } } 158.toByte() -> { return (readValue(buffer) as? List)?.let { - IconResult.fromList(it) + ResourceSize.fromList(it) } } 159.toByte() -> { return (readValue(buffer) as? List)?.let { - CookiePartitionKey.fromList(it) + Resource.fromList(it) } } 160.toByte() -> { return (readValue(buffer) as? List)?.let { - Cookie.fromList(it) + IconResult.fromList(it) } } 161.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryItem.fromList(it) + CookiePartitionKey.fromList(it) } } 162.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryState.fromList(it) + Cookie.fromList(it) } } 163.toByte() -> { return (readValue(buffer) as? List)?.let { - ReaderableState.fromList(it) + HistoryItem.fromList(it) } } 164.toByte() -> { return (readValue(buffer) as? List)?.let { - SecurityInfoState.fromList(it) + HistoryState.fromList(it) } } 165.toByte() -> { return (readValue(buffer) as? List)?.let { - TabContentState.fromList(it) + ReaderableState.fromList(it) } } 166.toByte() -> { return (readValue(buffer) as? List)?.let { - FindResultState.fromList(it) + SecurityInfoState.fromList(it) } } 167.toByte() -> { return (readValue(buffer) as? List)?.let { - CustomSelectionAction.fromList(it) + TabContentState.fromList(it) } } 168.toByte() -> { return (readValue(buffer) as? List)?.let { - WebExtensionData.fromList(it) + FindResultState.fromList(it) } } 169.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoSuggestion.fromList(it) + CustomSelectionAction.fromList(it) } } 170.toByte() -> { return (readValue(buffer) as? List)?.let { - TabContent.fromList(it) + WebExtensionData.fromList(it) } } 171.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoEngineSettings.fromList(it) + GeckoSuggestion.fromList(it) } } 172.toByte() -> { return (readValue(buffer) as? List)?.let { - AutocompleteResult.fromList(it) + TabContent.fromList(it) } } 173.toByte() -> { return (readValue(buffer) as? List)?.let { - UnknownHitResult.fromList(it) + ContentBlocking.fromList(it) } } 174.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageHitResult.fromList(it) + GeckoEngineSettings.fromList(it) } } 175.toByte() -> { return (readValue(buffer) as? List)?.let { - VideoHitResult.fromList(it) + AutocompleteResult.fromList(it) } } 176.toByte() -> { return (readValue(buffer) as? List)?.let { - AudioHitResult.fromList(it) + UnknownHitResult.fromList(it) } } 177.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageSrcHitResult.fromList(it) + ImageHitResult.fromList(it) } } 178.toByte() -> { return (readValue(buffer) as? List)?.let { - PhoneHitResult.fromList(it) + VideoHitResult.fromList(it) } } 179.toByte() -> { return (readValue(buffer) as? List)?.let { - EmailHitResult.fromList(it) + AudioHitResult.fromList(it) } } 180.toByte() -> { return (readValue(buffer) as? List)?.let { - GeoHitResult.fromList(it) + ImageSrcHitResult.fromList(it) } } 181.toByte() -> { return (readValue(buffer) as? List)?.let { - DownloadState.fromList(it) + PhoneHitResult.fromList(it) } } 182.toByte() -> { + return (readValue(buffer) as? List)?.let { + EmailHitResult.fromList(it) + } + } + 183.toByte() -> { + return (readValue(buffer) as? List)?.let { + GeoHitResult.fromList(it) + } + } + 184.toByte() -> { + return (readValue(buffer) as? List)?.let { + DownloadState.fromList(it) + } + } + 185.toByte() -> { return (readValue(buffer) as? List)?.let { ShareInternetResourceState.fromList(it) } @@ -2260,182 +2351,194 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { stream.write(138) writeValue(stream, value.raw) } - is ColorScheme -> { + is QueryParameterStripping -> { stream.write(139) writeValue(stream, value.raw) } - is CookieBannerHandlingMode -> { + is BounceTrackingProtectionMode -> { stream.write(140) writeValue(stream, value.raw) } - is WebContentIsolationStrategy -> { + is ColorScheme -> { stream.write(141) writeValue(stream, value.raw) } - is DownloadStatus -> { + is CookieBannerHandlingMode -> { stream.write(142) writeValue(stream, value.raw) } - is LogLevel -> { + is WebContentIsolationStrategy -> { stream.write(143) writeValue(stream, value.raw) } - is TranslationOptions -> { + is DownloadStatus -> { stream.write(144) - writeValue(stream, value.toList()) + writeValue(stream, value.raw) } - is ReaderState -> { + is LogLevel -> { stream.write(145) - writeValue(stream, value.toList()) + writeValue(stream, value.raw) } - is LastMediaAccessState -> { + is TranslationOptions -> { stream.write(146) writeValue(stream, value.toList()) } - is HistoryMetadataKey -> { + is ReaderState -> { stream.write(147) writeValue(stream, value.toList()) } - is PackageCategoryValue -> { + is LastMediaAccessState -> { stream.write(148) writeValue(stream, value.toList()) } - is ExternalPackage -> { + is HistoryMetadataKey -> { stream.write(149) writeValue(stream, value.toList()) } - is LoadUrlFlagsValue -> { + is PackageCategoryValue -> { stream.write(150) writeValue(stream, value.toList()) } - is SourceValue -> { + is ExternalPackage -> { stream.write(151) writeValue(stream, value.toList()) } - is TabState -> { + is LoadUrlFlagsValue -> { stream.write(152) writeValue(stream, value.toList()) } - is RecoverableTab -> { + is SourceValue -> { stream.write(153) writeValue(stream, value.toList()) } - is RecoverableBrowserState -> { + is TabState -> { stream.write(154) writeValue(stream, value.toList()) } - is IconRequest -> { + is RecoverableTab -> { stream.write(155) writeValue(stream, value.toList()) } - is ResourceSize -> { + is RecoverableBrowserState -> { stream.write(156) writeValue(stream, value.toList()) } - is Resource -> { + is IconRequest -> { stream.write(157) writeValue(stream, value.toList()) } - is IconResult -> { + is ResourceSize -> { stream.write(158) writeValue(stream, value.toList()) } - is CookiePartitionKey -> { + is Resource -> { stream.write(159) writeValue(stream, value.toList()) } - is Cookie -> { + is IconResult -> { stream.write(160) writeValue(stream, value.toList()) } - is HistoryItem -> { + is CookiePartitionKey -> { stream.write(161) writeValue(stream, value.toList()) } - is HistoryState -> { + is Cookie -> { stream.write(162) writeValue(stream, value.toList()) } - is ReaderableState -> { + is HistoryItem -> { stream.write(163) writeValue(stream, value.toList()) } - is SecurityInfoState -> { + is HistoryState -> { stream.write(164) writeValue(stream, value.toList()) } - is TabContentState -> { + is ReaderableState -> { stream.write(165) writeValue(stream, value.toList()) } - is FindResultState -> { + is SecurityInfoState -> { stream.write(166) writeValue(stream, value.toList()) } - is CustomSelectionAction -> { + is TabContentState -> { stream.write(167) writeValue(stream, value.toList()) } - is WebExtensionData -> { + is FindResultState -> { stream.write(168) writeValue(stream, value.toList()) } - is GeckoSuggestion -> { + is CustomSelectionAction -> { stream.write(169) writeValue(stream, value.toList()) } - is TabContent -> { + is WebExtensionData -> { stream.write(170) writeValue(stream, value.toList()) } - is GeckoEngineSettings -> { + is GeckoSuggestion -> { stream.write(171) writeValue(stream, value.toList()) } - is AutocompleteResult -> { + is TabContent -> { stream.write(172) writeValue(stream, value.toList()) } - is UnknownHitResult -> { + is ContentBlocking -> { stream.write(173) writeValue(stream, value.toList()) } - is ImageHitResult -> { + is GeckoEngineSettings -> { stream.write(174) writeValue(stream, value.toList()) } - is VideoHitResult -> { + is AutocompleteResult -> { stream.write(175) writeValue(stream, value.toList()) } - is AudioHitResult -> { + is UnknownHitResult -> { stream.write(176) writeValue(stream, value.toList()) } - is ImageSrcHitResult -> { + is ImageHitResult -> { stream.write(177) writeValue(stream, value.toList()) } - is PhoneHitResult -> { + is VideoHitResult -> { stream.write(178) writeValue(stream, value.toList()) } - is EmailHitResult -> { + is AudioHitResult -> { stream.write(179) writeValue(stream, value.toList()) } - is GeoHitResult -> { + is ImageSrcHitResult -> { stream.write(180) writeValue(stream, value.toList()) } - is DownloadState -> { + is PhoneHitResult -> { stream.write(181) writeValue(stream, value.toList()) } - is ShareInternetResourceState -> { + is EmailHitResult -> { stream.write(182) writeValue(stream, value.toList()) } + is GeoHitResult -> { + stream.write(183) + writeValue(stream, value.toList()) + } + is DownloadState -> { + stream.write(184) + writeValue(stream, value.toList()) + } + is ShareInternetResourceState -> { + stream.write(185) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -2444,7 +2547,7 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface GeckoBrowserApi { - fun initialize(logLevel: LogLevel) + fun initialize(logLevel: LogLevel, contentBlocking: ContentBlocking) fun showNativeFragment(): Boolean fun onTrimMemory(level: Long) @@ -2463,8 +2566,9 @@ interface GeckoBrowserApi { channel.setMessageHandler { message, reply -> val args = message as List val logLevelArg = args[0] as LogLevel + val contentBlockingArg = args[1] as ContentBlocking val wrapped: List = try { - api.initialize(logLevelArg) + api.initialize(logLevelArg, contentBlockingArg) listOf(null) } catch (exception: Throwable) { GeckoPigeonUtils.wrapError(exception) diff --git a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart index 58a9346c..7052636a 100644 --- a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart +++ b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart @@ -31,7 +31,9 @@ export 'src/geckoview_widget.dart'; export 'src/pigeons/gecko.g.dart' show AudioHitResult, + BounceTrackingProtectionMode, ColorScheme, + ContentBlocking, CookieBannerHandlingMode, CookieSameSiteStatus, EmailHitResult, @@ -48,6 +50,7 @@ export 'src/pigeons/gecko.g.dart' ImageSrcHitResult, LogLevel, PhoneHitResult, + QueryParameterStripping, Resource, ResourceSize, SecurityInfoState, diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart index ced8bd6a..d70c67c9 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_browser.dart @@ -13,8 +13,8 @@ class GeckoBrowserService { GeckoBrowserService({GeckoBrowserApi? api}) : _api = api ?? _apiInstance; - Future initialize(LogLevel logLevel) { - return _api.initialize(logLevel); + Future initialize(LogLevel logLevel, ContentBlocking contentBlocking) { + return _api.initialize(logLevel, contentBlocking); } Future showNativeFragment() { 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 1ca9bcbf..30f7ad92 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 @@ -84,4 +84,10 @@ class GeckoEngineSettingsService { GeckoEngineSettings(webContentIsolationStrategy: state), ); } + + Future contentBlocking(ContentBlocking state) { + return _api.updateRuntimeSettings( + GeckoEngineSettings(contentBlocking: 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 026bd7bb..6a6ab20e 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -121,6 +121,26 @@ enum HttpsOnlyMode { enabled, } +enum QueryParameterStripping { + disabled, + privateOnly, + enabled, +} + +enum BounceTrackingProtectionMode { + /// Fully disabled. + disabled, + /// Fully enabled. + enabled, + /// Disabled, but collects user interaction data. Use this mode as the + /// "disabled" state when the feature can be toggled on and off, e.g. via + /// preferences. + enabledStandby, + /// Feature enabled, but tracker purging is only simulated. Used for + /// testing and telemetry collection. + enabledDryRun, +} + enum ColorScheme { system, light, @@ -1790,6 +1810,62 @@ class TabContent { ; } +class ContentBlocking { + ContentBlocking({ + required this.queryParameterStripping, + required this.queryParameterStrippingAllowList, + required this.queryParameterStrippingStripList, + required this.bounceTrackingProtectionMode, + }); + + QueryParameterStripping queryParameterStripping; + + String queryParameterStrippingAllowList; + + String queryParameterStrippingStripList; + + BounceTrackingProtectionMode bounceTrackingProtectionMode; + + List _toList() { + return [ + queryParameterStripping, + queryParameterStrippingAllowList, + queryParameterStrippingStripList, + bounceTrackingProtectionMode, + ]; + } + + Object encode() { + return _toList(); } + + static ContentBlocking decode(Object result) { + result as List; + return ContentBlocking( + queryParameterStripping: result[0]! as QueryParameterStripping, + queryParameterStrippingAllowList: result[1]! as String, + queryParameterStrippingStripList: result[2]! as String, + bounceTrackingProtectionMode: result[3]! as BounceTrackingProtectionMode, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ContentBlocking || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + class GeckoEngineSettings { GeckoEngineSettings({ this.javascriptEnabled, @@ -1803,6 +1879,7 @@ class GeckoEngineSettings { this.cookieBannerHandlingGlobalRulesSubFrames, this.webContentIsolationStrategy, this.userAgent, + this.contentBlocking, }); bool? javascriptEnabled; @@ -1827,6 +1904,8 @@ class GeckoEngineSettings { String? userAgent; + ContentBlocking? contentBlocking; + List _toList() { return [ javascriptEnabled, @@ -1840,6 +1919,7 @@ class GeckoEngineSettings { cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy, userAgent, + contentBlocking, ]; } @@ -1860,6 +1940,7 @@ class GeckoEngineSettings { cookieBannerHandlingGlobalRulesSubFrames: result[8] as bool?, webContentIsolationStrategy: result[9] as WebContentIsolationStrategy?, userAgent: result[10] as String?, + contentBlocking: result[11] as ContentBlocking?, ); } @@ -2518,138 +2599,147 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is HttpsOnlyMode) { buffer.putUint8(138); writeValue(buffer, value.index); - } else if (value is ColorScheme) { + } else if (value is QueryParameterStripping) { buffer.putUint8(139); writeValue(buffer, value.index); - } else if (value is CookieBannerHandlingMode) { + } else if (value is BounceTrackingProtectionMode) { buffer.putUint8(140); writeValue(buffer, value.index); - } else if (value is WebContentIsolationStrategy) { + } else if (value is ColorScheme) { buffer.putUint8(141); writeValue(buffer, value.index); - } else if (value is DownloadStatus) { + } else if (value is CookieBannerHandlingMode) { buffer.putUint8(142); writeValue(buffer, value.index); - } else if (value is LogLevel) { + } else if (value is WebContentIsolationStrategy) { buffer.putUint8(143); writeValue(buffer, value.index); - } else if (value is TranslationOptions) { + } else if (value is DownloadStatus) { buffer.putUint8(144); - writeValue(buffer, value.encode()); - } else if (value is ReaderState) { + writeValue(buffer, value.index); + } else if (value is LogLevel) { buffer.putUint8(145); - writeValue(buffer, value.encode()); - } else if (value is LastMediaAccessState) { + writeValue(buffer, value.index); + } else if (value is TranslationOptions) { buffer.putUint8(146); writeValue(buffer, value.encode()); - } else if (value is HistoryMetadataKey) { + } else if (value is ReaderState) { buffer.putUint8(147); writeValue(buffer, value.encode()); - } else if (value is PackageCategoryValue) { + } else if (value is LastMediaAccessState) { buffer.putUint8(148); writeValue(buffer, value.encode()); - } else if (value is ExternalPackage) { + } else if (value is HistoryMetadataKey) { buffer.putUint8(149); writeValue(buffer, value.encode()); - } else if (value is LoadUrlFlagsValue) { + } else if (value is PackageCategoryValue) { buffer.putUint8(150); writeValue(buffer, value.encode()); - } else if (value is SourceValue) { + } else if (value is ExternalPackage) { buffer.putUint8(151); writeValue(buffer, value.encode()); - } else if (value is TabState) { + } else if (value is LoadUrlFlagsValue) { buffer.putUint8(152); writeValue(buffer, value.encode()); - } else if (value is RecoverableTab) { + } else if (value is SourceValue) { buffer.putUint8(153); writeValue(buffer, value.encode()); - } else if (value is RecoverableBrowserState) { + } else if (value is TabState) { buffer.putUint8(154); writeValue(buffer, value.encode()); - } else if (value is IconRequest) { + } else if (value is RecoverableTab) { buffer.putUint8(155); writeValue(buffer, value.encode()); - } else if (value is ResourceSize) { + } else if (value is RecoverableBrowserState) { buffer.putUint8(156); writeValue(buffer, value.encode()); - } else if (value is Resource) { + } else if (value is IconRequest) { buffer.putUint8(157); writeValue(buffer, value.encode()); - } else if (value is IconResult) { + } else if (value is ResourceSize) { buffer.putUint8(158); writeValue(buffer, value.encode()); - } else if (value is CookiePartitionKey) { + } else if (value is Resource) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is Cookie) { + } else if (value is IconResult) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is HistoryItem) { + } else if (value is CookiePartitionKey) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is HistoryState) { + } else if (value is Cookie) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is ReaderableState) { + } else if (value is HistoryItem) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is SecurityInfoState) { + } else if (value is HistoryState) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is TabContentState) { + } else if (value is ReaderableState) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is FindResultState) { + } else if (value is SecurityInfoState) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is CustomSelectionAction) { + } else if (value is TabContentState) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is WebExtensionData) { + } else if (value is FindResultState) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is GeckoSuggestion) { + } else if (value is CustomSelectionAction) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is TabContent) { + } else if (value is WebExtensionData) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is GeckoEngineSettings) { + } else if (value is GeckoSuggestion) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is AutocompleteResult) { + } else if (value is TabContent) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is UnknownHitResult) { + } else if (value is ContentBlocking) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is ImageHitResult) { + } else if (value is GeckoEngineSettings) { buffer.putUint8(174); writeValue(buffer, value.encode()); - } else if (value is VideoHitResult) { + } else if (value is AutocompleteResult) { buffer.putUint8(175); writeValue(buffer, value.encode()); - } else if (value is AudioHitResult) { + } else if (value is UnknownHitResult) { buffer.putUint8(176); writeValue(buffer, value.encode()); - } else if (value is ImageSrcHitResult) { + } else if (value is ImageHitResult) { buffer.putUint8(177); writeValue(buffer, value.encode()); - } else if (value is PhoneHitResult) { + } else if (value is VideoHitResult) { buffer.putUint8(178); writeValue(buffer, value.encode()); - } else if (value is EmailHitResult) { + } else if (value is AudioHitResult) { buffer.putUint8(179); writeValue(buffer, value.encode()); - } else if (value is GeoHitResult) { + } else if (value is ImageSrcHitResult) { buffer.putUint8(180); writeValue(buffer, value.encode()); - } else if (value is DownloadState) { + } else if (value is PhoneHitResult) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is ShareInternetResourceState) { + } else if (value is EmailHitResult) { buffer.putUint8(182); writeValue(buffer, value.encode()); + } else if (value is GeoHitResult) { + buffer.putUint8(183); + writeValue(buffer, value.encode()); + } else if (value is DownloadState) { + buffer.putUint8(184); + writeValue(buffer, value.encode()); + } else if (value is ShareInternetResourceState) { + buffer.putUint8(185); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -2690,96 +2780,104 @@ class _PigeonCodec extends StandardMessageCodec { return value == null ? null : HttpsOnlyMode.values[value]; case 139: final int? value = readValue(buffer) as int?; - return value == null ? null : ColorScheme.values[value]; + return value == null ? null : QueryParameterStripping.values[value]; case 140: final int? value = readValue(buffer) as int?; - return value == null ? null : CookieBannerHandlingMode.values[value]; + return value == null ? null : BounceTrackingProtectionMode.values[value]; case 141: final int? value = readValue(buffer) as int?; - return value == null ? null : WebContentIsolationStrategy.values[value]; + return value == null ? null : ColorScheme.values[value]; case 142: final int? value = readValue(buffer) as int?; - return value == null ? null : DownloadStatus.values[value]; + return value == null ? null : CookieBannerHandlingMode.values[value]; case 143: final int? value = readValue(buffer) as int?; - return value == null ? null : LogLevel.values[value]; + return value == null ? null : WebContentIsolationStrategy.values[value]; case 144: - return TranslationOptions.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : DownloadStatus.values[value]; case 145: - return ReaderState.decode(readValue(buffer)!); + final int? value = readValue(buffer) as int?; + return value == null ? null : LogLevel.values[value]; case 146: - return LastMediaAccessState.decode(readValue(buffer)!); + return TranslationOptions.decode(readValue(buffer)!); case 147: - return HistoryMetadataKey.decode(readValue(buffer)!); + return ReaderState.decode(readValue(buffer)!); case 148: - return PackageCategoryValue.decode(readValue(buffer)!); + return LastMediaAccessState.decode(readValue(buffer)!); case 149: - return ExternalPackage.decode(readValue(buffer)!); + return HistoryMetadataKey.decode(readValue(buffer)!); case 150: - return LoadUrlFlagsValue.decode(readValue(buffer)!); + return PackageCategoryValue.decode(readValue(buffer)!); case 151: - return SourceValue.decode(readValue(buffer)!); + return ExternalPackage.decode(readValue(buffer)!); case 152: - return TabState.decode(readValue(buffer)!); + return LoadUrlFlagsValue.decode(readValue(buffer)!); case 153: - return RecoverableTab.decode(readValue(buffer)!); + return SourceValue.decode(readValue(buffer)!); case 154: - return RecoverableBrowserState.decode(readValue(buffer)!); + return TabState.decode(readValue(buffer)!); case 155: - return IconRequest.decode(readValue(buffer)!); + return RecoverableTab.decode(readValue(buffer)!); case 156: - return ResourceSize.decode(readValue(buffer)!); + return RecoverableBrowserState.decode(readValue(buffer)!); case 157: - return Resource.decode(readValue(buffer)!); + return IconRequest.decode(readValue(buffer)!); case 158: - return IconResult.decode(readValue(buffer)!); + return ResourceSize.decode(readValue(buffer)!); case 159: - return CookiePartitionKey.decode(readValue(buffer)!); + return Resource.decode(readValue(buffer)!); case 160: - return Cookie.decode(readValue(buffer)!); + return IconResult.decode(readValue(buffer)!); case 161: - return HistoryItem.decode(readValue(buffer)!); + return CookiePartitionKey.decode(readValue(buffer)!); case 162: - return HistoryState.decode(readValue(buffer)!); + return Cookie.decode(readValue(buffer)!); case 163: - return ReaderableState.decode(readValue(buffer)!); + return HistoryItem.decode(readValue(buffer)!); case 164: - return SecurityInfoState.decode(readValue(buffer)!); + return HistoryState.decode(readValue(buffer)!); case 165: - return TabContentState.decode(readValue(buffer)!); + return ReaderableState.decode(readValue(buffer)!); case 166: - return FindResultState.decode(readValue(buffer)!); + return SecurityInfoState.decode(readValue(buffer)!); case 167: - return CustomSelectionAction.decode(readValue(buffer)!); + return TabContentState.decode(readValue(buffer)!); case 168: - return WebExtensionData.decode(readValue(buffer)!); + return FindResultState.decode(readValue(buffer)!); case 169: - return GeckoSuggestion.decode(readValue(buffer)!); + return CustomSelectionAction.decode(readValue(buffer)!); case 170: - return TabContent.decode(readValue(buffer)!); + return WebExtensionData.decode(readValue(buffer)!); case 171: - return GeckoEngineSettings.decode(readValue(buffer)!); + return GeckoSuggestion.decode(readValue(buffer)!); case 172: - return AutocompleteResult.decode(readValue(buffer)!); + return TabContent.decode(readValue(buffer)!); case 173: - return UnknownHitResult.decode(readValue(buffer)!); + return ContentBlocking.decode(readValue(buffer)!); case 174: - return ImageHitResult.decode(readValue(buffer)!); + return GeckoEngineSettings.decode(readValue(buffer)!); case 175: - return VideoHitResult.decode(readValue(buffer)!); + return AutocompleteResult.decode(readValue(buffer)!); case 176: - return AudioHitResult.decode(readValue(buffer)!); + return UnknownHitResult.decode(readValue(buffer)!); case 177: - return ImageSrcHitResult.decode(readValue(buffer)!); + return ImageHitResult.decode(readValue(buffer)!); case 178: - return PhoneHitResult.decode(readValue(buffer)!); + return VideoHitResult.decode(readValue(buffer)!); case 179: - return EmailHitResult.decode(readValue(buffer)!); + return AudioHitResult.decode(readValue(buffer)!); case 180: - return GeoHitResult.decode(readValue(buffer)!); + return ImageSrcHitResult.decode(readValue(buffer)!); case 181: - return DownloadState.decode(readValue(buffer)!); + return PhoneHitResult.decode(readValue(buffer)!); case 182: + return EmailHitResult.decode(readValue(buffer)!); + case 183: + return GeoHitResult.decode(readValue(buffer)!); + case 184: + return DownloadState.decode(readValue(buffer)!); + case 185: return ShareInternetResourceState.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -2800,14 +2898,14 @@ class GeckoBrowserApi { final String pigeonVar_messageChannelSuffix; - Future initialize(LogLevel logLevel) async { + Future initialize(LogLevel logLevel, ContentBlocking contentBlocking) async { final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.initialize$pigeonVar_messageChannelSuffix'; final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([logLevel]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([logLevel, contentBlocking]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index 556c08c3..41b162e2 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -536,6 +536,25 @@ enum TrackingProtectionPolicy { none, recommended, strict, custom } enum HttpsOnlyMode { disabled, privateOnly, enabled } +enum QueryParameterStripping { disabled, privateOnly, enabled } + +enum BounceTrackingProtectionMode { + /// Fully disabled. + disabled, + + /// Fully enabled. + enabled, + + /// Disabled, but collects user interaction data. Use this mode as the + /// "disabled" state when the feature can be toggled on and off, e.g. via + /// preferences. + enabledStandby, + + /// Feature enabled, but tracker purging is only simulated. Used for + /// testing and telemetry collection. + enabledDryRun, +} + enum ColorScheme { system, light, dark } enum CookieBannerHandlingMode { disabled, rejectAll, rejectOrAcceptAll } @@ -546,6 +565,20 @@ enum WebContentIsolationStrategy { isolateHighValue, } +class ContentBlocking { + QueryParameterStripping queryParameterStripping; + String queryParameterStrippingAllowList; + String queryParameterStrippingStripList; + BounceTrackingProtectionMode bounceTrackingProtectionMode; + + ContentBlocking( + this.queryParameterStripping, + this.queryParameterStrippingAllowList, + this.queryParameterStrippingStripList, + this.bounceTrackingProtectionMode, + ); +} + class GeckoEngineSettings { final bool? javascriptEnabled; final TrackingProtectionPolicy? trackingProtectionPolicy; @@ -558,6 +591,7 @@ class GeckoEngineSettings { final bool? cookieBannerHandlingGlobalRulesSubFrames; final WebContentIsolationStrategy? webContentIsolationStrategy; final String? userAgent; + final ContentBlocking? contentBlocking; GeckoEngineSettings( this.javascriptEnabled, @@ -571,6 +605,7 @@ class GeckoEngineSettings { this.cookieBannerHandlingGlobalRulesSubFrames, this.webContentIsolationStrategy, this.userAgent, + this.contentBlocking, ); } @@ -748,7 +783,7 @@ enum LogLevel { debug, info, warn, error } ) @HostApi() abstract class GeckoBrowserApi { - void initialize(LogLevel logLevel); + void initialize(LogLevel logLevel, ContentBlocking contentBlocking); bool showNativeFragment(); void onTrimMemory(int level); }