implement tracking protection per site
This commit is contained in:
@@ -64,6 +64,7 @@ import 'package:weblibre/features/settings/presentation/screens/privacy_security
|
|||||||
import 'package:weblibre/features/settings/presentation/screens/search_content_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/search_content_settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/settings.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/tabs_behavior_settings.dart';
|
import 'package:weblibre/features/settings/presentation/screens/tabs_behavior_settings.dart';
|
||||||
|
import 'package:weblibre/features/settings/presentation/screens/tracking_protection_exceptions.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
|
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening.dart';
|
||||||
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening_group.dart';
|
import 'package:weblibre/features/settings/presentation/screens/web_engine_hardening_group.dart';
|
||||||
import 'package:weblibre/features/tor/presentation/screens/tor_proxy.dart';
|
import 'package:weblibre/features/tor/presentation/screens/tor_proxy.dart';
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ RouteBase get $settingsRoute => GoRouteData.$route(
|
|||||||
name: 'AddonCollectionRoute',
|
name: 'AddonCollectionRoute',
|
||||||
factory: $AddonCollectionRoute._fromState,
|
factory: $AddonCollectionRoute._fromState,
|
||||||
),
|
),
|
||||||
|
GoRouteData.$route(
|
||||||
|
path: 'tracking_protection_exceptions',
|
||||||
|
name: 'TrackingProtectionExceptionsRoute',
|
||||||
|
factory: $TrackingProtectionExceptionsRoute._fromState,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -446,6 +451,28 @@ mixin $AddonCollectionRoute on GoRouteData {
|
|||||||
void replace(BuildContext context) => context.replace(location);
|
void replace(BuildContext context) => context.replace(location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixin $TrackingProtectionExceptionsRoute on GoRouteData {
|
||||||
|
static TrackingProtectionExceptionsRoute _fromState(GoRouterState state) =>
|
||||||
|
TrackingProtectionExceptionsRoute();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get location =>
|
||||||
|
GoRouteData.$location('/settings/tracking_protection_exceptions');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void go(BuildContext context) => context.go(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void pushReplacement(BuildContext context) =>
|
||||||
|
context.pushReplacement(location);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void replace(BuildContext context) => context.replace(location);
|
||||||
|
}
|
||||||
|
|
||||||
RouteBase get $browserRoute => GoRouteData.$route(
|
RouteBase get $browserRoute => GoRouteData.$route(
|
||||||
path: '/browser',
|
path: '/browser',
|
||||||
name: 'BrowserRoute',
|
name: 'BrowserRoute',
|
||||||
|
|||||||
@@ -71,6 +71,10 @@ part of 'routes.dart';
|
|||||||
name: 'AddonCollectionRoute',
|
name: 'AddonCollectionRoute',
|
||||||
path: 'addon_collection',
|
path: 'addon_collection',
|
||||||
),
|
),
|
||||||
|
TypedGoRoute<TrackingProtectionExceptionsRoute>(
|
||||||
|
name: 'TrackingProtectionExceptionsRoute',
|
||||||
|
path: 'tracking_protection_exceptions',
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class SettingsRoute extends GoRouteData with $SettingsRoute {
|
class SettingsRoute extends GoRouteData with $SettingsRoute {
|
||||||
@@ -182,3 +186,11 @@ class WebEngineHardeningGroupRoute extends GoRouteData
|
|||||||
return WebEngineHardeningGroupScreen(groupName: group);
|
return WebEngineHardeningGroupScreen(groupName: group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TrackingProtectionExceptionsRoute extends GoRouteData
|
||||||
|
with $TrackingProtectionExceptionsRoute {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, GoRouterState state) {
|
||||||
|
return const TrackingProtectionExceptionsScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2025 Fabian Freund.
|
||||||
|
*
|
||||||
|
* This file is part of WebLibre
|
||||||
|
* (see https://weblibre.eu).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
|
part 'tracking_protection.g.dart';
|
||||||
|
|
||||||
|
/// Repository for managing per-site Enhanced Tracking Protection exceptions
|
||||||
|
///
|
||||||
|
/// Wraps the GeckoTrackingProtectionApi and handles state invalidation
|
||||||
|
/// automatically after mutations.
|
||||||
|
@Riverpod(keepAlive: true)
|
||||||
|
class TrackingProtectionRepository extends _$TrackingProtectionRepository {
|
||||||
|
final _api = GeckoTrackingProtectionApi();
|
||||||
|
|
||||||
|
/// Check if a tab has a tracking protection exception
|
||||||
|
///
|
||||||
|
/// Returns true if ETP is disabled for this site
|
||||||
|
Future<bool> containsException(String tabId) {
|
||||||
|
return _api.containsException(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add tracking protection exception for a tab (disable ETP for this site)
|
||||||
|
Future<void> addException(String tabId) async {
|
||||||
|
await _api.addException(tabId);
|
||||||
|
await _invalidateWithDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove tracking protection exception for a tab (enable ETP for this site)
|
||||||
|
Future<void> removeException(String tabId) async {
|
||||||
|
await _api.removeException(tabId);
|
||||||
|
await _invalidateWithDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a specific exception by URL and refresh the exceptions list
|
||||||
|
Future<void> removeExceptionByUrl(String url) async {
|
||||||
|
await _api.removeExceptionByUrl(url);
|
||||||
|
await _invalidateWithDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove all tracking protection exceptions
|
||||||
|
Future<void> removeAllExceptions() async {
|
||||||
|
await _api.removeAllExceptions();
|
||||||
|
await _invalidateWithDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to invalidate repository state after mutations with delay
|
||||||
|
Future<void> _invalidateWithDelay() async {
|
||||||
|
await Future.delayed(const Duration(milliseconds: 100)).whenComplete(() {
|
||||||
|
if (ref.mounted) {
|
||||||
|
ref.invalidateSelf();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<TrackingProtectionException>> build() {
|
||||||
|
return _api.fetchExceptions();
|
||||||
|
}
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'tracking_protection.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
/// Repository for managing per-site Enhanced Tracking Protection exceptions
|
||||||
|
///
|
||||||
|
/// Wraps the GeckoTrackingProtectionApi and handles state invalidation
|
||||||
|
/// automatically after mutations.
|
||||||
|
|
||||||
|
@ProviderFor(TrackingProtectionRepository)
|
||||||
|
final trackingProtectionRepositoryProvider =
|
||||||
|
TrackingProtectionRepositoryProvider._();
|
||||||
|
|
||||||
|
/// Repository for managing per-site Enhanced Tracking Protection exceptions
|
||||||
|
///
|
||||||
|
/// Wraps the GeckoTrackingProtectionApi and handles state invalidation
|
||||||
|
/// automatically after mutations.
|
||||||
|
final class TrackingProtectionRepositoryProvider
|
||||||
|
extends
|
||||||
|
$AsyncNotifierProvider<
|
||||||
|
TrackingProtectionRepository,
|
||||||
|
List<TrackingProtectionException>
|
||||||
|
> {
|
||||||
|
/// Repository for managing per-site Enhanced Tracking Protection exceptions
|
||||||
|
///
|
||||||
|
/// Wraps the GeckoTrackingProtectionApi and handles state invalidation
|
||||||
|
/// automatically after mutations.
|
||||||
|
TrackingProtectionRepositoryProvider._()
|
||||||
|
: super(
|
||||||
|
from: null,
|
||||||
|
argument: null,
|
||||||
|
retry: null,
|
||||||
|
name: r'trackingProtectionRepositoryProvider',
|
||||||
|
isAutoDispose: false,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$trackingProtectionRepositoryHash();
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
TrackingProtectionRepository create() => TrackingProtectionRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$trackingProtectionRepositoryHash() =>
|
||||||
|
r'6d4407034b2c67317d0a0e106002f1785fd86a34';
|
||||||
|
|
||||||
|
/// Repository for managing per-site Enhanced Tracking Protection exceptions
|
||||||
|
///
|
||||||
|
/// Wraps the GeckoTrackingProtectionApi and handles state invalidation
|
||||||
|
/// automatically after mutations.
|
||||||
|
|
||||||
|
abstract class _$TrackingProtectionRepository
|
||||||
|
extends $AsyncNotifier<List<TrackingProtectionException>> {
|
||||||
|
FutureOr<List<TrackingProtectionException>> build();
|
||||||
|
@$mustCallSuper
|
||||||
|
@override
|
||||||
|
void runBuild() {
|
||||||
|
final ref =
|
||||||
|
this.ref
|
||||||
|
as $Ref<
|
||||||
|
AsyncValue<List<TrackingProtectionException>>,
|
||||||
|
List<TrackingProtectionException>
|
||||||
|
>;
|
||||||
|
final element =
|
||||||
|
ref.element
|
||||||
|
as $ClassProviderElement<
|
||||||
|
AnyNotifier<
|
||||||
|
AsyncValue<List<TrackingProtectionException>>,
|
||||||
|
List<TrackingProtectionException>
|
||||||
|
>,
|
||||||
|
AsyncValue<List<TrackingProtectionException>>,
|
||||||
|
Object?,
|
||||||
|
Object?
|
||||||
|
>;
|
||||||
|
element.handleCreate(ref, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-9
@@ -28,17 +28,14 @@ Future<SitePermissions?> sitePermissions(
|
|||||||
Ref ref,
|
Ref ref,
|
||||||
String origin,
|
String origin,
|
||||||
bool isPrivate,
|
bool isPrivate,
|
||||||
) async {
|
) {
|
||||||
final api = GeckoSitePermissionsApi();
|
final api = GeckoSitePermissionsApi();
|
||||||
return api.getSitePermissions(origin, isPrivate);
|
return api.getSitePermissions(origin, isPrivate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provider to get the public suffix plus one (eTLD+1) for a host
|
/// Provider to get the public suffix plus one (eTLD+1) for a host
|
||||||
@Riverpod()
|
@Riverpod()
|
||||||
Future<String> publicSuffixPlusOne(
|
Future<String> publicSuffixPlusOne(Ref ref, String host) {
|
||||||
Ref ref,
|
|
||||||
String host,
|
|
||||||
) async {
|
|
||||||
final api = GeckoPublicSuffixListApi();
|
final api = GeckoPublicSuffixListApi();
|
||||||
return api.getPublicSuffixPlusOne(host);
|
return api.getPublicSuffixPlusOne(host);
|
||||||
}
|
}
|
||||||
@@ -49,10 +46,7 @@ class SelectedClearDataTypes extends _$SelectedClearDataTypes {
|
|||||||
@override
|
@override
|
||||||
Set<ClearDataType> build() {
|
Set<ClearDataType> build() {
|
||||||
// Default to clearing all site data and auth sessions (most common use case)
|
// Default to clearing all site data and auth sessions (most common use case)
|
||||||
return {
|
return {ClearDataType.allSiteData, ClearDataType.authSessions};
|
||||||
ClearDataType.allSiteData,
|
|
||||||
ClearDataType.authSessions,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void toggle(ClearDataType type) {
|
void toggle(ClearDataType type) {
|
||||||
|
|||||||
+2
-2
@@ -68,7 +68,7 @@ final class SitePermissionsProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$sitePermissionsHash() => r'c605e9a1acb261ec46659431b5bb1e105b8aa2a1';
|
String _$sitePermissionsHash() => r'785af0f938bfe53799fc8b6ee8be71015afbc86c';
|
||||||
|
|
||||||
/// Provider to fetch site permissions for a given origin
|
/// Provider to fetch site permissions for a given origin
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ final class PublicSuffixPlusOneProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$publicSuffixPlusOneHash() =>
|
String _$publicSuffixPlusOneHash() =>
|
||||||
r'e259f66e3ebf278468223d8e25618eb9f67a3504';
|
r'2871740bee033e634047b1374ae8dfad508f2cb2';
|
||||||
|
|
||||||
/// Provider to get the public suffix plus one (eTLD+1) for a host
|
/// Provider to get the public suffix plus one (eTLD+1) for a host
|
||||||
|
|
||||||
|
|||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2025 Fabian Freund.
|
||||||
|
*
|
||||||
|
* This file is part of WebLibre
|
||||||
|
* (see https://weblibre.eu).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart';
|
||||||
|
|
||||||
|
part 'tracking_protection_provider.g.dart';
|
||||||
|
|
||||||
|
/// Provider that checks if a specific tab has a tracking protection exception
|
||||||
|
/// Automatically rebuilds when repository changes after mutations
|
||||||
|
@Riverpod()
|
||||||
|
Future<bool> hasTrackingProtectionException(Ref ref, String tabId) {
|
||||||
|
ref.watch(trackingProtectionRepositoryProvider);
|
||||||
|
|
||||||
|
return ref
|
||||||
|
.read(trackingProtectionRepositoryProvider.notifier)
|
||||||
|
.containsException(tabId);
|
||||||
|
}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
|
||||||
|
part of 'tracking_protection_provider.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// RiverpodGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// ignore_for_file: type=lint, type=warning
|
||||||
|
/// Provider that checks if a specific tab has a tracking protection exception
|
||||||
|
/// Automatically rebuilds when repository changes after mutations
|
||||||
|
|
||||||
|
@ProviderFor(hasTrackingProtectionException)
|
||||||
|
final hasTrackingProtectionExceptionProvider =
|
||||||
|
HasTrackingProtectionExceptionFamily._();
|
||||||
|
|
||||||
|
/// Provider that checks if a specific tab has a tracking protection exception
|
||||||
|
/// Automatically rebuilds when repository changes after mutations
|
||||||
|
|
||||||
|
final class HasTrackingProtectionExceptionProvider
|
||||||
|
extends $FunctionalProvider<AsyncValue<bool>, bool, FutureOr<bool>>
|
||||||
|
with $FutureModifier<bool>, $FutureProvider<bool> {
|
||||||
|
/// Provider that checks if a specific tab has a tracking protection exception
|
||||||
|
/// Automatically rebuilds when repository changes after mutations
|
||||||
|
HasTrackingProtectionExceptionProvider._({
|
||||||
|
required HasTrackingProtectionExceptionFamily super.from,
|
||||||
|
required String super.argument,
|
||||||
|
}) : super(
|
||||||
|
retry: null,
|
||||||
|
name: r'hasTrackingProtectionExceptionProvider',
|
||||||
|
isAutoDispose: true,
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String debugGetCreateSourceHash() => _$hasTrackingProtectionExceptionHash();
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return r'hasTrackingProtectionExceptionProvider'
|
||||||
|
''
|
||||||
|
'($argument)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@$internal
|
||||||
|
@override
|
||||||
|
$FutureProviderElement<bool> $createElement($ProviderPointer pointer) =>
|
||||||
|
$FutureProviderElement(pointer);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<bool> create(Ref ref) {
|
||||||
|
final argument = this.argument as String;
|
||||||
|
return hasTrackingProtectionException(ref, argument);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return other is HasTrackingProtectionExceptionProvider &&
|
||||||
|
other.argument == argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode {
|
||||||
|
return argument.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _$hasTrackingProtectionExceptionHash() =>
|
||||||
|
r'3c6c2d4a2c75d52c33211bc8da8191185fb6c542';
|
||||||
|
|
||||||
|
/// Provider that checks if a specific tab has a tracking protection exception
|
||||||
|
/// Automatically rebuilds when repository changes after mutations
|
||||||
|
|
||||||
|
final class HasTrackingProtectionExceptionFamily extends $Family
|
||||||
|
with $FunctionalFamilyOverride<FutureOr<bool>, String> {
|
||||||
|
HasTrackingProtectionExceptionFamily._()
|
||||||
|
: super(
|
||||||
|
retry: null,
|
||||||
|
name: r'hasTrackingProtectionExceptionProvider',
|
||||||
|
dependencies: null,
|
||||||
|
$allTransitiveDependencies: null,
|
||||||
|
isAutoDispose: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Provider that checks if a specific tab has a tracking protection exception
|
||||||
|
/// Automatically rebuilds when repository changes after mutations
|
||||||
|
|
||||||
|
HasTrackingProtectionExceptionProvider call(String tabId) =>
|
||||||
|
HasTrackingProtectionExceptionProvider._(argument: tabId, from: this);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => r'hasTrackingProtectionExceptionProvider';
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2025 Fabian Freund.
|
||||||
|
*
|
||||||
|
* This file is part of WebLibre
|
||||||
|
* (see https://weblibre.eu).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/tracking_protection_provider.dart';
|
||||||
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
|
|
||||||
|
/// Section widget displaying Enhanced Tracking Protection toggle
|
||||||
|
class TrackingProtectionSection extends HookConsumerWidget {
|
||||||
|
final String tabId;
|
||||||
|
|
||||||
|
const TrackingProtectionSection({required this.tabId, super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final hasExceptionAsync = ref.watch(
|
||||||
|
hasTrackingProtectionExceptionProvider(tabId),
|
||||||
|
);
|
||||||
|
|
||||||
|
return hasExceptionAsync.when(
|
||||||
|
data: (hasException) => _TrackingProtectionTile(
|
||||||
|
tabId: tabId,
|
||||||
|
isEnabled: !hasException, // ETP enabled when NOT in exceptions
|
||||||
|
),
|
||||||
|
loading: () => const SizedBox.shrink(),
|
||||||
|
error: (error, stack) => const SizedBox.shrink(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TrackingProtectionTile extends ConsumerWidget {
|
||||||
|
final String tabId;
|
||||||
|
final bool isEnabled;
|
||||||
|
|
||||||
|
const _TrackingProtectionTile({required this.tabId, required this.isEnabled});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return SwitchListTile.adaptive(
|
||||||
|
value: isEnabled,
|
||||||
|
onChanged: (enabled) => _toggleProtection(context, ref, enabled),
|
||||||
|
title: const Text('Enhanced Tracking Protection'),
|
||||||
|
subtitle: Text(
|
||||||
|
isEnabled
|
||||||
|
? 'Trackers on this site are being blocked'
|
||||||
|
: 'Trackers on this site are allowed',
|
||||||
|
),
|
||||||
|
secondary: Icon(
|
||||||
|
isEnabled ? Icons.shield : Icons.shield_outlined,
|
||||||
|
color: isEnabled
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _toggleProtection(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
bool enabled,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
if (enabled) {
|
||||||
|
// Remove exception to enable ETP
|
||||||
|
await ref
|
||||||
|
.read(trackingProtectionRepositoryProvider.notifier)
|
||||||
|
.removeException(tabId);
|
||||||
|
} else {
|
||||||
|
// Add exception to disable ETP
|
||||||
|
await ref
|
||||||
|
.read(trackingProtectionRepositoryProvider.notifier)
|
||||||
|
.addException(tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reload tab to apply changes
|
||||||
|
await ref.read(selectedTabSessionProvider).reload();
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorMessage(context, 'Failed to toggle tracking protection: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/widget
|
|||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/clear_site_data_section.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/clear_site_data_section.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/permissions_section.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/permissions_section.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/tracking_protection_section.dart';
|
||||||
import 'package:weblibre/presentation/widgets/website_title_tile.dart';
|
import 'package:weblibre/presentation/widgets/website_title_tile.dart';
|
||||||
|
|
||||||
class ClampingScrollPhysicsWithoutImplicit extends ClampingScrollPhysics {
|
class ClampingScrollPhysicsWithoutImplicit extends ClampingScrollPhysics {
|
||||||
@@ -208,6 +209,11 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
|
// Tracking Protection Section
|
||||||
|
TrackingProtectionSection(
|
||||||
|
tabId: initialTabState.id,
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
// Permissions Section
|
// Permissions Section
|
||||||
PermissionsSection(
|
PermissionsSection(
|
||||||
origin: initialTabState.url.origin,
|
origin: initialTabState.url.origin,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import 'package:fading_scroll/fading_scroll.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
@@ -87,11 +88,27 @@ class _TrackingProtectionSection extends StatelessWidget {
|
|||||||
_EnhancedTrackingProtectionSection(),
|
_EnhancedTrackingProtectionSection(),
|
||||||
_BounceTrackingProtectionTile(),
|
_BounceTrackingProtectionTile(),
|
||||||
_QueryParameterStrippingSection(),
|
_QueryParameterStrippingSection(),
|
||||||
|
_TrackingProtectionExceptionsTile(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _TrackingProtectionExceptionsTile extends StatelessWidget {
|
||||||
|
const _TrackingProtectionExceptionsTile();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(MdiIcons.shieldOffOutline),
|
||||||
|
title: const Text('Tracking Protection Exceptions'),
|
||||||
|
subtitle: const Text('Sites where tracking protection is disabled'),
|
||||||
|
trailing: const Icon(Icons.chevron_right),
|
||||||
|
onTap: () => context.push('/settings/tracking_protection_exceptions'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _ConnectionSecuritySection extends StatelessWidget {
|
class _ConnectionSecuritySection extends StatelessWidget {
|
||||||
const _ConnectionSecuritySection();
|
const _ConnectionSecuritySection();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,225 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024-2025 Fabian Freund.
|
||||||
|
*
|
||||||
|
* This file is part of WebLibre
|
||||||
|
* (see https://weblibre.eu).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/domain/repositories/tracking_protection.dart';
|
||||||
|
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||||
|
import 'package:weblibre/utils/ui_helper.dart';
|
||||||
|
|
||||||
|
/// Screen to view and manage tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// Shows list of all sites where ETP is disabled, with options
|
||||||
|
/// to remove individual exceptions or remove all exceptions.
|
||||||
|
class TrackingProtectionExceptionsScreen extends HookConsumerWidget {
|
||||||
|
const TrackingProtectionExceptionsScreen({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final exceptionsAsync = ref.watch(trackingProtectionRepositoryProvider);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Tracking Protection Exceptions'),
|
||||||
|
actions: [
|
||||||
|
exceptionsAsync.maybeWhen(
|
||||||
|
data: (exceptions) => exceptions.isNotEmpty
|
||||||
|
? MenuAnchor(
|
||||||
|
builder: (context, controller, child) => IconButton(
|
||||||
|
icon: const Icon(Icons.more_vert),
|
||||||
|
onPressed: () {
|
||||||
|
if (controller.isOpen) {
|
||||||
|
controller.close();
|
||||||
|
} else {
|
||||||
|
controller.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
menuChildren: [
|
||||||
|
MenuItemButton(
|
||||||
|
leadingIcon: const Icon(Icons.delete_sweep),
|
||||||
|
onPressed: () => _showDeleteAllDialog(context, ref),
|
||||||
|
child: const Text('Delete All'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
|
orElse: () => const SizedBox.shrink(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: exceptionsAsync.when(
|
||||||
|
data: (exceptions) {
|
||||||
|
if (exceptions.isEmpty) {
|
||||||
|
return const _EmptyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: exceptions.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final exception = exceptions[index];
|
||||||
|
return _ExceptionTile(
|
||||||
|
exception: exception,
|
||||||
|
onDelete: () => _deleteException(context, ref, exception),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (error, stack) => _ErrorState(error: error.toString()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showDeleteAllDialog(BuildContext context, WidgetRef ref) async {
|
||||||
|
await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Delete All Exceptions?'),
|
||||||
|
content: const Text(
|
||||||
|
'This will re-enable tracking protection for all exception sites.',
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
Navigator.pop(context);
|
||||||
|
try {
|
||||||
|
await ref
|
||||||
|
.read(trackingProtectionRepositoryProvider.notifier)
|
||||||
|
.removeAllExceptions();
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorMessage(context, 'Failed to delete exceptions: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('Delete'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _deleteException(
|
||||||
|
BuildContext context,
|
||||||
|
WidgetRef ref,
|
||||||
|
TrackingProtectionException exception,
|
||||||
|
) async {
|
||||||
|
try {
|
||||||
|
await ref
|
||||||
|
.read(trackingProtectionRepositoryProvider.notifier)
|
||||||
|
.removeExceptionByUrl(exception.url);
|
||||||
|
} catch (e) {
|
||||||
|
if (context.mounted) {
|
||||||
|
showErrorMessage(context, 'Failed to remove exception: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ExceptionTile extends StatelessWidget {
|
||||||
|
final TrackingProtectionException exception;
|
||||||
|
final VoidCallback onDelete;
|
||||||
|
|
||||||
|
const _ExceptionTile({required this.exception, required this.onDelete});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final uri = Uri.tryParse(exception.url);
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
leading: uri != null
|
||||||
|
? UrlIcon([uri], iconSize: 24)
|
||||||
|
: const Icon(MdiIcons.shieldOutline),
|
||||||
|
title: Text(exception.url),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
onPressed: onDelete,
|
||||||
|
tooltip: 'Remove exception',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EmptyState extends StatelessWidget {
|
||||||
|
const _EmptyState();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.shield_outlined,
|
||||||
|
size: 64,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text('No exceptions', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'Sites added to exceptions will appear here',
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ErrorState extends StatelessWidget {
|
||||||
|
final String error;
|
||||||
|
|
||||||
|
const _ErrorState({required this.error});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.error_outline, size: 64),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
'Error loading exceptions',
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
error,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
@@ -35,6 +35,7 @@ import eu.weblibre.flutter_mozilla_components.pigeons.GeckoHistoryApi
|
|||||||
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoIconsApi
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoIconsApi
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPublicSuffixListApi
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPublicSuffixListApi
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoSitePermissionsApi
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoSitePermissionsApi
|
||||||
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoTrackingProtectionApi
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoLogging
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoLogging
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoMlApi
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoMlApi
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPrefApi
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoPrefApi
|
||||||
@@ -268,6 +269,7 @@ class GeckoBrowserApiImpl : GeckoBrowserApi {
|
|||||||
GeckoBookmarksApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBookmarksApiImpl())
|
GeckoBookmarksApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoBookmarksApiImpl())
|
||||||
GeckoSitePermissionsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoSitePermissionsApiImpl())
|
GeckoSitePermissionsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoSitePermissionsApiImpl())
|
||||||
GeckoPublicSuffixListApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoPublicSuffixListApiImpl(profileApplicationContext))
|
GeckoPublicSuffixListApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoPublicSuffixListApiImpl(profileApplicationContext))
|
||||||
|
GeckoTrackingProtectionApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoTrackingProtectionApiImpl())
|
||||||
|
|
||||||
// Viewport API for dynamic toolbar and keyboard handling
|
// Viewport API for dynamic toolbar and keyboard handling
|
||||||
val viewportEvents = GeckoViewportEvents(_flutterPluginBinding.binaryMessenger)
|
val viewportEvents = GeckoViewportEvents(_flutterPluginBinding.binaryMessenger)
|
||||||
|
|||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package eu.weblibre.flutter_mozilla_components.api
|
||||||
|
|
||||||
|
import eu.weblibre.flutter_mozilla_components.GlobalComponents
|
||||||
|
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoTrackingProtectionApi
|
||||||
|
import eu.weblibre.flutter_mozilla_components.pigeons.TrackingProtectionException
|
||||||
|
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException as MozillaTrackingProtectionException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of GeckoTrackingProtectionApi that manages per-site
|
||||||
|
* Enhanced Tracking Protection exceptions using Mozilla Android Components'
|
||||||
|
* TrackingProtectionUseCases.
|
||||||
|
*/
|
||||||
|
class GeckoTrackingProtectionApiImpl : GeckoTrackingProtectionApi {
|
||||||
|
private val components by lazy {
|
||||||
|
requireNotNull(GlobalComponents.components) { "Components not initialized" }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun containsException(tabId: String, callback: (Result<Boolean>) -> Unit) {
|
||||||
|
try {
|
||||||
|
components.useCases.trackingProtectionUseCases.containsException(tabId) { hasException ->
|
||||||
|
callback(Result.success(hasException))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback(Result.failure(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addException(tabId: String) {
|
||||||
|
components.useCases.trackingProtectionUseCases.addException(tabId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeException(tabId: String) {
|
||||||
|
components.useCases.trackingProtectionUseCases.removeException(tabId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeExceptionByUrl(url: String, callback: (Result<Unit>) -> Unit) {
|
||||||
|
try {
|
||||||
|
// Create a simple TrackingProtectionException implementation for removal
|
||||||
|
val exception = object : MozillaTrackingProtectionException {
|
||||||
|
override val url: String = url
|
||||||
|
}
|
||||||
|
components.useCases.trackingProtectionUseCases.removeException(exception)
|
||||||
|
// The Mozilla API is synchronous, callback immediately after completion
|
||||||
|
callback(Result.success(Unit))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback(Result.failure(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchExceptions(callback: (Result<List<TrackingProtectionException>>) -> Unit) {
|
||||||
|
try {
|
||||||
|
components.useCases.trackingProtectionUseCases.fetchExceptions { mozillaExceptions ->
|
||||||
|
val pigeonExceptions = mozillaExceptions.map { mozillaException ->
|
||||||
|
TrackingProtectionException(url = mozillaException.url)
|
||||||
|
}
|
||||||
|
callback(Result.success(pigeonExceptions))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback(Result.failure(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeAllExceptions(callback: (Result<Unit>) -> Unit) {
|
||||||
|
try {
|
||||||
|
components.useCases.trackingProtectionUseCases.removeAllExceptions {
|
||||||
|
callback(Result.success(Unit))
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback(Result.failure(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -51,5 +51,5 @@ class UseCases(
|
|||||||
|
|
||||||
val appLinksUseCases by lazy { AppLinksUseCases(context) }
|
val appLinksUseCases by lazy { AppLinksUseCases(context) }
|
||||||
|
|
||||||
//val trackingProtectionUseCases by lazy { TrackingProtectionUseCases(store, engine) }
|
val trackingProtectionUseCases by lazy { TrackingProtectionUseCases(store, engine) }
|
||||||
}
|
}
|
||||||
|
|||||||
+217
@@ -2825,6 +2825,41 @@ data class SitePermissions (
|
|||||||
|
|
||||||
override fun hashCode(): Int = toList().hashCode()
|
override fun hashCode(): Int = toList().hashCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracking protection exception for a site
|
||||||
|
*
|
||||||
|
* This represents a site that has been added to the exceptions list,
|
||||||
|
* meaning tracking protection is disabled for this specific site.
|
||||||
|
*
|
||||||
|
* Generated class from Pigeon that represents data sent in messages.
|
||||||
|
*/
|
||||||
|
data class TrackingProtectionException (
|
||||||
|
val url: String
|
||||||
|
)
|
||||||
|
{
|
||||||
|
companion object {
|
||||||
|
fun fromList(pigeonVar_list: List<Any?>): TrackingProtectionException {
|
||||||
|
val url = pigeonVar_list[0] as String
|
||||||
|
return TrackingProtectionException(url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun toList(): List<Any?> {
|
||||||
|
return listOf(
|
||||||
|
url,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is TrackingProtectionException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (this === other) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
|
||||||
|
|
||||||
|
override fun hashCode(): Int = toList().hashCode()
|
||||||
|
}
|
||||||
private open class GeckoPigeonCodec : StandardMessageCodec() {
|
private open class GeckoPigeonCodec : StandardMessageCodec() {
|
||||||
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
@@ -3233,6 +3268,11 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
|
|||||||
SitePermissions.fromList(it)
|
SitePermissions.fromList(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
210.toByte() -> {
|
||||||
|
return (readValue(buffer) as? List<Any?>)?.let {
|
||||||
|
TrackingProtectionException.fromList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> super.readValueOfType(type, buffer)
|
else -> super.readValueOfType(type, buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3562,6 +3602,10 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
|
|||||||
stream.write(209)
|
stream.write(209)
|
||||||
writeValue(stream, value.toList())
|
writeValue(stream, value.toList())
|
||||||
}
|
}
|
||||||
|
is TrackingProtectionException -> {
|
||||||
|
stream.write(210)
|
||||||
|
writeValue(stream, value.toList())
|
||||||
|
}
|
||||||
else -> super.writeValue(stream, value)
|
else -> super.writeValue(stream, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6717,3 +6761,176 @@ interface GeckoPublicSuffixListApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* API for managing per-site tracking protection exceptions
|
||||||
|
*
|
||||||
|
* This API wraps Mozilla Android Components' TrackingProtectionUseCases
|
||||||
|
* to allow Flutter code to add/remove/check tracking protection exceptions
|
||||||
|
* on a per-site basis.
|
||||||
|
*
|
||||||
|
* Generated interface from Pigeon that represents a handler of messages from Flutter.
|
||||||
|
*/
|
||||||
|
interface GeckoTrackingProtectionApi {
|
||||||
|
/**
|
||||||
|
* Check if a tab has a tracking protection exception
|
||||||
|
*
|
||||||
|
* Uses callback pattern to match Mozilla Android Components API.
|
||||||
|
* Returns true if the site is in the exceptions list (ETP disabled).
|
||||||
|
*/
|
||||||
|
fun containsException(tabId: String, callback: (Result<Boolean>) -> Unit)
|
||||||
|
/**
|
||||||
|
* Add tracking protection exception for a tab (disable ETP for this site)
|
||||||
|
*
|
||||||
|
* This adds the current tab's URL to the exceptions list.
|
||||||
|
* ETP will be disabled for this site until the exception is removed.
|
||||||
|
*/
|
||||||
|
fun addException(tabId: String)
|
||||||
|
/**
|
||||||
|
* Remove tracking protection exception for a tab (enable ETP for this site)
|
||||||
|
*
|
||||||
|
* This removes the current tab's URL from the exceptions list.
|
||||||
|
* ETP will be re-enabled for this site.
|
||||||
|
*/
|
||||||
|
fun removeException(tabId: String)
|
||||||
|
/**
|
||||||
|
* Remove a specific exception by URL
|
||||||
|
*
|
||||||
|
* Alternative to removeException(tabId) for cases where you
|
||||||
|
* have a URL rather than a tabId.
|
||||||
|
*/
|
||||||
|
fun removeExceptionByUrl(url: String, callback: (Result<Unit>) -> Unit)
|
||||||
|
/**
|
||||||
|
* Fetch all tracking protection exceptions
|
||||||
|
*
|
||||||
|
* Returns list of all sites that have exceptions (ETP disabled).
|
||||||
|
*/
|
||||||
|
fun fetchExceptions(callback: (Result<List<TrackingProtectionException>>) -> Unit)
|
||||||
|
/**
|
||||||
|
* Remove all tracking protection exceptions
|
||||||
|
*
|
||||||
|
* This re-enables ETP for all exception sites.
|
||||||
|
*/
|
||||||
|
fun removeAllExceptions(callback: (Result<Unit>) -> Unit)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/** The codec used by GeckoTrackingProtectionApi. */
|
||||||
|
val codec: MessageCodec<Any?> by lazy {
|
||||||
|
GeckoPigeonCodec()
|
||||||
|
}
|
||||||
|
/** Sets up an instance of `GeckoTrackingProtectionApi` to handle messages through the `binaryMessenger`. */
|
||||||
|
@JvmOverloads
|
||||||
|
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoTrackingProtectionApi?, messageChannelSuffix: String = "") {
|
||||||
|
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.containsException$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val tabIdArg = args[0] as String
|
||||||
|
api.containsException(tabIdArg) { result: Result<Boolean> ->
|
||||||
|
val error = result.exceptionOrNull()
|
||||||
|
if (error != null) {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapError(error))
|
||||||
|
} else {
|
||||||
|
val data = result.getOrNull()
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapResult(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.addException$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val tabIdArg = args[0] as String
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
api.addException(tabIdArg)
|
||||||
|
listOf(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
GeckoPigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.removeException$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val tabIdArg = args[0] as String
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
api.removeException(tabIdArg)
|
||||||
|
listOf(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
GeckoPigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.removeExceptionByUrl$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val urlArg = args[0] as String
|
||||||
|
api.removeExceptionByUrl(urlArg) { result: Result<Unit> ->
|
||||||
|
val error = result.exceptionOrNull()
|
||||||
|
if (error != null) {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapError(error))
|
||||||
|
} else {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapResult(null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.fetchExceptions$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
api.fetchExceptions{ result: Result<List<TrackingProtectionException>> ->
|
||||||
|
val error = result.exceptionOrNull()
|
||||||
|
if (error != null) {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapError(error))
|
||||||
|
} else {
|
||||||
|
val data = result.getOrNull()
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapResult(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.removeAllExceptions$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { _, reply ->
|
||||||
|
api.removeAllExceptions{ result: Result<Unit> ->
|
||||||
|
val error = result.exceptionOrNull()
|
||||||
|
if (error != null) {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapError(error))
|
||||||
|
} else {
|
||||||
|
reply.reply(GeckoPigeonUtils.wrapResult(null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ export 'src/pigeons/gecko.g.dart'
|
|||||||
GeckoSitePermissionsApi,
|
GeckoSitePermissionsApi,
|
||||||
GeckoSuggestion,
|
GeckoSuggestion,
|
||||||
GeckoSuggestionType,
|
GeckoSuggestionType,
|
||||||
|
GeckoTrackingProtectionApi,
|
||||||
GeoHitResult,
|
GeoHitResult,
|
||||||
HistoryMetadataKey,
|
HistoryMetadataKey,
|
||||||
HitResult,
|
HitResult,
|
||||||
@@ -75,10 +76,11 @@ export 'src/pigeons/gecko.g.dart'
|
|||||||
Resource,
|
Resource,
|
||||||
ResourceSize,
|
ResourceSize,
|
||||||
SecurityInfoState,
|
SecurityInfoState,
|
||||||
SitePermissions,
|
|
||||||
SitePermissionStatus,
|
SitePermissionStatus,
|
||||||
|
SitePermissions,
|
||||||
TabContent,
|
TabContent,
|
||||||
TabContentState,
|
TabContentState,
|
||||||
|
TrackingProtectionException,
|
||||||
TrackingProtectionPolicy,
|
TrackingProtectionPolicy,
|
||||||
UnknownHitResult,
|
UnknownHitResult,
|
||||||
VideoHitResult,
|
VideoHitResult,
|
||||||
|
|||||||
@@ -3613,6 +3613,51 @@ class SitePermissions {
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tracking protection exception for a site
|
||||||
|
///
|
||||||
|
/// This represents a site that has been added to the exceptions list,
|
||||||
|
/// meaning tracking protection is disabled for this specific site.
|
||||||
|
class TrackingProtectionException {
|
||||||
|
TrackingProtectionException({
|
||||||
|
required this.url,
|
||||||
|
});
|
||||||
|
|
||||||
|
String url;
|
||||||
|
|
||||||
|
List<Object?> _toList() {
|
||||||
|
return <Object?>[
|
||||||
|
url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object encode() {
|
||||||
|
return _toList(); }
|
||||||
|
|
||||||
|
static TrackingProtectionException decode(Object result) {
|
||||||
|
result as List<Object?>;
|
||||||
|
return TrackingProtectionException(
|
||||||
|
url: result[0]! as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
// ignore: avoid_equals_and_hash_code_on_mutable_classes
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (other is! TrackingProtectionException || 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 _PigeonCodec extends StandardMessageCodec {
|
class _PigeonCodec extends StandardMessageCodec {
|
||||||
const _PigeonCodec();
|
const _PigeonCodec();
|
||||||
@@ -3864,6 +3909,9 @@ class _PigeonCodec extends StandardMessageCodec {
|
|||||||
} else if (value is SitePermissions) {
|
} else if (value is SitePermissions) {
|
||||||
buffer.putUint8(209);
|
buffer.putUint8(209);
|
||||||
writeValue(buffer, value.encode());
|
writeValue(buffer, value.encode());
|
||||||
|
} else if (value is TrackingProtectionException) {
|
||||||
|
buffer.putUint8(210);
|
||||||
|
writeValue(buffer, value.encode());
|
||||||
} else {
|
} else {
|
||||||
super.writeValue(buffer, value);
|
super.writeValue(buffer, value);
|
||||||
}
|
}
|
||||||
@@ -4062,6 +4110,8 @@ class _PigeonCodec extends StandardMessageCodec {
|
|||||||
return BookmarkInfo.decode(readValue(buffer)!);
|
return BookmarkInfo.decode(readValue(buffer)!);
|
||||||
case 209:
|
case 209:
|
||||||
return SitePermissions.decode(readValue(buffer)!);
|
return SitePermissions.decode(readValue(buffer)!);
|
||||||
|
case 210:
|
||||||
|
return TrackingProtectionException.decode(readValue(buffer)!);
|
||||||
default:
|
default:
|
||||||
return super.readValueOfType(type, buffer);
|
return super.readValueOfType(type, buffer);
|
||||||
}
|
}
|
||||||
@@ -7866,3 +7916,186 @@ class GeckoPublicSuffixListApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// API for managing per-site tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// This API wraps Mozilla Android Components' TrackingProtectionUseCases
|
||||||
|
/// to allow Flutter code to add/remove/check tracking protection exceptions
|
||||||
|
/// on a per-site basis.
|
||||||
|
class GeckoTrackingProtectionApi {
|
||||||
|
/// Constructor for [GeckoTrackingProtectionApi]. The [binaryMessenger] named argument is
|
||||||
|
/// available for dependency injection. If it is left null, the default
|
||||||
|
/// BinaryMessenger will be used which routes to the host platform.
|
||||||
|
GeckoTrackingProtectionApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
|
||||||
|
: pigeonVar_binaryMessenger = binaryMessenger,
|
||||||
|
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
|
||||||
|
final BinaryMessenger? pigeonVar_binaryMessenger;
|
||||||
|
|
||||||
|
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
|
||||||
|
|
||||||
|
final String pigeonVar_messageChannelSuffix;
|
||||||
|
|
||||||
|
/// Check if a tab has a tracking protection exception
|
||||||
|
///
|
||||||
|
/// Uses callback pattern to match Mozilla Android Components API.
|
||||||
|
/// Returns true if the site is in the exceptions list (ETP disabled).
|
||||||
|
Future<bool> containsException(String tabId) async {
|
||||||
|
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.containsException$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[tabId]);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else if (pigeonVar_replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (pigeonVar_replyList[0] as bool?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add tracking protection exception for a tab (disable ETP for this site)
|
||||||
|
///
|
||||||
|
/// This adds the current tab's URL to the exceptions list.
|
||||||
|
/// ETP will be disabled for this site until the exception is removed.
|
||||||
|
Future<void> addException(String tabId) async {
|
||||||
|
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.addException$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[tabId]);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove tracking protection exception for a tab (enable ETP for this site)
|
||||||
|
///
|
||||||
|
/// This removes the current tab's URL from the exceptions list.
|
||||||
|
/// ETP will be re-enabled for this site.
|
||||||
|
Future<void> removeException(String tabId) async {
|
||||||
|
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.removeException$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[tabId]);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a specific exception by URL
|
||||||
|
///
|
||||||
|
/// Alternative to removeException(tabId) for cases where you
|
||||||
|
/// have a URL rather than a tabId.
|
||||||
|
Future<void> removeExceptionByUrl(String url) async {
|
||||||
|
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.removeExceptionByUrl$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[url]);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch all tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// Returns list of all sites that have exceptions (ETP disabled).
|
||||||
|
Future<List<TrackingProtectionException>> fetchExceptions() async {
|
||||||
|
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.fetchExceptions$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else if (pigeonVar_replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (pigeonVar_replyList[0] as List<Object?>?)!.cast<TrackingProtectionException>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove all tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// This re-enables ETP for all exception sites.
|
||||||
|
Future<void> removeAllExceptions() async {
|
||||||
|
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTrackingProtectionApi.removeAllExceptions$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1825,3 +1825,59 @@ abstract class GeckoPublicSuffixListApi {
|
|||||||
@async
|
@async
|
||||||
String getPublicSuffixPlusOne(String host);
|
String getPublicSuffixPlusOne(String host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tracking protection exception for a site
|
||||||
|
///
|
||||||
|
/// This represents a site that has been added to the exceptions list,
|
||||||
|
/// meaning tracking protection is disabled for this specific site.
|
||||||
|
class TrackingProtectionException {
|
||||||
|
final String url;
|
||||||
|
|
||||||
|
TrackingProtectionException({required this.url});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// API for managing per-site tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// This API wraps Mozilla Android Components' TrackingProtectionUseCases
|
||||||
|
/// to allow Flutter code to add/remove/check tracking protection exceptions
|
||||||
|
/// on a per-site basis.
|
||||||
|
@HostApi()
|
||||||
|
abstract class GeckoTrackingProtectionApi {
|
||||||
|
/// Check if a tab has a tracking protection exception
|
||||||
|
///
|
||||||
|
/// Uses callback pattern to match Mozilla Android Components API.
|
||||||
|
/// Returns true if the site is in the exceptions list (ETP disabled).
|
||||||
|
@async
|
||||||
|
bool containsException(String tabId);
|
||||||
|
|
||||||
|
/// Add tracking protection exception for a tab (disable ETP for this site)
|
||||||
|
///
|
||||||
|
/// This adds the current tab's URL to the exceptions list.
|
||||||
|
/// ETP will be disabled for this site until the exception is removed.
|
||||||
|
void addException(String tabId);
|
||||||
|
|
||||||
|
/// Remove tracking protection exception for a tab (enable ETP for this site)
|
||||||
|
///
|
||||||
|
/// This removes the current tab's URL from the exceptions list.
|
||||||
|
/// ETP will be re-enabled for this site.
|
||||||
|
void removeException(String tabId);
|
||||||
|
|
||||||
|
/// Remove a specific exception by URL
|
||||||
|
///
|
||||||
|
/// Alternative to removeException(tabId) for cases where you
|
||||||
|
/// have a URL rather than a tabId.
|
||||||
|
@async
|
||||||
|
void removeExceptionByUrl(String url);
|
||||||
|
|
||||||
|
/// Fetch all tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// Returns list of all sites that have exceptions (ETP disabled).
|
||||||
|
@async
|
||||||
|
List<TrackingProtectionException> fetchExceptions();
|
||||||
|
|
||||||
|
/// Remove all tracking protection exceptions
|
||||||
|
///
|
||||||
|
/// This re-enables ETP for all exception sites.
|
||||||
|
@async
|
||||||
|
void removeAllExceptions();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user