From d4585921ce5fb7d6c7d863a226de2e24cca554a0 Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Wed, 31 Dec 2025 06:22:48 +0100 Subject: [PATCH] clear data of isolated containers; improved tab handling/state/duplication; --- .../geckoview/domain/entities/states/tab.dart | 4 +- .../geckoview/domain/providers/tab_state.dart | 31 +- .../domain/providers/tab_state.g.dart | 2 +- .../geckoview/domain/repositories/tab.dart | 61 +++- .../geckoview/domain/repositories/tab.g.dart | 2 +- .../browser/domain/services/browser_data.dart | 4 + .../domain/services/browser_data.g.dart | 2 +- .../presentation/widgets/tab_menu.dart | 42 ++- .../widgets/tab_view/tab_preview.dart | 29 +- .../widgets/tab_view/tab_view_header.dart | 178 ++++++++++ .../features/tabs/data/database/daos/tab.dart | 18 +- .../tabs/domain/repositories/tab.dart | 54 ++- .../tabs/domain/repositories/tab.g.dart | 2 +- .../screens/web_engine_settings.dart | 2 +- .../GeckoDeleteBrowsingDataControllerImpl.kt | 13 + .../api/GeckoTabsApiImpl.kt | 56 +++ .../pigeons/Gecko.g.kt | 298 +++++++++++----- .../lib/flutter_mozilla_components.dart | 1 + .../services/gecko_delete_browser_data.dart | 4 + .../lib/src/domain/services/gecko_tab.dart | 10 + .../lib/src/pigeons/gecko.g.dart | 330 +++++++++++++----- .../pigeons/gecko.dart | 46 ++- 22 files changed, 909 insertions(+), 280 deletions(-) diff --git a/app/lib/features/geckoview/domain/entities/states/tab.dart b/app/lib/features/geckoview/domain/entities/states/tab.dart index f8db3a22..2ace729b 100644 --- a/app/lib/features/geckoview/domain/entities/states/tab.dart +++ b/app/lib/features/geckoview/domain/entities/states/tab.dart @@ -32,6 +32,8 @@ part 'tab.g.dart'; @CopyWith() class TabState extends WebPageInfo { + static final defaultUrl = Uri.parse('about:blank'); + @CopyWithField(immutable: true) final String id; @@ -92,7 +94,7 @@ class TabState extends WebPageInfo { id: tabId, parentId: null, contextId: null, - url: Uri.parse('about:blank'), + url: defaultUrl, title: "", icon: null, thumbnail: null, diff --git a/app/lib/features/geckoview/domain/providers/tab_state.dart b/app/lib/features/geckoview/domain/providers/tab_state.dart index 639c263d..ec797f7f 100644 --- a/app/lib/features/geckoview/domain/providers/tab_state.dart +++ b/app/lib/features/geckoview/domain/providers/tab_state.dart @@ -46,9 +46,9 @@ part 'tab_state.g.dart'; @Riverpod(keepAlive: true) class TabStates extends _$TabStates { - void _onTabContentStateChange(TabContentState contentState) { - final current = - state[contentState.id] ?? TabState.$default(contentState.id); + Future _onTabContentStateChange(TabContentState contentState) async { + final current = await patchedState(contentState.id); + final url = Uri.parse(contentState.url); // Determine title based on priority: new non-empty title > existing title if URL authority unchanged > new title @@ -81,6 +81,27 @@ class TabStates extends _$TabStates { } } + Future patchedState(String id) async { + var current = stateOrNull?[id]; + + if (current == null || current.url == TabState.defaultUrl) { + current ??= TabState.$default(id); + + final tabData = await ref + .read(tabDataRepositoryProvider.notifier) + .getTabDataById(id); + + if (tabData?.url != null) { + current = current.copyWith( + title: tabData!.title ?? current.title, + url: tabData.url ?? current.url, + ); + } + } + + return current; + } + Future _onIconChange(IconChangeEvent event) async { final IconChangeEvent(:tabId, :bytes) = event; @@ -174,8 +195,8 @@ class TabStates extends _$TabStates { final eventService = ref.watch(eventServiceProvider); final subscriptions = [ - eventService.tabContentEvents.listen((event) { - _onTabContentStateChange(event); + eventService.tabContentEvents.listen((event) async { + await _onTabContentStateChange(event); }), eventService.iconChangeEvents.listen((event) async { await _onIconChange(event); diff --git a/app/lib/features/geckoview/domain/providers/tab_state.g.dart b/app/lib/features/geckoview/domain/providers/tab_state.g.dart index cf25b701..feae1501 100644 --- a/app/lib/features/geckoview/domain/providers/tab_state.g.dart +++ b/app/lib/features/geckoview/domain/providers/tab_state.g.dart @@ -41,7 +41,7 @@ final class TabStatesProvider } } -String _$tabStatesHash() => r'778242f0b67eb7646014d0df857a3a71de214c40'; +String _$tabStatesHash() => r'cd73ea6ea479f2708a2c9e5ca86054c9d2464116'; abstract class _$TabStates extends $Notifier> { Map build(); diff --git a/app/lib/features/geckoview/domain/repositories/tab.dart b/app/lib/features/geckoview/domain/repositories/tab.dart index 0850f5b8..68f33566 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.dart @@ -75,7 +75,7 @@ class TabRepository extends _$TabRepository { await ref.read(selectedContainerProvider.notifier).fetchData(), ); - final newTabId = await tabDao.upsertContainerTabTransactional( + final newTabId = await tabDao.upsertTabTransactional( () { return _tabsService.addTab( url: url, @@ -93,6 +93,7 @@ class TabRepository extends _$TabRepository { parentId: Value(parentId), containerId: Value(assingedContainer.value?.id), isPrivate: Value(private), + url: Value(url), ); if (launchedFromIntent) { @@ -102,10 +103,42 @@ class TabRepository extends _$TabRepository { return newTabId; } + Future> addMultipleTabs({ + required List tabs, + String? selectTabId, + Value? container, + }) async { + final tabDao = ref.read(tabDatabaseProvider).tabDao; + final db = ref.read(tabDatabaseProvider); + + return await db.transaction(() async { + final createdTabIds = await _tabsService.addMultipleTabs( + tabs: tabs, + selectTabId: selectTabId, + ); + + // Upsert all tabs in the database + for (var i = 0; i < createdTabIds.length; i++) { + final tabId = createdTabIds[i]; + final tab = tabs[i]; + + await tabDao.insertTab( + tabId, + parentId: Value(tab.parentId), + containerId: Value(container?.value?.id), + isPrivate: Value(tab.private), + url: Value(Uri.tryParse(tab.url)), + ); + } + + return createdTabIds; + }); + } + Future duplicateTab({ required String selectTabId, - String? containerId, - bool selectTab = true, + required String? containerId, + bool selectTab = false, }) async { final tabDao = ref.read(tabDatabaseProvider).tabDao; @@ -115,7 +148,7 @@ class TabRepository extends _$TabRepository { .getContainerData(containerId), ); - return await tabDao.upsertContainerTabTransactional( + return await tabDao.upsertTabTransactional( () { return _tabsService.duplicateTab( selectTabId: selectTabId, @@ -376,14 +409,16 @@ class TabRepository extends _$TabRepository { final db = ref.watch(tabDatabaseProvider); final tabAddedSub = eventSerivce.tabAddedStream.listen((tabId) async { - // ignore: only_use_keep_alive_inside_keep_alive - final containerId = ref.read(selectedContainerProvider); - await db.tabDao.upsertUnassignedTab( - tabId, - parentId: const Value.absent(), - containerId: Value(containerId), - isPrivate: const Value.absent(), - ); + if (await db.tabDao.getTabDataById(tabId).getSingleOrNull() == null) { + // ignore: only_use_keep_alive_inside_keep_alive + final containerId = ref.read(selectedContainerProvider); + await db.tabDao.insertTab( + tabId, + parentId: const Value.absent(), + containerId: Value(containerId), + isPrivate: const Value.absent(), + ); + } }); final containerSiteAssignementSub = eventSerivce.siteAssignementEvent.listen(( @@ -407,7 +442,7 @@ class TabRepository extends _$TabRepository { if (containerData != null) { final tabIsEmpty = - tabState.url == TabState.$default(tabState.id).url && + tabState.url == TabState.defaultUrl && tabState.historyState.items.isEmpty; if (event.blocked || tabIsEmpty) { diff --git a/app/lib/features/geckoview/domain/repositories/tab.g.dart b/app/lib/features/geckoview/domain/repositories/tab.g.dart index 8982c57c..5699df9e 100644 --- a/app/lib/features/geckoview/domain/repositories/tab.g.dart +++ b/app/lib/features/geckoview/domain/repositories/tab.g.dart @@ -41,7 +41,7 @@ final class TabRepositoryProvider } } -String _$tabRepositoryHash() => r'0411e8b3fa9c3e344106f8fa8178bd6eb613309a'; +String _$tabRepositoryHash() => r'de20adb4a38f8cd443be7d2f4ea07eded58803a3'; abstract class _$TabRepository extends $Notifier { void build(); diff --git a/app/lib/features/geckoview/features/browser/domain/services/browser_data.dart b/app/lib/features/geckoview/features/browser/domain/services/browser_data.dart index e764a28e..69bbfd22 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/browser_data.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/browser_data.dart @@ -59,6 +59,10 @@ class BrowserDataService extends _$BrowserDataService { } } + Future clearDataForContext(String contextId) { + return _service.clearDataForContext(contextId); + } + @override void build() {} } diff --git a/app/lib/features/geckoview/features/browser/domain/services/browser_data.g.dart b/app/lib/features/geckoview/features/browser/domain/services/browser_data.g.dart index 0c1f9df4..066e1488 100644 --- a/app/lib/features/geckoview/features/browser/domain/services/browser_data.g.dart +++ b/app/lib/features/geckoview/features/browser/domain/services/browser_data.g.dart @@ -42,7 +42,7 @@ final class BrowserDataServiceProvider } String _$browserDataServiceHash() => - r'0a00db5b143d3851f2c171f4f939940f959b67bb'; + r'502dff526bcf7c10c218d2d9fa2cd0f41ad25622'; abstract class _$BrowserDataService extends $Notifier { void build(); diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart index b4135b80..c2891f29 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart @@ -196,13 +196,20 @@ class TabMenu extends HookConsumerWidget { onPressed: () async { final tabState = ref.read(tabStateProvider(selectedTabId))!; - final tabId = await ref - .read(tabRepositoryProvider.notifier) - .addTab( - url: tabState.url, - private: false, - selectTab: false, - ); + final tabId = (tabState.isPrivate) + ? await ref + .read(tabRepositoryProvider.notifier) + .addTab( + url: tabState.url, + private: false, + selectTab: false, + ) + : await ref + .read(tabRepositoryProvider.notifier) + .duplicateTab( + selectTabId: selectedTabId, + containerId: tabState.contextId, + ); if (context.mounted) { //save reference before pop `ref` gets disposed @@ -223,13 +230,20 @@ class TabMenu extends HookConsumerWidget { onPressed: () async { final tabState = ref.read(tabStateProvider(selectedTabId))!; - final tabId = await ref - .read(tabRepositoryProvider.notifier) - .addTab( - url: tabState.url, - private: true, - selectTab: false, - ); + final tabId = (!tabState.isPrivate) + ? await ref + .read(tabRepositoryProvider.notifier) + .addTab( + url: tabState.url, + private: true, + selectTab: false, + ) + : await ref + .read(tabRepositoryProvider.notifier) + .duplicateTab( + selectTabId: selectedTabId, + containerId: tabState.contextId, + ); if (context.mounted) { //save reference before pop `ref` gets disposed diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_preview.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_preview.dart index 8343a562..02c154e0 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_preview.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_preview.dart @@ -24,6 +24,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; +import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_icon.dart'; @@ -95,10 +96,8 @@ class GridTabPreview extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final tabState = ref.watch(tabStateProvider(tabId)); - if (tabState == null) { - return const SizedBox.shrink(); - } + final tabState = + ref.watch(tabStateProvider(tabId)) ?? TabState.$default(tabId); final extendedDeleteMenuController = useMenuController(); @@ -255,10 +254,8 @@ class ListTabPreview extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final colorScheme = Theme.of(context).colorScheme; - final tabState = ref.watch(tabStateProvider(tabId)); - if (tabState == null) { - return const SizedBox.shrink(); - } + final tabState = + ref.watch(tabStateProvider(tabId)) ?? TabState.$default(tabId); final extendedDeleteMenuController = useMenuController(); @@ -388,14 +385,6 @@ class SingleGridTabPreview extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final hasTabState = ref.watch( - tabStateProvider(tabId).select((value) => value != null), - ); - - if (!hasTabState) { - return const SizedBox.shrink(); - } - final dragStartPosition = useRef(Offset.zero); final draggedDistance = useState(0.0); @@ -512,14 +501,6 @@ class SingleListTabPreview extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final hasTabState = ref.watch( - tabStateProvider(tabId).select((value) => value != null), - ); - - if (!hasTabState) { - return const SizedBox.shrink(); - } - final dragStartPosition = useRef(Offset.zero); final draggedDistance = useState(0.0); diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart index 8c1ec8de..9b1542c0 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:drift/drift.dart' show Value; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; @@ -8,6 +9,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; +import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_view_controllers.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; @@ -381,6 +383,182 @@ class TabViewHeader extends HookConsumerWidget { } }, ), + Consumer( + builder: (context, ref, child) { + final selectedContainer = ref.watch( + selectedContainerDataProvider.select( + (value) => value.value, + ), + ); + + // Only show if container has cookie isolation + if (selectedContainer + ?.metadata + .contextualIdentity == + null) { + return const SizedBox.shrink(); + } + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Divider(), + MenuItemButton( + closeOnActivate: false, + leadingIcon: const Icon( + Icons.cleaning_services, + ), + child: const Text('Clear Container Data'), + onPressed: () async { + final containerId = selectedContainer!.id; + final tabs = await ref + .read( + tabDataRepositoryProvider.notifier, + ) + .getContainerTabsData(containerId); + + if (!context.mounted) return; + + final result = await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon( + Icons.cleaning_services, + ), + title: const Text( + 'Clear Container Data?', + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const Text( + 'This will clear all data for this container:', + ), + const SizedBox(height: 8), + const Text('• Cookies'), + const Text('• Site data'), + const Text('• Cache'), + const Text('• Permissions'), + const SizedBox(height: 8), + Text( + '${tabs.length} tab(s) will be closed and reopened fresh.', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of( + context, + ).colorScheme.tertiary, + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Clear Data'), + ), + ], + ); + }, + ); + + if (result == true) { + try { + await ref + .read( + tabDataRepositoryProvider + .notifier, + ) + .closeContainerTabs(containerId); + + await ref + .read( + browserDataServiceProvider + .notifier, + ) + .clearDataForContext( + selectedContainer + .metadata + .contextualIdentity!, + ); + + await ref + .read( + tabRepositoryProvider.notifier, + ) + .addMultipleTabs( + tabs: tabs + .map( + (tab) => AddTabParams( + url: tab.url.toString(), + startLoading: true, + parentId: tab.parentId, + private: + tab.isPrivate ?? + false, + flags: LoadUrlFlags.NONE + .toValue(), + source: Internal.newTab + .toValue(), + contextId: selectedContainer + .metadata + .contextualIdentity, + ), + ) + .toList(), + container: Value( + selectedContainer, + ), + ); + + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar( + const SnackBar( + content: Text( + 'Container data cleared successfully', + ), + ), + ); + } + } catch (e) { + if (context.mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar( + SnackBar( + content: Text( + 'Error clearing data: $e', + ), + backgroundColor: Theme.of( + context, + ).colorScheme.error, + ), + ); + } + } + } + + if (context.mounted) { + MenuController.maybeOf(context)?.close(); + } + }, + ), + ], + ); + }, + ), ], child: IconButton( onPressed: () { diff --git a/app/lib/features/geckoview/features/tabs/data/database/daos/tab.dart b/app/lib/features/geckoview/features/tabs/data/database/daos/tab.dart index 4786a784..06fe604d 100644 --- a/app/lib/features/geckoview/features/tabs/data/database/daos/tab.dart +++ b/app/lib/features/geckoview/features/tabs/data/database/daos/tab.dart @@ -103,12 +103,14 @@ class TabDao extends DatabaseAccessor with $TabDaoMixin { } } - Future upsertContainerTabTransactional( + Future upsertTabTransactional( Future Function() createTab, { required Value isPrivate, required Value parentId, Value containerId = const Value.absent(), Value orderKey = const Value.absent(), + Value url = const Value.absent(), + Value title = const Value.absent(), }) { return db.transaction(() async { final tabId = await createTab(); @@ -122,6 +124,8 @@ class TabDao extends DatabaseAccessor with $TabDaoMixin { parentId: parentId, timestamp: DateTime.now(), containerId: containerId, + url: url, + title: title, isPrivate: isPrivate, orderKey: currentOrderKey, ), @@ -130,6 +134,8 @@ class TabDao extends DatabaseAccessor with $TabDaoMixin { parentId: parentId, containerId: containerId, orderKey: Value.absentIfNull(orderKey.value), + url: url, + title: title, ), ), ); @@ -139,12 +145,14 @@ class TabDao extends DatabaseAccessor with $TabDaoMixin { } //Upsert an tab only if there is no container assigned yet - Future upsertUnassignedTab( + Future insertTab( String tabId, { required Value isPrivate, required Value parentId, Value containerId = const Value.absent(), Value orderKey = const Value.absent(), + Value url = const Value.absent(), + Value title = const Value.absent(), }) { return db.transaction(() async { final currentOrderKey = @@ -159,6 +167,8 @@ class TabDao extends DatabaseAccessor with $TabDaoMixin { containerId: containerId, orderKey: currentOrderKey, isPrivate: isPrivate, + url: url, + title: title, ), mode: InsertMode.insertOrIgnore, ); @@ -218,7 +228,9 @@ class TabDao extends DatabaseAccessor with $TabDaoMixin { db.tab, TabCompanion( parentId: (previousState?.parentId != state.parentId) - ? Value(state.parentId) + ? Value( + next.containsKey(state.parentId) ? state.parentId : null, + ) : const Value.absent(), url: (previousState?.url != state.url) ? Value(state.url) diff --git a/app/lib/features/geckoview/features/tabs/domain/repositories/tab.dart b/app/lib/features/geckoview/features/tabs/domain/repositories/tab.dart index 1ee0ab37..c6f33698 100644 --- a/app/lib/features/geckoview/features/tabs/domain/repositories/tab.dart +++ b/app/lib/features/geckoview/features/tabs/domain/repositories/tab.dart @@ -17,8 +17,10 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +import 'package:drift/drift.dart'; import 'package:nullability/nullability.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/database/definitions.drift.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart'; @@ -49,21 +51,53 @@ class TabDataRepository extends _$TabDataRepository { .tabDao .assignContainer(tabId, containerId: targetContainer.id); } else { - await ref - .read(tabRepositoryProvider.notifier) - .duplicateTab(selectTabId: tabId, containerId: targetContainer.id); + final tabState = ref.read(tabStateProvider(tabId)); + if (tabState != null) { + if (closeOldTab) { + await ref.read(tabRepositoryProvider.notifier).closeTab(tabId); + } - if (closeOldTab) { - await ref.read(tabRepositoryProvider.notifier).closeTab(tabId); + await ref + .read(tabRepositoryProvider.notifier) + .addTab( + url: tabState.url, + private: tabState.isPrivate, + container: Value(targetContainer), + parentId: tabState.parentId, + ); } } } - Future unassignContainer(String tabId) { - return ref - .read(tabDatabaseProvider) - .tabDao - .assignContainer(tabId, containerId: null); + Future unassignContainer(String tabId) async { + final currentContainerId = await getTabContainerId(tabId); + + final currentContainerData = await currentContainerId.mapNotNull( + (containerId) => ref + .read(containerRepositoryProvider.notifier) + .getContainerData(containerId), + ); + + if (currentContainerData?.metadata.contextualIdentity == null) { + return ref + .read(tabDatabaseProvider) + .tabDao + .assignContainer(tabId, containerId: null); + } else { + final tabState = ref.read(tabStateProvider(tabId)); + if (tabState != null) { + await ref.read(tabRepositoryProvider.notifier).closeTab(tabId); + + await ref + .read(tabRepositoryProvider.notifier) + .addTab( + url: tabState.url, + private: tabState.isPrivate, + container: const Value(null), + parentId: tabState.parentId, + ); + } + } } Future assignOrderKey(String tabId, String orderKey) { diff --git a/app/lib/features/geckoview/features/tabs/domain/repositories/tab.g.dart b/app/lib/features/geckoview/features/tabs/domain/repositories/tab.g.dart index b27df3bd..71e516b4 100644 --- a/app/lib/features/geckoview/features/tabs/domain/repositories/tab.g.dart +++ b/app/lib/features/geckoview/features/tabs/domain/repositories/tab.g.dart @@ -41,7 +41,7 @@ final class TabDataRepositoryProvider } } -String _$tabDataRepositoryHash() => r'63ab2765ea5be640d6c6a596852fee0df5a958e4'; +String _$tabDataRepositoryHash() => r'eab2a5040541fec955ffe23947cbb57952d29c6c'; abstract class _$TabDataRepository extends $Notifier { void build(); 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 5ea41eca..ad7b126f 100644 --- a/app/lib/features/settings/presentation/screens/web_engine_settings.dart +++ b/app/lib/features/settings/presentation/screens/web_engine_settings.dart @@ -121,7 +121,7 @@ class WebEngineSettingsScreen extends HookConsumerWidget { vertical: 8.0, horizontal: 16.0, ), - leading: const Icon(MdiIcons.delete), + leading: const Icon(Icons.cleaning_services), trailing: const Icon(Icons.chevron_right), onTap: () async { await showDialog( diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoDeleteBrowsingDataControllerImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoDeleteBrowsingDataControllerImpl.kt index 91e59f88..45bf3bac 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoDeleteBrowsingDataControllerImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoDeleteBrowsingDataControllerImpl.kt @@ -110,4 +110,17 @@ class GeckoDeleteBrowsingDataControllerImpl : GeckoDeleteBrowsingDataController } } } + + override fun clearDataForSessionContext( + contextId: String, + callback: (Result) -> Unit + ) { + coroutineScope.launch { + withContext(Dispatchers.Main) { + components.core.runtime.storageController.clearDataForSessionContext(contextId) + + callback(Result.success(Unit)) + } + } + } } \ No newline at end of file diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt index 3f964465..c86e89f0 100644 --- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt +++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt @@ -6,6 +6,7 @@ package eu.weblibre.flutter_mozilla_components.api +import eu.weblibre.flutter_mozilla_components.pigeons.AddTabParams import eu.weblibre.flutter_mozilla_components.pigeons.GeckoTabsApi import eu.weblibre.flutter_mozilla_components.pigeons.HistoryMetadataKey as PigeonHistoryMetadataKey import eu.weblibre.flutter_mozilla_components.pigeons.LoadUrlFlagsValue @@ -33,6 +34,7 @@ import kotlinx.coroutines.withContext import mozilla.components.browser.icons.BrowserIcons import mozilla.components.browser.icons.IconRequest import mozilla.components.browser.session.storage.RecoverableBrowserState +import mozilla.components.browser.state.action.EngineAction import mozilla.components.browser.state.action.TabListAction import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.state.BrowserState @@ -518,4 +520,58 @@ class GeckoTabsApiImpl : GeckoTabsApi { throw e } } + + override fun addMultipleTabs(tabs: List, selectTabId: String?): List { + try { + val tabSessionStates = tabs.map { params -> + createTab( + url = params.url, + private = params.private, + source = restoreSource(params.source), + contextId = params.contextId, + parent = params.parentId?.let { components.core.store.state.findTab(it) }, + historyMetadata = params.historyMetadata?.let { metadata -> + HistoryMetadataKey( + url = metadata.url, + searchTerm = metadata.searchTerm, + referrerUrl = metadata.referrerUrl + ) + }, + desktopMode = components.core.store.state.desktopMode + ) + } + + components.core.store.dispatch( + TabListAction.AddMultipleTabsAction( + tabs = tabSessionStates + ) + ) + + // Load URLs for tabs that need loading + tabs.zip(tabSessionStates).forEach { (params, tabState) -> + if (params.startLoading) { + components.core.store.dispatch( + EngineAction.LoadUrlAction( + tabId = tabState.id, + url = params.url, + flags = EngineSession.LoadUrlFlags.select(params.flags.value.toInt()), + additionalHeaders = params.additionalHeaders, + includeParent = true + ) + ) + } + } + + selectTabId?.let { + components.useCases.tabsUseCases.selectTab(tabId = it) + } + + val createdTabIds = tabSessionStates.map { it.id } + logger.debug("$TAG: Added ${tabs.size} tabs: ${createdTabIds.joinToString()}") + return createdTabIds + } catch (e: Exception) { + logger.error("$TAG: Failed to add multiple tabs", e) + throw e + } + } } 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 62093a3a..f02436a5 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 @@ -567,6 +567,62 @@ data class ReaderState ( override fun hashCode(): Int = toList().hashCode() } +/** + * Parameters for adding a new tab. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class AddTabParams ( + val url: String, + val startLoading: Boolean, + val parentId: String? = null, + val flags: LoadUrlFlagsValue, + val contextId: String? = null, + val source: SourceValue, + val private: Boolean, + val historyMetadata: HistoryMetadataKey? = null, + val additionalHeaders: Map? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): AddTabParams { + val url = pigeonVar_list[0] as String + val startLoading = pigeonVar_list[1] as Boolean + val parentId = pigeonVar_list[2] as String? + val flags = pigeonVar_list[3] as LoadUrlFlagsValue + val contextId = pigeonVar_list[4] as String? + val source = pigeonVar_list[5] as SourceValue + val private = pigeonVar_list[6] as Boolean + val historyMetadata = pigeonVar_list[7] as HistoryMetadataKey? + val additionalHeaders = pigeonVar_list[8] as Map? + return AddTabParams(url, startLoading, parentId, flags, contextId, source, private, historyMetadata, additionalHeaders) + } + } + fun toList(): List { + return listOf( + url, + startLoading, + parentId, + flags, + contextId, + source, + private, + historyMetadata, + additionalHeaders, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is AddTabParams) { + return false + } + if (this === other) { + return true + } + return GeckoPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} + /** * Details about the last playing media in this tab. * @@ -2789,245 +2845,250 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { } 156.toByte() -> { return (readValue(buffer) as? List)?.let { - LastMediaAccessState.fromList(it) + AddTabParams.fromList(it) } } 157.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryMetadataKey.fromList(it) + LastMediaAccessState.fromList(it) } } 158.toByte() -> { return (readValue(buffer) as? List)?.let { - PackageCategoryValue.fromList(it) + HistoryMetadataKey.fromList(it) } } 159.toByte() -> { return (readValue(buffer) as? List)?.let { - ExternalPackage.fromList(it) + PackageCategoryValue.fromList(it) } } 160.toByte() -> { return (readValue(buffer) as? List)?.let { - LoadUrlFlagsValue.fromList(it) + ExternalPackage.fromList(it) } } 161.toByte() -> { return (readValue(buffer) as? List)?.let { - SourceValue.fromList(it) + LoadUrlFlagsValue.fromList(it) } } 162.toByte() -> { return (readValue(buffer) as? List)?.let { - TabState.fromList(it) + SourceValue.fromList(it) } } 163.toByte() -> { return (readValue(buffer) as? List)?.let { - RecoverableTab.fromList(it) + TabState.fromList(it) } } 164.toByte() -> { return (readValue(buffer) as? List)?.let { - RecoverableBrowserState.fromList(it) + RecoverableTab.fromList(it) } } 165.toByte() -> { return (readValue(buffer) as? List)?.let { - IconRequest.fromList(it) + RecoverableBrowserState.fromList(it) } } 166.toByte() -> { return (readValue(buffer) as? List)?.let { - ResourceSize.fromList(it) + IconRequest.fromList(it) } } 167.toByte() -> { return (readValue(buffer) as? List)?.let { - Resource.fromList(it) + ResourceSize.fromList(it) } } 168.toByte() -> { return (readValue(buffer) as? List)?.let { - IconResult.fromList(it) + Resource.fromList(it) } } 169.toByte() -> { return (readValue(buffer) as? List)?.let { - CookiePartitionKey.fromList(it) + IconResult.fromList(it) } } 170.toByte() -> { return (readValue(buffer) as? List)?.let { - Cookie.fromList(it) + CookiePartitionKey.fromList(it) } } 171.toByte() -> { return (readValue(buffer) as? List)?.let { - VisitInfo.fromList(it) + Cookie.fromList(it) } } 172.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryItem.fromList(it) + VisitInfo.fromList(it) } } 173.toByte() -> { return (readValue(buffer) as? List)?.let { - HistoryState.fromList(it) + HistoryItem.fromList(it) } } 174.toByte() -> { return (readValue(buffer) as? List)?.let { - ReaderableState.fromList(it) + HistoryState.fromList(it) } } 175.toByte() -> { return (readValue(buffer) as? List)?.let { - SecurityInfoState.fromList(it) + ReaderableState.fromList(it) } } 176.toByte() -> { return (readValue(buffer) as? List)?.let { - TabContentState.fromList(it) + SecurityInfoState.fromList(it) } } 177.toByte() -> { return (readValue(buffer) as? List)?.let { - FindResultState.fromList(it) + TabContentState.fromList(it) } } 178.toByte() -> { return (readValue(buffer) as? List)?.let { - CustomSelectionAction.fromList(it) + FindResultState.fromList(it) } } 179.toByte() -> { return (readValue(buffer) as? List)?.let { - WebExtensionData.fromList(it) + CustomSelectionAction.fromList(it) } } 180.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoSuggestion.fromList(it) + WebExtensionData.fromList(it) } } 181.toByte() -> { return (readValue(buffer) as? List)?.let { - TabContent.fromList(it) + GeckoSuggestion.fromList(it) } } 182.toByte() -> { return (readValue(buffer) as? List)?.let { - ContentBlocking.fromList(it) + TabContent.fromList(it) } } 183.toByte() -> { return (readValue(buffer) as? List)?.let { - DohSettings.fromList(it) + ContentBlocking.fromList(it) } } 184.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoEngineSettings.fromList(it) + DohSettings.fromList(it) } } 185.toByte() -> { return (readValue(buffer) as? List)?.let { - AutocompleteResult.fromList(it) + GeckoEngineSettings.fromList(it) } } 186.toByte() -> { return (readValue(buffer) as? List)?.let { - UnknownHitResult.fromList(it) + AutocompleteResult.fromList(it) } } 187.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageHitResult.fromList(it) + UnknownHitResult.fromList(it) } } 188.toByte() -> { return (readValue(buffer) as? List)?.let { - VideoHitResult.fromList(it) + ImageHitResult.fromList(it) } } 189.toByte() -> { return (readValue(buffer) as? List)?.let { - AudioHitResult.fromList(it) + VideoHitResult.fromList(it) } } 190.toByte() -> { return (readValue(buffer) as? List)?.let { - ImageSrcHitResult.fromList(it) + AudioHitResult.fromList(it) } } 191.toByte() -> { return (readValue(buffer) as? List)?.let { - PhoneHitResult.fromList(it) + ImageSrcHitResult.fromList(it) } } 192.toByte() -> { return (readValue(buffer) as? List)?.let { - EmailHitResult.fromList(it) + PhoneHitResult.fromList(it) } } 193.toByte() -> { return (readValue(buffer) as? List)?.let { - GeoHitResult.fromList(it) + EmailHitResult.fromList(it) } } 194.toByte() -> { return (readValue(buffer) as? List)?.let { - DownloadState.fromList(it) + GeoHitResult.fromList(it) } } 195.toByte() -> { return (readValue(buffer) as? List)?.let { - ShareInternetResourceState.fromList(it) + DownloadState.fromList(it) } } 196.toByte() -> { return (readValue(buffer) as? List)?.let { - AddonCollection.fromList(it) + ShareInternetResourceState.fromList(it) } } 197.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoPref.fromList(it) + AddonCollection.fromList(it) } } 198.toByte() -> { return (readValue(buffer) as? List)?.let { - MlProgressData.fromList(it) + GeckoPref.fromList(it) } } 199.toByte() -> { return (readValue(buffer) as? List)?.let { - ContainerSiteAssignment.fromList(it) + MlProgressData.fromList(it) } } 200.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoHeader.fromList(it) + ContainerSiteAssignment.fromList(it) } } 201.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoFetchRequest.fromList(it) + GeckoHeader.fromList(it) } } 202.toByte() -> { return (readValue(buffer) as? List)?.let { - GeckoFetchResponse.fromList(it) + GeckoFetchRequest.fromList(it) } } 203.toByte() -> { return (readValue(buffer) as? List)?.let { - BookmarkNode.fromList(it) + GeckoFetchResponse.fromList(it) } } 204.toByte() -> { + return (readValue(buffer) as? List)?.let { + BookmarkNode.fromList(it) + } + } + 205.toByte() -> { return (readValue(buffer) as? List)?.let { BookmarkInfo.fromList(it) } @@ -3145,202 +3206,206 @@ private open class GeckoPigeonCodec : StandardMessageCodec() { stream.write(155) writeValue(stream, value.toList()) } - is LastMediaAccessState -> { + is AddTabParams -> { stream.write(156) writeValue(stream, value.toList()) } - is HistoryMetadataKey -> { + is LastMediaAccessState -> { stream.write(157) writeValue(stream, value.toList()) } - is PackageCategoryValue -> { + is HistoryMetadataKey -> { stream.write(158) writeValue(stream, value.toList()) } - is ExternalPackage -> { + is PackageCategoryValue -> { stream.write(159) writeValue(stream, value.toList()) } - is LoadUrlFlagsValue -> { + is ExternalPackage -> { stream.write(160) writeValue(stream, value.toList()) } - is SourceValue -> { + is LoadUrlFlagsValue -> { stream.write(161) writeValue(stream, value.toList()) } - is TabState -> { + is SourceValue -> { stream.write(162) writeValue(stream, value.toList()) } - is RecoverableTab -> { + is TabState -> { stream.write(163) writeValue(stream, value.toList()) } - is RecoverableBrowserState -> { + is RecoverableTab -> { stream.write(164) writeValue(stream, value.toList()) } - is IconRequest -> { + is RecoverableBrowserState -> { stream.write(165) writeValue(stream, value.toList()) } - is ResourceSize -> { + is IconRequest -> { stream.write(166) writeValue(stream, value.toList()) } - is Resource -> { + is ResourceSize -> { stream.write(167) writeValue(stream, value.toList()) } - is IconResult -> { + is Resource -> { stream.write(168) writeValue(stream, value.toList()) } - is CookiePartitionKey -> { + is IconResult -> { stream.write(169) writeValue(stream, value.toList()) } - is Cookie -> { + is CookiePartitionKey -> { stream.write(170) writeValue(stream, value.toList()) } - is VisitInfo -> { + is Cookie -> { stream.write(171) writeValue(stream, value.toList()) } - is HistoryItem -> { + is VisitInfo -> { stream.write(172) writeValue(stream, value.toList()) } - is HistoryState -> { + is HistoryItem -> { stream.write(173) writeValue(stream, value.toList()) } - is ReaderableState -> { + is HistoryState -> { stream.write(174) writeValue(stream, value.toList()) } - is SecurityInfoState -> { + is ReaderableState -> { stream.write(175) writeValue(stream, value.toList()) } - is TabContentState -> { + is SecurityInfoState -> { stream.write(176) writeValue(stream, value.toList()) } - is FindResultState -> { + is TabContentState -> { stream.write(177) writeValue(stream, value.toList()) } - is CustomSelectionAction -> { + is FindResultState -> { stream.write(178) writeValue(stream, value.toList()) } - is WebExtensionData -> { + is CustomSelectionAction -> { stream.write(179) writeValue(stream, value.toList()) } - is GeckoSuggestion -> { + is WebExtensionData -> { stream.write(180) writeValue(stream, value.toList()) } - is TabContent -> { + is GeckoSuggestion -> { stream.write(181) writeValue(stream, value.toList()) } - is ContentBlocking -> { + is TabContent -> { stream.write(182) writeValue(stream, value.toList()) } - is DohSettings -> { + is ContentBlocking -> { stream.write(183) writeValue(stream, value.toList()) } - is GeckoEngineSettings -> { + is DohSettings -> { stream.write(184) writeValue(stream, value.toList()) } - is AutocompleteResult -> { + is GeckoEngineSettings -> { stream.write(185) writeValue(stream, value.toList()) } - is UnknownHitResult -> { + is AutocompleteResult -> { stream.write(186) writeValue(stream, value.toList()) } - is ImageHitResult -> { + is UnknownHitResult -> { stream.write(187) writeValue(stream, value.toList()) } - is VideoHitResult -> { + is ImageHitResult -> { stream.write(188) writeValue(stream, value.toList()) } - is AudioHitResult -> { + is VideoHitResult -> { stream.write(189) writeValue(stream, value.toList()) } - is ImageSrcHitResult -> { + is AudioHitResult -> { stream.write(190) writeValue(stream, value.toList()) } - is PhoneHitResult -> { + is ImageSrcHitResult -> { stream.write(191) writeValue(stream, value.toList()) } - is EmailHitResult -> { + is PhoneHitResult -> { stream.write(192) writeValue(stream, value.toList()) } - is GeoHitResult -> { + is EmailHitResult -> { stream.write(193) writeValue(stream, value.toList()) } - is DownloadState -> { + is GeoHitResult -> { stream.write(194) writeValue(stream, value.toList()) } - is ShareInternetResourceState -> { + is DownloadState -> { stream.write(195) writeValue(stream, value.toList()) } - is AddonCollection -> { + is ShareInternetResourceState -> { stream.write(196) writeValue(stream, value.toList()) } - is GeckoPref -> { + is AddonCollection -> { stream.write(197) writeValue(stream, value.toList()) } - is MlProgressData -> { + is GeckoPref -> { stream.write(198) writeValue(stream, value.toList()) } - is ContainerSiteAssignment -> { + is MlProgressData -> { stream.write(199) writeValue(stream, value.toList()) } - is GeckoHeader -> { + is ContainerSiteAssignment -> { stream.write(200) writeValue(stream, value.toList()) } - is GeckoFetchRequest -> { + is GeckoHeader -> { stream.write(201) writeValue(stream, value.toList()) } - is GeckoFetchResponse -> { + is GeckoFetchRequest -> { stream.write(202) writeValue(stream, value.toList()) } - is BookmarkNode -> { + is GeckoFetchResponse -> { stream.write(203) writeValue(stream, value.toList()) } - is BookmarkInfo -> { + is BookmarkNode -> { stream.write(204) writeValue(stream, value.toList()) } + is BookmarkInfo -> { + stream.write(205) + writeValue(stream, value.toList()) + } else -> super.writeValue(stream, value) } } @@ -3847,6 +3912,7 @@ interface GeckoTabsApi { fun selectTab(tabId: String) fun removeTab(tabId: String) fun addTab(url: String, selectTab: Boolean, startLoading: Boolean, parentId: String?, flags: LoadUrlFlagsValue, contextId: String?, source: SourceValue, private: Boolean, historyMetadata: HistoryMetadataKey?, additionalHeaders: Map?): String + fun addMultipleTabs(tabs: List, selectTabId: String?): List fun removeAllTabs(recoverable: Boolean) fun removeTabs(ids: List) fun removeNormalTabs() @@ -3962,6 +4028,24 @@ interface GeckoTabsApi { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTabsApi.addMultipleTabs$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val tabsArg = args[0] as List + val selectTabIdArg = args[1] as String? + val wrapped: List = try { + listOf(api.addMultipleTabs(tabsArg, selectTabIdArg)) + } catch (exception: Throwable) { + GeckoPigeonUtils.wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } run { val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoTabsApi.removeAllTabs$separatedMessageChannelSuffix", codec) if (api != null) { @@ -5497,6 +5581,7 @@ interface GeckoDeleteBrowsingDataController { fun deleteCachedFiles(callback: (Result) -> Unit) fun deleteSitePermissions(callback: (Result) -> Unit) fun deleteDownloads(callback: (Result) -> Unit) + fun clearDataForSessionContext(contextId: String, callback: (Result) -> Unit) companion object { /** The codec used by GeckoDeleteBrowsingDataController. */ @@ -5609,6 +5694,25 @@ interface GeckoDeleteBrowsingDataController { channel.setMessageHandler(null) } } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoDeleteBrowsingDataController.clearDataForSessionContext$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val contextIdArg = args[0] as String + api.clearDataForSessionContext(contextIdArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeckoPigeonUtils.wrapError(error)) + } else { + reply.reply(GeckoPigeonUtils.wrapResult(null)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } } } } diff --git a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart index 504db898..240c6ef8 100644 --- a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart +++ b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart @@ -33,6 +33,7 @@ export 'src/domain/services/gecko_tab_content.dart'; export 'src/geckoview_widget.dart'; export 'src/pigeons/gecko.g.dart' show + AddTabParams, AddonCollection, AudioHitResult, BookmarkInfo, diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_delete_browser_data.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_delete_browser_data.dart index 8dd6614f..85aaa51f 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_delete_browser_data.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_delete_browser_data.dart @@ -37,4 +37,8 @@ class GeckoDeleteBrowserDataService { Future deleteDownloads() { return _api.deleteDownloads(); } + + Future clearDataForContext(String contextId) { + return _api.clearDataForSessionContext(contextId); + } } diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart index c009ea82..784742bf 100644 --- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart +++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart @@ -180,4 +180,14 @@ class GeckoTabService { alternativeUrl: alternativeUrl?.toString(), ); } + + Future> addMultipleTabs({ + required List tabs, + String? selectTabId, + }) { + return _api.addMultipleTabs( + tabs: tabs, + selectTabId: selectTabId, + ); + } } 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 dd271d3b..ecec1f18 100644 --- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart +++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart @@ -387,6 +387,88 @@ class ReaderState { ; } +/// Parameters for adding a new tab. +class AddTabParams { + AddTabParams({ + required this.url, + required this.startLoading, + this.parentId, + required this.flags, + this.contextId, + required this.source, + required this.private, + this.historyMetadata, + this.additionalHeaders, + }); + + String url; + + bool startLoading; + + String? parentId; + + LoadUrlFlagsValue flags; + + String? contextId; + + SourceValue source; + + bool private; + + HistoryMetadataKey? historyMetadata; + + Map? additionalHeaders; + + List _toList() { + return [ + url, + startLoading, + parentId, + flags, + contextId, + source, + private, + historyMetadata, + additionalHeaders, + ]; + } + + Object encode() { + return _toList(); } + + static AddTabParams decode(Object result) { + result as List; + return AddTabParams( + url: result[0]! as String, + startLoading: result[1]! as bool, + parentId: result[2] as String?, + flags: result[3]! as LoadUrlFlagsValue, + contextId: result[4] as String?, + source: result[5]! as SourceValue, + private: result[6]! as bool, + historyMetadata: result[7] as HistoryMetadataKey?, + additionalHeaders: (result[8] as Map?)?.cast(), + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! AddTabParams || 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()) +; +} + /// Details about the last playing media in this tab. class LastMediaAccessState { LastMediaAccessState({ @@ -3484,153 +3566,156 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is ReaderState) { buffer.putUint8(155); writeValue(buffer, value.encode()); - } else if (value is LastMediaAccessState) { + } else if (value is AddTabParams) { buffer.putUint8(156); writeValue(buffer, value.encode()); - } else if (value is HistoryMetadataKey) { + } else if (value is LastMediaAccessState) { buffer.putUint8(157); writeValue(buffer, value.encode()); - } else if (value is PackageCategoryValue) { + } else if (value is HistoryMetadataKey) { buffer.putUint8(158); writeValue(buffer, value.encode()); - } else if (value is ExternalPackage) { + } else if (value is PackageCategoryValue) { buffer.putUint8(159); writeValue(buffer, value.encode()); - } else if (value is LoadUrlFlagsValue) { + } else if (value is ExternalPackage) { buffer.putUint8(160); writeValue(buffer, value.encode()); - } else if (value is SourceValue) { + } else if (value is LoadUrlFlagsValue) { buffer.putUint8(161); writeValue(buffer, value.encode()); - } else if (value is TabState) { + } else if (value is SourceValue) { buffer.putUint8(162); writeValue(buffer, value.encode()); - } else if (value is RecoverableTab) { + } else if (value is TabState) { buffer.putUint8(163); writeValue(buffer, value.encode()); - } else if (value is RecoverableBrowserState) { + } else if (value is RecoverableTab) { buffer.putUint8(164); writeValue(buffer, value.encode()); - } else if (value is IconRequest) { + } else if (value is RecoverableBrowserState) { buffer.putUint8(165); writeValue(buffer, value.encode()); - } else if (value is ResourceSize) { + } else if (value is IconRequest) { buffer.putUint8(166); writeValue(buffer, value.encode()); - } else if (value is Resource) { + } else if (value is ResourceSize) { buffer.putUint8(167); writeValue(buffer, value.encode()); - } else if (value is IconResult) { + } else if (value is Resource) { buffer.putUint8(168); writeValue(buffer, value.encode()); - } else if (value is CookiePartitionKey) { + } else if (value is IconResult) { buffer.putUint8(169); writeValue(buffer, value.encode()); - } else if (value is Cookie) { + } else if (value is CookiePartitionKey) { buffer.putUint8(170); writeValue(buffer, value.encode()); - } else if (value is VisitInfo) { + } else if (value is Cookie) { buffer.putUint8(171); writeValue(buffer, value.encode()); - } else if (value is HistoryItem) { + } else if (value is VisitInfo) { buffer.putUint8(172); writeValue(buffer, value.encode()); - } else if (value is HistoryState) { + } else if (value is HistoryItem) { buffer.putUint8(173); writeValue(buffer, value.encode()); - } else if (value is ReaderableState) { + } else if (value is HistoryState) { buffer.putUint8(174); writeValue(buffer, value.encode()); - } else if (value is SecurityInfoState) { + } else if (value is ReaderableState) { buffer.putUint8(175); writeValue(buffer, value.encode()); - } else if (value is TabContentState) { + } else if (value is SecurityInfoState) { buffer.putUint8(176); writeValue(buffer, value.encode()); - } else if (value is FindResultState) { + } else if (value is TabContentState) { buffer.putUint8(177); writeValue(buffer, value.encode()); - } else if (value is CustomSelectionAction) { + } else if (value is FindResultState) { buffer.putUint8(178); writeValue(buffer, value.encode()); - } else if (value is WebExtensionData) { + } else if (value is CustomSelectionAction) { buffer.putUint8(179); writeValue(buffer, value.encode()); - } else if (value is GeckoSuggestion) { + } else if (value is WebExtensionData) { buffer.putUint8(180); writeValue(buffer, value.encode()); - } else if (value is TabContent) { + } else if (value is GeckoSuggestion) { buffer.putUint8(181); writeValue(buffer, value.encode()); - } else if (value is ContentBlocking) { + } else if (value is TabContent) { buffer.putUint8(182); writeValue(buffer, value.encode()); - } else if (value is DohSettings) { + } else if (value is ContentBlocking) { buffer.putUint8(183); writeValue(buffer, value.encode()); - } else if (value is GeckoEngineSettings) { + } else if (value is DohSettings) { buffer.putUint8(184); writeValue(buffer, value.encode()); - } else if (value is AutocompleteResult) { + } else if (value is GeckoEngineSettings) { buffer.putUint8(185); writeValue(buffer, value.encode()); - } else if (value is UnknownHitResult) { + } else if (value is AutocompleteResult) { buffer.putUint8(186); writeValue(buffer, value.encode()); - } else if (value is ImageHitResult) { + } else if (value is UnknownHitResult) { buffer.putUint8(187); writeValue(buffer, value.encode()); - } else if (value is VideoHitResult) { + } else if (value is ImageHitResult) { buffer.putUint8(188); writeValue(buffer, value.encode()); - } else if (value is AudioHitResult) { + } else if (value is VideoHitResult) { buffer.putUint8(189); writeValue(buffer, value.encode()); - } else if (value is ImageSrcHitResult) { + } else if (value is AudioHitResult) { buffer.putUint8(190); writeValue(buffer, value.encode()); - } else if (value is PhoneHitResult) { + } else if (value is ImageSrcHitResult) { buffer.putUint8(191); writeValue(buffer, value.encode()); - } else if (value is EmailHitResult) { + } else if (value is PhoneHitResult) { buffer.putUint8(192); writeValue(buffer, value.encode()); - } else if (value is GeoHitResult) { + } else if (value is EmailHitResult) { buffer.putUint8(193); writeValue(buffer, value.encode()); - } else if (value is DownloadState) { + } else if (value is GeoHitResult) { buffer.putUint8(194); writeValue(buffer, value.encode()); - } else if (value is ShareInternetResourceState) { + } else if (value is DownloadState) { buffer.putUint8(195); writeValue(buffer, value.encode()); - } else if (value is AddonCollection) { + } else if (value is ShareInternetResourceState) { buffer.putUint8(196); writeValue(buffer, value.encode()); - } else if (value is GeckoPref) { + } else if (value is AddonCollection) { buffer.putUint8(197); writeValue(buffer, value.encode()); - } else if (value is MlProgressData) { + } else if (value is GeckoPref) { buffer.putUint8(198); writeValue(buffer, value.encode()); - } else if (value is ContainerSiteAssignment) { + } else if (value is MlProgressData) { buffer.putUint8(199); writeValue(buffer, value.encode()); - } else if (value is GeckoHeader) { + } else if (value is ContainerSiteAssignment) { buffer.putUint8(200); writeValue(buffer, value.encode()); - } else if (value is GeckoFetchRequest) { + } else if (value is GeckoHeader) { buffer.putUint8(201); writeValue(buffer, value.encode()); - } else if (value is GeckoFetchResponse) { + } else if (value is GeckoFetchRequest) { buffer.putUint8(202); writeValue(buffer, value.encode()); - } else if (value is BookmarkNode) { + } else if (value is GeckoFetchResponse) { buffer.putUint8(203); writeValue(buffer, value.encode()); - } else if (value is BookmarkInfo) { + } else if (value is BookmarkNode) { buffer.putUint8(204); writeValue(buffer, value.encode()); + } else if (value is BookmarkInfo) { + buffer.putUint8(205); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -3719,102 +3804,104 @@ class _PigeonCodec extends StandardMessageCodec { case 155: return ReaderState.decode(readValue(buffer)!); case 156: - return LastMediaAccessState.decode(readValue(buffer)!); + return AddTabParams.decode(readValue(buffer)!); case 157: - return HistoryMetadataKey.decode(readValue(buffer)!); + return LastMediaAccessState.decode(readValue(buffer)!); case 158: - return PackageCategoryValue.decode(readValue(buffer)!); + return HistoryMetadataKey.decode(readValue(buffer)!); case 159: - return ExternalPackage.decode(readValue(buffer)!); + return PackageCategoryValue.decode(readValue(buffer)!); case 160: - return LoadUrlFlagsValue.decode(readValue(buffer)!); + return ExternalPackage.decode(readValue(buffer)!); case 161: - return SourceValue.decode(readValue(buffer)!); + return LoadUrlFlagsValue.decode(readValue(buffer)!); case 162: - return TabState.decode(readValue(buffer)!); + return SourceValue.decode(readValue(buffer)!); case 163: - return RecoverableTab.decode(readValue(buffer)!); + return TabState.decode(readValue(buffer)!); case 164: - return RecoverableBrowserState.decode(readValue(buffer)!); + return RecoverableTab.decode(readValue(buffer)!); case 165: - return IconRequest.decode(readValue(buffer)!); + return RecoverableBrowserState.decode(readValue(buffer)!); case 166: - return ResourceSize.decode(readValue(buffer)!); + return IconRequest.decode(readValue(buffer)!); case 167: - return Resource.decode(readValue(buffer)!); + return ResourceSize.decode(readValue(buffer)!); case 168: - return IconResult.decode(readValue(buffer)!); + return Resource.decode(readValue(buffer)!); case 169: - return CookiePartitionKey.decode(readValue(buffer)!); + return IconResult.decode(readValue(buffer)!); case 170: - return Cookie.decode(readValue(buffer)!); + return CookiePartitionKey.decode(readValue(buffer)!); case 171: - return VisitInfo.decode(readValue(buffer)!); + return Cookie.decode(readValue(buffer)!); case 172: - return HistoryItem.decode(readValue(buffer)!); + return VisitInfo.decode(readValue(buffer)!); case 173: - return HistoryState.decode(readValue(buffer)!); + return HistoryItem.decode(readValue(buffer)!); case 174: - return ReaderableState.decode(readValue(buffer)!); + return HistoryState.decode(readValue(buffer)!); case 175: - return SecurityInfoState.decode(readValue(buffer)!); + return ReaderableState.decode(readValue(buffer)!); case 176: - return TabContentState.decode(readValue(buffer)!); + return SecurityInfoState.decode(readValue(buffer)!); case 177: - return FindResultState.decode(readValue(buffer)!); + return TabContentState.decode(readValue(buffer)!); case 178: - return CustomSelectionAction.decode(readValue(buffer)!); + return FindResultState.decode(readValue(buffer)!); case 179: - return WebExtensionData.decode(readValue(buffer)!); + return CustomSelectionAction.decode(readValue(buffer)!); case 180: - return GeckoSuggestion.decode(readValue(buffer)!); + return WebExtensionData.decode(readValue(buffer)!); case 181: - return TabContent.decode(readValue(buffer)!); + return GeckoSuggestion.decode(readValue(buffer)!); case 182: - return ContentBlocking.decode(readValue(buffer)!); + return TabContent.decode(readValue(buffer)!); case 183: - return DohSettings.decode(readValue(buffer)!); + return ContentBlocking.decode(readValue(buffer)!); case 184: - return GeckoEngineSettings.decode(readValue(buffer)!); + return DohSettings.decode(readValue(buffer)!); case 185: - return AutocompleteResult.decode(readValue(buffer)!); + return GeckoEngineSettings.decode(readValue(buffer)!); case 186: - return UnknownHitResult.decode(readValue(buffer)!); + return AutocompleteResult.decode(readValue(buffer)!); case 187: - return ImageHitResult.decode(readValue(buffer)!); + return UnknownHitResult.decode(readValue(buffer)!); case 188: - return VideoHitResult.decode(readValue(buffer)!); + return ImageHitResult.decode(readValue(buffer)!); case 189: - return AudioHitResult.decode(readValue(buffer)!); + return VideoHitResult.decode(readValue(buffer)!); case 190: - return ImageSrcHitResult.decode(readValue(buffer)!); + return AudioHitResult.decode(readValue(buffer)!); case 191: - return PhoneHitResult.decode(readValue(buffer)!); + return ImageSrcHitResult.decode(readValue(buffer)!); case 192: - return EmailHitResult.decode(readValue(buffer)!); + return PhoneHitResult.decode(readValue(buffer)!); case 193: - return GeoHitResult.decode(readValue(buffer)!); + return EmailHitResult.decode(readValue(buffer)!); case 194: - return DownloadState.decode(readValue(buffer)!); + return GeoHitResult.decode(readValue(buffer)!); case 195: - return ShareInternetResourceState.decode(readValue(buffer)!); + return DownloadState.decode(readValue(buffer)!); case 196: - return AddonCollection.decode(readValue(buffer)!); + return ShareInternetResourceState.decode(readValue(buffer)!); case 197: - return GeckoPref.decode(readValue(buffer)!); + return AddonCollection.decode(readValue(buffer)!); case 198: - return MlProgressData.decode(readValue(buffer)!); + return GeckoPref.decode(readValue(buffer)!); case 199: - return ContainerSiteAssignment.decode(readValue(buffer)!); + return MlProgressData.decode(readValue(buffer)!); case 200: - return GeckoHeader.decode(readValue(buffer)!); + return ContainerSiteAssignment.decode(readValue(buffer)!); case 201: - return GeckoFetchRequest.decode(readValue(buffer)!); + return GeckoHeader.decode(readValue(buffer)!); case 202: - return GeckoFetchResponse.decode(readValue(buffer)!); + return GeckoFetchRequest.decode(readValue(buffer)!); case 203: - return BookmarkNode.decode(readValue(buffer)!); + return GeckoFetchResponse.decode(readValue(buffer)!); case 204: + return BookmarkNode.decode(readValue(buffer)!); + case 205: return BookmarkInfo.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -4486,6 +4573,33 @@ class GeckoTabsApi { } } + Future> addMultipleTabs({required List tabs, required String? selectTabId}) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTabsApi.addMultipleTabs$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([tabs, selectTabId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + 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?)!.cast(); + } + } + Future removeAllTabs({required bool recoverable}) async { final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTabsApi.removeAllTabs$pigeonVar_messageChannelSuffix'; final pigeonVar_channel = BasicMessageChannel( @@ -6668,6 +6782,28 @@ class GeckoDeleteBrowsingDataController { return; } } + + Future clearDataForSessionContext(String contextId) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoDeleteBrowsingDataController.clearDataForSessionContext$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([contextId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + 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; + } + } } class GeckoHistoryApi { diff --git a/packages/flutter_mozilla_components/pigeons/gecko.dart b/packages/flutter_mozilla_components/pigeons/gecko.dart index 388420ca..94bde099 100644 --- a/packages/flutter_mozilla_components/pigeons/gecko.dart +++ b/packages/flutter_mozilla_components/pigeons/gecko.dart @@ -54,6 +54,31 @@ class ReaderState { }); } +/// Parameters for adding a new tab. +class AddTabParams { + final String url; + final bool startLoading; + final String? parentId; + final LoadUrlFlagsValue flags; + final String? contextId; + final SourceValue source; + final bool private; + final HistoryMetadataKey? historyMetadata; + final Map? additionalHeaders; + + const AddTabParams({ + required this.url, + required this.startLoading, + this.parentId, + required this.flags, + this.contextId, + required this.source, + required this.private, + this.historyMetadata, + this.additionalHeaders, + }); +} + /// Details about the last playing media in this tab. class LastMediaAccessState { /// [TabContentState.url] when media started playing. @@ -1008,6 +1033,11 @@ abstract class GeckoTabsApi { required Map? additionalHeaders, }); + List addMultipleTabs({ + required List tabs, + required String? selectTabId, + }); + void removeAllTabs({required bool recoverable}); void removeTabs({required List ids}); @@ -1114,19 +1144,10 @@ abstract class GeckoPrefApi { } /// Type of ML model operation -enum MlProgressType { - downloading, - loadingFromCache, - runningInference, -} +enum MlProgressType { downloading, loadingFromCache, runningInference } /// Status of the ML operation -enum MlProgressStatus { - initiate, - sizeEstimate, - inProgress, - done, -} +enum MlProgressStatus { initiate, sizeEstimate, inProgress, done } /// Progress information for ML model operations class MlProgressData { @@ -1392,6 +1413,9 @@ abstract class GeckoDeleteBrowsingDataController { void deleteSitePermissions(); @async void deleteDownloads(); + + @async + void clearDataForSessionContext(String contextId); } @HostApi()