clear container data on exit

This commit is contained in:
Fabian Freund
2026-01-01 07:02:08 +01:00
parent 0764cf4c10
commit af8c2b4f4d
11 changed files with 94 additions and 4 deletions
@@ -63,6 +63,16 @@ class BrowserDataService extends _$BrowserDataService {
return _service.clearDataForContext(contextId); return _service.clearDataForContext(contextId);
} }
Future<void> clearContainerDataOnEngineStart(
List<String> contextIds,
) async {
if (!_onStartDeleted && contextIds.isNotEmpty) {
for (final contextId in contextIds) {
await clearDataForContext(contextId);
}
}
}
@override @override
void build() {} void build() {}
} }
@@ -42,7 +42,7 @@ final class BrowserDataServiceProvider
} }
String _$browserDataServiceHash() => String _$browserDataServiceHash() =>
r'502dff526bcf7c10c218d2d9fa2cd0f41ad25622'; r'861943be10c4dea325484b6a28ba21f3ff0d29fa';
abstract class _$BrowserDataService extends $Notifier<void> { abstract class _$BrowserDataService extends $Notifier<void> {
void build(); void build();
@@ -46,6 +46,7 @@ import 'package:weblibre/features/geckoview/features/browser/domain/services/eng
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
import 'package:weblibre/features/geckoview/features/history/domain/repositories/history.dart'; import 'package:weblibre/features/geckoview/features/history/domain/repositories/history.dart';
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart'; import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart'; import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
import 'package:weblibre/features/user/data/models/general_settings.dart'; import 'package:weblibre/features/user/data/models/general_settings.dart';
import 'package:weblibre/features/user/domain/repositories/cache.dart'; import 'package:weblibre/features/user/domain/repositories/cache.dart';
@@ -116,6 +117,17 @@ class _BrowserViewState extends ConsumerState<BrowserView>
); );
} }
}); });
// Clear data for containers with clearDataOnExit enabled
final containersToClear = await ref
.read(containerRepositoryProvider.notifier)
.getContainersToClearOnExit();
if (containersToClear.isNotEmpty) {
await ref
.read(browserDataServiceProvider.notifier)
.clearContainerDataOnEngineStart(containersToClear);
}
}); });
final hasTab = ref.watch( final hasTab = ref.watch(
@@ -193,4 +193,8 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
Selectable<SiteAssignment> allAssignedSites() { Selectable<SiteAssignment> allAssignedSites() {
return db.definitionsDrift.allAssignedSites(); return db.definitionsDrift.allAssignedSites();
} }
Selectable<String?> containersToClearOnExit() {
return db.definitionsDrift.containersToClearOnExit();
}
} }
@@ -317,3 +317,10 @@ allAssignedSites WITH SiteAssignment:
FROM container FROM container
CROSS JOIN json_each(container.metadata, '$.assignedSites') CROSS JOIN json_each(container.metadata, '$.assignedSites')
WHERE value IS NOT NULL; WHERE value IS NOT NULL;
containersToClearOnExit:
SELECT container.metadata ->> '$.contextualIdentity' AS contextual_identity
FROM container
WHERE
json_extract(container.metadata, '$.clearDataOnExit') = 1
AND container.metadata ->> '$.contextualIdentity' IS NOT NULL;
@@ -2383,6 +2383,14 @@ class DefinitionsDrift extends i7.ModularAccessor {
); );
} }
i0.Selectable<String?> containersToClearOnExit() {
return customSelect(
'SELECT container.metadata ->> \'\$.contextualIdentity\' AS contextual_identity FROM container WHERE json_extract(container.metadata, \'\$.clearDataOnExit\') = 1 AND container.metadata ->> \'\$.contextualIdentity\' IS NOT NULL',
variables: [],
readsFrom: {container},
).map((i0.QueryRow row) => row.readNullable<String>('contextual_identity'));
}
i3.TabFts get tabFts => i7.ReadDatabaseContainer( i3.TabFts get tabFts => i7.ReadDatabaseContainer(
attachedDatabase, attachedDatabase,
).resultSet<i3.TabFts>('tab_fts'); ).resultSet<i3.TabFts>('tab_fts');
@@ -73,6 +73,9 @@ class ContainerMetadata with FastEquatable {
@JsonKey(defaultValue: false) @JsonKey(defaultValue: false)
final bool useProxy; final bool useProxy;
@JsonKey(defaultValue: false)
final bool clearDataOnExit;
final List<Uri>? assignedSites; final List<Uri>? assignedSites;
ContainerMetadata({ ContainerMetadata({
@@ -80,6 +83,7 @@ class ContainerMetadata with FastEquatable {
required this.contextualIdentity, required this.contextualIdentity,
required this.authSettings, required this.authSettings,
required this.useProxy, required this.useProxy,
required this.clearDataOnExit,
required this.assignedSites, required this.assignedSites,
}); });
@@ -88,12 +92,14 @@ class ContainerMetadata with FastEquatable {
String? contextualIdentity, String? contextualIdentity,
ContainerAuthSettings? authSettings, ContainerAuthSettings? authSettings,
bool? useProxy, bool? useProxy,
bool? clearDataOnExit,
List<Uri>? assignedSites, List<Uri>? assignedSites,
}) : this( }) : this(
iconData: iconData, iconData: iconData,
contextualIdentity: contextualIdentity, contextualIdentity: contextualIdentity,
authSettings: authSettings ?? ContainerAuthSettings.withDefaults(), authSettings: authSettings ?? ContainerAuthSettings.withDefaults(),
useProxy: useProxy ?? false, useProxy: useProxy ?? false,
clearDataOnExit: clearDataOnExit ?? false,
assignedSites: assignedSites, assignedSites: assignedSites,
); );
@@ -108,6 +114,7 @@ class ContainerMetadata with FastEquatable {
contextualIdentity, contextualIdentity,
authSettings, authSettings,
useProxy, useProxy,
clearDataOnExit,
assignedSites, assignedSites,
]; ];
} }
@@ -98,6 +98,8 @@ abstract class _$ContainerMetadataCWProxy {
ContainerMetadata useProxy(bool useProxy); ContainerMetadata useProxy(bool useProxy);
ContainerMetadata clearDataOnExit(bool clearDataOnExit);
ContainerMetadata assignedSites(List<Uri>? assignedSites); ContainerMetadata assignedSites(List<Uri>? assignedSites);
/// Creates a new instance with the provided field values. /// Creates a new instance with the provided field values.
@@ -112,6 +114,7 @@ abstract class _$ContainerMetadataCWProxy {
String? contextualIdentity, String? contextualIdentity,
ContainerAuthSettings authSettings, ContainerAuthSettings authSettings,
bool useProxy, bool useProxy,
bool clearDataOnExit,
List<Uri>? assignedSites, List<Uri>? assignedSites,
}); });
} }
@@ -137,6 +140,10 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
@override @override
ContainerMetadata useProxy(bool useProxy) => call(useProxy: useProxy); ContainerMetadata useProxy(bool useProxy) => call(useProxy: useProxy);
@override
ContainerMetadata clearDataOnExit(bool clearDataOnExit) =>
call(clearDataOnExit: clearDataOnExit);
@override @override
ContainerMetadata assignedSites(List<Uri>? assignedSites) => ContainerMetadata assignedSites(List<Uri>? assignedSites) =>
call(assignedSites: assignedSites); call(assignedSites: assignedSites);
@@ -154,6 +161,7 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
Object? contextualIdentity = const $CopyWithPlaceholder(), Object? contextualIdentity = const $CopyWithPlaceholder(),
Object? authSettings = const $CopyWithPlaceholder(), Object? authSettings = const $CopyWithPlaceholder(),
Object? useProxy = const $CopyWithPlaceholder(), Object? useProxy = const $CopyWithPlaceholder(),
Object? clearDataOnExit = const $CopyWithPlaceholder(),
Object? assignedSites = const $CopyWithPlaceholder(), Object? assignedSites = const $CopyWithPlaceholder(),
}) { }) {
return ContainerMetadata( return ContainerMetadata(
@@ -174,6 +182,12 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
? _value.useProxy ? _value.useProxy
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: useProxy as bool, : useProxy as bool,
clearDataOnExit:
clearDataOnExit == const $CopyWithPlaceholder() ||
clearDataOnExit == null
? _value.clearDataOnExit
// ignore: cast_nullable_to_non_nullable
: clearDataOnExit as bool,
assignedSites: assignedSites == const $CopyWithPlaceholder() assignedSites: assignedSites == const $CopyWithPlaceholder()
? _value.assignedSites ? _value.assignedSites
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
@@ -311,6 +325,7 @@ ContainerMetadata _$ContainerMetadataFromJson(Map<String, dynamic> json) =>
json['authSettings'] as Map<String, dynamic>, json['authSettings'] as Map<String, dynamic>,
), ),
useProxy: json['useProxy'] as bool? ?? false, useProxy: json['useProxy'] as bool? ?? false,
clearDataOnExit: json['clearDataOnExit'] as bool? ?? false,
assignedSites: (json['assignedSites'] as List<dynamic>?) assignedSites: (json['assignedSites'] as List<dynamic>?)
?.map((e) => Uri.parse(e as String)) ?.map((e) => Uri.parse(e as String))
.toList(), .toList(),
@@ -326,6 +341,7 @@ Map<String, dynamic> _$ContainerMetadataToJson(
'contextualIdentity': instance.contextualIdentity, 'contextualIdentity': instance.contextualIdentity,
'authSettings': instance.authSettings.toJson(), 'authSettings': instance.authSettings.toJson(),
'useProxy': instance.useProxy, 'useProxy': instance.useProxy,
'clearDataOnExit': instance.clearDataOnExit,
'assignedSites': instance.assignedSites?.map((e) => e.toString()).toList(), 'assignedSites': instance.assignedSites?.map((e) => e.toString()).toList(),
}; };
@@ -170,6 +170,15 @@ class ContainerRepository extends _$ContainerRepository {
.getSingle(); .getSingle();
} }
Future<List<String>> getContainersToClearOnExit() async {
final contextIds = await ref
.read(tabDatabaseProvider)
.containerDao
.containersToClearOnExit()
.get();
return contextIds.whereType<String>().toList();
}
@override @override
void build() {} void build() {}
} }
@@ -42,7 +42,7 @@ final class ContainerRepositoryProvider
} }
String _$containerRepositoryHash() => String _$containerRepositoryHash() =>
r'2a377774f9f18604bb90cb1a8eb7cda5f944c601'; r'3154277f7a6029d6f5b9fcc62ba057e117248e0c';
abstract class _$ContainerRepository extends $Notifier<void> { abstract class _$ContainerRepository extends $Notifier<void> {
void build(); void build();
@@ -77,6 +77,7 @@ class ContainerEditScreen extends HookConsumerWidget {
); );
final authSettings = useState(initialContainer.metadata.authSettings); final authSettings = useState(initialContainer.metadata.authSettings);
final useProxy = useState(initialContainer.metadata.useProxy); final useProxy = useState(initialContainer.metadata.useProxy);
final clearDataOnExit = useState(initialContainer.metadata.clearDataOnExit);
final assignedSites = useState(initialContainer.metadata.assignedSites); final assignedSites = useState(initialContainer.metadata.assignedSites);
final textController = useTextEditingController( final textController = useTextEditingController(
@@ -105,6 +106,8 @@ class ContainerEditScreen extends HookConsumerWidget {
contextualIdentity: contextualIdentity.value, contextualIdentity: contextualIdentity.value,
authSettings: authSettings.value, authSettings: authSettings.value,
useProxy: useProxy.value && contextualIdentity.value != null, useProxy: useProxy.value && contextualIdentity.value != null,
clearDataOnExit:
clearDataOnExit.value && contextualIdentity.value != null,
assignedSites: assignedSites.value, assignedSites: assignedSites.value,
), ),
); );
@@ -262,6 +265,20 @@ class ContainerEditScreen extends HookConsumerWidget {
: null, : null,
}, },
), ),
SwitchListTile.adaptive(
value: clearDataOnExit.value,
title: const Text('Clear Data on Exit'),
subtitle: const Text(
'Clear cookies and site data when app closes',
),
secondary: const Icon(MdiIcons.databaseRemove),
contentPadding: EdgeInsets.zero,
onChanged: (contextualIdentity.value != null)
? (value) {
clearDataOnExit.value = value;
}
: null,
),
SwitchListTile.adaptive( SwitchListTile.adaptive(
value: authSettings.value.authenticationRequired, value: authSettings.value.authenticationRequired,
title: const Text('Require Authentication'), title: const Text('Require Authentication'),