intermediate prefs

This commit is contained in:
Fabian Freund
2025-01-30 10:46:38 +01:00
parent 78c17a70c5
commit 36acd46318
51 changed files with 3744 additions and 1719 deletions
+2 -1
View File
@@ -169,7 +169,8 @@
<service
android:name="eu.lensai.flutter_mozilla_components.services.DownloadService"
android:foregroundServiceType="dataSync" />
android:foregroundServiceType="dataSync"
android:exported="false" />
<service android:name="eu.lensai.flutter_mozilla_components.services.MediaSessionService"
android:foregroundServiceType="mediaPlayback"
+261
View File
@@ -0,0 +1,261 @@
{
"system": {
"incognito_mode": {
"preferences": {
"privacy.sanitize.sanitizeOnShutdown": true,
"privacy.clearOnShutdown.cache": true,
"privacy.clearOnShutdown_v2.cache": true,
"privacy.clearOnShutdown.downloads": true,
"privacy.clearOnShutdown.formdata": true,
"privacy.clearOnShutdown.history": true,
"privacy.clearOnShutdown_v2.historyFormDataAndDownloads": true,
"privacy.clearOnShutdown.cookies": true,
"privacy.clearOnShutdown.offlineApps": true,
"privacy.clearOnShutdown.sessions": true,
"privacy.clearOnShutdown_v2.cookiesAndStorage": true,
"privacy.clearSiteData.cache": true,
"privacy.clearSiteData.cookiesAndStorage": false,
"privacy.clearSiteData.historyFormDataAndDownloads": true,
"privacy.cpd.cache": true,
"privacy.clearHistory.cache": true,
"privacy.cpd.formdata": true,
"privacy.cpd.history": true,
"privacy.clearHistory.historyFormDataAndDownloads": true,
"privacy.cpd.cookies": false,
"privacy.cpd.sessions": true,
"privacy.cpd.offlineApps": false,
"privacy.clearHistory.cookiesAndStorage": false,
"privacy.sanitize.timeSpan": 0
}
}
},
"user": {
"Safe Browsing": {
"description": "Google Safe Browsing is a service from Google that warns users when they attempt to navigate to a dangerous website or download dangerous files.",
"preferences": {
"browser.safebrowsing.downloads.remote.enabled": {
"value": false,
"title": "Disable Google Safe Browsing for downloads",
"description": "To verify the safety of certain executable files, GeckoView may submit some information about the file, including the name, origin, size and a cryptographic hash of the contents, to the Google Safe Browsing service which helps GeckoView determine whether or not the file should be blocked."
}
}
},
"Block Implicit Outbound": {
"description": "Control not explicitly user invoked interactions",
"preferences": {
"network.prefetch-next": {
"value": false,
"title": "Disable link prefetching"
},
"network.dns.disablePrefetch": {
"value": true,
"title": "Disable DNS prefetching"
},
"network.dns.disablePrefetchFromHTTPS": {
"value": true,
"title": "Disable HTTPS DNS prefetching"
},
"network.predictor.enabled": {
"value": false,
"title": "Disable prefetching predictor"
},
"network.predictor.enable-prefetch": {
"value": false,
"title": "Disable prefetching predictions"
},
"network.http.speculative-parallel-limit": {
"value": 0,
"title": "Disable link-mouseover opening connection to linked server"
}
}
},
"DNS": {
"description": "Improve DNS and Proxy/SOCKS security",
"preferences": {
"network.trr.mode": {
"value": 3,
"title": "Enable DNS over HTTPS",
"requireUserOptIn": true
},
"network.trr.uri": {
"value": "https://dns.quad9.net/dns-query",
"title": "Use quad9 for Trusted Recursive Resolver (TRR)",
"description": "Quad9 is a privacy-focused DNS resolver that provides malware blocking and security features.",
"requireUserOptIn": true
},
"network.trr.custom.uri": {
"value": "https://dns.quad9.net/dns-query",
"title": "Use quad9 for custom Trusted Recursive Resolver (TRR)",
"description": "Quad9 is a privacy-focused DNS resolver that provides malware blocking and security features.",
"requireUserOptIn": true
},
"network.dns.native_https_query": {
"value": true,
"title": "Enable native DNS HTTPS Query",
"description": "DNS over HTTPS",
"shouldBeDefault": true
},
"network.proxy.socks_remote_dns": {
"value": true,
"title": "Set the proxy server to do any DNS lookups when using SOCKS",
"description": "e.g. in Tor, this stops your local DNS server from knowing your Tor destination as a remote Tor node will handle the DNS request"
},
"network.file.disable_unc_paths": {
"value": true,
"title": "Disable using UNC (Uniform Naming Convention) paths",
"description": "Warning: Can break extensions for profiles on network shares",
"requireUserOptIn": true
},
"network.gio.supported-protocols": {
"value": "",
"title": "Disable GIO as a potential proxy bypass vector"
},
"media.peerconnection.ice.proxy_only_if_behind_proxy": {
"value": true,
"title": "Force WebRTC inside the proxy"
},
"media.peerconnection.ice.default_address_only": {
"value": true,
"title": "Force a single network interface for ICE candidates generation",
"description": "When using a system-wide proxy, it uses the proxy interface"
}
}
},
"Disk Avoidance": {
"description": "Control what data to persist to disk",
"preferences": {
"browser.cache.disk.enable": {
"value": false,
"title": "Disable disk cache",
"description": "Don't cache data to disk. Might impact performance negatively",
"requireUserOptIn": true
},
"browser.privatebrowsing.forceMediaMemoryCache": {
"value": true,
"title": "Set media cache in Private Browsing to in-memory"
},
"media.memory_cache_max_size": {
"value": 65536,
"title": "Increase in-memory media cache maximum size"
},
"browser.sessionstore.privacy_level": {
"value": 2,
"title": "Disable storing extra session data",
"description": "Extra session data such as form content, cookies and POST data"
}
}
},
"Secure Connections": {
"description": "SSL/TLS / OCSP / CERTS / HPKP",
"preferences": {
"security.ssl.require_safe_negotiation": {
"value": true,
"title": "Require safe negotiation",
"description": "Blocks connections to servers that don't support RFC 5746 as they're potentially vulnerable to a MiTM attack."
},
"security.tls.enable_0rtt_data": {
"value": false,
"title": "Disable TLS1.3 0-RTT (round-trip time)",
"description": "This data is not forward secret, as it is encrypted solely under keys derived using the offered PSK. There are no guarantees of non-replay between connections."
},
"security.OCSP.enabled": {
"value": 1,
"title": "Enforce OCSP fetching to confirm current validity of certificates"
},
"security.OCSP.require": {
"value": true,
"title": "Set OCSP fetch failures to hard-fail",
"description": "When a CA cannot be reached to validate a cert, GeckoView just continues the connection (=soft-fail). Setting this pref to true tells GeckoView to instead terminate the connection (=hard-fail)."
},
"security.cert_pinning.enforcement_level": {
"value": 2,
"title": "Enable strict PKP (Public Key Pinning)"
},
"security.remote_settings.crlite_filters.enabled": {
"value": true,
"title": "Enable CRLite"
},
"security.pki.crlite_mode": {
"value": 2,
"title": "Enable CRLite for PKI"
},
"security.tls.enable_kyber": {
"value": true,
"title": "Enable Post-Quantum Kyber TLS"
},
"network.http.http3.enable_kyber": {
"value": true,
"title": "Enable HTTP3 Post-Quantum Kyber TLS"
},
"security.ssl.treat_unsafe_negotiation_as_broken": {
"value": true,
"title": "Display warning on the padlock for 'broken security'"
},
"browser.xul.error_pages.expert_bad_cert": {
"value": true,
"title": "Display advanced information on Insecure Connection warning pages"
},
"dom.security.https_first": {
"value": true,
"title": "Attempt to establish HTTPS connections first before falling back to HTTP",
"shouldBeDefault": true
}
}
},
"Miscelaneous": {
"preferences": {
"permissions.manager.defaultsUrl": {
"value": "",
"title": "Remove special permissions for certain mozilla domains"
},
"network.IDN_show_punycode": {
"value": true,
"title": "Use Punycode in Internationalized Domain Names to eliminate possible spoofing"
},
"extensions.enabledScopes": {
"value": 5,
"title": "Limit allowed extension directories",
"description": "Only allow profile and application directories",
"shouldBeDefault": true
},
"extensions.postDownloadThirdPartyPrompt": {
"value": false,
"title": "Disable bypassing 3rd party extension install prompts"
},
"pdfjs.enableScripting": {
"value": false,
"title": "Disable PDFJS scripting"
}
}
},
"Privacy": {
"preferences": {
"privacy.globalprivacycontrol.enabled": {
"value": true,
"title": "Enable Global Privacy Control (GPC)",
"shouldBeDefault": true
},
"browser.contentblocking.category": {
"value": "strict",
"title": "Enable ETP Strict Mode",
"description": "ETP Strict Mode enables Total Cookie Protection (TCP)"
},
"network.http.referer.XOriginTrimmingPolicy": {
"value": 2,
"title": "Trim cross-origin referrers",
"description": "Trims down referrers to just the scheme, hostname and port"
},
"general.useragent.updates.enabled": {
"value": false,
"title": "Disable automatic User Agent updates",
"description": "This preference controls whether Firefox automatically updates its User-Agent string. When set to false, it prevents Firefox from automatically updating the browser's User-Agent string, which is a text identifier that tells websites which browser and version you're using."
},
"dom.private-attribution.submission.enabled": {
"value": false,
"title": "Disable Private Attribution Submission Control",
"shouldBeDefault": true
}
}
}
}
}
+2 -4
View File
@@ -1,5 +1,3 @@
import 'dart:io';
import 'package:langchain_openai/langchain_openai.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -18,7 +16,7 @@ part 'models.g.dart';
@Riverpod(keepAlive: true)
ChatOpenAI chatModel(Ref ref) {
final openaiApiKey = 'FfTBKW1t5dOOXVoiaF2hbXPAHiz3fvfy';
const openaiApiKey = 'FfTBKW1t5dOOXVoiaF2hbXPAHiz3fvfy';
return ChatOpenAI(
apiKey: openaiApiKey,
baseUrl: 'https://api.deepinfra.com/v1/openai',
@@ -30,7 +28,7 @@ ChatOpenAI chatModel(Ref ref) {
@Riverpod(keepAlive: true)
OpenAIEmbeddings embeddingModel(Ref ref) {
final openaiApiKey = 'FfTBKW1t5dOOXVoiaF2hbXPAHiz3fvfy';
const openaiApiKey = 'FfTBKW1t5dOOXVoiaF2hbXPAHiz3fvfy';
return OpenAIEmbeddings(
apiKey: openaiApiKey,
baseUrl: 'https://api.deepinfra.com/v1/openai',
+2 -2
View File
@@ -6,7 +6,7 @@ part of 'models.dart';
// RiverpodGenerator
// **************************************************************************
String _$chatModelHash() => r'e7538b9f0dd43ea64953164c5402e76fde96f5e9';
String _$chatModelHash() => r'9e44e8fa1d688968841933aac8e13212cf41f4ed';
/// See also [chatModel].
@ProviderFor(chatModel)
@@ -22,7 +22,7 @@ final chatModelProvider = Provider<ChatOpenAI>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef ChatModelRef = ProviderRef<ChatOpenAI>;
String _$embeddingModelHash() => r'5d65c75df195a26adefac5a75f0e8f6b0f1b8590';
String _$embeddingModelHash() => r'40111a5f08b43afe6edd399ff0e6c022b3387265';
/// See also [embeddingModel].
@ProviderFor(embeddingModel)
+17
View File
@@ -1,5 +1,6 @@
import 'package:go_router/go_router.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/data/models/drag_data.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -12,3 +13,19 @@ GoRouter router(Ref ref) {
routes: $appRoutes,
);
}
@Riverpod()
class WillAcceptDrop extends _$WillAcceptDrop {
void setData(DropTargetData data) {
state = data;
}
void clear() {
state = null;
}
@override
DropTargetData? build() {
return null;
}
}
+16
View File
@@ -22,5 +22,21 @@ final routerProvider = Provider<GoRouter>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef RouterRef = ProviderRef<GoRouter>;
String _$willAcceptDropHash() => r'97b47784ef9101757602b4408d1f545ef2308830';
/// See also [WillAcceptDrop].
@ProviderFor(WillAcceptDrop)
final willAcceptDropProvider =
AutoDisposeNotifierProvider<WillAcceptDrop, DropTargetData?>.internal(
WillAcceptDrop.new,
name: r'willAcceptDropProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$willAcceptDropHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$WillAcceptDrop = AutoDisposeNotifier<DropTargetData?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
+32
View File
@@ -13,6 +13,8 @@ import 'package:lensai/features/geckoview/features/browser/presentation/dialogs/
import 'package:lensai/features/geckoview/features/browser/presentation/screens/browser.dart';
import 'package:lensai/features/geckoview/features/tabs/presentation/screens/container_list.dart';
import 'package:lensai/features/search/presentation/screens/search.dart';
import 'package:lensai/features/settings/presentation/screens/browser_hardening.dart';
import 'package:lensai/features/settings/presentation/screens/browser_hardening_group.dart';
import 'package:lensai/features/settings/presentation/screens/settings.dart';
import 'package:lensai/features/user/presentation/screens/auth.dart';
@@ -180,6 +182,18 @@ class ContainerListRoute extends GoRouteData {
@TypedGoRoute<SettingsRoute>(
name: 'SettingsRoute',
path: '/settings',
routes: [
TypedGoRoute<BrowserHardeningRoute>(
name: 'BrowserHardeningRoute',
path: 'hardening',
routes: [
TypedGoRoute<BrowserHardeningGroupRoute>(
name: 'BrowserHardeningGroupRoute',
path: 'group/:group',
),
],
),
],
)
class SettingsRoute extends GoRouteData {
@override
@@ -188,6 +202,24 @@ class SettingsRoute extends GoRouteData {
}
}
class BrowserHardeningRoute extends GoRouteData {
@override
Widget build(BuildContext context, GoRouterState state) {
return const BrowserHardeningScreen();
}
}
class BrowserHardeningGroupRoute extends GoRouteData {
final String group;
const BrowserHardeningGroupRoute({required this.group});
@override
Widget build(BuildContext context, GoRouterState state) {
return BrowserHardeningGroupScreen(groupName: group);
}
}
@TypedGoRoute<ChatArchiveListRoute>(
name: 'ChatArchiveListRoute',
path: '/chat_archive',
+52
View File
@@ -259,6 +259,20 @@ RouteBase get $settingsRoute => GoRouteData.$route(
path: '/settings',
name: 'SettingsRoute',
factory: $SettingsRouteExtension._fromState,
routes: [
GoRouteData.$route(
path: 'hardening',
name: 'BrowserHardeningRoute',
factory: $BrowserHardeningRouteExtension._fromState,
routes: [
GoRouteData.$route(
path: 'group/:group',
name: 'BrowserHardeningGroupRoute',
factory: $BrowserHardeningGroupRouteExtension._fromState,
),
],
),
],
);
extension $SettingsRouteExtension on SettingsRoute {
@@ -278,6 +292,44 @@ extension $SettingsRouteExtension on SettingsRoute {
void replace(BuildContext context) => context.replace(location);
}
extension $BrowserHardeningRouteExtension on BrowserHardeningRoute {
static BrowserHardeningRoute _fromState(GoRouterState state) =>
BrowserHardeningRoute();
String get location => GoRouteData.$location(
'/settings/hardening',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $BrowserHardeningGroupRouteExtension on BrowserHardeningGroupRoute {
static BrowserHardeningGroupRoute _fromState(GoRouterState state) =>
BrowserHardeningGroupRoute(
group: state.pathParameters['group']!,
);
String get location => GoRouteData.$location(
'/settings/hardening/group/${Uri.encodeComponent(group)}',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $chatArchiveListRoute => GoRouteData.$route(
path: '/chat_archive',
name: 'ChatArchiveListRoute',
+41
View File
@@ -0,0 +1,41 @@
import 'package:fast_equatable/fast_equatable.dart';
sealed class DropTargetData with FastEquatable {}
final class ContainerDropData extends DropTargetData {
final String tabId;
ContainerDropData(this.tabId);
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [tabId];
}
final class DeleteDropData extends DropTargetData {
final String tabId;
DeleteDropData(this.tabId);
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [tabId];
}
sealed class DragTargetData with FastEquatable {}
final class TabDragData extends DragTargetData {
final String tabId;
TabDragData(this.tabId);
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [tabId];
}
@@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/bangs/data/models/bang_data.dart';
import 'package:lensai/features/bangs/domain/providers/search.dart';
import 'package:lensai/features/bangs/presentation/widgets/bang_icon.dart';
import 'package:lensai/features/geckoview/domain/controllers/overlay_dialog.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/providers.dart';
import 'package:lensai/features/search/domain/providers/search_suggestions.dart';
@@ -1,4 +1,3 @@
import 'package:collection/collection.dart';
import 'package:exceptions/exceptions.dart';
import 'package:flutter_chat_core/flutter_chat_core.dart';
import 'package:langchain/langchain.dart';
@@ -4,7 +4,9 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.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:lensai/core/providers.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/data/models/drag_data.dart';
import 'package:lensai/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:lensai/features/geckoview/domain/controllers/overlay_dialog.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
@@ -16,7 +18,6 @@ import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/browser/domain/services/create_tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/services/engine_settings.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/dialogs/web_page_dialog.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/app_bar_title.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/browser_view.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
@@ -164,7 +165,8 @@ class BrowserScreen extends HookConsumerWidget {
// preferredTool: KagiTool.search,
// ),
// );
await context.push(SearchRoute().location);
await context
.push(const SearchRoute().location);
},
icon: Icon(KagiTool.search.icon),
),
@@ -588,13 +590,19 @@ class BrowserScreen extends HookConsumerWidget {
),
MenuItemButton(
onPressed: () async {
final x =
await context.push(UserAuthRoute().location);
print(x);
},
leadingIcon: const Icon(Icons.info),
child: const Text('Auth'),
),
MenuItemButton(
onPressed: () async {
await context
.push(BrowserHardeningRoute().location);
},
leadingIcon: const Icon(Icons.info),
child: const Text('Pref'),
),
],
),
],
@@ -603,7 +611,22 @@ class BrowserScreen extends HookConsumerWidget {
);
},
),
body: OverlayPortal(
body: DragTarget<TabDragData>(
onMove: (details) {
ref
.read(willAcceptDropProvider.notifier)
.setData(DeleteDropData(details.data.tabId));
},
onLeave: (data) {
ref.read(willAcceptDropProvider.notifier).clear();
},
onAcceptWithDetails: (details) async {
await ref
.read(tabRepositoryProvider.notifier)
.closeTab(details.data.tabId);
},
builder: (context, _, __) {
return OverlayPortal(
controller: overlayController,
overlayChildBuilder: (context) {
return displayedOverlayDialog!;
@@ -611,7 +634,9 @@ class BrowserScreen extends HookConsumerWidget {
child: Listener(
onPointerDown: (displayedSheet != null)
? (_) {
ref.read(bottomSheetControllerProvider.notifier).dismiss();
ref
.read(bottomSheetControllerProvider.notifier)
.dismiss();
}
: null,
child: HookConsumer(
@@ -622,8 +647,8 @@ class BrowserScreen extends HookConsumerWidget {
? ref.read(tabStateProvider(selectedTabId))
: null;
final tabCount =
ref.read(tabListProvider.select((tabs) => tabs.length));
final tabCount = ref.read(
tabListProvider.select((tabs) => tabs.length));
//Don't do anything if a child route is active
if (GoRouterState.of(context).topRoute?.path !=
@@ -664,7 +689,8 @@ class BrowserScreen extends HookConsumerWidget {
}
if (lastBackButtonPress.value != null &&
DateTime.now().difference(lastBackButtonPress.value!) <
DateTime.now()
.difference(lastBackButtonPress.value!) <
const Duration(seconds: 2)) {
lastBackButtonPress.value = null;
@@ -757,7 +783,8 @@ class BrowserScreen extends HookConsumerWidget {
),
if (displayedSheet != null)
ModalBarrier(
color: Theme.of(context).dialogTheme.barrierColor ??
color:
Theme.of(context).dialogTheme.barrierColor ??
Colors.black54,
),
],
@@ -766,6 +793,8 @@ class BrowserScreen extends HookConsumerWidget {
},
),
),
);
},
),
floatingActionButton: ReaderAppearanceButton(),
bottomSheet: (displayedSheet != null)
@@ -4,7 +4,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/kagi/presentation/tabs/search_tab.dart';
import 'package:lensai/features/kagi/presentation/tabs/summarize_tab.dart';
import 'package:lensai/features/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/presentation/hooks/sync_page_tab.dart';
@@ -3,24 +3,74 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_reorderable_grid_view/widgets/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/providers.dart';
import 'package:lensai/data/models/drag_data.dart';
import 'package:lensai/data/models/equatable_iterable.dart';
import 'package:lensai/features/geckoview/domain/controllers/overlay_dialog.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/providers.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/dialogs/tab_action.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
import 'package:lensai/presentation/widgets/speech_to_text_button.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/tab_preview.dart';
import 'package:lensai/features/geckoview/domain/controllers/overlay_dialog.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/providers/selected_container.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab_search.dart';
import 'package:lensai/features/geckoview/features/tabs/presentation/widgets/container_chips.dart';
import 'package:lensai/presentation/hooks/listenable_callback.dart';
import 'package:reorderable_grid/reorderable_grid.dart';
import 'package:lensai/presentation/widgets/speech_to_text_button.dart';
class _TabDraggable extends HookConsumerWidget {
final String tabId;
final String? activeTabId;
final void Function() onClose;
_TabDraggable({
required this.tabId,
required this.activeTabId,
required this.onClose,
}) : super(key: ValueKey(tabId));
@override
Widget build(BuildContext context, WidgetRef ref) {
final tab = ref.watch(tabStateProvider(tabId));
if (tab == null) {
return const SizedBox.shrink();
}
return TabPreview(
tab: tab,
isActive: tabId == activeTabId,
onTap: () async {
if (tabId != activeTabId) {
//Close first to avoid rebuilds
onClose();
await ref.read(tabRepositoryProvider.notifier).selectTab(tab.id);
} else {
onClose();
}
},
onDoubleTap: () {
ref.read(overlayDialogControllerProvider.notifier).show(
TabActionDialog(
initialTab: tab,
onDismiss:
ref.read(overlayDialogControllerProvider.notifier).dismiss,
),
);
},
onDelete: () async {
await ref.read(tabRepositoryProvider.notifier).closeTab(tab.id);
},
);
}
}
class _Tab extends HookConsumerWidget {
static const headerSize = 124.0;
@@ -194,10 +244,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
child: _Tab(onClose: onClose),
),
Expanded(
child: CustomScrollView(
controller: sheetScrollController,
slivers: [
HookConsumer(
child: HookConsumer(
builder: (context, ref, child) {
final container = ref.watch(selectedContainerProvider);
@@ -252,60 +299,50 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
() {
return filteredTabs
.mapIndexed(
(index, tabId) =>
ReorderableGridDelayedDragStartListener(
key: ValueKey(tabId),
index: index,
(index, tabId) => CustomDraggable(
key: Key(tabId),
data: TabDragData(tabId),
child: Consumer(
builder: (context, ref, child) {
final tab =
ref.watch(tabStateProvider(tabId));
return (tab != null)
? TabPreview(
tab: tab,
isActive: tabId == activeTab,
onTap: () async {
if (tabId != activeTab) {
//Close first to avoid rebuilds
onClose();
await ref
.read(
tabRepositoryProvider
.notifier,
)
.selectTab(tab.id);
} else {
onClose();
}
},
onDoubleTap: () {
ref
.read(
overlayDialogControllerProvider
.notifier,
)
.show(
TabActionDialog(
initialTab: tab,
onDismiss: ref
.read(
overlayDialogControllerProvider
.notifier,
)
.dismiss,
child: _TabDraggable(
tabId: tabId,
activeTabId: activeTab,
onClose: onClose,
),
builder: (context, ref, child) {
final dragData = ref.watch(
willAcceptDropProvider.select((value) {
final dragTabId = switch (value) {
ContainerDropData() => value.tabId,
DeleteDropData() => value.tabId,
null => null,
};
return (dragTabId == tabId)
? value
: null;
}),
);
},
onDelete: () async {
await ref
.read(
tabRepositoryProvider
.notifier,
)
.closeTab(tab.id);
},
)
: const SizedBox.shrink();
return switch (dragData) {
ContainerDropData() => Opacity(
opacity: 0.3,
child: Transform.scale(
scale: 0.9,
child: child,
),
),
DeleteDropData() => Opacity(
opacity: 0.3,
child: ColorFiltered(
colorFilter: const ColorFilter.mode(
Colors.red,
BlendMode.modulate,
),
child: child,
),
),
null => child!,
};
},
),
),
@@ -318,20 +355,23 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
],
);
return SliverPadding(
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
sliver: SliverReorderableGrid(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
//Sync values for itemHeight calculation _calculateItemHeight
childAspectRatio: 0.75,
mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0,
crossAxisCount: 2,
),
itemCount: tabs.length,
itemBuilder: (context, index) => tabs[index],
onReorder: (oldIndex, newIndex) async {
child: ReorderableBuilder(
scrollController: sheetScrollController,
children: tabs,
onDragStarted: (index) {
ref.read(willAcceptDropProvider.notifier).clear();
},
onReorderPositions: (positions) async {
assert(
positions.length == 1,
'Not ready for multiple reorders',
);
final oldIndex = positions.first.oldIndex;
final newIndex = positions.first.newIndex;
final containerRepository =
ref.read(containerRepositoryProvider.notifier);
@@ -349,8 +389,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
.getTrailingOrderKey(containerId);
} else {
final orderAfterIndex = newIndex;
key =
await containerRepository.getOrderKeyAfterTab(
key = await containerRepository.getOrderKeyAfterTab(
filteredTabs[orderAfterIndex],
containerId,
);
@@ -360,11 +399,24 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
.read(tabDataRepositoryProvider.notifier)
.assignOrderKey(tabId, key);
},
builder: (children) {
return GridView.builder(
controller: sheetScrollController,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
//Sync values for itemHeight calculation _calculateItemHeight
childAspectRatio: 0.75,
mainAxisSpacing: 8.0,
crossAxisSpacing: 8.0,
crossAxisCount: 2,
),
itemCount: children.length,
itemBuilder: (context, index) => children[index],
);
},
),
],
);
},
),
),
],
@@ -376,13 +428,14 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
),
child: FloatingActionButton.small(
onPressed: () async {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: Uri.https('kagi.com'));
await ref.read(tabRepositoryProvider.notifier).addTab(
url: Uri.https('setcookie.net'),
// contextId: uuid.v4(),
);
onClose();
},
child: Icon(Icons.add),
child: const Icon(Icons.add),
),
),
],
@@ -0,0 +1,75 @@
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:fast_equatable/fast_equatable.dart';
import 'package:json_annotation/json_annotation.dart';
part 'preference_setting.g.dart';
@CopyWith()
class PreferenceSettingGroup with FastEquatable {
final String? description;
final Map<String, PreferenceSetting> settings;
bool get isActive => settings.values
.every((setting) => setting.requireUserOptIn || setting.isActive);
bool get isPartlyActive => settings.values.any((setting) => setting.isActive);
PreferenceSettingGroup({
required this.description,
required this.settings,
});
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [
description,
settings,
];
}
@JsonSerializable()
@CopyWith()
class PreferenceSetting with FastEquatable {
final Object value;
@JsonKey(includeFromJson: false, includeToJson: false)
final Object? actualValue;
bool get isActive => value == actualValue;
final String title;
final String? description;
final bool requireUserOptIn;
final bool shouldBeDefault;
PreferenceSetting({
required this.value,
required this.title,
required this.description,
this.actualValue,
this.requireUserOptIn = false,
this.shouldBeDefault = false,
});
factory PreferenceSetting.fromJson(Map<String, dynamic> json) =>
_$PreferenceSettingFromJson(json);
Map<String, dynamic> toJson() => _$PreferenceSettingToJson(this);
@override
bool get cacheHash => true;
@override
List<Object?> get hashParameters => [
value,
actualValue,
title,
description,
requireUserOptIn,
shouldBeDefault,
];
}
@@ -0,0 +1,202 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'preference_setting.dart';
// **************************************************************************
// CopyWithGenerator
// **************************************************************************
abstract class _$PreferenceSettingGroupCWProxy {
PreferenceSettingGroup description(String? description);
PreferenceSettingGroup settings(Map<String, PreferenceSetting> settings);
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `PreferenceSettingGroup(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// PreferenceSettingGroup(...).copyWith(id: 12, name: "My name")
/// ````
PreferenceSettingGroup call({
String? description,
Map<String, PreferenceSetting> settings,
});
}
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfPreferenceSettingGroup.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfPreferenceSettingGroup.copyWith.fieldName(...)`
class _$PreferenceSettingGroupCWProxyImpl
implements _$PreferenceSettingGroupCWProxy {
const _$PreferenceSettingGroupCWProxyImpl(this._value);
final PreferenceSettingGroup _value;
@override
PreferenceSettingGroup description(String? description) =>
this(description: description);
@override
PreferenceSettingGroup settings(Map<String, PreferenceSetting> settings) =>
this(settings: settings);
@override
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `PreferenceSettingGroup(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// PreferenceSettingGroup(...).copyWith(id: 12, name: "My name")
/// ````
PreferenceSettingGroup call({
Object? description = const $CopyWithPlaceholder(),
Object? settings = const $CopyWithPlaceholder(),
}) {
return PreferenceSettingGroup(
description: description == const $CopyWithPlaceholder()
? _value.description
// ignore: cast_nullable_to_non_nullable
: description as String?,
settings: settings == const $CopyWithPlaceholder()
? _value.settings
// ignore: cast_nullable_to_non_nullable
: settings as Map<String, PreferenceSetting>,
);
}
}
extension $PreferenceSettingGroupCopyWith on PreferenceSettingGroup {
/// Returns a callable class that can be used as follows: `instanceOfPreferenceSettingGroup.copyWith(...)` or like so:`instanceOfPreferenceSettingGroup.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$PreferenceSettingGroupCWProxy get copyWith =>
_$PreferenceSettingGroupCWProxyImpl(this);
}
abstract class _$PreferenceSettingCWProxy {
PreferenceSetting value(Object value);
PreferenceSetting title(String title);
PreferenceSetting description(String? description);
PreferenceSetting actualValue(Object? actualValue);
PreferenceSetting requireUserOptIn(bool requireUserOptIn);
PreferenceSetting shouldBeDefault(bool shouldBeDefault);
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `PreferenceSetting(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// PreferenceSetting(...).copyWith(id: 12, name: "My name")
/// ````
PreferenceSetting call({
Object value,
String title,
String? description,
Object? actualValue,
bool requireUserOptIn,
bool shouldBeDefault,
});
}
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfPreferenceSetting.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfPreferenceSetting.copyWith.fieldName(...)`
class _$PreferenceSettingCWProxyImpl implements _$PreferenceSettingCWProxy {
const _$PreferenceSettingCWProxyImpl(this._value);
final PreferenceSetting _value;
@override
PreferenceSetting value(Object value) => this(value: value);
@override
PreferenceSetting title(String title) => this(title: title);
@override
PreferenceSetting description(String? description) =>
this(description: description);
@override
PreferenceSetting actualValue(Object? actualValue) =>
this(actualValue: actualValue);
@override
PreferenceSetting requireUserOptIn(bool requireUserOptIn) =>
this(requireUserOptIn: requireUserOptIn);
@override
PreferenceSetting shouldBeDefault(bool shouldBeDefault) =>
this(shouldBeDefault: shouldBeDefault);
@override
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `PreferenceSetting(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// PreferenceSetting(...).copyWith(id: 12, name: "My name")
/// ````
PreferenceSetting call({
Object? value = const $CopyWithPlaceholder(),
Object? title = const $CopyWithPlaceholder(),
Object? description = const $CopyWithPlaceholder(),
Object? actualValue = const $CopyWithPlaceholder(),
Object? requireUserOptIn = const $CopyWithPlaceholder(),
Object? shouldBeDefault = const $CopyWithPlaceholder(),
}) {
return PreferenceSetting(
value: value == const $CopyWithPlaceholder()
? _value.value
// ignore: cast_nullable_to_non_nullable
: value as Object,
title: title == const $CopyWithPlaceholder()
? _value.title
// ignore: cast_nullable_to_non_nullable
: title as String,
description: description == const $CopyWithPlaceholder()
? _value.description
// ignore: cast_nullable_to_non_nullable
: description as String?,
actualValue: actualValue == const $CopyWithPlaceholder()
? _value.actualValue
// ignore: cast_nullable_to_non_nullable
: actualValue as Object?,
requireUserOptIn: requireUserOptIn == const $CopyWithPlaceholder()
? _value.requireUserOptIn
// ignore: cast_nullable_to_non_nullable
: requireUserOptIn as bool,
shouldBeDefault: shouldBeDefault == const $CopyWithPlaceholder()
? _value.shouldBeDefault
// ignore: cast_nullable_to_non_nullable
: shouldBeDefault as bool,
);
}
}
extension $PreferenceSettingCopyWith on PreferenceSetting {
/// Returns a callable class that can be used as follows: `instanceOfPreferenceSetting.copyWith(...)` or like so:`instanceOfPreferenceSetting.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$PreferenceSettingCWProxy get copyWith =>
_$PreferenceSettingCWProxyImpl(this);
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
PreferenceSetting _$PreferenceSettingFromJson(Map<String, dynamic> json) =>
PreferenceSetting(
value: json['value'] as Object,
title: json['title'] as String,
description: json['description'] as String?,
requireUserOptIn: json['requireUserOptIn'] as bool? ?? false,
shouldBeDefault: json['shouldBeDefault'] as bool? ?? false,
);
Map<String, dynamic> _$PreferenceSettingToJson(PreferenceSetting instance) =>
<String, dynamic>{
'value': instance.value,
'title': instance.title,
'description': instance.description,
'requireUserOptIn': instance.requireUserOptIn,
'shouldBeDefault': instance.shouldBeDefault,
};
@@ -0,0 +1,193 @@
import 'dart:async';
import 'dart:convert';
import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:lensai/features/geckoview/features/preferences/data/models/preference_setting.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
part 'preference_settings.g.dart';
@Riverpod(keepAlive: true)
Future<Map<String, PreferenceSettingGroup>> _preferenceSettingGroups(
Ref ref,
) async {
final content = await rootBundle
.loadString('assets/preferences/settings.json')
.then(jsonDecode) as Map<String, dynamic>;
final userContent = content['user'] as Map<String, dynamic>;
return userContent.map(
(key, value) => MapEntry(
key,
PreferenceSettingGroup(
// ignore: avoid_dynamic_calls
description: value['description'] as String?,
// ignore: avoid_dynamic_calls
settings: (value['preferences'] as Map<String, dynamic>).map(
(key, value) => MapEntry(
key,
PreferenceSetting.fromJson(value as Map<String, dynamic>),
),
),
),
),
);
}
@Riverpod(keepAlive: true)
Future<PreferenceSettingGroup> _preferenceSettingGroup(
Ref ref,
String groupName,
) {
return ref.watch(
_preferenceSettingGroupsProvider.selectAsync(
(groups) =>
groups[groupName] ?? (throw Exception('Unknown setting group')),
),
);
}
@Riverpod()
class _PreferenceRepository extends _$PreferenceRepository {
final _prefManager = GeckoPrefService();
// Use BehaviorSubject instead of StreamController
late BehaviorSubject<Map<String, Object>> _prefSubject;
Future<void> _updatePrefs() async {
final prefs = await _prefManager.getAllPrefs();
_prefSubject.add(prefs);
}
Future<void> applyPrefs(Map<String, Object> prefs) async {
await _prefManager.applyPrefs(prefs);
await _updatePrefs();
}
Future<void> resetPrefs(List<String> prefNames) async {
await _prefManager.resetPrefs(prefNames);
await _updatePrefs();
}
@override
Raw<Stream<Map<String, Object>>> build() {
_prefSubject = BehaviorSubject<Map<String, Object>>();
ref.onDispose(() {
unawaited(_prefSubject.close());
});
ref.onAddListener(() async {
await _updatePrefs();
});
return _prefSubject.stream;
}
}
@Riverpod()
class PreferenceSettingsGeneralRepository
extends _$PreferenceSettingsGeneralRepository {
late Map<String, PreferenceSettingGroup> _statelessGroups;
Future<void> apply() async {
final prefs = {
for (final group in _statelessGroups.values)
...Map.fromEntries(
group.settings.entries
.where((e) => !e.value.requireUserOptIn)
.map((e) => MapEntry(e.key, e.value.value)),
),
};
await ref.read(_preferenceRepositoryProvider.notifier).applyPrefs(prefs);
}
Future<void> reset() async {
final prefs = _statelessGroups.values
.map((group) => group.settings.keys)
.flattened
.toList();
await ref.read(_preferenceRepositoryProvider.notifier).resetPrefs(prefs);
}
@override
Stream<Map<String, PreferenceSettingGroup>> build() async* {
_statelessGroups = await ref.watch(_preferenceSettingGroupsProvider.future);
final prefStream = ref.watch(_preferenceRepositoryProvider);
yield* prefStream.map(
(prefs) => _statelessGroups.map(
(groupName, group) => MapEntry(
groupName,
group.copyWith.settings(
group.settings.map(
(prefName, setting) => MapEntry(
prefName,
setting.copyWith.actualValue(prefs[prefName]),
),
),
),
),
),
);
}
}
@Riverpod()
class PreferenceSettingsGroupRepository
extends _$PreferenceSettingsGroupRepository {
late PreferenceSettingGroup _statelessSettingGroup;
Future<void> apply({List<String>? filter}) async {
final prefs = Map.fromEntries(
filter?.map(
(e) => MapEntry(
e,
_statelessSettingGroup.settings[e]?.value ??
(throw Exception('Preference not part of group')),
),
) ??
_statelessSettingGroup.settings.entries
.where((e) => !e.value.requireUserOptIn)
.map((e) => MapEntry(e.key, e.value.value)),
);
await ref.read(_preferenceRepositoryProvider.notifier).applyPrefs(prefs);
}
Future<void> reset({List<String>? filter}) async {
if (filter != null &&
filter.any(
(value) => !_statelessSettingGroup.settings.containsKey(value),
)) {
throw Exception('Preference not part of group');
}
await ref
.read(_preferenceRepositoryProvider.notifier)
.resetPrefs(filter ?? _statelessSettingGroup.settings.keys.toList());
}
@override
Stream<PreferenceSettingGroup> build(String groupName) async* {
_statelessSettingGroup =
await ref.watch(_preferenceSettingGroupProvider(groupName).future);
final prefStream = ref.watch(_preferenceRepositoryProvider);
yield* prefStream.map(
(prefs) => _statelessSettingGroup.copyWith.settings(
_statelessSettingGroup.settings.map(
(key, value) => MapEntry(key, value.copyWith.actualValue(prefs[key])),
),
),
);
}
}
@@ -0,0 +1,378 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'preference_settings.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$preferenceSettingGroupsHash() =>
r'fa9286248cc71c88c888a518441721dc99b7e0c2';
/// See also [_preferenceSettingGroups].
@ProviderFor(_preferenceSettingGroups)
final _preferenceSettingGroupsProvider =
FutureProvider<Map<String, PreferenceSettingGroup>>.internal(
_preferenceSettingGroups,
name: r'_preferenceSettingGroupsProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$preferenceSettingGroupsHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef _PreferenceSettingGroupsRef
= FutureProviderRef<Map<String, PreferenceSettingGroup>>;
String _$preferenceSettingGroupHash() =>
r'960169ab9f6985a5b73a63663e671a8eac7cd776';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [_preferenceSettingGroup].
@ProviderFor(_preferenceSettingGroup)
const _preferenceSettingGroupProvider = _PreferenceSettingGroupFamily();
/// See also [_preferenceSettingGroup].
class _PreferenceSettingGroupFamily
extends Family<AsyncValue<PreferenceSettingGroup>> {
/// See also [_preferenceSettingGroup].
const _PreferenceSettingGroupFamily();
/// See also [_preferenceSettingGroup].
_PreferenceSettingGroupProvider call(
String groupName,
) {
return _PreferenceSettingGroupProvider(
groupName,
);
}
@override
_PreferenceSettingGroupProvider getProviderOverride(
covariant _PreferenceSettingGroupProvider provider,
) {
return call(
provider.groupName,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'_preferenceSettingGroupProvider';
}
/// See also [_preferenceSettingGroup].
class _PreferenceSettingGroupProvider
extends FutureProvider<PreferenceSettingGroup> {
/// See also [_preferenceSettingGroup].
_PreferenceSettingGroupProvider(
String groupName,
) : this._internal(
(ref) => _preferenceSettingGroup(
ref as _PreferenceSettingGroupRef,
groupName,
),
from: _preferenceSettingGroupProvider,
name: r'_preferenceSettingGroupProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$preferenceSettingGroupHash,
dependencies: _PreferenceSettingGroupFamily._dependencies,
allTransitiveDependencies:
_PreferenceSettingGroupFamily._allTransitiveDependencies,
groupName: groupName,
);
_PreferenceSettingGroupProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.groupName,
}) : super.internal();
final String groupName;
@override
Override overrideWith(
FutureOr<PreferenceSettingGroup> Function(
_PreferenceSettingGroupRef provider)
create,
) {
return ProviderOverride(
origin: this,
override: _PreferenceSettingGroupProvider._internal(
(ref) => create(ref as _PreferenceSettingGroupRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
groupName: groupName,
),
);
}
@override
FutureProviderElement<PreferenceSettingGroup> createElement() {
return _PreferenceSettingGroupProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is _PreferenceSettingGroupProvider &&
other.groupName == groupName;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, groupName.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin _PreferenceSettingGroupRef on FutureProviderRef<PreferenceSettingGroup> {
/// The parameter `groupName` of this provider.
String get groupName;
}
class _PreferenceSettingGroupProviderElement
extends FutureProviderElement<PreferenceSettingGroup>
with _PreferenceSettingGroupRef {
_PreferenceSettingGroupProviderElement(super.provider);
@override
String get groupName => (origin as _PreferenceSettingGroupProvider).groupName;
}
String _$preferenceRepositoryHash() =>
r'53c0cbbf60e44c3834fe3e3255836f7e27f09c7f';
/// See also [_PreferenceRepository].
@ProviderFor(_PreferenceRepository)
final _preferenceRepositoryProvider = AutoDisposeNotifierProvider<
_PreferenceRepository, Raw<Stream<Map<String, Object>>>>.internal(
_PreferenceRepository.new,
name: r'_preferenceRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$preferenceRepositoryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$PreferenceRepository
= AutoDisposeNotifier<Raw<Stream<Map<String, Object>>>>;
String _$preferenceSettingsGeneralRepositoryHash() =>
r'24a70de8284107bb43778e1014abe41bd805987a';
/// See also [PreferenceSettingsGeneralRepository].
@ProviderFor(PreferenceSettingsGeneralRepository)
final preferenceSettingsGeneralRepositoryProvider =
AutoDisposeStreamNotifierProvider<PreferenceSettingsGeneralRepository,
Map<String, PreferenceSettingGroup>>.internal(
PreferenceSettingsGeneralRepository.new,
name: r'preferenceSettingsGeneralRepositoryProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$preferenceSettingsGeneralRepositoryHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$PreferenceSettingsGeneralRepository
= AutoDisposeStreamNotifier<Map<String, PreferenceSettingGroup>>;
String _$preferenceSettingsGroupRepositoryHash() =>
r'023794294c5639ac6c6941f87a4ed91e1551fbc9';
abstract class _$PreferenceSettingsGroupRepository
extends BuildlessAutoDisposeStreamNotifier<PreferenceSettingGroup> {
late final String groupName;
Stream<PreferenceSettingGroup> build(
String groupName,
);
}
/// See also [PreferenceSettingsGroupRepository].
@ProviderFor(PreferenceSettingsGroupRepository)
const preferenceSettingsGroupRepositoryProvider =
PreferenceSettingsGroupRepositoryFamily();
/// See also [PreferenceSettingsGroupRepository].
class PreferenceSettingsGroupRepositoryFamily
extends Family<AsyncValue<PreferenceSettingGroup>> {
/// See also [PreferenceSettingsGroupRepository].
const PreferenceSettingsGroupRepositoryFamily();
/// See also [PreferenceSettingsGroupRepository].
PreferenceSettingsGroupRepositoryProvider call(
String groupName,
) {
return PreferenceSettingsGroupRepositoryProvider(
groupName,
);
}
@override
PreferenceSettingsGroupRepositoryProvider getProviderOverride(
covariant PreferenceSettingsGroupRepositoryProvider provider,
) {
return call(
provider.groupName,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'preferenceSettingsGroupRepositoryProvider';
}
/// See also [PreferenceSettingsGroupRepository].
class PreferenceSettingsGroupRepositoryProvider
extends AutoDisposeStreamNotifierProviderImpl<
PreferenceSettingsGroupRepository, PreferenceSettingGroup> {
/// See also [PreferenceSettingsGroupRepository].
PreferenceSettingsGroupRepositoryProvider(
String groupName,
) : this._internal(
() => PreferenceSettingsGroupRepository()..groupName = groupName,
from: preferenceSettingsGroupRepositoryProvider,
name: r'preferenceSettingsGroupRepositoryProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$preferenceSettingsGroupRepositoryHash,
dependencies: PreferenceSettingsGroupRepositoryFamily._dependencies,
allTransitiveDependencies: PreferenceSettingsGroupRepositoryFamily
._allTransitiveDependencies,
groupName: groupName,
);
PreferenceSettingsGroupRepositoryProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.groupName,
}) : super.internal();
final String groupName;
@override
Stream<PreferenceSettingGroup> runNotifierBuild(
covariant PreferenceSettingsGroupRepository notifier,
) {
return notifier.build(
groupName,
);
}
@override
Override overrideWith(PreferenceSettingsGroupRepository Function() create) {
return ProviderOverride(
origin: this,
override: PreferenceSettingsGroupRepositoryProvider._internal(
() => create()..groupName = groupName,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
groupName: groupName,
),
);
}
@override
AutoDisposeStreamNotifierProviderElement<PreferenceSettingsGroupRepository,
PreferenceSettingGroup> createElement() {
return _PreferenceSettingsGroupRepositoryProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is PreferenceSettingsGroupRepositoryProvider &&
other.groupName == groupName;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, groupName.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin PreferenceSettingsGroupRepositoryRef
on AutoDisposeStreamNotifierProviderRef<PreferenceSettingGroup> {
/// The parameter `groupName` of this provider.
String get groupName;
}
class _PreferenceSettingsGroupRepositoryProviderElement
extends AutoDisposeStreamNotifierProviderElement<
PreferenceSettingsGroupRepository,
PreferenceSettingGroup> with PreferenceSettingsGroupRepositoryRef {
_PreferenceSettingsGroupRepositoryProviderElement(super.provider);
@override
String get groupName =>
(origin as PreferenceSettingsGroupRepositoryProvider).groupName;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
@@ -9,9 +9,7 @@ import 'package:lensai/features/geckoview/features/tabs/data/models/tab_query_re
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/database/daos/vector.dart';
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/database/database.dart';
import 'package:lensai/features/geckoview/features/tabs/features/vector_store/data/models/vector_result.dart';
import 'package:lensai/features/search/domain/entities/abstract/i_query_builder.dart';
import 'package:lensai/features/search/domain/fts_tokenizer.dart';
import 'package:lensai/features/search/domain/unix_tokenizer.dart';
part 'database.g.dart';
@@ -2,9 +2,13 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/providers.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/data/models/drag_data.dart';
import 'package:lensai/features/geckoview/features/tabs/data/models/container_data.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/providers.dart';
import 'package:lensai/features/geckoview/features/tabs/domain/repositories/tab.dart';
import 'package:lensai/presentation/hooks/overlay_portal_controller.dart';
import 'package:lensai/presentation/widgets/selectable_chips.dart';
class ContainerChips extends HookConsumerWidget {
@@ -34,6 +38,8 @@ class ContainerChips extends HookConsumerWidget {
final containersAsync =
ref.watch(filteredContainersWithCountProvider(searchText));
final dragTargetTabId = useValueNotifier<String?>(null);
return containersAsync.when(
data: (availableContainers) {
if (selectedContainer == null &&
@@ -62,6 +68,74 @@ class ContainerChips extends HookConsumerWidget {
itemLabel: (container) =>
Text(container.name ?? 'New Container'),
itemBadgeCount: (container) => container.tabCount,
itemWrap: (child, container) {
return HookBuilder(
builder: (context) {
final overlayController =
useOverlayPortalController();
return DragTarget<TabDragData>(
onMove: (details) {
ref.read(willAcceptDropProvider.notifier).setData(
ContainerDropData(details.data.tabId),
);
dragTargetTabId.value = details.data.tabId;
overlayController.show();
},
onLeave: (data) {
ref.read(willAcceptDropProvider.notifier).clear();
dragTargetTabId.value = null;
overlayController.hide();
},
onAcceptWithDetails: (details) async {
await ref
.read(tabDataRepositoryProvider.notifier)
.assignContainer(
details.data.tabId,
container.id,
);
dragTargetTabId.value = null;
overlayController.hide();
},
builder: (context, candidateData, rejectedData) {
final renderBox =
context.findRenderObject() as RenderBox?;
final position =
renderBox?.localToGlobal(Offset.zero) ??
Offset.zero;
return OverlayPortal(
controller: overlayController,
overlayChildBuilder: (context) => Positioned(
top: position.dy,
left: position.dx,
child: IgnorePointer(
child: Transform.scale(
scale: 1.1,
child: child,
),
),
),
child: HookBuilder(
builder: (context) {
final dragTabId =
useValueListenable(dragTargetTabId);
return Opacity(
opacity: (dragTabId == null) ? 1.0 : 0.0,
child: child,
);
},
),
);
},
);
},
);
},
availableItems: availableContainers,
selectedItem: selectedContainer,
onSelected: onSelected,
@@ -1,4 +1,3 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
@@ -0,0 +1,110 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/data/models/equatable_iterable.dart';
import 'package:lensai/features/geckoview/features/preferences/data/repositories/preference_settings.dart';
import 'package:lensai/features/settings/presentation/widgets/hardening_group_icon.dart';
import 'package:lensai/presentation/widgets/failure_widget.dart';
class BrowserHardeningScreen extends HookConsumerWidget {
const BrowserHardeningScreen();
@override
Widget build(BuildContext context, WidgetRef ref) {
final preferenceGroups =
ref.watch(preferenceSettingsGeneralRepositoryProvider);
final allGroupsActive = useMemoized(
() =>
preferenceGroups.valueOrNull?.values.every(
(element) => element.isActive,
) ??
false,
[EquatableCollection(preferenceGroups.valueOrNull, immutable: true)],
);
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: const Text('Browser Hardening')),
body: preferenceGroups.when(
data: (data) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
color: theme.colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SwitchListTile(
value: allGroupsActive,
title: Text(
'Complete Hardening',
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
),
),
onChanged: (value) async {
final notifier = ref.read(
preferenceSettingsGeneralRepositoryProvider.notifier,
);
if (value) {
await notifier.apply();
} else {
await notifier.reset();
}
},
),
),
),
),
Expanded(
child: ListView(
children: data.entries.map(
(group) {
return Row(
children: [
Expanded(
child: ListTile(
title: Text(group.key),
subtitle: (group.value.description != null)
? Text(group.value.description!)
: null,
leading: HardeningGroupIcon(
isActive: group.value.isActive,
isPartlyActive: group.value.isPartlyActive,
),
trailing: const Icon(Icons.chevron_right),
onTap: () async {
await context.push(
BrowserHardeningGroupRoute(
group: group.key,
).location,
);
},
),
),
],
);
},
).toList(),
),
),
],
);
},
error: (error, stackTrace) => FailureWidget(
title: 'Could not load preference settings',
exception: error,
onRetry: () =>
ref.refresh(preferenceSettingsGeneralRepositoryProvider),
),
loading: () => const SizedBox.shrink(),
),
);
}
}
@@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/geckoview/features/preferences/data/repositories/preference_settings.dart';
import 'package:lensai/features/settings/presentation/widgets/hardening_group_icon.dart';
import 'package:lensai/presentation/widgets/failure_widget.dart';
class BrowserHardeningGroupScreen extends HookConsumerWidget {
final String groupName;
const BrowserHardeningGroupScreen({super.key, required this.groupName});
@override
Widget build(BuildContext context, WidgetRef ref) {
final settings =
ref.watch(preferenceSettingsGroupRepositoryProvider(groupName));
final theme = Theme.of(context);
return Scaffold(
appBar: AppBar(title: Text(groupName)),
body: settings.when(
data: (group) {
return Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
color: theme.colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SwitchListTile(
value: group.isActive,
title: Text(
groupName,
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
),
),
subtitle: (group.description != null)
? Text(
group.description!,
style: TextStyle(
color: theme.colorScheme.onPrimaryContainer,
),
)
: null,
onChanged: (value) async {
final notifier = ref.read(
preferenceSettingsGroupRepositoryProvider(groupName)
.notifier,
);
if (value) {
await notifier.apply();
} else {
await notifier.reset();
}
},
),
),
),
),
Expanded(
child: ListView(
children: group.settings.entries.map(
(setting) {
return Row(
children: [
if (!setting.value.shouldBeDefault ||
!setting.value.isActive)
Expanded(
child: SwitchListTile(
value: setting.value.isActive,
title: Text(setting.value.title),
subtitle: (setting.value.description != null)
? Text(setting.value.description!)
: null,
secondary: HardeningGroupIcon(
isActive: setting.value.isActive,
),
onChanged: (value) async {
final notifier = ref.read(
preferenceSettingsGroupRepositoryProvider(
groupName,
).notifier,
);
if (value) {
await notifier.apply(filter: [setting.key]);
} else {
await notifier.reset(filter: [setting.key]);
}
},
),
)
else
Expanded(
child: ListTile(
title: Text(setting.value.title),
subtitle: (setting.value.description != null)
? Text(setting.value.description!)
: null,
leading: HardeningGroupIcon(
isActive: setting.value.isActive,
),
trailing: Padding(
padding: const EdgeInsets.only(right: 18.0),
child: const Icon(Icons.check),
),
),
),
],
);
},
).toList(),
),
),
],
);
},
error: (error, stackTrace) => FailureWidget(
title: 'Could not load preference settings',
exception: error,
onRetry: () =>
ref.refresh(preferenceSettingsGroupRepositoryProvider(groupName)),
),
loading: () => const SizedBox.shrink(),
),
);
}
}
@@ -1,7 +1,9 @@
import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/features/bangs/data/models/bang.dart';
import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:lensai/features/settings/presentation/controllers/save_settings.dart';
@@ -50,6 +52,17 @@ class SettingsScreen extends HookConsumerWidget {
controller: controller,
children: [
_buildSection(theme, 'General'),
Card(
child: ListTile(
title: const Text('Browser Hardening'),
trailing: IconButton(
onPressed: () async {
await context.push(BrowserHardeningRoute().location);
},
icon: const Icon(Icons.chevron_right),
),
),
),
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8),
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
class HardeningGroupIcon extends StatelessWidget {
final bool isActive;
final bool isPartlyActive;
const HardeningGroupIcon({
super.key,
required this.isActive,
this.isPartlyActive = false,
});
@override
Widget build(BuildContext context) {
if (isActive) {
return Icon(
MdiIcons.shieldLockOutline,
color: Theme.of(context).colorScheme.secondary,
);
} else if (isPartlyActive) {
return Icon(
MdiIcons.shieldLockOpenOutline,
color: Theme.of(context).colorScheme.secondary,
);
}
return Icon(
MdiIcons.shieldOffOutline,
color: Theme.of(context).colorScheme.error,
);
}
}
@@ -0,0 +1,53 @@
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:json_annotation/json_annotation.dart';
part 'engine_settings.g.dart';
@CopyWith()
@JsonSerializable(includeIfNull: true, constructor: 'withDefaults')
class EngineSettings extends GeckoEngineSettings {
EngineSettings({
super.javascriptEnabled,
super.trackingProtectionPolicy,
super.httpsOnlyMode,
super.globalPrivacyControlEnabled,
super.preferredColorScheme,
super.cookieBannerHandlingMode,
super.cookieBannerHandlingModePrivateBrowsing,
super.cookieBannerHandlingGlobalRules,
super.cookieBannerHandlingGlobalRulesSubFrames,
super.webContentIsolationStrategy,
});
EngineSettings.withDefaults({
bool? javascriptEnabled,
TrackingProtectionPolicy? trackingProtectionPolicy,
HttpsOnlyMode? httpsOnlyMode,
bool? globalPrivacyControlEnabled,
ColorScheme? preferredColorScheme,
CookieBannerHandlingMode? cookieBannerHandlingMode,
CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing,
bool? cookieBannerHandlingGlobalRules,
bool? cookieBannerHandlingGlobalRulesSubFrames,
WebContentIsolationStrategy? webContentIsolationStrategy,
}) : super(
javascriptEnabled: javascriptEnabled ?? true,
trackingProtectionPolicy:
trackingProtectionPolicy ?? TrackingProtectionPolicy.strict,
httpsOnlyMode: httpsOnlyMode ?? HttpsOnlyMode.enabled,
globalPrivacyControlEnabled: globalPrivacyControlEnabled ?? true,
preferredColorScheme: preferredColorScheme ?? ColorScheme.system,
cookieBannerHandlingMode:
cookieBannerHandlingMode ?? CookieBannerHandlingMode.rejectAll,
cookieBannerHandlingModePrivateBrowsing:
cookieBannerHandlingModePrivateBrowsing ??
CookieBannerHandlingMode.rejectAll,
cookieBannerHandlingGlobalRules:
cookieBannerHandlingGlobalRules ?? true,
cookieBannerHandlingGlobalRulesSubFrames:
cookieBannerHandlingGlobalRulesSubFrames ?? true,
webContentIsolationStrategy: webContentIsolationStrategy ??
WebContentIsolationStrategy.isolateHighValue,
);
}
@@ -0,0 +1,275 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'engine_settings.dart';
// **************************************************************************
// CopyWithGenerator
// **************************************************************************
abstract class _$EngineSettingsCWProxy {
EngineSettings javascriptEnabled(bool? javascriptEnabled);
EngineSettings trackingProtectionPolicy(
TrackingProtectionPolicy? trackingProtectionPolicy);
EngineSettings httpsOnlyMode(HttpsOnlyMode? httpsOnlyMode);
EngineSettings globalPrivacyControlEnabled(bool? globalPrivacyControlEnabled);
EngineSettings preferredColorScheme(ColorScheme? preferredColorScheme);
EngineSettings cookieBannerHandlingMode(
CookieBannerHandlingMode? cookieBannerHandlingMode);
EngineSettings cookieBannerHandlingModePrivateBrowsing(
CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing);
EngineSettings cookieBannerHandlingGlobalRules(
bool? cookieBannerHandlingGlobalRules);
EngineSettings cookieBannerHandlingGlobalRulesSubFrames(
bool? cookieBannerHandlingGlobalRulesSubFrames);
EngineSettings webContentIsolationStrategy(
WebContentIsolationStrategy? webContentIsolationStrategy);
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `EngineSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// EngineSettings(...).copyWith(id: 12, name: "My name")
/// ````
EngineSettings call({
bool? javascriptEnabled,
TrackingProtectionPolicy? trackingProtectionPolicy,
HttpsOnlyMode? httpsOnlyMode,
bool? globalPrivacyControlEnabled,
ColorScheme? preferredColorScheme,
CookieBannerHandlingMode? cookieBannerHandlingMode,
CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing,
bool? cookieBannerHandlingGlobalRules,
bool? cookieBannerHandlingGlobalRulesSubFrames,
WebContentIsolationStrategy? webContentIsolationStrategy,
});
}
/// Proxy class for `copyWith` functionality. This is a callable class and can be used as follows: `instanceOfEngineSettings.copyWith(...)`. Additionally contains functions for specific fields e.g. `instanceOfEngineSettings.copyWith.fieldName(...)`
class _$EngineSettingsCWProxyImpl implements _$EngineSettingsCWProxy {
const _$EngineSettingsCWProxyImpl(this._value);
final EngineSettings _value;
@override
EngineSettings javascriptEnabled(bool? javascriptEnabled) =>
this(javascriptEnabled: javascriptEnabled);
@override
EngineSettings trackingProtectionPolicy(
TrackingProtectionPolicy? trackingProtectionPolicy) =>
this(trackingProtectionPolicy: trackingProtectionPolicy);
@override
EngineSettings httpsOnlyMode(HttpsOnlyMode? httpsOnlyMode) =>
this(httpsOnlyMode: httpsOnlyMode);
@override
EngineSettings globalPrivacyControlEnabled(
bool? globalPrivacyControlEnabled) =>
this(globalPrivacyControlEnabled: globalPrivacyControlEnabled);
@override
EngineSettings preferredColorScheme(ColorScheme? preferredColorScheme) =>
this(preferredColorScheme: preferredColorScheme);
@override
EngineSettings cookieBannerHandlingMode(
CookieBannerHandlingMode? cookieBannerHandlingMode) =>
this(cookieBannerHandlingMode: cookieBannerHandlingMode);
@override
EngineSettings cookieBannerHandlingModePrivateBrowsing(
CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing) =>
this(
cookieBannerHandlingModePrivateBrowsing:
cookieBannerHandlingModePrivateBrowsing);
@override
EngineSettings cookieBannerHandlingGlobalRules(
bool? cookieBannerHandlingGlobalRules) =>
this(cookieBannerHandlingGlobalRules: cookieBannerHandlingGlobalRules);
@override
EngineSettings cookieBannerHandlingGlobalRulesSubFrames(
bool? cookieBannerHandlingGlobalRulesSubFrames) =>
this(
cookieBannerHandlingGlobalRulesSubFrames:
cookieBannerHandlingGlobalRulesSubFrames);
@override
EngineSettings webContentIsolationStrategy(
WebContentIsolationStrategy? webContentIsolationStrategy) =>
this(webContentIsolationStrategy: webContentIsolationStrategy);
@override
/// This function **does support** nullification of nullable fields. All `null` values passed to `non-nullable` fields will be ignored. You can also use `EngineSettings(...).copyWith.fieldName(...)` to override fields one at a time with nullification support.
///
/// Usage
/// ```dart
/// EngineSettings(...).copyWith(id: 12, name: "My name")
/// ````
EngineSettings call({
Object? javascriptEnabled = const $CopyWithPlaceholder(),
Object? trackingProtectionPolicy = const $CopyWithPlaceholder(),
Object? httpsOnlyMode = const $CopyWithPlaceholder(),
Object? globalPrivacyControlEnabled = const $CopyWithPlaceholder(),
Object? preferredColorScheme = const $CopyWithPlaceholder(),
Object? cookieBannerHandlingMode = const $CopyWithPlaceholder(),
Object? cookieBannerHandlingModePrivateBrowsing =
const $CopyWithPlaceholder(),
Object? cookieBannerHandlingGlobalRules = const $CopyWithPlaceholder(),
Object? cookieBannerHandlingGlobalRulesSubFrames =
const $CopyWithPlaceholder(),
Object? webContentIsolationStrategy = const $CopyWithPlaceholder(),
}) {
return EngineSettings(
javascriptEnabled: javascriptEnabled == const $CopyWithPlaceholder()
? _value.javascriptEnabled
// ignore: cast_nullable_to_non_nullable
: javascriptEnabled as bool?,
trackingProtectionPolicy:
trackingProtectionPolicy == const $CopyWithPlaceholder()
? _value.trackingProtectionPolicy
// ignore: cast_nullable_to_non_nullable
: trackingProtectionPolicy as TrackingProtectionPolicy?,
httpsOnlyMode: httpsOnlyMode == const $CopyWithPlaceholder()
? _value.httpsOnlyMode
// ignore: cast_nullable_to_non_nullable
: httpsOnlyMode as HttpsOnlyMode?,
globalPrivacyControlEnabled:
globalPrivacyControlEnabled == const $CopyWithPlaceholder()
? _value.globalPrivacyControlEnabled
// ignore: cast_nullable_to_non_nullable
: globalPrivacyControlEnabled as bool?,
preferredColorScheme: preferredColorScheme == const $CopyWithPlaceholder()
? _value.preferredColorScheme
// ignore: cast_nullable_to_non_nullable
: preferredColorScheme as ColorScheme?,
cookieBannerHandlingMode:
cookieBannerHandlingMode == const $CopyWithPlaceholder()
? _value.cookieBannerHandlingMode
// ignore: cast_nullable_to_non_nullable
: cookieBannerHandlingMode as CookieBannerHandlingMode?,
cookieBannerHandlingModePrivateBrowsing:
cookieBannerHandlingModePrivateBrowsing ==
const $CopyWithPlaceholder()
? _value.cookieBannerHandlingModePrivateBrowsing
// ignore: cast_nullable_to_non_nullable
: cookieBannerHandlingModePrivateBrowsing
as CookieBannerHandlingMode?,
cookieBannerHandlingGlobalRules:
cookieBannerHandlingGlobalRules == const $CopyWithPlaceholder()
? _value.cookieBannerHandlingGlobalRules
// ignore: cast_nullable_to_non_nullable
: cookieBannerHandlingGlobalRules as bool?,
cookieBannerHandlingGlobalRulesSubFrames:
cookieBannerHandlingGlobalRulesSubFrames ==
const $CopyWithPlaceholder()
? _value.cookieBannerHandlingGlobalRulesSubFrames
// ignore: cast_nullable_to_non_nullable
: cookieBannerHandlingGlobalRulesSubFrames as bool?,
webContentIsolationStrategy:
webContentIsolationStrategy == const $CopyWithPlaceholder()
? _value.webContentIsolationStrategy
// ignore: cast_nullable_to_non_nullable
: webContentIsolationStrategy as WebContentIsolationStrategy?,
);
}
}
extension $EngineSettingsCopyWith on EngineSettings {
/// Returns a callable class that can be used as follows: `instanceOfEngineSettings.copyWith(...)` or like so:`instanceOfEngineSettings.copyWith.fieldName(...)`.
// ignore: library_private_types_in_public_api
_$EngineSettingsCWProxy get copyWith => _$EngineSettingsCWProxyImpl(this);
}
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
EngineSettings _$EngineSettingsFromJson(Map<String, dynamic> json) =>
EngineSettings.withDefaults(
javascriptEnabled: json['javascriptEnabled'] as bool?,
trackingProtectionPolicy: $enumDecodeNullable(
_$TrackingProtectionPolicyEnumMap, json['trackingProtectionPolicy']),
httpsOnlyMode:
$enumDecodeNullable(_$HttpsOnlyModeEnumMap, json['httpsOnlyMode']),
globalPrivacyControlEnabled: json['globalPrivacyControlEnabled'] as bool?,
preferredColorScheme: $enumDecodeNullable(
_$ColorSchemeEnumMap, json['preferredColorScheme']),
cookieBannerHandlingMode: $enumDecodeNullable(
_$CookieBannerHandlingModeEnumMap, json['cookieBannerHandlingMode']),
cookieBannerHandlingModePrivateBrowsing: $enumDecodeNullable(
_$CookieBannerHandlingModeEnumMap,
json['cookieBannerHandlingModePrivateBrowsing']),
cookieBannerHandlingGlobalRules:
json['cookieBannerHandlingGlobalRules'] as bool?,
cookieBannerHandlingGlobalRulesSubFrames:
json['cookieBannerHandlingGlobalRulesSubFrames'] as bool?,
webContentIsolationStrategy: $enumDecodeNullable(
_$WebContentIsolationStrategyEnumMap,
json['webContentIsolationStrategy']),
);
Map<String, dynamic> _$EngineSettingsToJson(EngineSettings instance) =>
<String, dynamic>{
'javascriptEnabled': instance.javascriptEnabled,
'trackingProtectionPolicy':
_$TrackingProtectionPolicyEnumMap[instance.trackingProtectionPolicy],
'httpsOnlyMode': _$HttpsOnlyModeEnumMap[instance.httpsOnlyMode],
'globalPrivacyControlEnabled': instance.globalPrivacyControlEnabled,
'preferredColorScheme':
_$ColorSchemeEnumMap[instance.preferredColorScheme],
'cookieBannerHandlingMode':
_$CookieBannerHandlingModeEnumMap[instance.cookieBannerHandlingMode],
'cookieBannerHandlingModePrivateBrowsing':
_$CookieBannerHandlingModeEnumMap[
instance.cookieBannerHandlingModePrivateBrowsing],
'cookieBannerHandlingGlobalRules':
instance.cookieBannerHandlingGlobalRules,
'cookieBannerHandlingGlobalRulesSubFrames':
instance.cookieBannerHandlingGlobalRulesSubFrames,
'webContentIsolationStrategy': _$WebContentIsolationStrategyEnumMap[
instance.webContentIsolationStrategy],
};
const _$TrackingProtectionPolicyEnumMap = {
TrackingProtectionPolicy.none: 'none',
TrackingProtectionPolicy.recommended: 'recommended',
TrackingProtectionPolicy.strict: 'strict',
TrackingProtectionPolicy.custom: 'custom',
};
const _$HttpsOnlyModeEnumMap = {
HttpsOnlyMode.disabled: 'disabled',
HttpsOnlyMode.privateOnly: 'privateOnly',
HttpsOnlyMode.enabled: 'enabled',
};
const _$ColorSchemeEnumMap = {
ColorScheme.system: 'system',
ColorScheme.light: 'light',
ColorScheme.dark: 'dark',
};
const _$CookieBannerHandlingModeEnumMap = {
CookieBannerHandlingMode.disabled: 'disabled',
CookieBannerHandlingMode.rejectAll: 'rejectAll',
CookieBannerHandlingMode.rejectOrAcceptAll: 'rejectOrAcceptAll',
};
const _$WebContentIsolationStrategyEnumMap = {
WebContentIsolationStrategy.isolateNothing: 'isolateNothing',
WebContentIsolationStrategy.isolateEverything: 'isolateEverything',
WebContentIsolationStrategy.isolateHighValue: 'isolateHighValue',
};
@@ -29,6 +29,8 @@ class SelectableChips<T extends S, S, K> extends StatelessWidget {
final Widget? Function(T item)? itemAvatar;
final int? Function(T item)? itemBadgeCount;
final Widget Function(Widget child, S item)? itemWrap;
final void Function(T item)? onSelected;
final void Function(T item)? onDeleted;
@@ -37,6 +39,7 @@ class SelectableChips<T extends S, S, K> extends StatelessWidget {
required this.itemLabel,
this.itemAvatar,
this.itemBadgeCount,
this.itemWrap,
required this.availableItems,
this.selectedItem,
this.maxCount = 25,
@@ -76,7 +79,7 @@ class SelectableChips<T extends S, S, K> extends StatelessWidget {
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Padding(
final child = Padding(
padding: const EdgeInsets.only(right: 8.0),
child: _BadgeWrapper(
count: itemBadgeCount?.call(item),
@@ -101,6 +104,8 @@ class SelectableChips<T extends S, S, K> extends StatelessWidget {
),
),
);
return (itemWrap != null) ? itemWrap!(child, item) : child;
},
);
},
+4 -2
View File
@@ -30,9 +30,11 @@ dependencies:
flutter_mozilla_components:
path: ../packages/flutter_mozilla_components
flutter_pdf_text: ^0.8.1
flutter_reorderable_grid_view:
path: ../../flutter-reorderable-grid-view
flutter_secure_storage: ^10.0.0-beta.4
flutter_sharing_intent: ^1.1.1
go_router: ^14.6.3
go_router: ^14.7.1
google_fonts: ^6.2.1
home_widget: ^0.7.0
hooks_riverpod: ^2.6.1
@@ -54,7 +56,6 @@ dependencies:
path_provider: ^2.1.5
pocketbase: ^0.21.0
provider: ^6.1.2
reorderable_grid: ^1.0.10
riverpod: ^2.6.1
riverpod_annotation: ^2.6.1
rxdart: ^0.28.0
@@ -103,6 +104,7 @@ flutter:
- assets/icon/
- assets/landing/
- assets/fonts/
- assets/preferences/
flutter_launcher_icons:
android: "launcher_icon"
+2 -2
View File
@@ -1,8 +1,8 @@
# melos_managed_dependency_overrides: flutter_mozilla_components,sqlite3_vec,flutter_chat_core
dependency_overrides:
flutter_chat_core:
path: ../../flutter_chat_ui/packages/flutter_chat_core
flutter_mozilla_components:
path: ../packages/flutter_mozilla_components
sqlite3_vec:
path: ../packages/sqlite3_vec
flutter_chat_core:
path: ../../flutter_chat_ui/packages/flutter_chat_core
@@ -3,7 +3,7 @@ version = "1.0-SNAPSHOT"
buildscript {
ext.kotlin_version = "1.9.22"
ext.mozillaComponentsVersion = '134.0.20241030214633'
ext.mozillaComponentsVersion = '136.0.20250127162202'
repositories {
google()
@@ -0,0 +1,119 @@
"use strict";
const { BackgroundTasksUtils } = ChromeUtils.importESModule(
"resource://gre/modules/BackgroundTasksUtils.sys.mjs"
);
const lazy = {};
ChromeUtils.defineLazyGetter(lazy, "log", () => {
let { ConsoleAPI } = ChromeUtils.importESModule(
"resource://gre/modules/Console.sys.mjs"
);
let consoleOptions = {
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.sys.mjs for details.
maxLogLevel: "error",
maxLogLevelPref: "app.update.background.loglevel",
prefix: "PrefsManager",
};
return new ConsoleAPI(consoleOptions);
});
var prefmanager = class extends ExtensionAPI {
getAPI(context) {
return {
experiments: {
prefmanager: {
async resetPrefs(prefNames) {
if (prefNames && prefNames.length > 0) {
for (const prefName of prefNames) {
Services.prefs.clearUserPref(prefName);
}
} else {
Services.prefs.resetPrefs();
}
},
async getPrefs(prefNames) {
const prefs = (prefNames && prefNames.length > 0)
? prefNames
: Services.prefs.getChildList("");
const result = {};
for (const prefName of prefs) {
try {
switch (Services.prefs.getPrefType(prefName)) {
case Services.prefs.PREF_BOOL:
result[prefName] = Services.prefs.getBoolPref(prefName);
break;
case Services.prefs.PREF_INT:
result[prefName] = Services.prefs.getIntPref(prefName);
break;
case Services.prefs.PREF_STRING:
result[prefName] = Services.prefs.getCharPref(prefName);
break;
default:
// Skip complex values or invalid preferences
continue;
}
} catch (e) {
lazy.log.error(`Error reading preference ${prefName}: ${e}`);
continue;
}
}
lazy.log.debug(`getAll: retrieved ${Object.keys(result).length} preferences`);
return result;
},
async parsePrefsAndApply(prefsFileContent, predicate = null) {
let prefs = {};
let addPref = (kind, name, value) => {
if (predicate && !predicate(name)) {
return;
}
prefs[name] = value;
};
Services.prefs.parsePrefsFromBuffer(
prefsFileContent,
{
onStringPref: addPref,
onIntPref: addPref,
onBoolPref: addPref,
onError(message) {
throw new Error(
`Error parsing preferences "${message}"`
);
},
},
);
await BackgroundTasksUtils.withProfileLock(profileLock => {
for (let [name, value] of Object.entries(prefs)) {
switch (typeof value) {
case "boolean":
Services.prefs.setBoolPref(name, value);
break;
case "number":
Services.prefs.setIntPref(name, value);
break;
case "string":
Services.prefs.setCharPref(name, value);
break;
default:
throw new Error(
`Pref from default profile with name "${name}" has unrecognized type`
);
}
}
});
lazy.log.debug(`applyPreferences: parsed prefs from buffer`, prefs);
return prefs;
}
}
}
};
}
};
@@ -0,0 +1,47 @@
'use strict';
const encoder = new TextEncoder();
const port = browser.runtime.connectNative("prefManager");
function sendJsonResultForRequest(id) {
return function (result) {
port.postMessage({
"id": id,
"status": "success",
"result": result
})
}
}
function sendErrorForRequest(id) {
return function (error) {
console.error(error);
port.postMessage({
"id": id,
"status": "error",
"error": error
});
}
}
port.onMessage.addListener(message => {
let requestId = message["id"]
switch (message["action"]) {
case "parsePrefsAndApply":
browser.experiments.prefmanager.parsePrefsAndApply(encoder.encode(message["args"]), null)
.then(sendJsonResultForRequest(requestId))
.catch(sendErrorForRequest(requestId))
break
case "getPrefs":
browser.experiments.prefmanager.getPrefs(message["args"])
.then(sendJsonResultForRequest(requestId))
.catch(sendErrorForRequest(requestId))
break
case "resetPrefs":
browser.experiments.prefmanager.resetPrefs(message["args"])
.then(sendJsonResultForRequest(requestId))
.catch(sendErrorForRequest(requestId))
break
}
});
@@ -0,0 +1,39 @@
{
"manifest_version": 2,
"name": "Pref Manager",
"version": "1.0",
"description": "Manipulate GeckoView preferences",
"browser_specific_settings": {
"gecko": {
"id": "pref-manager@movenext.me"
}
},
"background": {
"scripts": [
"background.js"
]
},
"experiment_apis": {
"prefmanager": {
"schema": "schema.json",
"parent": {
"scopes": [
"addon_parent"
],
"paths": [
[
"experiments",
"prefmanager"
]
],
"script": "api.js"
}
}
},
"permissions": [
"nativeMessaging",
"nativeMessagingFromContent",
"geckoViewAddons",
"<all_urls>"
]
}
@@ -0,0 +1,78 @@
[
{
"namespace": "experiments.prefmanager",
"description": "Experimental API for updating Geckoview preferences",
"functions": [
{
"name": "getPrefs",
"type": "function",
"description": "Retrieves preferences and their values",
"async": true,
"parameters": [
{
"name": "prefNames",
"type": "array",
"optional": true,
"description": "Array of preference names to retrieve. If empty or null, retrieves all preferences",
"items": {
"type": "string"
}
}
],
"returns": {
"type": "object",
"description": "Object containing the preferences as key-value pairs",
"additionalProperties": {
"type": "any",
"description": "Preference values can be boolean, number, or string"
}
}
},
{
"name": "parsePrefsAndApply",
"type": "function",
"description": "Reads and sets preferences from an encoded preferences file content",
"async": true,
"parameters": [
{
"name": "prefsFileContent",
"type": "object",
"isInstanceOf": "Uint8Array",
"description": "Encoded content of the preferences file to parse (Uint8Array)"
},
{
"name": "predicate",
"type": "function",
"optional": true,
"description": "Optional filter function to determine which preferences to include"
}
],
"returns": {
"type": "object",
"description": "Object containing the parsed preferences as key-value pairs",
"additionalProperties": {
"type": "any",
"description": "Preference values can be boolean, number, or string"
}
}
},
{
"name": "resetPrefs",
"type": "function",
"description": "Resets multiple or all preferences to their default values",
"async": true,
"parameters": [
{
"name": "prefNames",
"type": "array",
"optional": true,
"description": "Array of preference names to reset. If empty or null, resets all preferences",
"items": {
"type": "string"
}
}
]
}
]
}
]
@@ -6,6 +6,7 @@ package eu.lensai.flutter_mozilla_components
import android.content.Context
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
import eu.lensai.flutter_mozilla_components.feature.PrefManagerFeature
import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
import mozilla.components.concept.engine.DefaultSettings
@@ -31,6 +32,7 @@ object EngineProvider {
// About config it's no longer enabled by default
builder.aboutConfigEnabled(true)
builder.extensionsProcessEnabled(true)
builder.extensionsWebAPIEnabled(true)
builder.consoleOutput(true)
@@ -47,6 +49,7 @@ object EngineProvider {
return GeckoEngine(context, defaultSettings, runtime).also {
WebCompatFeature.install(it)
CookieManagerFeature.install(it)
PrefManagerFeature.install(it)
}
}
@@ -10,6 +10,7 @@ import eu.lensai.flutter_mozilla_components.api.GeckoCookieApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoEngineSettingsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoFindApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoIconsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoPrefApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSelectionActionControllerImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSessionApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSuggestionApiImpl
@@ -22,6 +23,7 @@ import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoFindApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoIconsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoPrefApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionController
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionEvents
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSessionApi
@@ -91,6 +93,7 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
GeckoTabsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoTabsApiImpl())
GeckoIconsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoIconsApiImpl())
GeckoCookieApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoCookieApiImpl())
GeckoPrefApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoPrefApiImpl())
GeckoFindApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFindApiImpl())
GeckoSelectionActionController.setUp(_flutterPluginBinding.binaryMessenger, GeckoSelectionActionControllerImpl(
selectionActionDelegate
@@ -1,9 +1,19 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.GlobalComponents
import eu.lensai.flutter_mozilla_components.pigeons.ColorScheme
import eu.lensai.flutter_mozilla_components.pigeons.CookieBannerHandlingMode
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettings
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
import eu.lensai.flutter_mozilla_components.pigeons.HttpsOnlyMode
import eu.lensai.flutter_mozilla_components.pigeons.WebContentIsolationStrategy
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.feature.addons.logger
import mozilla.components.feature.session.SettingsUseCases
import mozilla.components.feature.session.TrackingProtectionUseCases
/**
* Implementation of GeckoEngineSettingsApi that manages engine-specific settings
@@ -17,13 +27,99 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
override fun javaScriptEnabled(state: Boolean) {
try {
components.core.engine.settings.javascriptEnabled = state
logger.debug("$TAG: JavaScript enabled state changed to: $state")
} catch (e: Exception) {
logger.error("$TAG: Failed to set JavaScript enabled state", e)
throw IllegalStateException("Failed to set JavaScript enabled state", e)
override fun setDefaultSettings(settings: GeckoEngineSettings) {
if(settings.javascriptEnabled != null) {
components.core.engineSettings.javascriptEnabled = settings.javascriptEnabled;
}
if(settings.trackingProtectionPolicy != null) {
components.core.engineSettings.trackingProtectionPolicy = when(settings.trackingProtectionPolicy) {
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.NONE -> TrackingProtectionPolicy.none()
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.RECOMMENDED -> TrackingProtectionPolicy.recommended()
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> TrackingProtectionPolicy.strict()
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.CUSTOM -> TODO()
}
}
if(settings.httpsOnlyMode != null) {
components.core.engineSettings.httpsOnlyMode = when(settings.httpsOnlyMode) {
HttpsOnlyMode.DISABLED -> Engine.HttpsOnlyMode.DISABLED
HttpsOnlyMode.PRIVATE_ONLY -> Engine.HttpsOnlyMode.ENABLED_PRIVATE_ONLY
HttpsOnlyMode.ENABLED -> Engine.HttpsOnlyMode.ENABLED
}
}
if(settings.globalPrivacyControlEnabled != null) {
components.core.engineSettings.globalPrivacyControlEnabled = settings.globalPrivacyControlEnabled;
}
if(settings.preferredColorScheme != null) {
components.core.engineSettings.preferredColorScheme = when(settings.preferredColorScheme) {
ColorScheme.SYSTEM -> PreferredColorScheme.System
ColorScheme.LIGHT -> PreferredColorScheme.Light
ColorScheme.DARK -> PreferredColorScheme.Dark
}
}
if(settings.cookieBannerHandlingMode != null) {
components.core.engineSettings.cookieBannerHandlingMode = when(settings.cookieBannerHandlingMode) {
CookieBannerHandlingMode.DISABLED -> EngineSession.CookieBannerHandlingMode.DISABLED
CookieBannerHandlingMode.REJECT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_ALL
CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL
}
}
if(settings.cookieBannerHandlingModePrivateBrowsing != null) {
components.core.engineSettings.cookieBannerHandlingModePrivateBrowsing = when(settings.cookieBannerHandlingModePrivateBrowsing) {
CookieBannerHandlingMode.DISABLED -> EngineSession.CookieBannerHandlingMode.DISABLED
CookieBannerHandlingMode.REJECT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_ALL
CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL
}
}
if(settings.cookieBannerHandlingGlobalRules != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRules = settings.cookieBannerHandlingGlobalRules;
}
if(settings.cookieBannerHandlingGlobalRulesSubFrames != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRulesSubFrames = settings.cookieBannerHandlingGlobalRulesSubFrames;
}
if(settings.webContentIsolationStrategy != null) {
components.core.engineSettings.webContentIsolationStrategy = when(settings.webContentIsolationStrategy) {
WebContentIsolationStrategy.ISOLATE_NOTHING -> mozilla.components.concept.engine.fission.WebContentIsolationStrategy.ISOLATE_NOTHING
WebContentIsolationStrategy.ISOLATE_EVERYTHING -> mozilla.components.concept.engine.fission.WebContentIsolationStrategy.ISOLATE_EVERYTHING
WebContentIsolationStrategy.ISOLATE_HIGH_VALUE -> mozilla.components.concept.engine.fission.WebContentIsolationStrategy.ISOLATE_HIGH_VALUE
}
}
}
override fun updateRuntimeSettings(settings: GeckoEngineSettings) {
//First parse and set default values
setDefaultSettings(settings);
//Then copy default settings into runtime
if(settings.javascriptEnabled != null) {
components.core.engine.settings.javascriptEnabled = components.core.engineSettings.javascriptEnabled
}
if(settings.trackingProtectionPolicy != null) {
components.useCases.settingsUseCases.updateTrackingProtection(components.core.engineSettings.trackingProtectionPolicy!!)
components.useCases.sessionUseCases.reload()
}
if(settings.httpsOnlyMode != null) {
components.core.engineSettings.httpsOnlyMode = components.core.engineSettings.httpsOnlyMode
}
if(settings.globalPrivacyControlEnabled != null) {
components.core.engineSettings.globalPrivacyControlEnabled = components.core.engineSettings.globalPrivacyControlEnabled
}
if(settings.preferredColorScheme != null) {
components.core.engineSettings.preferredColorScheme = components.core.engineSettings.preferredColorScheme
}
if(settings.cookieBannerHandlingMode != null) {
components.core.engineSettings.cookieBannerHandlingMode = components.core.engineSettings.cookieBannerHandlingMode
}
if(settings.cookieBannerHandlingModePrivateBrowsing != null) {
components.core.engineSettings.cookieBannerHandlingModePrivateBrowsing = components.core.engineSettings.cookieBannerHandlingModePrivateBrowsing
}
if(settings.cookieBannerHandlingGlobalRules != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRules = components.core.engineSettings.cookieBannerHandlingGlobalRules
}
if(settings.cookieBannerHandlingGlobalRulesSubFrames != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRulesSubFrames = components.core.engineSettings.cookieBannerHandlingGlobalRulesSubFrames
}
if(settings.webContentIsolationStrategy != null) {
components.core.engineSettings.webContentIsolationStrategy = components.core.engineSettings.webContentIsolationStrategy
}
}
}
@@ -0,0 +1,73 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.feature.PrefManagerFeature
import eu.lensai.flutter_mozilla_components.feature.ResultConsumer
import eu.lensai.flutter_mozilla_components.pigeons.GeckoPrefApi
import org.json.JSONObject
import org.json.JSONArray
class GeckoPrefApiImpl : GeckoPrefApi {
private fun List<String>?.toJson(): JSONArray {
return JSONArray().apply {
this@toJson?.forEach { put(it) }
}
}
fun JSONObject.toMap(): Map<String, Any> {
val map = mutableMapOf<String, Any>()
val keys = this.keys()
while (keys.hasNext()) {
val key = keys.next()
when (val value = this.get(key)) {
is JSONObject -> map[key] = value.toMap()
// is JSONArray -> map[key] = value.toList()
// JSONObject.NULL -> map[key] = null
else -> map[key] = value
}
}
return map
}
override fun getPrefs(
preferenceFilter: List<String>?,
callback: (Result<Map<String, Any>>) -> Unit
) {
PrefManagerFeature.scheduleRequest("getPrefs", preferenceFilter.toJson(), object : ResultConsumer<JSONObject> {
override fun success(result: JSONObject) {
callback(Result.success(result.getJSONObject("result").toMap()))
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
}
})
}
override fun applyPrefs(prefBuffer: String, callback: (Result<Map<String, Any>>) -> Unit) {
PrefManagerFeature.scheduleRequest("parsePrefsAndApply", prefBuffer, object : ResultConsumer<JSONObject> {
override fun success(result: JSONObject) {
callback(Result.success(result.getJSONObject("result").toMap()))
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
}
})
}
override fun resetPrefs(preferenceNames: List<String>?, callback: (Result<Unit>) -> Unit) {
PrefManagerFeature.scheduleRequest("resetPrefs", preferenceNames.toJson(), object : ResultConsumer<JSONObject> {
override fun success(result: JSONObject) {
callback(Result.success(Unit))
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
}
})
}
}
@@ -2,6 +2,7 @@ package eu.lensai.flutter_mozilla_components.components
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import eu.lensai.flutter_mozilla_components.Components
import eu.lensai.flutter_mozilla_components.interceptor.AppRequestInterceptor
@@ -25,7 +26,10 @@ import mozilla.components.browser.thumbnails.ThumbnailsMiddleware
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.concept.engine.DefaultSettings
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.fission.WebContentIsolationStrategy
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.concept.fetch.Client
import mozilla.components.feature.addons.AddonManager
import mozilla.components.feature.addons.amo.AMOAddonsProvider
@@ -54,21 +58,40 @@ class Core(private val context: Context,
PreferenceManager.getDefaultSharedPreferences(context)
}
private val engineSettings by lazy {
DefaultSettings().apply {
val engineSettings by lazy {
DefaultSettings(
//historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage)
requestInterceptor = AppRequestInterceptor(context)
remoteDebuggingEnabled = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_remote_debugging), false)
testingModeEnabled = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_testing_mode), false)
historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage)
trackingProtectionPolicy = createTrackingProtectionPolicy(prefs)
httpsOnlyMode = Engine.HttpsOnlyMode.ENABLED
globalPrivacyControlEnabled = prefs.getBoolean(
context.getPreferenceKey(R.string.pref_key_global_privacy_control),
false,
requestInterceptor = AppRequestInterceptor(context),
historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage),
testingModeEnabled = false,
remoteDebuggingEnabled = false,
automaticFontSizeAdjustment = true,
fontInflationEnabled = true,
suspendMediaWhenInactive = false,
getDesktopMode = {
store.state.desktopMode
},
enterpriseRootsEnabled = false,
emailTrackerBlockingPrivateBrowsing = true,
// clearColor = ContextCompat.getColor(
// context,
// R.color.fx_mobile_layer_color_1,
// ),
trackingProtectionPolicy = createTrackingProtectionPolicy(TrackingProtectionPolicy.strict()),
//FP Protection is handled by trackingPolicy
//fingerprintingProtection
//fingerprintingProtectionPrivateBrowsing
httpsOnlyMode = Engine.HttpsOnlyMode.ENABLED,
globalPrivacyControlEnabled = true,
preferredColorScheme = PreferredColorScheme.Dark,
cookieBannerHandlingMode = EngineSession.CookieBannerHandlingMode.REJECT_ALL,
cookieBannerHandlingModePrivateBrowsing = EngineSession.CookieBannerHandlingMode.REJECT_ALL,
cookieBannerHandlingGlobalRules = true,
cookieBannerHandlingGlobalRulesSubFrames = true,
webContentIsolationStrategy = WebContentIsolationStrategy.ISOLATE_HIGH_VALUE
)
}
}
val engine: Engine by lazy {
EngineProvider.createEngine(context, engineSettings)
@@ -136,7 +159,17 @@ class Core(private val context: Context,
// PromptMiddleware(),
SessionPrioritizationMiddleware(),
RecordingDevicesMiddleware(context, components.notificationsDelegate),
) + EngineMiddleware.create(engine),
) + EngineMiddleware.create(
engine,
// We are disabling automatic suspending of engine sessions under memory pressure.
// Instead we solely rely on GeckoView and the Android system to reclaim memory
// when needed. For details, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1752594
// https://github.com/mozilla-mobile/fenix/issues/12731
// https://github.com/mozilla-mobile/android-components/issues/11300
// https://github.com/mozilla-mobile/android-components/issues/11653
trimMemoryAutomatically = false,
)
).apply {
components.events.registerFlowEvents(this)
@@ -191,11 +224,10 @@ class Core(private val context: Context,
* @return the constructed tracking protection policy based on preferences.
*/
private fun createTrackingProtectionPolicy(
prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context),
normalMode: Boolean = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_tracking_protection_normal), true),
privateMode: Boolean = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_tracking_protection_private), true),
trackingPolicy: EngineSession. TrackingProtectionPolicyForSessionTypes,
normalMode: Boolean = true,
privateMode: Boolean = true,
): TrackingProtectionPolicy {
val trackingPolicy = TrackingProtectionPolicy.recommended()
return when {
normalMode && privateMode -> trackingPolicy
normalMode && !privateMode -> trackingPolicy.forRegularSessionsOnly()
@@ -11,6 +11,7 @@ import mozilla.components.feature.app.links.AppLinksUseCases
import mozilla.components.feature.downloads.DownloadsUseCases
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.session.SettingsUseCases
import mozilla.components.feature.session.TrackingProtectionUseCases
import mozilla.components.feature.tabs.CustomTabsUseCases
import mozilla.components.feature.tabs.TabsUseCases
@@ -49,4 +50,6 @@ class UseCases(
val customTabsUseCases: CustomTabsUseCases by lazy { CustomTabsUseCases(store, sessionUseCases.loadUrl) }
val appLinksUseCases by lazy { AppLinksUseCases(context) }
//val trackingProtectionUseCases by lazy { TrackingProtectionUseCases(store, engine) }
}
@@ -0,0 +1,100 @@
package eu.lensai.flutter_mozilla_components.feature
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import mozilla.components.concept.engine.webextension.MessageHandler
import mozilla.components.concept.engine.webextension.Port
import mozilla.components.concept.engine.webextension.WebExtensionRuntime
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.webextensions.WebExtensionController
import org.json.JSONObject
object PrefManagerFeature {
private val logger = Logger("pref-manager")
private const val PREF_MANAGER_REPORTER_EXTENSION_ID = "pref-manager@movenext.me"
private const val PREF_MANAGER_REPORTER_EXTENSION_URL = "resource://android/assets/extensions/pref_manager/"
private const val PREF_MANAGER_REPORTER_MESSAGING_ID = "prefManager"
private var nextRequestId: Int = 0
private val requestHandlers = HashMap<Int, ResultConsumer<JSONObject>>()
private val mutex = Mutex()
@VisibleForTesting
// This is an internal var to make it mutable for unit testing purposes only
internal var extensionController = WebExtensionController(
PREF_MANAGER_REPORTER_EXTENSION_ID,
PREF_MANAGER_REPORTER_EXTENSION_URL,
PREF_MANAGER_REPORTER_MESSAGING_ID,
)
fun scheduleRequest(command: String, args: Any, callback: ResultConsumer<JSONObject>) {
val message = JSONObject()
message.put("action", command);
message.put("args", args)
runBlocking {
withContext(Dispatchers.Default) {
mutex.withLock {
message.put("id", nextRequestId)
requestHandlers[nextRequestId] = callback
nextRequestId += 1
extensionController.sendBackgroundMessage(message)
}
}
}
}
private class PrefManagerReporterBackgroundMessageHandler() : MessageHandler {
override fun onPortMessage(message: Any, port: Port) {
runBlocking {
withContext(Dispatchers.Default) {
mutex.withLock {
val messageJSON = message as JSONObject;
val requestId = messageJSON.getInt("id")
val status = messageJSON.getString("status")
if (status == "success") {
requestHandlers[requestId]?.success(message)
} else {
requestHandlers[requestId]?.error(
"Pref Manager",
"Failed to perform operation",
message.getString("error")
)
}
}
}
}
}
}
/**
* Installs the web extension in the runtime through the WebExtensionRuntime install method
*
* @param runtime a WebExtensionRuntime.
* @param productName a custom product name used to automatically label reports. Defaults to
* "android-components".
*/
fun install(runtime: WebExtensionRuntime) {
extensionController.registerBackgroundMessageHandler(
PrefManagerReporterBackgroundMessageHandler(),
)
extensionController.install(
runtime,
onSuccess = {
logger.debug("Installed PrefManager webextension: ${it.id}")
},
onError = { throwable ->
logger.error("Failed to install PrefManager webextension: ", throwable)
},
)
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.7.2), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -168,6 +168,67 @@ enum class GeckoSuggestionType(val raw: Int) {
}
}
enum class TrackingProtectionPolicy(val raw: Int) {
NONE(0),
RECOMMENDED(1),
STRICT(2),
CUSTOM(3);
companion object {
fun ofRaw(raw: Int): TrackingProtectionPolicy? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class HttpsOnlyMode(val raw: Int) {
DISABLED(0),
PRIVATE_ONLY(1),
ENABLED(2);
companion object {
fun ofRaw(raw: Int): HttpsOnlyMode? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class ColorScheme(val raw: Int) {
SYSTEM(0),
LIGHT(1),
DARK(2);
companion object {
fun ofRaw(raw: Int): ColorScheme? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class CookieBannerHandlingMode(val raw: Int) {
DISABLED(0),
REJECT_ALL(1),
REJECT_OR_ACCEPT_ALL(2);
companion object {
fun ofRaw(raw: Int): CookieBannerHandlingMode? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class WebContentIsolationStrategy(val raw: Int) {
ISOLATE_NOTHING(0),
ISOLATE_EVERYTHING(1),
ISOLATE_HIGH_VALUE(2);
companion object {
fun ofRaw(raw: Int): WebContentIsolationStrategy? {
return values().firstOrNull { it.raw == raw }
}
}
}
/**
* Translation options that map to the Gecko Translations Options.
*
@@ -1051,6 +1112,51 @@ data class TabContent (
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class GeckoEngineSettings (
val javascriptEnabled: Boolean? = null,
val trackingProtectionPolicy: TrackingProtectionPolicy? = null,
val httpsOnlyMode: HttpsOnlyMode? = null,
val globalPrivacyControlEnabled: Boolean? = null,
val preferredColorScheme: ColorScheme? = null,
val cookieBannerHandlingMode: CookieBannerHandlingMode? = null,
val cookieBannerHandlingModePrivateBrowsing: CookieBannerHandlingMode? = null,
val cookieBannerHandlingGlobalRules: Boolean? = null,
val cookieBannerHandlingGlobalRulesSubFrames: Boolean? = null,
val webContentIsolationStrategy: WebContentIsolationStrategy? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): GeckoEngineSettings {
val javascriptEnabled = pigeonVar_list[0] as Boolean?
val trackingProtectionPolicy = pigeonVar_list[1] as TrackingProtectionPolicy?
val httpsOnlyMode = pigeonVar_list[2] as HttpsOnlyMode?
val globalPrivacyControlEnabled = pigeonVar_list[3] as Boolean?
val preferredColorScheme = pigeonVar_list[4] as ColorScheme?
val cookieBannerHandlingMode = pigeonVar_list[5] as CookieBannerHandlingMode?
val cookieBannerHandlingModePrivateBrowsing = pigeonVar_list[6] as CookieBannerHandlingMode?
val cookieBannerHandlingGlobalRules = pigeonVar_list[7] as Boolean?
val cookieBannerHandlingGlobalRulesSubFrames = pigeonVar_list[8] as Boolean?
val webContentIsolationStrategy = pigeonVar_list[9] as WebContentIsolationStrategy?
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy)
}
}
fun toList(): List<Any?> {
return listOf(
javascriptEnabled,
trackingProtectionPolicy,
httpsOnlyMode,
globalPrivacyControlEnabled,
preferredColorScheme,
cookieBannerHandlingMode,
cookieBannerHandlingModePrivateBrowsing,
cookieBannerHandlingGlobalRules,
cookieBannerHandlingGlobalRulesSubFrames,
webContentIsolationStrategy,
)
}
}
private open class GeckoPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
@@ -1095,140 +1201,170 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
}
}
137.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TranslationOptions.fromList(it)
return (readValue(buffer) as Long?)?.let {
TrackingProtectionPolicy.ofRaw(it.toInt())
}
}
138.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ReaderState.fromList(it)
return (readValue(buffer) as Long?)?.let {
HttpsOnlyMode.ofRaw(it.toInt())
}
}
139.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
LastMediaAccessState.fromList(it)
return (readValue(buffer) as Long?)?.let {
ColorScheme.ofRaw(it.toInt())
}
}
140.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
HistoryMetadataKey.fromList(it)
return (readValue(buffer) as Long?)?.let {
CookieBannerHandlingMode.ofRaw(it.toInt())
}
}
141.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
PackageCategoryValue.fromList(it)
return (readValue(buffer) as Long?)?.let {
WebContentIsolationStrategy.ofRaw(it.toInt())
}
}
142.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ExternalPackage.fromList(it)
TranslationOptions.fromList(it)
}
}
143.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
LoadUrlFlagsValue.fromList(it)
ReaderState.fromList(it)
}
}
144.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SourceValue.fromList(it)
LastMediaAccessState.fromList(it)
}
}
145.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabState.fromList(it)
HistoryMetadataKey.fromList(it)
}
}
146.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RecoverableTab.fromList(it)
PackageCategoryValue.fromList(it)
}
}
147.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RecoverableBrowserState.fromList(it)
ExternalPackage.fromList(it)
}
}
148.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
IconRequest.fromList(it)
LoadUrlFlagsValue.fromList(it)
}
}
149.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ResourceSize.fromList(it)
SourceValue.fromList(it)
}
}
150.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
Resource.fromList(it)
TabState.fromList(it)
}
}
151.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
IconResult.fromList(it)
RecoverableTab.fromList(it)
}
}
152.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CookiePartitionKey.fromList(it)
RecoverableBrowserState.fromList(it)
}
}
153.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
Cookie.fromList(it)
IconRequest.fromList(it)
}
}
154.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
HistoryItem.fromList(it)
ResourceSize.fromList(it)
}
}
155.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
HistoryState.fromList(it)
Resource.fromList(it)
}
}
156.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ReaderableState.fromList(it)
IconResult.fromList(it)
}
}
157.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SecurityInfoState.fromList(it)
CookiePartitionKey.fromList(it)
}
}
158.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it)
Cookie.fromList(it)
}
}
159.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it)
HistoryItem.fromList(it)
}
}
160.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it)
HistoryState.fromList(it)
}
}
161.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
WebExtensionData.fromList(it)
ReaderableState.fromList(it)
}
}
162.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoSuggestion.fromList(it)
SecurityInfoState.fromList(it)
}
}
163.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it)
}
}
164.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it)
}
}
165.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it)
}
}
166.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
WebExtensionData.fromList(it)
}
}
167.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoSuggestion.fromList(it)
}
}
168.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContent.fromList(it)
}
}
169.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoEngineSettings.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@@ -1266,114 +1402,138 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(136)
writeValue(stream, value.raw)
}
is TranslationOptions -> {
is TrackingProtectionPolicy -> {
stream.write(137)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is ReaderState -> {
is HttpsOnlyMode -> {
stream.write(138)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is LastMediaAccessState -> {
is ColorScheme -> {
stream.write(139)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is HistoryMetadataKey -> {
is CookieBannerHandlingMode -> {
stream.write(140)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is PackageCategoryValue -> {
is WebContentIsolationStrategy -> {
stream.write(141)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is ExternalPackage -> {
is TranslationOptions -> {
stream.write(142)
writeValue(stream, value.toList())
}
is LoadUrlFlagsValue -> {
is ReaderState -> {
stream.write(143)
writeValue(stream, value.toList())
}
is SourceValue -> {
is LastMediaAccessState -> {
stream.write(144)
writeValue(stream, value.toList())
}
is TabState -> {
is HistoryMetadataKey -> {
stream.write(145)
writeValue(stream, value.toList())
}
is RecoverableTab -> {
is PackageCategoryValue -> {
stream.write(146)
writeValue(stream, value.toList())
}
is RecoverableBrowserState -> {
is ExternalPackage -> {
stream.write(147)
writeValue(stream, value.toList())
}
is IconRequest -> {
is LoadUrlFlagsValue -> {
stream.write(148)
writeValue(stream, value.toList())
}
is ResourceSize -> {
is SourceValue -> {
stream.write(149)
writeValue(stream, value.toList())
}
is Resource -> {
is TabState -> {
stream.write(150)
writeValue(stream, value.toList())
}
is IconResult -> {
is RecoverableTab -> {
stream.write(151)
writeValue(stream, value.toList())
}
is CookiePartitionKey -> {
is RecoverableBrowserState -> {
stream.write(152)
writeValue(stream, value.toList())
}
is Cookie -> {
is IconRequest -> {
stream.write(153)
writeValue(stream, value.toList())
}
is HistoryItem -> {
is ResourceSize -> {
stream.write(154)
writeValue(stream, value.toList())
}
is HistoryState -> {
is Resource -> {
stream.write(155)
writeValue(stream, value.toList())
}
is ReaderableState -> {
is IconResult -> {
stream.write(156)
writeValue(stream, value.toList())
}
is SecurityInfoState -> {
is CookiePartitionKey -> {
stream.write(157)
writeValue(stream, value.toList())
}
is TabContentState -> {
is Cookie -> {
stream.write(158)
writeValue(stream, value.toList())
}
is FindResultState -> {
is HistoryItem -> {
stream.write(159)
writeValue(stream, value.toList())
}
is CustomSelectionAction -> {
is HistoryState -> {
stream.write(160)
writeValue(stream, value.toList())
}
is WebExtensionData -> {
is ReaderableState -> {
stream.write(161)
writeValue(stream, value.toList())
}
is GeckoSuggestion -> {
is SecurityInfoState -> {
stream.write(162)
writeValue(stream, value.toList())
}
is TabContent -> {
is TabContentState -> {
stream.write(163)
writeValue(stream, value.toList())
}
is FindResultState -> {
stream.write(164)
writeValue(stream, value.toList())
}
is CustomSelectionAction -> {
stream.write(165)
writeValue(stream, value.toList())
}
is WebExtensionData -> {
stream.write(166)
writeValue(stream, value.toList())
}
is GeckoSuggestion -> {
stream.write(167)
writeValue(stream, value.toList())
}
is TabContent -> {
stream.write(168)
writeValue(stream, value.toList())
}
is GeckoEngineSettings -> {
stream.write(169)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -1433,7 +1593,8 @@ interface GeckoBrowserApi {
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoEngineSettingsApi {
fun javaScriptEnabled(state: Boolean)
fun setDefaultSettings(settings: GeckoEngineSettings)
fun updateRuntimeSettings(settings: GeckoEngineSettings)
companion object {
/** The codec used by GeckoEngineSettingsApi. */
@@ -1445,13 +1606,31 @@ interface GeckoEngineSettingsApi {
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoEngineSettingsApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.javaScriptEnabled$separatedMessageChannelSuffix", codec)
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.setDefaultSettings$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val stateArg = args[0] as Boolean
val settingsArg = args[0] as GeckoEngineSettings
val wrapped: List<Any?> = try {
api.javaScriptEnabled(stateArg)
api.setDefaultSettings(settingsArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.updateRuntimeSettings$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val settingsArg = args[0] as GeckoEngineSettings
val wrapped: List<Any?> = try {
api.updateRuntimeSettings(settingsArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
@@ -2271,6 +2450,83 @@ interface GeckoIconsApi {
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoPrefApi {
fun getPrefs(preferenceFilter: List<String>?, callback: (Result<Map<String, Any>>) -> Unit)
fun applyPrefs(prefBuffer: String, callback: (Result<Map<String, Any>>) -> Unit)
fun resetPrefs(preferenceNames: List<String>?, callback: (Result<Unit>) -> Unit)
companion object {
/** The codec used by GeckoPrefApi. */
val codec: MessageCodec<Any?> by lazy {
GeckoPigeonCodec()
}
/** Sets up an instance of `GeckoPrefApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoPrefApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.getPrefs$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val preferenceFilterArg = args[0] as List<String>?
api.getPrefs(preferenceFilterArg) { result: Result<Map<String, Any>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.applyPrefs$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val prefBufferArg = args[0] as String
api.applyPrefs(prefBufferArg) { result: Result<Map<String, Any>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.resetPrefs$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val preferenceNamesArg = args[0] as List<String>?
api.resetPrefs(preferenceNamesArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoCookieApi {
fun getCookie(firstPartyDomain: String?, name: String, partitionKey: CookiePartitionKey?, storeId: String?, url: String, callback: (Result<Cookie>) -> Unit)
fun getAllCookies(domain: String?, firstPartyDomain: String?, name: String?, partitionKey: CookiePartitionKey?, storeId: String?, url: String, callback: (Result<List<Cookie>>) -> Unit)
@@ -22,6 +22,8 @@
<string name="pref_key_tracking_protection_normal" translatable="false">pref_key_tracking_protection_normal</string>
<string name="pref_key_tracking_protection_private" translatable="false">pref_key_tracking_protection_private</string>
<string name="pref_key_global_privacy_control" translatable="false">pref_key_global_privacy_control</string>
<string name="pref_key_fingerprinting_protection" translatable="false">pref_key_fingerprinting_protection</string>
<string name="pref_key_enterprise_roots_enabled" translatable="false">pref_key_enterprise_roots_enabled</string>
<string name="pref_key_launch_external_app" translatable="false">pref_key_launch_external_app</string>
<string name="pref_key_override_amo_collection" translatable="false">pref_key_override_amo_collection</string>
<string name="pref_key_override_amo_user" translatable="false">pref_key_override_amo_user</string>
@@ -8,6 +8,7 @@ export 'src/domain/services/gecko_engine_settings.dart';
export 'src/domain/services/gecko_event.dart';
export 'src/domain/services/gecko_find_in_page.dart';
export 'src/domain/services/gecko_icon.dart';
export 'src/domain/services/gecko_pref.dart';
export 'src/domain/services/gecko_readerable.dart';
export 'src/domain/services/gecko_selection_action.dart';
export 'src/domain/services/gecko_session.dart';
@@ -17,10 +18,14 @@ export 'src/domain/services/gecko_tab_content.dart';
export 'src/geckoview_widget.dart';
export 'src/pigeons/gecko.g.dart'
show
ColorScheme,
CookieBannerHandlingMode,
CookieSameSiteStatus,
GeckoEngineSettings,
GeckoSuggestion,
GeckoSuggestionType,
HistoryMetadataKey,
HttpsOnlyMode,
IconSource,
IconType,
Resource,
@@ -28,5 +33,7 @@ export 'src/pigeons/gecko.g.dart'
SecurityInfoState,
TabContent,
TabContentState,
TrackingProtectionPolicy,
WebContentIsolationStrategy,
WebExtensionActionType,
WebExtensionData;
@@ -0,0 +1,32 @@
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
final _apiInstance = GeckoPrefApi();
class GeckoPrefService {
Future<Map<String, Object>> getAllPrefs() {
return _apiInstance.getPrefs(null);
}
Future<Map<String, Object>> getPrefs(List<String> prefs) {
return _apiInstance.getPrefs(prefs);
}
Future<Map<String, Object>> applyPrefs(Map<String, Object> prefs) {
final buffer = prefs.entries.map((pref) {
final value = switch (pref.value) {
final bool x => '$x',
final int x => '$x',
final String x => '"$x"',
_ => throw Exception('Unknow pref type')
};
return 'user_pref("${pref.key}", $value);';
}).join();
return _apiInstance.applyPrefs(buffer);
}
Future<void> resetPrefs(List<String> prefs) {
return _apiInstance.resetPrefs(prefs);
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.7.2), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
@@ -94,6 +94,37 @@ enum GeckoSuggestionType {
history,
}
enum TrackingProtectionPolicy {
none,
recommended,
strict,
custom,
}
enum HttpsOnlyMode {
disabled,
privateOnly,
enabled,
}
enum ColorScheme {
system,
light,
dark,
}
enum CookieBannerHandlingMode {
disabled,
rejectAll,
rejectOrAcceptAll,
}
enum WebContentIsolationStrategy {
isolateNothing,
isolateEverything,
isolateHighValue,
}
/// Translation options that map to the Gecko Translations Options.
///
/// @property downloadModel If the necessary models should be downloaded on request. If false, then
@@ -1176,6 +1207,72 @@ class TabContent {
}
}
class GeckoEngineSettings {
GeckoEngineSettings({
this.javascriptEnabled,
this.trackingProtectionPolicy,
this.httpsOnlyMode,
this.globalPrivacyControlEnabled,
this.preferredColorScheme,
this.cookieBannerHandlingMode,
this.cookieBannerHandlingModePrivateBrowsing,
this.cookieBannerHandlingGlobalRules,
this.cookieBannerHandlingGlobalRulesSubFrames,
this.webContentIsolationStrategy,
});
bool? javascriptEnabled;
TrackingProtectionPolicy? trackingProtectionPolicy;
HttpsOnlyMode? httpsOnlyMode;
bool? globalPrivacyControlEnabled;
ColorScheme? preferredColorScheme;
CookieBannerHandlingMode? cookieBannerHandlingMode;
CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing;
bool? cookieBannerHandlingGlobalRules;
bool? cookieBannerHandlingGlobalRulesSubFrames;
WebContentIsolationStrategy? webContentIsolationStrategy;
Object encode() {
return <Object?>[
javascriptEnabled,
trackingProtectionPolicy,
httpsOnlyMode,
globalPrivacyControlEnabled,
preferredColorScheme,
cookieBannerHandlingMode,
cookieBannerHandlingModePrivateBrowsing,
cookieBannerHandlingGlobalRules,
cookieBannerHandlingGlobalRulesSubFrames,
webContentIsolationStrategy,
];
}
static GeckoEngineSettings decode(Object result) {
result as List<Object?>;
return GeckoEngineSettings(
javascriptEnabled: result[0] as bool?,
trackingProtectionPolicy: result[1] as TrackingProtectionPolicy?,
httpsOnlyMode: result[2] as HttpsOnlyMode?,
globalPrivacyControlEnabled: result[3] as bool?,
preferredColorScheme: result[4] as ColorScheme?,
cookieBannerHandlingMode: result[5] as CookieBannerHandlingMode?,
cookieBannerHandlingModePrivateBrowsing: result[6] as CookieBannerHandlingMode?,
cookieBannerHandlingGlobalRules: result[7] as bool?,
cookieBannerHandlingGlobalRulesSubFrames: result[8] as bool?,
webContentIsolationStrategy: result[9] as WebContentIsolationStrategy?,
);
}
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@@ -1208,87 +1305,105 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is GeckoSuggestionType) {
buffer.putUint8(136);
writeValue(buffer, value.index);
} else if (value is TranslationOptions) {
} else if (value is TrackingProtectionPolicy) {
buffer.putUint8(137);
writeValue(buffer, value.encode());
} else if (value is ReaderState) {
writeValue(buffer, value.index);
} else if (value is HttpsOnlyMode) {
buffer.putUint8(138);
writeValue(buffer, value.encode());
} else if (value is LastMediaAccessState) {
writeValue(buffer, value.index);
} else if (value is ColorScheme) {
buffer.putUint8(139);
writeValue(buffer, value.encode());
} else if (value is HistoryMetadataKey) {
writeValue(buffer, value.index);
} else if (value is CookieBannerHandlingMode) {
buffer.putUint8(140);
writeValue(buffer, value.encode());
} else if (value is PackageCategoryValue) {
writeValue(buffer, value.index);
} else if (value is WebContentIsolationStrategy) {
buffer.putUint8(141);
writeValue(buffer, value.encode());
} else if (value is ExternalPackage) {
writeValue(buffer, value.index);
} else if (value is TranslationOptions) {
buffer.putUint8(142);
writeValue(buffer, value.encode());
} else if (value is LoadUrlFlagsValue) {
} else if (value is ReaderState) {
buffer.putUint8(143);
writeValue(buffer, value.encode());
} else if (value is SourceValue) {
} else if (value is LastMediaAccessState) {
buffer.putUint8(144);
writeValue(buffer, value.encode());
} else if (value is TabState) {
} else if (value is HistoryMetadataKey) {
buffer.putUint8(145);
writeValue(buffer, value.encode());
} else if (value is RecoverableTab) {
} else if (value is PackageCategoryValue) {
buffer.putUint8(146);
writeValue(buffer, value.encode());
} else if (value is RecoverableBrowserState) {
} else if (value is ExternalPackage) {
buffer.putUint8(147);
writeValue(buffer, value.encode());
} else if (value is IconRequest) {
} else if (value is LoadUrlFlagsValue) {
buffer.putUint8(148);
writeValue(buffer, value.encode());
} else if (value is ResourceSize) {
} else if (value is SourceValue) {
buffer.putUint8(149);
writeValue(buffer, value.encode());
} else if (value is Resource) {
} else if (value is TabState) {
buffer.putUint8(150);
writeValue(buffer, value.encode());
} else if (value is IconResult) {
} else if (value is RecoverableTab) {
buffer.putUint8(151);
writeValue(buffer, value.encode());
} else if (value is CookiePartitionKey) {
} else if (value is RecoverableBrowserState) {
buffer.putUint8(152);
writeValue(buffer, value.encode());
} else if (value is Cookie) {
} else if (value is IconRequest) {
buffer.putUint8(153);
writeValue(buffer, value.encode());
} else if (value is HistoryItem) {
} else if (value is ResourceSize) {
buffer.putUint8(154);
writeValue(buffer, value.encode());
} else if (value is HistoryState) {
} else if (value is Resource) {
buffer.putUint8(155);
writeValue(buffer, value.encode());
} else if (value is ReaderableState) {
} else if (value is IconResult) {
buffer.putUint8(156);
writeValue(buffer, value.encode());
} else if (value is SecurityInfoState) {
} else if (value is CookiePartitionKey) {
buffer.putUint8(157);
writeValue(buffer, value.encode());
} else if (value is TabContentState) {
} else if (value is Cookie) {
buffer.putUint8(158);
writeValue(buffer, value.encode());
} else if (value is FindResultState) {
} else if (value is HistoryItem) {
buffer.putUint8(159);
writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) {
} else if (value is HistoryState) {
buffer.putUint8(160);
writeValue(buffer, value.encode());
} else if (value is WebExtensionData) {
} else if (value is ReaderableState) {
buffer.putUint8(161);
writeValue(buffer, value.encode());
} else if (value is GeckoSuggestion) {
} else if (value is SecurityInfoState) {
buffer.putUint8(162);
writeValue(buffer, value.encode());
} else if (value is TabContent) {
} else if (value is TabContentState) {
buffer.putUint8(163);
writeValue(buffer, value.encode());
} else if (value is FindResultState) {
buffer.putUint8(164);
writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) {
buffer.putUint8(165);
writeValue(buffer, value.encode());
} else if (value is WebExtensionData) {
buffer.putUint8(166);
writeValue(buffer, value.encode());
} else if (value is GeckoSuggestion) {
buffer.putUint8(167);
writeValue(buffer, value.encode());
} else if (value is TabContent) {
buffer.putUint8(168);
writeValue(buffer, value.encode());
} else if (value is GeckoEngineSettings) {
buffer.putUint8(169);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -1322,59 +1437,76 @@ class _PigeonCodec extends StandardMessageCodec {
final int? value = readValue(buffer) as int?;
return value == null ? null : GeckoSuggestionType.values[value];
case 137:
return TranslationOptions.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : TrackingProtectionPolicy.values[value];
case 138:
return ReaderState.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : HttpsOnlyMode.values[value];
case 139:
return LastMediaAccessState.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : ColorScheme.values[value];
case 140:
return HistoryMetadataKey.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : CookieBannerHandlingMode.values[value];
case 141:
return PackageCategoryValue.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : WebContentIsolationStrategy.values[value];
case 142:
return ExternalPackage.decode(readValue(buffer)!);
return TranslationOptions.decode(readValue(buffer)!);
case 143:
return LoadUrlFlagsValue.decode(readValue(buffer)!);
return ReaderState.decode(readValue(buffer)!);
case 144:
return SourceValue.decode(readValue(buffer)!);
return LastMediaAccessState.decode(readValue(buffer)!);
case 145:
return TabState.decode(readValue(buffer)!);
return HistoryMetadataKey.decode(readValue(buffer)!);
case 146:
return RecoverableTab.decode(readValue(buffer)!);
return PackageCategoryValue.decode(readValue(buffer)!);
case 147:
return RecoverableBrowserState.decode(readValue(buffer)!);
return ExternalPackage.decode(readValue(buffer)!);
case 148:
return IconRequest.decode(readValue(buffer)!);
return LoadUrlFlagsValue.decode(readValue(buffer)!);
case 149:
return ResourceSize.decode(readValue(buffer)!);
return SourceValue.decode(readValue(buffer)!);
case 150:
return Resource.decode(readValue(buffer)!);
return TabState.decode(readValue(buffer)!);
case 151:
return IconResult.decode(readValue(buffer)!);
return RecoverableTab.decode(readValue(buffer)!);
case 152:
return CookiePartitionKey.decode(readValue(buffer)!);
return RecoverableBrowserState.decode(readValue(buffer)!);
case 153:
return Cookie.decode(readValue(buffer)!);
return IconRequest.decode(readValue(buffer)!);
case 154:
return HistoryItem.decode(readValue(buffer)!);
return ResourceSize.decode(readValue(buffer)!);
case 155:
return HistoryState.decode(readValue(buffer)!);
return Resource.decode(readValue(buffer)!);
case 156:
return ReaderableState.decode(readValue(buffer)!);
return IconResult.decode(readValue(buffer)!);
case 157:
return SecurityInfoState.decode(readValue(buffer)!);
return CookiePartitionKey.decode(readValue(buffer)!);
case 158:
return TabContentState.decode(readValue(buffer)!);
return Cookie.decode(readValue(buffer)!);
case 159:
return FindResultState.decode(readValue(buffer)!);
return HistoryItem.decode(readValue(buffer)!);
case 160:
return CustomSelectionAction.decode(readValue(buffer)!);
return HistoryState.decode(readValue(buffer)!);
case 161:
return WebExtensionData.decode(readValue(buffer)!);
return ReaderableState.decode(readValue(buffer)!);
case 162:
return GeckoSuggestion.decode(readValue(buffer)!);
return SecurityInfoState.decode(readValue(buffer)!);
case 163:
return TabContentState.decode(readValue(buffer)!);
case 164:
return FindResultState.decode(readValue(buffer)!);
case 165:
return CustomSelectionAction.decode(readValue(buffer)!);
case 166:
return WebExtensionData.decode(readValue(buffer)!);
case 167:
return GeckoSuggestion.decode(readValue(buffer)!);
case 168:
return TabContent.decode(readValue(buffer)!);
case 169:
return GeckoEngineSettings.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
@@ -1452,15 +1584,37 @@ class GeckoEngineSettingsApi {
final String pigeonVar_messageChannelSuffix;
Future<void> javaScriptEnabled(bool state) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.javaScriptEnabled$pigeonVar_messageChannelSuffix';
Future<void> setDefaultSettings(GeckoEngineSettings settings) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.setDefaultSettings$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[state]) as List<Object?>?;
await pigeonVar_channel.send(<Object?>[settings]) 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;
}
}
Future<void> updateRuntimeSettings(GeckoEngineSettings settings) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.updateRuntimeSettings$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[settings]) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -2378,6 +2532,96 @@ class GeckoIconsApi {
}
}
class GeckoPrefApi {
/// Constructor for [GeckoPrefApi]. 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.
GeckoPrefApi({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;
Future<Map<String, Object>> getPrefs(List<String>? preferenceFilter) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.getPrefs$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[preferenceFilter]) 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 Map<Object?, Object?>?)!.cast<String, Object>();
}
}
Future<Map<String, Object>> applyPrefs(String prefBuffer) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.applyPrefs$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[prefBuffer]) 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 Map<Object?, Object?>?)!.cast<String, Object>();
}
}
Future<void> resetPrefs(List<String>? preferenceNames) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.resetPrefs$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[preferenceNames]) 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;
}
}
}
class GeckoCookieApi {
/// Constructor for [GeckoCookieApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
@@ -539,6 +539,63 @@ class TabContent {
);
}
enum TrackingProtectionPolicy {
none,
recommended,
strict,
custom,
}
enum HttpsOnlyMode {
disabled,
privateOnly,
enabled,
}
enum ColorScheme {
system,
light,
dark,
}
enum CookieBannerHandlingMode {
disabled,
rejectAll,
rejectOrAcceptAll,
}
enum WebContentIsolationStrategy {
isolateNothing,
isolateEverything,
isolateHighValue,
}
class GeckoEngineSettings {
final bool? javascriptEnabled;
final TrackingProtectionPolicy? trackingProtectionPolicy;
final HttpsOnlyMode? httpsOnlyMode;
final bool? globalPrivacyControlEnabled;
final ColorScheme? preferredColorScheme;
final CookieBannerHandlingMode? cookieBannerHandlingMode;
final CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing;
final bool? cookieBannerHandlingGlobalRules;
final bool? cookieBannerHandlingGlobalRulesSubFrames;
final WebContentIsolationStrategy? webContentIsolationStrategy;
GeckoEngineSettings(
this.javascriptEnabled,
this.trackingProtectionPolicy,
this.httpsOnlyMode,
this.globalPrivacyControlEnabled,
this.preferredColorScheme,
this.cookieBannerHandlingMode,
this.cookieBannerHandlingModePrivateBrowsing,
this.cookieBannerHandlingGlobalRules,
this.cookieBannerHandlingGlobalRulesSubFrames,
this.webContentIsolationStrategy,
);
}
// /// Represents all the different supported types of data that can be found from long clicking
// /// an element.
// sealed class HitResult {
@@ -614,7 +671,8 @@ abstract class GeckoBrowserApi {
@HostApi()
abstract class GeckoEngineSettingsApi {
void javaScriptEnabled(bool state);
void setDefaultSettings(GeckoEngineSettings settings);
void updateRuntimeSettings(GeckoEngineSettings settings);
}
@HostApi()
@@ -804,6 +862,16 @@ abstract class GeckoIconsApi {
IconResult loadIcon(IconRequest request);
}
@HostApi()
abstract class GeckoPrefApi {
@async
Map<String, Object> getPrefs(List<String>? preferenceFilter);
@async
Map<String, Object> applyPrefs(String prefBuffer);
@async
void resetPrefs(List<String>? preferenceNames);
}
@HostApi()
abstract class GeckoCookieApi {
@async
@@ -17,7 +17,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
lint: ^2.3.0
pigeon: ^22.7.3
pigeon: ^22.7.4
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"macos":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"windows":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]},{"name":"sqlite3_flutter_libs","dependencies":[]},{"name":"sqlite3_vec","dependencies":[]}],"date_created":"2025-01-22 22:25:30.421767","version":"3.27.1","swift_package_manager_enabled":false}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"macos":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"windows":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]},{"name":"sqlite3_flutter_libs","dependencies":[]},{"name":"sqlite3_vec","dependencies":[]}],"date_created":"2025-01-26 00:44:40.809594","version":"3.27.3","swift_package_manager_enabled":false}