diff --git a/app/lib/features/geckoview/domain/entities/states/tab.dart b/app/lib/features/geckoview/domain/entities/states/tab.dart
index 0817d3dc..bec0acc5 100644
--- a/app/lib/features/geckoview/domain/entities/states/tab.dart
+++ b/app/lib/features/geckoview/domain/entities/states/tab.dart
@@ -27,6 +27,7 @@ import 'package:weblibre/features/geckoview/domain/entities/states/find_result.d
import 'package:weblibre/features/geckoview/domain/entities/states/history.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/readerable.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/security.dart';
+import 'package:weblibre/features/geckoview/domain/entities/states/translation.dart';
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_mode.dart';
part 'tab.g.dart';
@@ -75,6 +76,7 @@ class TabState extends WebPageInfo {
final HistoryState historyState;
final ReaderableState readerableState;
final FindResultState findResultState;
+ final TranslationState translationState;
TabState({
required this.id,
@@ -93,6 +95,7 @@ class TabState extends WebPageInfo {
required this.historyState,
required this.readerableState,
required this.findResultState,
+ required this.translationState,
}) : super(title: title.trim());
factory TabState.$default(String tabId) => TabState(
@@ -111,6 +114,7 @@ class TabState extends WebPageInfo {
historyState: HistoryState.$default(),
readerableState: ReaderableState.$default(),
findResultState: FindResultState.$default(),
+ translationState: TranslationState.$default(),
);
@override
@@ -130,5 +134,6 @@ class TabState extends WebPageInfo {
historyState,
readerableState,
findResultState,
+ translationState,
];
}
diff --git a/app/lib/features/geckoview/domain/entities/states/tab.g.dart b/app/lib/features/geckoview/domain/entities/states/tab.g.dart
index 49eac367..811ff204 100644
--- a/app/lib/features/geckoview/domain/entities/states/tab.g.dart
+++ b/app/lib/features/geckoview/domain/entities/states/tab.g.dart
@@ -37,6 +37,8 @@ abstract class _$TabStateCWProxy {
TabState findResultState(FindResultState findResultState);
+ TabState translationState(TranslationState translationState);
+
/// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `TabState(...).copyWith.fieldName(value)`.
///
@@ -60,6 +62,7 @@ abstract class _$TabStateCWProxy {
HistoryState historyState,
ReaderableState readerableState,
FindResultState findResultState,
+ TranslationState translationState,
});
}
@@ -120,6 +123,10 @@ class _$TabStateCWProxyImpl implements _$TabStateCWProxy {
TabState findResultState(FindResultState findResultState) =>
call(findResultState: findResultState);
+ @override
+ TabState translationState(TranslationState translationState) =>
+ call(translationState: translationState);
+
@override
/// Creates a new instance with the provided field values.
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `TabState(...).copyWith.fieldName(value)`.
@@ -144,6 +151,7 @@ class _$TabStateCWProxyImpl implements _$TabStateCWProxy {
Object? historyState = const $CopyWithPlaceholder(),
Object? readerableState = const $CopyWithPlaceholder(),
Object? findResultState = const $CopyWithPlaceholder(),
+ Object? translationState = const $CopyWithPlaceholder(),
}) {
return TabState(
id: _value.id,
@@ -217,6 +225,12 @@ class _$TabStateCWProxyImpl implements _$TabStateCWProxy {
? _value.findResultState
// ignore: cast_nullable_to_non_nullable
: findResultState as FindResultState,
+ translationState:
+ translationState == const $CopyWithPlaceholder() ||
+ translationState == null
+ ? _value.translationState
+ // ignore: cast_nullable_to_non_nullable
+ : translationState as TranslationState,
);
}
}
diff --git a/app/lib/features/geckoview/domain/entities/states/translation.dart b/app/lib/features/geckoview/domain/entities/states/translation.dart
new file mode 100644
index 00000000..0176da73
--- /dev/null
+++ b/app/lib/features/geckoview/domain/entities/states/translation.dart
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2024-2026 Fabian Freund.
+ *
+ * This file is part of WebLibre
+ * (see https://weblibre.eu).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+import 'package:fast_equatable/fast_equatable.dart';
+import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
+
+class TranslationState with FastEquatable {
+ final bool isTranslated;
+ final bool isTranslateProcessing;
+ final bool isOfferTranslate;
+ final bool isExpectedTranslate;
+ final String? detectedLanguageCode;
+ final String? userPreferredLanguageCode;
+ final String? requestedFromLanguage;
+ final String? requestedToLanguage;
+ final String? translationErrorName;
+ final bool? displayError;
+
+ TranslationState({
+ required this.isTranslated,
+ required this.isTranslateProcessing,
+ required this.isOfferTranslate,
+ required this.isExpectedTranslate,
+ this.detectedLanguageCode,
+ this.userPreferredLanguageCode,
+ this.requestedFromLanguage,
+ this.requestedToLanguage,
+ this.translationErrorName,
+ this.displayError,
+ });
+
+ factory TranslationState.$default() => TranslationState(
+ isTranslated: false,
+ isTranslateProcessing: false,
+ isOfferTranslate: false,
+ isExpectedTranslate: false,
+ );
+
+ factory TranslationState.fromData(TabTranslationStateData data) =>
+ TranslationState(
+ isTranslated: data.isTranslated,
+ isTranslateProcessing: data.isTranslateProcessing,
+ isOfferTranslate: data.isOfferTranslate,
+ isExpectedTranslate: data.isExpectedTranslate,
+ detectedLanguageCode: data.detectedLanguageCode,
+ userPreferredLanguageCode: data.userPreferredLanguageCode,
+ requestedFromLanguage: data.requestedFromLanguage,
+ requestedToLanguage: data.requestedToLanguage,
+ translationErrorName: data.translationErrorName,
+ displayError: data.displayError,
+ );
+
+ bool get hasError =>
+ translationErrorName != null && (displayError ?? true);
+
+ @override
+ List get hashParameters => [
+ isTranslated,
+ isTranslateProcessing,
+ isOfferTranslate,
+ isExpectedTranslate,
+ detectedLanguageCode,
+ userPreferredLanguageCode,
+ requestedFromLanguage,
+ requestedToLanguage,
+ translationErrorName,
+ displayError,
+ ];
+}
diff --git a/app/lib/features/geckoview/domain/providers.dart b/app/lib/features/geckoview/domain/providers.dart
index 835d8628..4bf08f0a 100644
--- a/app/lib/features/geckoview/domain/providers.dart
+++ b/app/lib/features/geckoview/domain/providers.dart
@@ -239,6 +239,27 @@ Stream mlProgressEvents(Ref ref) {
return service.mlProgressEvents;
}
+/// Translation engine state (browser-level: supported languages, engine availability)
+@Riverpod(keepAlive: true)
+class TranslationEngineState extends _$TranslationEngineState {
+ @override
+ TranslationEngineStateData? build() {
+ final eventService = ref.watch(eventServiceProvider);
+
+ final sub = eventService.translationEngineEvents.listen((value) {
+ if (ref.mounted) {
+ state = value;
+ }
+ });
+
+ ref.onDispose(() async {
+ await sub.cancel();
+ });
+
+ return eventService.translationEngineEvents.valueOrNull;
+ }
+}
+
/// Tracks active ML model downloads
@Riverpod()
class MlDownloadState extends _$MlDownloadState {
diff --git a/app/lib/features/geckoview/domain/providers.g.dart b/app/lib/features/geckoview/domain/providers.g.dart
index 451377e1..457766fc 100644
--- a/app/lib/features/geckoview/domain/providers.g.dart
+++ b/app/lib/features/geckoview/domain/providers.g.dart
@@ -389,6 +389,72 @@ final class MlProgressEventsProvider
String _$mlProgressEventsHash() => r'41c1e6aece7f9ee2bebe5d189c6bec753c2fefc8';
+/// Translation engine state (browser-level: supported languages, engine availability)
+
+@ProviderFor(TranslationEngineState)
+final translationEngineStateProvider = TranslationEngineStateProvider._();
+
+/// Translation engine state (browser-level: supported languages, engine availability)
+final class TranslationEngineStateProvider
+ extends
+ $NotifierProvider {
+ /// Translation engine state (browser-level: supported languages, engine availability)
+ TranslationEngineStateProvider._()
+ : super(
+ from: null,
+ argument: null,
+ retry: null,
+ name: r'translationEngineStateProvider',
+ isAutoDispose: false,
+ dependencies: null,
+ $allTransitiveDependencies: null,
+ );
+
+ @override
+ String debugGetCreateSourceHash() => _$translationEngineStateHash();
+
+ @$internal
+ @override
+ TranslationEngineState create() => TranslationEngineState();
+
+ /// {@macro riverpod.override_with_value}
+ Override overrideWithValue(TranslationEngineStateData? value) {
+ return $ProviderOverride(
+ origin: this,
+ providerOverride: $SyncValueProvider(value),
+ );
+ }
+}
+
+String _$translationEngineStateHash() =>
+ r'04592e18715b0e237e9bc027287a880a4bb023da';
+
+/// Translation engine state (browser-level: supported languages, engine availability)
+
+abstract class _$TranslationEngineState
+ extends $Notifier {
+ TranslationEngineStateData? build();
+ @$mustCallSuper
+ @override
+ void runBuild() {
+ final ref =
+ this.ref
+ as $Ref;
+ final element =
+ ref.element
+ as $ClassProviderElement<
+ AnyNotifier<
+ TranslationEngineStateData?,
+ TranslationEngineStateData?
+ >,
+ TranslationEngineStateData?,
+ Object?,
+ Object?
+ >;
+ element.handleCreate(ref, build);
+ }
+}
+
/// Tracks active ML model downloads
@ProviderFor(MlDownloadState)
diff --git a/app/lib/features/geckoview/domain/providers/tab_session.dart b/app/lib/features/geckoview/domain/providers/tab_session.dart
index c485026b..be323f89 100644
--- a/app/lib/features/geckoview/domain/providers/tab_session.dart
+++ b/app/lib/features/geckoview/domain/providers/tab_session.dart
@@ -76,6 +76,26 @@ class TabSession extends _$TabSession {
return _sessionService.saveToPdf();
}
+ Future printContent() {
+ return _sessionService.printContent();
+ }
+
+ Future translate({
+ required String fromLanguage,
+ required String toLanguage,
+ bool? downloadModel,
+ }) {
+ return _sessionService.translate(
+ fromLanguage: fromLanguage,
+ toLanguage: toLanguage,
+ options: TranslationOptions(downloadModel: downloadModel ?? true),
+ );
+ }
+
+ Future translateRestore() {
+ return _sessionService.translateRestore();
+ }
+
@override
void build({required String? tabId}) {
_sessionService = (tabId != null)
diff --git a/app/lib/features/geckoview/domain/providers/tab_session.g.dart b/app/lib/features/geckoview/domain/providers/tab_session.g.dart
index ab9e0f5b..6b4772c5 100644
--- a/app/lib/features/geckoview/domain/providers/tab_session.g.dart
+++ b/app/lib/features/geckoview/domain/providers/tab_session.g.dart
@@ -57,7 +57,7 @@ final class TabSessionProvider extends $NotifierProvider {
}
}
-String _$tabSessionHash() => r'5be471574bb868889cc1eee37ae7e4570a9e21a4';
+String _$tabSessionHash() => r'b9381c334672fd3f733a4cc86ce696fb7de6fa5d';
final class TabSessionFamily extends $Family
with $ClassFamilyOverride {
diff --git a/app/lib/features/geckoview/domain/providers/tab_state.dart b/app/lib/features/geckoview/domain/providers/tab_state.dart
index 07495c9e..84879d9b 100644
--- a/app/lib/features/geckoview/domain/providers/tab_state.dart
+++ b/app/lib/features/geckoview/domain/providers/tab_state.dart
@@ -31,6 +31,7 @@ import 'package:weblibre/features/geckoview/domain/entities/states/history.dart'
import 'package:weblibre/features/geckoview/domain/entities/states/readerable.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/security.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
+import 'package:weblibre/features/geckoview/domain/entities/states/translation.dart';
import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/domain/repositories/find_in_page.dart';
@@ -203,6 +204,16 @@ class TabStates extends _$TabStates {
);
}
+ void _onTabTranslationStateChange(TabTranslationEvent event) {
+ final TabTranslationEvent(:tabId, :state) = event;
+
+ final current = this.state[tabId] ?? TabState.$default(tabId);
+ this.state = {...this.state}
+ ..[tabId] = current.copyWith.translationState(
+ TranslationState.fromData(state),
+ );
+ }
+
void _onFindResultsChange(FindResultsEvent event) {
final FindResultsEvent(:tabId, :results) = event;
final current = state[tabId] ?? TabState.$default(tabId);
@@ -317,6 +328,18 @@ class TabStates extends _$TabStates {
);
},
),
+ eventService.tabTranslationEvents.listen(
+ (event) {
+ _onTabTranslationStateChange(event);
+ },
+ onError: (Object error, StackTrace stackTrace) {
+ logger.e(
+ 'Error in tab translation events',
+ error: error,
+ stackTrace: stackTrace,
+ );
+ },
+ ),
];
ref.listen(
@@ -331,6 +354,7 @@ class TabStates extends _$TabStates {
onSecurityInfoStateChange: true,
onHistoryStateChange: true,
onFindResults: true,
+ onTranslationStateChange: true,
);
}
},
diff --git a/app/lib/features/geckoview/domain/providers/tab_state.g.dart b/app/lib/features/geckoview/domain/providers/tab_state.g.dart
index 498f9927..460881a6 100644
--- a/app/lib/features/geckoview/domain/providers/tab_state.g.dart
+++ b/app/lib/features/geckoview/domain/providers/tab_state.g.dart
@@ -41,7 +41,7 @@ final class TabStatesProvider
}
}
-String _$tabStatesHash() => r'0fd97193ff963006e1943436b25b025dd3885c65';
+String _$tabStatesHash() => r'6fc9a91b1343f9ae0b442100bed20e42448afa37';
abstract class _$TabStates extends $Notifier> {
Map build();
diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart
index 8fc2d5d4..c5f87986 100644
--- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart
+++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_menu_sheet.dart
@@ -49,6 +49,7 @@ import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/content_selection_dialog.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/qr_code.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_badge_icon.dart';
+import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
import 'package:weblibre/features/geckoview/features/open_link_tools/domain/services/url_cleaner_catalog_service.dart';
import 'package:weblibre/features/geckoview/features/open_link_tools/presentation/hooks/url_cleaner_controller.dart';
@@ -395,6 +396,9 @@ class _PageActionsCard extends HookConsumerWidget {
},
),
+ // Translate Page
+ _TranslatePageTile(selectedTabId: selectedTabId),
+
// Add to Home Screen (conditional)
_AddToHomeScreenTile(selectedTabId: selectedTabId),
],
@@ -571,6 +575,48 @@ class _AddToHomeScreenTile extends ConsumerWidget {
}
}
+class _TranslatePageTile extends ConsumerWidget {
+ final String selectedTabId;
+
+ const _TranslatePageTile({required this.selectedTabId});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final engineState = ref.watch(translationEngineStateProvider);
+ final translationState = ref.watch(
+ tabStateProvider(selectedTabId).select((s) => s?.translationState),
+ );
+ final readerActive = ref.watch(
+ tabStateProvider(selectedTabId)
+ .select((s) => s?.readerableState.active ?? false),
+ );
+
+ // Hide when reader mode is active (Fenix-aligned)
+ if (readerActive || engineState?.isEngineSupported != true) {
+ return const SizedBox.shrink();
+ }
+
+ final isTranslated = translationState?.isTranslated ?? false;
+
+ return ListTile(
+ leading: Icon(
+ Icons.translate,
+ color: isTranslated ? Theme.of(context).colorScheme.primary : null,
+ ),
+ title: Text(isTranslated ? 'Translated' : 'Translate Page'),
+ onTap: () async {
+ Navigator.pop(context);
+ if (context.mounted) {
+ await showTranslationBottomSheet(
+ context,
+ selectedTabId: selectedTabId,
+ );
+ }
+ },
+ );
+ }
+}
+
class _FetchFeedsTile extends HookConsumerWidget {
final String selectedTabId;
@@ -1385,6 +1431,24 @@ class _ExportExpansion extends ConsumerWidget {
if (context.mounted) Navigator.pop(context);
},
),
+
+ // Print
+ _buildSubTile(
+ 'Print',
+ icon: MdiIcons.printer,
+ onTap: () async {
+ try {
+ await ref
+ .read(tabSessionProvider(tabId: selectedTabId).notifier)
+ .printContent();
+ } catch (e) {
+ if (context.mounted) {
+ ui_helper.showErrorMessage(context, 'Failed to print page');
+ }
+ }
+ if (context.mounted) Navigator.pop(context);
+ },
+ ),
],
),
);
diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart
index e92b860e..aed6cdab 100644
--- a/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart
+++ b/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart
@@ -109,6 +109,36 @@ class SaveToPdfMenuItemButton extends HookConsumerWidget {
}
}
+class PrintMenuItemButton extends HookConsumerWidget {
+ const PrintMenuItemButton({super.key, required this.selectedTabId});
+
+ final String? selectedTabId;
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ return MenuItemButton(
+ leadingIcon: const Icon(MdiIcons.printer),
+ closeOnActivate: false,
+ onPressed: () async {
+ try {
+ await ref
+ .read(tabSessionProvider(tabId: selectedTabId).notifier)
+ .printContent();
+ } catch (e) {
+ if (context.mounted) {
+ ui_helper.showErrorMessage(context, 'Failed to print page');
+ }
+ }
+
+ if (context.mounted) {
+ MenuController.maybeOf(context)?.close();
+ }
+ },
+ child: const Text('Print'),
+ );
+ }
+}
+
class ShareMarkdownActionMenuItemButton extends HookConsumerWidget {
const ShareMarkdownActionMenuItemButton({
super.key,
diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart
index 206beeeb..c9214ea6 100644
--- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart
+++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_menu.dart
@@ -31,12 +31,14 @@ import 'package:weblibre/core/design/app_colors.dart';
import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart';
import 'package:weblibre/features/geckoview/domain/entities/tab_container_selection.dart';
+import 'package:weblibre/features/geckoview/domain/providers.dart';
import 'package:weblibre/features/geckoview/domain/providers/desktop_mode.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/navigation_buttons.dart';
+import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart';
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/controllers/find_in_page.dart';
import 'package:weblibre/features/geckoview/features/pwa/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/pwa/presentation/widgets/pwa_install_button.dart';
@@ -577,10 +579,53 @@ class TabMenu extends HookConsumerWidget {
},
),
SaveToPdfMenuItemButton(selectedTabId: selectedTabId),
+ PrintMenuItemButton(selectedTabId: selectedTabId),
],
leadingIcon: const Icon(MdiIcons.fileExport),
child: const Text('Export'),
),
+ Consumer(
+ builder: (context, ref, child) {
+ final engineState = ref.watch(translationEngineStateProvider);
+ final translationState = ref.watch(
+ tabStateProvider(selectedTabId)
+ .select((s) => s?.translationState),
+ );
+ final readerActive = ref.watch(
+ tabStateProvider(selectedTabId)
+ .select((s) => s?.readerableState.active ?? false),
+ );
+
+ // Hide when reader mode is active (Fenix-aligned)
+ if (readerActive || engineState?.isEngineSupported != true) {
+ return const SizedBox.shrink();
+ }
+
+ final isTranslated = translationState?.isTranslated ?? false;
+
+ return MenuItemButton(
+ closeOnActivate: false,
+ leadingIcon: Icon(
+ Icons.translate,
+ color: isTranslated
+ ? Theme.of(context).colorScheme.primary
+ : null,
+ ),
+ onPressed: () async {
+ controller.close();
+ if (context.mounted) {
+ await showTranslationBottomSheet(
+ context,
+ selectedTabId: selectedTabId,
+ );
+ }
+ },
+ child: Text(
+ isTranslated ? 'Translated' : 'Translate Page',
+ ),
+ );
+ },
+ ),
if (enablePinTab)
Consumer(
builder: (context, childRef, child) {
diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart
new file mode 100644
index 00000000..3f7fb496
--- /dev/null
+++ b/app/lib/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2024-2026 Fabian Freund.
+ *
+ * This file is part of WebLibre
+ * (see https://weblibre.eu).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter_hooks/flutter_hooks.dart';
+import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
+import 'package:hooks_riverpod/hooks_riverpod.dart';
+import 'package:weblibre/core/logger.dart';
+import 'package:weblibre/features/geckoview/domain/providers.dart';
+import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart';
+import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
+import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
+
+enum _TranslatePhase { idle, submitting, processing }
+
+Future showTranslationBottomSheet(
+ BuildContext context, {
+ required String selectedTabId,
+}) {
+ unawaited(
+ GeckoTabService().syncEvents(onTranslationStateChange: true).catchError((
+ Object e,
+ StackTrace s,
+ ) {
+ logger.w(
+ 'Failed to refresh translation state before opening sheet',
+ error: e,
+ stackTrace: s,
+ );
+ }),
+ );
+
+ return showModalBottomSheet(
+ context: context,
+ shape: const RoundedRectangleBorder(
+ borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
+ ),
+ builder: (context) => TranslationBottomSheet(selectedTabId: selectedTabId),
+ );
+}
+
+class TranslationBottomSheet extends HookConsumerWidget {
+ final String selectedTabId;
+
+ const TranslationBottomSheet({super.key, required this.selectedTabId});
+
+ @override
+ Widget build(BuildContext context, WidgetRef ref) {
+ final colorScheme = Theme.of(context).colorScheme;
+
+ final tabState = ref.watch(tabStateProvider(selectedTabId));
+ final translationState = tabState?.translationState;
+ final engineState = ref.watch(translationEngineStateProvider);
+
+ final fromLanguages = engineState?.fromLanguages?.nonNulls.toList() ?? [];
+ final toLanguages = engineState?.toLanguages?.nonNulls.toList() ?? [];
+ final suggestedFrom =
+ translationState?.detectedLanguageCode ??
+ translationState?.requestedFromLanguage;
+ final suggestedTo =
+ translationState?.userPreferredLanguageCode ??
+ translationState?.requestedToLanguage;
+
+ final selectedFrom = useState(suggestedFrom);
+ final selectedTo = useState(suggestedTo);
+
+ useEffect(() {
+ if (selectedFrom.value == null && suggestedFrom != null) {
+ selectedFrom.value = suggestedFrom;
+ }
+ if (selectedTo.value == null && suggestedTo != null) {
+ selectedTo.value = suggestedTo;
+ }
+ return null;
+ }, [suggestedFrom, suggestedTo]);
+
+ final isTranslated = translationState?.isTranslated ?? false;
+ final isProcessing = translationState?.isTranslateProcessing ?? false;
+ final hasError = translationState?.hasError ?? false;
+ final errorName = translationState?.translationErrorName;
+ final translatePhase = useState(_TranslatePhase.idle);
+
+ final effectiveProcessing =
+ isProcessing || translatePhase.value != _TranslatePhase.idle;
+
+ useEffect(() {
+ switch (translatePhase.value) {
+ case _TranslatePhase.idle:
+ return null;
+ case _TranslatePhase.submitting:
+ if (isProcessing) {
+ translatePhase.value = _TranslatePhase.processing;
+ } else if (hasError) {
+ translatePhase.value = _TranslatePhase.idle;
+ }
+ return null;
+ case _TranslatePhase.processing:
+ if (!isProcessing && (isTranslated || hasError)) {
+ translatePhase.value = _TranslatePhase.idle;
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (context.mounted) Navigator.pop(context);
+ });
+ }
+ // Otherwise stay in processing — transient stop during retranslate.
+ return null;
+ }
+ }, [isProcessing, isTranslated, hasError, translatePhase.value]);
+
+ final canTranslate =
+ selectedFrom.value != null &&
+ selectedTo.value != null &&
+ selectedFrom.value != selectedTo.value &&
+ !effectiveProcessing;
+
+ return SafeArea(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ // Header
+ Row(
+ children: [
+ Icon(Icons.translate, color: colorScheme.primary),
+ const SizedBox(width: 8),
+ Text(
+ 'Translate Page',
+ style: Theme.of(context).textTheme.titleMedium,
+ ),
+ const Spacer(),
+ if (effectiveProcessing)
+ const SizedBox(
+ width: 20,
+ height: 20,
+ child: CircularProgressIndicator(strokeWidth: 2),
+ ),
+ ],
+ ),
+ const SizedBox(height: 16),
+
+ // From language dropdown
+ _LanguageDropdown(
+ label: 'From',
+ languages: fromLanguages,
+ selectedCode: selectedFrom.value,
+ onChanged: effectiveProcessing
+ ? null
+ : (code) => selectedFrom.value = code,
+ ),
+ const SizedBox(height: 12),
+
+ // To language dropdown
+ _LanguageDropdown(
+ label: 'To',
+ languages: toLanguages,
+ selectedCode: selectedTo.value,
+ onChanged: effectiveProcessing
+ ? null
+ : (code) => selectedTo.value = code,
+ ),
+ const SizedBox(height: 16),
+
+ // Error display
+ if (hasError && errorName != null)
+ Padding(
+ padding: const EdgeInsets.only(bottom: 12),
+ child: Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: colorScheme.errorContainer,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Text(
+ 'Translation error: $errorName',
+ style: TextStyle(color: colorScheme.onErrorContainer),
+ ),
+ ),
+ ),
+
+ // Action buttons
+ Row(
+ children: [
+ if (isTranslated)
+ Expanded(
+ child: OutlinedButton(
+ onPressed: effectiveProcessing
+ ? null
+ : () async {
+ try {
+ await ref
+ .read(
+ tabSessionProvider(
+ tabId: selectedTabId,
+ ).notifier,
+ )
+ .translateRestore();
+ } catch (e) {
+ if (context.mounted) {
+ ui_helper.showErrorMessage(
+ context,
+ 'Failed to restore page',
+ );
+ }
+ }
+ if (context.mounted) Navigator.pop(context);
+ },
+ child: const Text('Show Original'),
+ ),
+ ),
+ if (isTranslated) const SizedBox(width: 12),
+ Expanded(
+ child: FilledButton(
+ onPressed: canTranslate
+ ? () async {
+ translatePhase.value = _TranslatePhase.submitting;
+ try {
+ await ref
+ .read(
+ tabSessionProvider(
+ tabId: selectedTabId,
+ ).notifier,
+ )
+ .translate(
+ fromLanguage: selectedFrom.value!,
+ toLanguage: selectedTo.value!,
+ );
+ } catch (e) {
+ translatePhase.value = _TranslatePhase.idle;
+ if (context.mounted) {
+ ui_helper.showErrorMessage(
+ context,
+ 'Failed to translate page',
+ );
+ }
+ }
+ }
+ : null,
+ child: Text(isTranslated ? 'Retranslate' : 'Translate'),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class _LanguageDropdown extends StatelessWidget {
+ final String label;
+ final List languages;
+ final String? selectedCode;
+ final ValueChanged? onChanged;
+
+ const _LanguageDropdown({
+ required this.label,
+ required this.languages,
+ required this.selectedCode,
+ this.onChanged,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ // Ensure selectedCode exists in the list, otherwise use null
+ final effectiveSelection = languages.any((l) => l.code == selectedCode)
+ ? selectedCode
+ : null;
+
+ return DropdownButtonFormField(
+ decoration: InputDecoration(
+ labelText: label,
+ border: const OutlineInputBorder(),
+ contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ ),
+ initialValue: effectiveSelection,
+ isExpanded: true,
+ items: languages
+ .map(
+ (lang) => DropdownMenuItem(
+ value: lang.code,
+ child: Text(lang.localizedDisplayName),
+ ),
+ )
+ .toList(),
+ onChanged: onChanged,
+ );
+ }
+}
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/BaseBrowserFragment.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/BaseBrowserFragment.kt
index 7047e92c..9beee165 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/BaseBrowserFragment.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/BaseBrowserFragment.kt
@@ -227,8 +227,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
val engineView = createEngine(components)
fragmentEngineView = engineView // Track for lifecycle management
- val originalContext = ActivityContextWrapper.getOriginalContext(requireActivity())
- ?.let { ProfileContext(it, components.profileApplicationContext.relativePath) }
+ val originalContext =
+ ActivityContextWrapper.getOriginalContext(requireActivity()) ?: requireActivity()
val engineNativeView = engineView.asView()
engineNativeView.layoutParams = layoutParams
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt
index 852e2d5b..43c856b2 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/api/GeckoTabsApiImpl.kt
@@ -27,6 +27,9 @@ import eu.weblibre.flutter_mozilla_components.pigeons.ReaderableState
import eu.weblibre.flutter_mozilla_components.pigeons.RestoreLocation
import eu.weblibre.flutter_mozilla_components.pigeons.SecurityInfoState
import eu.weblibre.flutter_mozilla_components.pigeons.TabContentState
+import eu.weblibre.flutter_mozilla_components.pigeons.TabTranslationStateData
+import eu.weblibre.flutter_mozilla_components.pigeons.TranslationEngineStateData
+import eu.weblibre.flutter_mozilla_components.pigeons.TranslationLanguage
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
@@ -39,6 +42,7 @@ import mozilla.components.browser.icons.IconRequest
import mozilla.components.browser.session.storage.RecoverableBrowserState
import mozilla.components.browser.state.action.EngineAction
import mozilla.components.browser.state.action.TabListAction
+import mozilla.components.browser.state.action.TranslationsAction
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.LastMediaAccessState
@@ -55,6 +59,7 @@ import org.json.JSONObject
import org.mozilla.gecko.util.ThreadUtils.runOnUiThread
import mozilla.components.feature.addons.logger
import mozilla.components.concept.engine.EngineSession
+import mozilla.components.concept.engine.translate.TranslationOperation
class GeckoTabsApiImpl : GeckoTabsApi {
companion object {
@@ -185,6 +190,7 @@ class GeckoTabsApiImpl : GeckoTabsApi {
onPageExtensionsChange: Boolean,
onBrowserExtensionIcons: Boolean,
onPageExtensionIcons: Boolean,
+ onTranslationStateChange: Boolean,
) {
try {
val tabs = components.core.store.state.tabs.map { it.copy() }
@@ -293,11 +299,85 @@ class GeckoTabsApiImpl : GeckoTabsApi {
onPageExtensionIcons
)
}
+
+ // Sync translation events
+ if (onTranslationStateChange) {
+ syncTranslationEvents(tabs)
+ }
} catch (e: Exception) {
logger.error("$TAG: Failed to sync events", e)
}
}
+ private fun syncTranslationEvents(tabs: List) {
+ try {
+ val browserState = components.core.store.state
+
+ // Retry initialization when support status is still unknown.
+ if (browserState.translationEngine.isEngineSupported == null) {
+ components.core.store.dispatch(TranslationsAction.InitTranslationsBrowserState)
+ }
+
+ // Fetch supported languages when engine is supported but languages are still missing.
+ if (
+ browserState.translationEngine.isEngineSupported == true &&
+ (
+ browserState.translationEngine.supportedLanguages?.fromLanguages == null ||
+ browserState.translationEngine.supportedLanguages?.toLanguages == null
+ )
+ ) {
+ components.core.store.dispatch(
+ TranslationsAction.OperationRequestedAction(
+ tabId = browserState.selectedTabId,
+ operation = TranslationOperation.FETCH_SUPPORTED_LANGUAGES,
+ )
+ )
+ }
+
+ val translationEngine = browserState.translationEngine
+
+ // Emit browser-level engine state
+ val fromLanguages = translationEngine.supportedLanguages?.fromLanguages?.map { lang ->
+ TranslationLanguage(code = lang.code, localizedDisplayName = lang.localizedDisplayName ?: lang.code)
+ }
+ val toLanguages = translationEngine.supportedLanguages?.toLanguages?.map { lang ->
+ TranslationLanguage(code = lang.code, localizedDisplayName = lang.localizedDisplayName ?: lang.code)
+ }
+
+ components.flutterEvents.onTranslationEngineStateChange(
+ EventSequence.next(),
+ TranslationEngineStateData(
+ isEngineSupported = translationEngine.isEngineSupported,
+ fromLanguages = fromLanguages,
+ toLanguages = toLanguages,
+ )
+ ) { }
+
+ // Emit per-tab translation state
+ tabs.forEach { tab ->
+ val translationsState = tab.translationsState
+ components.flutterEvents.onTabTranslationStateChange(
+ EventSequence.next(),
+ TabTranslationStateData(
+ tabId = tab.id,
+ isTranslated = translationsState.isTranslated,
+ isTranslateProcessing = translationsState.isTranslateProcessing,
+ isOfferTranslate = translationsState.isOfferTranslate,
+ isExpectedTranslate = translationsState.isExpectedTranslate,
+ detectedLanguageCode = translationsState.translationEngineState?.detectedLanguages?.documentLangTag,
+ userPreferredLanguageCode = translationsState.translationEngineState?.detectedLanguages?.userPreferredLangTag,
+ requestedFromLanguage = translationsState.translationEngineState?.requestedTranslationPair?.fromLanguage,
+ requestedToLanguage = translationsState.translationEngineState?.requestedTranslationPair?.toLanguage,
+ translationErrorName = translationsState.translationError?.errorName,
+ displayError = translationsState.translationError?.displayError,
+ )
+ ) { }
+ }
+ } catch (e: Exception) {
+ logger.error("$TAG: Failed to sync translation events", e)
+ }
+ }
+
private fun syncExtensionEvents(
onBrowserExtensionsChange: Boolean,
onPageExtensionsChange: Boolean,
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt
index f2dc4b87..b57b27fe 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Core.kt
@@ -30,6 +30,7 @@ import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.session.storage.SessionStorage
import mozilla.components.browser.state.engine.EngineMiddleware
import mozilla.components.browser.state.engine.middleware.SessionPrioritizationMiddleware
+import mozilla.components.browser.state.engine.middleware.TranslationsMiddleware
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.storage.sync.PlacesBookmarksStorage
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
@@ -67,6 +68,7 @@ import mozilla.components.concept.base.crash.Breadcrumb
import mozilla.components.concept.base.crash.CrashReporting
import mozilla.components.support.base.worker.Frequency
import org.mozilla.geckoview.GeckoRuntime
+import kotlinx.coroutines.MainScope
import kotlinx.coroutines.Job
import java.util.concurrent.TimeUnit
@@ -207,6 +209,9 @@ class Core(
SaveToPDFMiddleware(),
FileUploadsDirCleanerMiddleware(fileUploadsDirCleaner),
LastMediaAccessMiddleware(),
+ // Keep parity with Fenix: do not initialize translations on store init.
+ // EngineObserver will dispatch InitTranslationsBrowserState after first completed page load.
+ TranslationsMiddleware(engine, MainScope(), false),
) + EngineMiddleware.create(
engine,
// We are disabling automatic suspending of engine sessions under memory pressure.
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt
index f7f94d2e..8d95bf14 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/components/Events.kt
@@ -23,6 +23,9 @@ import eu.weblibre.flutter_mozilla_components.pigeons.ShareTarget
import eu.weblibre.flutter_mozilla_components.pigeons.ShareTargetFiles
import eu.weblibre.flutter_mozilla_components.pigeons.ShareTargetParams
import eu.weblibre.flutter_mozilla_components.pigeons.TabContentState
+import eu.weblibre.flutter_mozilla_components.pigeons.TabTranslationStateData
+import eu.weblibre.flutter_mozilla_components.pigeons.TranslationEngineStateData
+import eu.weblibre.flutter_mozilla_components.pigeons.TranslationLanguage
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.debounce
@@ -222,6 +225,71 @@ class Events(
}
}
+ // Translation engine state (browser-level)
+ stateFlow.flowScoped { flow ->
+ flow.map { state -> state.translationEngine }
+ .distinctUntilChanged { old, new ->
+ old.isEngineSupported == new.isEngineSupported &&
+ old.supportedLanguages == new.supportedLanguages
+ }
+ .debounce(50)
+ .collect { translationEngine ->
+ val fromLanguages = translationEngine.supportedLanguages?.fromLanguages?.map { lang ->
+ TranslationLanguage(code = lang.code, localizedDisplayName = lang.localizedDisplayName ?: lang.code)
+ }
+ val toLanguages = translationEngine.supportedLanguages?.toLanguages?.map { lang ->
+ TranslationLanguage(code = lang.code, localizedDisplayName = lang.localizedDisplayName ?: lang.code)
+ }
+
+ flutterEvents.onTranslationEngineStateChange(
+ EventSequence.next(),
+ TranslationEngineStateData(
+ isEngineSupported = translationEngine.isEngineSupported,
+ fromLanguages = fromLanguages,
+ toLanguages = toLanguages,
+ )
+ ) { _ -> }
+ }
+ }
+
+ // Per-tab translation state
+ stateFlow.flowScoped { flow ->
+ flow.mapNotNull { state -> state.tabs }
+ .filterChanged {
+ it.translationsState
+ }
+ .ifAnyChanged {
+ arrayOf(
+ it.translationsState.isTranslated,
+ it.translationsState.isTranslateProcessing,
+ it.translationsState.isOfferTranslate,
+ it.translationsState.isExpectedTranslate,
+ it.translationsState.translationEngineState,
+ it.translationsState.translationError,
+ )
+ }
+ .debounce(25)
+ .collect { tab ->
+ val ts = tab.translationsState
+ flutterEvents.onTabTranslationStateChange(
+ EventSequence.next(),
+ TabTranslationStateData(
+ tabId = tab.id,
+ isTranslated = ts.isTranslated,
+ isTranslateProcessing = ts.isTranslateProcessing,
+ isOfferTranslate = ts.isOfferTranslate,
+ isExpectedTranslate = ts.isExpectedTranslate,
+ detectedLanguageCode = ts.translationEngineState?.detectedLanguages?.documentLangTag,
+ userPreferredLanguageCode = ts.translationEngineState?.detectedLanguages?.userPreferredLangTag,
+ requestedFromLanguage = ts.translationEngineState?.requestedTranslationPair?.fromLanguage,
+ requestedToLanguage = ts.translationEngineState?.requestedTranslationPair?.toLanguage,
+ translationErrorName = ts.translationError?.errorName,
+ displayError = ts.translationError?.displayError,
+ )
+ ) { _ -> }
+ }
+ }
+
// PWA manifest availability events - following Fenix MenuPresenter pattern
stateFlow.flowScoped { flow ->
flow.mapNotNull { state -> state.selectedTab }
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/SaveToPDFMiddleware.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/SaveToPDFMiddleware.kt
index 1828e6f4..b4529458 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/SaveToPDFMiddleware.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/middleware/SaveToPDFMiddleware.kt
@@ -24,6 +24,15 @@ class SaveToPDFMiddleware : Middleware {
is EngineAction.SaveToPdfExceptionAction -> {
logger.error("Unable to save PDF", action.throwable)
}
+ is EngineAction.PrintContentAction -> {
+ next(action)
+ }
+ is EngineAction.PrintContentCompletedAction -> {
+ logger.info("Print completed successfully")
+ }
+ is EngineAction.PrintContentExceptionAction -> {
+ logger.error("Unable to print content", action.throwable)
+ }
else -> {
next(action)
}
diff --git a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt
index 48e8142d..3f031078 100644
--- a/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt
+++ b/packages/flutter_mozilla_components/android/src/main/kotlin/eu/weblibre/flutter_mozilla_components/pigeons/Gecko.g.kt
@@ -627,6 +627,214 @@ data class TranslationOptions (
override fun hashCode(): Int = toList().hashCode()
}
+/**
+ * A language supported by the translation engine.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class TranslationLanguage (
+ val code: String,
+ val localizedDisplayName: String
+)
+ {
+ companion object {
+ fun fromList(pigeonVar_list: List): TranslationLanguage {
+ val code = pigeonVar_list[0] as String
+ val localizedDisplayName = pigeonVar_list[1] as String
+ return TranslationLanguage(code, localizedDisplayName)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ code,
+ localizedDisplayName,
+ )
+ }
+ override fun equals(other: Any?): Boolean {
+ if (other !is TranslationLanguage) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
+
+ override fun hashCode(): Int = toList().hashCode()
+}
+
+/**
+ * Detected languages for a page.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class TranslationDetectedLanguages (
+ val documentLangTag: String? = null,
+ val supportedDocumentLang: Boolean? = null,
+ val userPreferredLangTag: String? = null
+)
+ {
+ companion object {
+ fun fromList(pigeonVar_list: List): TranslationDetectedLanguages {
+ val documentLangTag = pigeonVar_list[0] as String?
+ val supportedDocumentLang = pigeonVar_list[1] as Boolean?
+ val userPreferredLangTag = pigeonVar_list[2] as String?
+ return TranslationDetectedLanguages(documentLangTag, supportedDocumentLang, userPreferredLangTag)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ documentLangTag,
+ supportedDocumentLang,
+ userPreferredLangTag,
+ )
+ }
+ override fun equals(other: Any?): Boolean {
+ if (other !is TranslationDetectedLanguages) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
+
+ override fun hashCode(): Int = toList().hashCode()
+}
+
+/**
+ * A from/to language pair for translation.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class TranslationPair (
+ val fromLanguage: String,
+ val toLanguage: String
+)
+ {
+ companion object {
+ fun fromList(pigeonVar_list: List): TranslationPair {
+ val fromLanguage = pigeonVar_list[0] as String
+ val toLanguage = pigeonVar_list[1] as String
+ return TranslationPair(fromLanguage, toLanguage)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ fromLanguage,
+ toLanguage,
+ )
+ }
+ override fun equals(other: Any?): Boolean {
+ if (other !is TranslationPair) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
+
+ override fun hashCode(): Int = toList().hashCode()
+}
+
+/**
+ * Browser-level translation engine state (global).
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class TranslationEngineStateData (
+ val isEngineSupported: Boolean? = null,
+ val fromLanguages: List? = null,
+ val toLanguages: List? = null
+)
+ {
+ companion object {
+ fun fromList(pigeonVar_list: List): TranslationEngineStateData {
+ val isEngineSupported = pigeonVar_list[0] as Boolean?
+ val fromLanguages = pigeonVar_list[1] as List?
+ val toLanguages = pigeonVar_list[2] as List?
+ return TranslationEngineStateData(isEngineSupported, fromLanguages, toLanguages)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ isEngineSupported,
+ fromLanguages,
+ toLanguages,
+ )
+ }
+ override fun equals(other: Any?): Boolean {
+ if (other !is TranslationEngineStateData) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
+
+ override fun hashCode(): Int = toList().hashCode()
+}
+
+/**
+ * Per-tab translation state.
+ *
+ * Generated class from Pigeon that represents data sent in messages.
+ */
+data class TabTranslationStateData (
+ val tabId: String,
+ val isTranslated: Boolean,
+ val isTranslateProcessing: Boolean,
+ val isOfferTranslate: Boolean,
+ val isExpectedTranslate: Boolean,
+ val detectedLanguageCode: String? = null,
+ val userPreferredLanguageCode: String? = null,
+ val requestedFromLanguage: String? = null,
+ val requestedToLanguage: String? = null,
+ val translationErrorName: String? = null,
+ val displayError: Boolean? = null
+)
+ {
+ companion object {
+ fun fromList(pigeonVar_list: List): TabTranslationStateData {
+ val tabId = pigeonVar_list[0] as String
+ val isTranslated = pigeonVar_list[1] as Boolean
+ val isTranslateProcessing = pigeonVar_list[2] as Boolean
+ val isOfferTranslate = pigeonVar_list[3] as Boolean
+ val isExpectedTranslate = pigeonVar_list[4] as Boolean
+ val detectedLanguageCode = pigeonVar_list[5] as String?
+ val userPreferredLanguageCode = pigeonVar_list[6] as String?
+ val requestedFromLanguage = pigeonVar_list[7] as String?
+ val requestedToLanguage = pigeonVar_list[8] as String?
+ val translationErrorName = pigeonVar_list[9] as String?
+ val displayError = pigeonVar_list[10] as Boolean?
+ return TabTranslationStateData(tabId, isTranslated, isTranslateProcessing, isOfferTranslate, isExpectedTranslate, detectedLanguageCode, userPreferredLanguageCode, requestedFromLanguage, requestedToLanguage, translationErrorName, displayError)
+ }
+ }
+ fun toList(): List {
+ return listOf(
+ tabId,
+ isTranslated,
+ isTranslateProcessing,
+ isOfferTranslate,
+ isExpectedTranslate,
+ detectedLanguageCode,
+ userPreferredLanguageCode,
+ requestedFromLanguage,
+ requestedToLanguage,
+ translationErrorName,
+ displayError,
+ )
+ }
+ override fun equals(other: Any?): Boolean {
+ if (other !is TabTranslationStateData) {
+ return false
+ }
+ if (this === other) {
+ return true
+ }
+ return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
+
+ override fun hashCode(): Int = toList().hashCode()
+}
+
/**
* Value type that represents the state of reader mode/view.
*
@@ -3662,325 +3870,350 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
}
162.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ReaderState.fromList(it)
+ TranslationLanguage.fromList(it)
}
}
163.toByte() -> {
return (readValue(buffer) as? List)?.let {
- AddTabParams.fromList(it)
+ TranslationDetectedLanguages.fromList(it)
}
}
164.toByte() -> {
return (readValue(buffer) as? List)?.let {
- LastMediaAccessState.fromList(it)
+ TranslationPair.fromList(it)
}
}
165.toByte() -> {
return (readValue(buffer) as? List)?.let {
- HistoryMetadataKey.fromList(it)
+ TranslationEngineStateData.fromList(it)
}
}
166.toByte() -> {
return (readValue(buffer) as? List)?.let {
- PackageCategoryValue.fromList(it)
+ TabTranslationStateData.fromList(it)
}
}
167.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ExternalPackage.fromList(it)
+ ReaderState.fromList(it)
}
}
168.toByte() -> {
return (readValue(buffer) as? List)?.let {
- LoadUrlFlagsValue.fromList(it)
+ AddTabParams.fromList(it)
}
}
169.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SourceValue.fromList(it)
+ LastMediaAccessState.fromList(it)
}
}
170.toByte() -> {
return (readValue(buffer) as? List)?.let {
- TabState.fromList(it)
+ HistoryMetadataKey.fromList(it)
}
}
171.toByte() -> {
return (readValue(buffer) as? List)?.let {
- RecoverableTab.fromList(it)
+ PackageCategoryValue.fromList(it)
}
}
172.toByte() -> {
return (readValue(buffer) as? List)?.let {
- RecoverableBrowserState.fromList(it)
+ ExternalPackage.fromList(it)
}
}
173.toByte() -> {
return (readValue(buffer) as? List)?.let {
- IconRequest.fromList(it)
+ LoadUrlFlagsValue.fromList(it)
}
}
174.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ResourceSize.fromList(it)
+ SourceValue.fromList(it)
}
}
175.toByte() -> {
return (readValue(buffer) as? List)?.let {
- Resource.fromList(it)
+ TabState.fromList(it)
}
}
176.toByte() -> {
return (readValue(buffer) as? List)?.let {
- IconResult.fromList(it)
+ RecoverableTab.fromList(it)
}
}
177.toByte() -> {
return (readValue(buffer) as? List)?.let {
- CookiePartitionKey.fromList(it)
+ RecoverableBrowserState.fromList(it)
}
}
178.toByte() -> {
return (readValue(buffer) as? List)?.let {
- Cookie.fromList(it)
+ IconRequest.fromList(it)
}
}
179.toByte() -> {
return (readValue(buffer) as? List)?.let {
- VisitInfo.fromList(it)
+ ResourceSize.fromList(it)
}
}
180.toByte() -> {
return (readValue(buffer) as? List)?.let {
- HistoryItem.fromList(it)
+ Resource.fromList(it)
}
}
181.toByte() -> {
return (readValue(buffer) as? List)?.let {
- HistoryState.fromList(it)
+ IconResult.fromList(it)
}
}
182.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ReaderableState.fromList(it)
+ CookiePartitionKey.fromList(it)
}
}
183.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SecurityInfoState.fromList(it)
+ Cookie.fromList(it)
}
}
184.toByte() -> {
return (readValue(buffer) as? List)?.let {
- TabContentState.fromList(it)
+ VisitInfo.fromList(it)
}
}
185.toByte() -> {
return (readValue(buffer) as? List)?.let {
- FindResultState.fromList(it)
+ HistoryItem.fromList(it)
}
}
186.toByte() -> {
return (readValue(buffer) as? List)?.let {
- CustomSelectionAction.fromList(it)
+ HistoryState.fromList(it)
}
}
187.toByte() -> {
return (readValue(buffer) as? List)?.let {
- WebExtensionData.fromList(it)
+ ReaderableState.fromList(it)
}
}
188.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeckoSuggestion.fromList(it)
+ SecurityInfoState.fromList(it)
}
}
189.toByte() -> {
return (readValue(buffer) as? List)?.let {
- TabContent.fromList(it)
+ TabContentState.fromList(it)
}
}
190.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ContentBlocking.fromList(it)
+ FindResultState.fromList(it)
}
}
191.toByte() -> {
return (readValue(buffer) as? List)?.let {
- DohSettings.fromList(it)
+ CustomSelectionAction.fromList(it)
}
}
192.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeckoEngineSettings.fromList(it)
+ WebExtensionData.fromList(it)
}
}
193.toByte() -> {
return (readValue(buffer) as? List)?.let {
- AutocompleteResult.fromList(it)
+ GeckoSuggestion.fromList(it)
}
}
194.toByte() -> {
return (readValue(buffer) as? List)?.let {
- UnknownHitResult.fromList(it)
+ TabContent.fromList(it)
}
}
195.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ImageHitResult.fromList(it)
+ ContentBlocking.fromList(it)
}
}
196.toByte() -> {
return (readValue(buffer) as? List)?.let {
- VideoHitResult.fromList(it)
+ DohSettings.fromList(it)
}
}
197.toByte() -> {
return (readValue(buffer) as? List)?.let {
- AudioHitResult.fromList(it)
+ GeckoEngineSettings.fromList(it)
}
}
198.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ImageSrcHitResult.fromList(it)
+ AutocompleteResult.fromList(it)
}
}
199.toByte() -> {
return (readValue(buffer) as? List)?.let {
- PhoneHitResult.fromList(it)
+ UnknownHitResult.fromList(it)
}
}
200.toByte() -> {
return (readValue(buffer) as? List)?.let {
- EmailHitResult.fromList(it)
+ ImageHitResult.fromList(it)
}
}
201.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeoHitResult.fromList(it)
+ VideoHitResult.fromList(it)
}
}
202.toByte() -> {
return (readValue(buffer) as? List)?.let {
- DownloadState.fromList(it)
+ AudioHitResult.fromList(it)
}
}
203.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ShareInternetResourceState.fromList(it)
+ ImageSrcHitResult.fromList(it)
}
}
204.toByte() -> {
return (readValue(buffer) as? List)?.let {
- AddonCollection.fromList(it)
+ PhoneHitResult.fromList(it)
}
}
205.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SyncEngineStatus.fromList(it)
+ EmailHitResult.fromList(it)
}
}
206.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SyncAccountInfo.fromList(it)
+ GeoHitResult.fromList(it)
}
}
207.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SyncDevice.fromList(it)
+ DownloadState.fromList(it)
}
}
208.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SyncIncomingTab.fromList(it)
+ ShareInternetResourceState.fromList(it)
}
}
209.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SyncRemoteTab.fromList(it)
+ AddonCollection.fromList(it)
}
}
210.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SyncDeviceTabs.fromList(it)
+ SyncEngineStatus.fromList(it)
}
}
211.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeckoPref.fromList(it)
+ SyncAccountInfo.fromList(it)
}
}
212.toByte() -> {
return (readValue(buffer) as? List)?.let {
- MlProgressData.fromList(it)
+ SyncDevice.fromList(it)
}
}
213.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ContainerSiteAssignment.fromList(it)
+ SyncIncomingTab.fromList(it)
}
}
214.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeckoHeader.fromList(it)
+ SyncRemoteTab.fromList(it)
}
}
215.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeckoFetchRequest.fromList(it)
+ SyncDeviceTabs.fromList(it)
}
}
216.toByte() -> {
return (readValue(buffer) as? List)?.let {
- GeckoFetchResponse.fromList(it)
+ GeckoPref.fromList(it)
}
}
217.toByte() -> {
return (readValue(buffer) as? List)?.let {
- BookmarkNode.fromList(it)
+ MlProgressData.fromList(it)
}
}
218.toByte() -> {
return (readValue(buffer) as? List)?.let {
- BookmarkInfo.fromList(it)
+ ContainerSiteAssignment.fromList(it)
}
}
219.toByte() -> {
return (readValue(buffer) as? List)?.let {
- SitePermissions.fromList(it)
+ GeckoHeader.fromList(it)
}
}
220.toByte() -> {
return (readValue(buffer) as? List)?.let {
- TrackingProtectionException.fromList(it)
+ GeckoFetchRequest.fromList(it)
}
}
221.toByte() -> {
return (readValue(buffer) as? List)?.let {
- PwaIcon.fromList(it)
+ GeckoFetchResponse.fromList(it)
}
}
222.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ShareTargetFiles.fromList(it)
+ BookmarkNode.fromList(it)
}
}
223.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ShareTargetParams.fromList(it)
+ BookmarkInfo.fromList(it)
}
}
224.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ShareTarget.fromList(it)
+ SitePermissions.fromList(it)
}
}
225.toByte() -> {
return (readValue(buffer) as? List)?.let {
- ExternalApplicationResource.fromList(it)
+ TrackingProtectionException.fromList(it)
}
}
226.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ PwaIcon.fromList(it)
+ }
+ }
+ 227.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ ShareTargetFiles.fromList(it)
+ }
+ }
+ 228.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ ShareTargetParams.fromList(it)
+ }
+ }
+ 229.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ ShareTarget.fromList(it)
+ }
+ }
+ 230.toByte() -> {
+ return (readValue(buffer) as? List)?.let {
+ ExternalApplicationResource.fromList(it)
+ }
+ }
+ 231.toByte() -> {
return (readValue(buffer) as? List)?.let {
PwaManifest.fromList(it)
}
@@ -4122,266 +4355,286 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(161)
writeValue(stream, value.toList())
}
- is ReaderState -> {
+ is TranslationLanguage -> {
stream.write(162)
writeValue(stream, value.toList())
}
- is AddTabParams -> {
+ is TranslationDetectedLanguages -> {
stream.write(163)
writeValue(stream, value.toList())
}
- is LastMediaAccessState -> {
+ is TranslationPair -> {
stream.write(164)
writeValue(stream, value.toList())
}
- is HistoryMetadataKey -> {
+ is TranslationEngineStateData -> {
stream.write(165)
writeValue(stream, value.toList())
}
- is PackageCategoryValue -> {
+ is TabTranslationStateData -> {
stream.write(166)
writeValue(stream, value.toList())
}
- is ExternalPackage -> {
+ is ReaderState -> {
stream.write(167)
writeValue(stream, value.toList())
}
- is LoadUrlFlagsValue -> {
+ is AddTabParams -> {
stream.write(168)
writeValue(stream, value.toList())
}
- is SourceValue -> {
+ is LastMediaAccessState -> {
stream.write(169)
writeValue(stream, value.toList())
}
- is TabState -> {
+ is HistoryMetadataKey -> {
stream.write(170)
writeValue(stream, value.toList())
}
- is RecoverableTab -> {
+ is PackageCategoryValue -> {
stream.write(171)
writeValue(stream, value.toList())
}
- is RecoverableBrowserState -> {
+ is ExternalPackage -> {
stream.write(172)
writeValue(stream, value.toList())
}
- is IconRequest -> {
+ is LoadUrlFlagsValue -> {
stream.write(173)
writeValue(stream, value.toList())
}
- is ResourceSize -> {
+ is SourceValue -> {
stream.write(174)
writeValue(stream, value.toList())
}
- is Resource -> {
+ is TabState -> {
stream.write(175)
writeValue(stream, value.toList())
}
- is IconResult -> {
+ is RecoverableTab -> {
stream.write(176)
writeValue(stream, value.toList())
}
- is CookiePartitionKey -> {
+ is RecoverableBrowserState -> {
stream.write(177)
writeValue(stream, value.toList())
}
- is Cookie -> {
+ is IconRequest -> {
stream.write(178)
writeValue(stream, value.toList())
}
- is VisitInfo -> {
+ is ResourceSize -> {
stream.write(179)
writeValue(stream, value.toList())
}
- is HistoryItem -> {
+ is Resource -> {
stream.write(180)
writeValue(stream, value.toList())
}
- is HistoryState -> {
+ is IconResult -> {
stream.write(181)
writeValue(stream, value.toList())
}
- is ReaderableState -> {
+ is CookiePartitionKey -> {
stream.write(182)
writeValue(stream, value.toList())
}
- is SecurityInfoState -> {
+ is Cookie -> {
stream.write(183)
writeValue(stream, value.toList())
}
- is TabContentState -> {
+ is VisitInfo -> {
stream.write(184)
writeValue(stream, value.toList())
}
- is FindResultState -> {
+ is HistoryItem -> {
stream.write(185)
writeValue(stream, value.toList())
}
- is CustomSelectionAction -> {
+ is HistoryState -> {
stream.write(186)
writeValue(stream, value.toList())
}
- is WebExtensionData -> {
+ is ReaderableState -> {
stream.write(187)
writeValue(stream, value.toList())
}
- is GeckoSuggestion -> {
+ is SecurityInfoState -> {
stream.write(188)
writeValue(stream, value.toList())
}
- is TabContent -> {
+ is TabContentState -> {
stream.write(189)
writeValue(stream, value.toList())
}
- is ContentBlocking -> {
+ is FindResultState -> {
stream.write(190)
writeValue(stream, value.toList())
}
- is DohSettings -> {
+ is CustomSelectionAction -> {
stream.write(191)
writeValue(stream, value.toList())
}
- is GeckoEngineSettings -> {
+ is WebExtensionData -> {
stream.write(192)
writeValue(stream, value.toList())
}
- is AutocompleteResult -> {
+ is GeckoSuggestion -> {
stream.write(193)
writeValue(stream, value.toList())
}
- is UnknownHitResult -> {
+ is TabContent -> {
stream.write(194)
writeValue(stream, value.toList())
}
- is ImageHitResult -> {
+ is ContentBlocking -> {
stream.write(195)
writeValue(stream, value.toList())
}
- is VideoHitResult -> {
+ is DohSettings -> {
stream.write(196)
writeValue(stream, value.toList())
}
- is AudioHitResult -> {
+ is GeckoEngineSettings -> {
stream.write(197)
writeValue(stream, value.toList())
}
- is ImageSrcHitResult -> {
+ is AutocompleteResult -> {
stream.write(198)
writeValue(stream, value.toList())
}
- is PhoneHitResult -> {
+ is UnknownHitResult -> {
stream.write(199)
writeValue(stream, value.toList())
}
- is EmailHitResult -> {
+ is ImageHitResult -> {
stream.write(200)
writeValue(stream, value.toList())
}
- is GeoHitResult -> {
+ is VideoHitResult -> {
stream.write(201)
writeValue(stream, value.toList())
}
- is DownloadState -> {
+ is AudioHitResult -> {
stream.write(202)
writeValue(stream, value.toList())
}
- is ShareInternetResourceState -> {
+ is ImageSrcHitResult -> {
stream.write(203)
writeValue(stream, value.toList())
}
- is AddonCollection -> {
+ is PhoneHitResult -> {
stream.write(204)
writeValue(stream, value.toList())
}
- is SyncEngineStatus -> {
+ is EmailHitResult -> {
stream.write(205)
writeValue(stream, value.toList())
}
- is SyncAccountInfo -> {
+ is GeoHitResult -> {
stream.write(206)
writeValue(stream, value.toList())
}
- is SyncDevice -> {
+ is DownloadState -> {
stream.write(207)
writeValue(stream, value.toList())
}
- is SyncIncomingTab -> {
+ is ShareInternetResourceState -> {
stream.write(208)
writeValue(stream, value.toList())
}
- is SyncRemoteTab -> {
+ is AddonCollection -> {
stream.write(209)
writeValue(stream, value.toList())
}
- is SyncDeviceTabs -> {
+ is SyncEngineStatus -> {
stream.write(210)
writeValue(stream, value.toList())
}
- is GeckoPref -> {
+ is SyncAccountInfo -> {
stream.write(211)
writeValue(stream, value.toList())
}
- is MlProgressData -> {
+ is SyncDevice -> {
stream.write(212)
writeValue(stream, value.toList())
}
- is ContainerSiteAssignment -> {
+ is SyncIncomingTab -> {
stream.write(213)
writeValue(stream, value.toList())
}
- is GeckoHeader -> {
+ is SyncRemoteTab -> {
stream.write(214)
writeValue(stream, value.toList())
}
- is GeckoFetchRequest -> {
+ is SyncDeviceTabs -> {
stream.write(215)
writeValue(stream, value.toList())
}
- is GeckoFetchResponse -> {
+ is GeckoPref -> {
stream.write(216)
writeValue(stream, value.toList())
}
- is BookmarkNode -> {
+ is MlProgressData -> {
stream.write(217)
writeValue(stream, value.toList())
}
- is BookmarkInfo -> {
+ is ContainerSiteAssignment -> {
stream.write(218)
writeValue(stream, value.toList())
}
- is SitePermissions -> {
+ is GeckoHeader -> {
stream.write(219)
writeValue(stream, value.toList())
}
- is TrackingProtectionException -> {
+ is GeckoFetchRequest -> {
stream.write(220)
writeValue(stream, value.toList())
}
- is PwaIcon -> {
+ is GeckoFetchResponse -> {
stream.write(221)
writeValue(stream, value.toList())
}
- is ShareTargetFiles -> {
+ is BookmarkNode -> {
stream.write(222)
writeValue(stream, value.toList())
}
- is ShareTargetParams -> {
+ is BookmarkInfo -> {
stream.write(223)
writeValue(stream, value.toList())
}
- is ShareTarget -> {
+ is SitePermissions -> {
stream.write(224)
writeValue(stream, value.toList())
}
- is ExternalApplicationResource -> {
+ is TrackingProtectionException -> {
stream.write(225)
writeValue(stream, value.toList())
}
- is PwaManifest -> {
+ is PwaIcon -> {
stream.write(226)
writeValue(stream, value.toList())
}
+ is ShareTargetFiles -> {
+ stream.write(227)
+ writeValue(stream, value.toList())
+ }
+ is ShareTargetParams -> {
+ stream.write(228)
+ writeValue(stream, value.toList())
+ }
+ is ShareTarget -> {
+ stream.write(229)
+ writeValue(stream, value.toList())
+ }
+ is ExternalApplicationResource -> {
+ stream.write(230)
+ writeValue(stream, value.toList())
+ }
+ is PwaManifest -> {
+ stream.write(231)
+ writeValue(stream, value.toList())
+ }
else -> super.writeValue(stream, value)
}
}
@@ -5289,7 +5542,7 @@ interface GeckoSessionApi {
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoTabsApi {
- fun syncEvents(onSelectedTabChange: Boolean, onTabListChange: Boolean, onTabContentStateChange: Boolean, onIconChange: Boolean, onSecurityInfoStateChange: Boolean, onReaderableStateChange: Boolean, onHistoryStateChange: Boolean, onFindResults: Boolean, onThumbnailChange: Boolean, onBrowserExtensionsChange: Boolean, onPageExtensionsChange: Boolean, onBrowserExtensionIcons: Boolean, onPageExtensionIcons: Boolean)
+ fun syncEvents(onSelectedTabChange: Boolean, onTabListChange: Boolean, onTabContentStateChange: Boolean, onIconChange: Boolean, onSecurityInfoStateChange: Boolean, onReaderableStateChange: Boolean, onHistoryStateChange: Boolean, onFindResults: Boolean, onThumbnailChange: Boolean, onBrowserExtensionsChange: Boolean, onPageExtensionsChange: Boolean, onBrowserExtensionIcons: Boolean, onPageExtensionIcons: Boolean, onTranslationStateChange: Boolean)
fun selectTab(tabId: String)
fun removeTab(tabId: String)
fun addTab(url: String, selectTab: Boolean, startLoading: Boolean, parentId: String?, flags: LoadUrlFlagsValue, contextId: String?, source: SourceValue, private: Boolean, historyMetadata: HistoryMetadataKey?, additionalHeaders: Map?): String
@@ -5339,8 +5592,9 @@ interface GeckoTabsApi {
val onPageExtensionsChangeArg = args[10] as Boolean
val onBrowserExtensionIconsArg = args[11] as Boolean
val onPageExtensionIconsArg = args[12] as Boolean
+ val onTranslationStateChangeArg = args[13] as Boolean
val wrapped: List = try {
- api.syncEvents(onSelectedTabChangeArg, onTabListChangeArg, onTabContentStateChangeArg, onIconChangeArg, onSecurityInfoStateChangeArg, onReaderableStateChangeArg, onHistoryStateChangeArg, onFindResultsArg, onThumbnailChangeArg, onBrowserExtensionsChangeArg, onPageExtensionsChangeArg, onBrowserExtensionIconsArg, onPageExtensionIconsArg)
+ api.syncEvents(onSelectedTabChangeArg, onTabListChangeArg, onTabContentStateChangeArg, onIconChangeArg, onSecurityInfoStateChangeArg, onReaderableStateChangeArg, onHistoryStateChangeArg, onFindResultsArg, onThumbnailChangeArg, onBrowserExtensionsChangeArg, onPageExtensionsChangeArg, onBrowserExtensionIconsArg, onPageExtensionIconsArg, onTranslationStateChangeArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
@@ -6571,6 +6825,40 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
+ fun onTranslationEngineStateChange(sequenceArg: Long, stateArg: TranslationEngineStateData, callback: (Result) -> Unit)
+{
+ val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
+ val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTranslationEngineStateChange$separatedMessageChannelSuffix"
+ val channel = BasicMessageChannel(binaryMessenger, channelName, codec)
+ channel.send(listOf(sequenceArg, stateArg)) {
+ if (it is List<*>) {
+ if (it.size > 1) {
+ callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
+ } else {
+ callback(Result.success(Unit))
+ }
+ } else {
+ callback(Result.failure(GeckoPigeonUtils.createConnectionError(channelName)))
+ }
+ }
+ }
+ fun onTabTranslationStateChange(sequenceArg: Long, stateArg: TabTranslationStateData, callback: (Result) -> Unit)
+{
+ val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
+ val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabTranslationStateChange$separatedMessageChannelSuffix"
+ val channel = BasicMessageChannel(binaryMessenger, channelName, codec)
+ channel.send(listOf(sequenceArg, stateArg)) {
+ if (it is List<*>) {
+ if (it.size > 1) {
+ callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
+ } else {
+ callback(Result.success(Unit))
+ }
+ } else {
+ callback(Result.failure(GeckoPigeonUtils.createConnectionError(channelName)))
+ }
+ }
+ }
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
class GeckoSyncStateEvents(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {
diff --git a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart
index e7c930e7..5290b4e6 100644
--- a/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart
+++ b/packages/flutter_mozilla_components/lib/flutter_mozilla_components.dart
@@ -94,9 +94,15 @@ export 'src/pigeons/gecko.g.dart'
SyncRemoteTab,
TabContent,
TabContentState,
+ TabTranslationStateData,
TrackingProtectionException,
TrackingProtectionPolicy,
TrackingScope,
+ TranslationDetectedLanguages,
+ TranslationEngineStateData,
+ TranslationLanguage,
+ TranslationOptions,
+ TranslationPair,
UnknownHitResult,
VideoHitResult,
VisitInfo,
diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart
index d4fa84ee..24540225 100644
--- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart
+++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_event.dart
@@ -21,6 +21,8 @@ typedef FindResultsEvent = ({String tabId, List results});
typedef LongPressEvent = ({String tabId, HitResult hitResult});
typedef ScrollEvent = ({String tabId, int scrollY});
typedef ManifestUpdateEvent = ({String tabId, PwaManifest? manifest});
+typedef TabTranslationEvent = ({String tabId, TabTranslationStateData state});
+typedef TranslationEngineEvent = TranslationEngineStateData;
class GeckoEventService extends GeckoStateEvents {
// Stream controllers
@@ -46,6 +48,9 @@ class GeckoEventService extends GeckoStateEvents {
final _tabAddedSubject = PublishSubject();
final _mlProgressSubject = PublishSubject();
final _manifestUpdateSubject = PublishSubject();
+ final _translationEngineSubject =
+ BehaviorSubject();
+ final _tabTranslationSubject = ReplaySubject();
// Event streams
ValueStream get viewReadyStateEvents => _viewStateSubject.stream;
@@ -72,6 +77,10 @@ class GeckoEventService extends GeckoStateEvents {
Stream get mlProgressEvents => _mlProgressSubject.stream;
Stream get manifestUpdateEvents =>
_manifestUpdateSubject.stream;
+ ValueStream get translationEngineEvents =>
+ _translationEngineSubject.stream;
+ Stream get tabTranslationEvents =>
+ _tabTranslationSubject.stream;
@override
void onViewReadyStateChange(int sequence, bool state) {
@@ -210,6 +219,25 @@ class GeckoEventService extends GeckoStateEvents {
));
}
+ @override
+ void onTranslationEngineStateChange(
+ int sequence,
+ TranslationEngineStateData state,
+ ) {
+ _translationEngineSubject.addWhenMoreRecent(sequence, null, state);
+ }
+
+ @override
+ void onTabTranslationStateChange(
+ int sequence,
+ TabTranslationStateData state,
+ ) {
+ _tabTranslationSubject.addWhenMoreRecent(sequence, state.tabId, (
+ tabId: state.tabId,
+ state: state,
+ ));
+ }
+
GeckoEventService.setUp({
BinaryMessenger? binaryMessenger,
String messageChannelSuffix = '',
@@ -241,5 +269,7 @@ class GeckoEventService extends GeckoStateEvents {
await _siteAssignementSubject.close();
await _mlProgressSubject.close();
await _manifestUpdateSubject.close();
+ await _translationEngineSubject.close();
+ await _tabTranslationSubject.close();
}
}
diff --git a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart
index c3ecf35d..7d6cdd5e 100644
--- a/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart
+++ b/packages/flutter_mozilla_components/lib/src/domain/services/gecko_tab.dart
@@ -29,6 +29,7 @@ class GeckoTabService {
bool onPageExtensionsChange = false,
bool onBrowserExtensionIcons = false,
bool onPageExtensionIcons = false,
+ bool onTranslationStateChange = false,
}) {
return _api.syncEvents(
onSelectedTabChange: onSelectedTabChange,
@@ -44,6 +45,7 @@ class GeckoTabService {
onPageExtensionsChange: onPageExtensionsChange,
onBrowserExtensionIcons: onBrowserExtensionIcons,
onPageExtensionIcons: onPageExtensionIcons,
+ onTranslationStateChange: onTranslationStateChange,
);
}
diff --git a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart
index e4177fb2..27be2906 100644
--- a/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart
+++ b/packages/flutter_mozilla_components/lib/src/pigeons/gecko.g.dart
@@ -15,11 +15,7 @@ PlatformException _createConnectionError(String channelName) {
);
}
-List wrapResponse({
- Object? result,
- PlatformException? error,
- bool empty = false,
-}) {
+List wrapResponse({Object? result, PlatformException? error, bool empty = false}) {
if (empty) {
return [];
}
@@ -28,33 +24,27 @@ List wrapResponse({
}
return [error.code, error.message, error.details];
}
-
bool _deepEquals(Object? a, Object? b) {
if (a is List && b is List) {
return a.length == b.length &&
- a.indexed.every(
- ((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]),
- );
+ a.indexed
+ .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1]));
}
if (a is Map && b is Map) {
- return a.length == b.length &&
- a.entries.every(
- (MapEntry entry) =>
- (b as Map).containsKey(entry.key) &&
- _deepEquals(entry.value, b[entry.key]),
- );
+ return a.length == b.length && a.entries.every((MapEntry entry) =>
+ (b as Map).containsKey(entry.key) &&
+ _deepEquals(entry.value, b[entry.key]));
}
return a == b;
}
+
/// Indicates what location the tabs should be restored at
enum RestoreLocation {
/// Restore tabs at the beginning of the tab list
beginning,
-
/// Restore tabs at the end of the tab list
end,
-
/// Restore tabs at a specific index in the tab list
atIndex,
}
@@ -75,104 +65,127 @@ enum IconType {
/// Supported sizes.
///
/// We are trying to limit the supported sizes in order to optimize our caching strategy.
-enum IconSize { defaultSize, launcher, launcherAdaptive }
+enum IconSize {
+ defaultSize,
+ launcher,
+ launcherAdaptive,
+}
/// The source of an [Icon].
enum IconSource {
/// This icon was generated.
generator,
-
/// This icon was downloaded.
download,
-
/// This icon was inlined in the document.
inline,
-
/// This icon was loaded from an in-memory cache.
memory,
-
/// This icon was loaded from a disk cache.
disk,
}
-enum CookieSameSiteStatus { noRestriction, lax, strict, unspecified }
+enum CookieSameSiteStatus {
+ noRestriction,
+ lax,
+ strict,
+ unspecified,
+}
enum VisitType {
/// The user followed a link and got a new toplevel window.
link,
-
/// The user typed the page's URL in the URL bar or selected it from
/// URL bar autocomplete results, clicked on it from a history query
/// (from the History sidebar, History menu, or history query in the
/// personal toolbar or Places organizer.
typed,
-
/// The user followed a bookmark to get to the page.
bookmark,
-
/// Some inner content is loaded. This is true of all images on a
/// page, and the contents of the iframe. It is also true of any
/// content in a frame if the user did not explicitly follow a link
/// to get there.
embed,
-
/// Set when the transition was a permanent redirect.
redirectPermanent,
-
/// Set when the transition was a temporary redirect.
redirectTemporary,
-
/// Set when the transition is a download.
download,
-
/// The user followed a link and got a visit in a frame.
framedLink,
-
/// The user reloaded a page.
reload,
}
-enum SelectionPattern { phone, email }
+enum SelectionPattern {
+ phone,
+ email,
+}
-enum WebExtensionActionType { browser, page }
+enum WebExtensionActionType {
+ browser,
+ page,
+}
-enum GeckoSuggestionType { session, clipboard, history }
+enum GeckoSuggestionType {
+ session,
+ clipboard,
+ history,
+}
-enum TrackingProtectionPolicy { none, recommended, strict, custom }
+enum TrackingProtectionPolicy {
+ none,
+ recommended,
+ strict,
+ custom,
+}
-enum HttpsOnlyMode { disabled, privateOnly, enabled }
+enum HttpsOnlyMode {
+ disabled,
+ privateOnly,
+ enabled,
+}
-enum QueryParameterStripping { disabled, privateOnly, enabled }
+enum QueryParameterStripping {
+ disabled,
+ privateOnly,
+ enabled,
+}
enum BounceTrackingProtectionMode {
/// Fully disabled.
disabled,
-
/// Fully enabled.
enabled,
-
/// Disabled, but collects user interaction data. Use this mode as the
/// "disabled" state when the feature can be toggled on and off, e.g. via
/// preferences.
enabledStandby,
-
/// Feature enabled, but tracker purging is only simulated. Used for
/// testing and telemetry collection.
enabledDryRun,
}
-enum ColorScheme { system, light, dark }
+enum ColorScheme {
+ system,
+ light,
+ dark,
+}
-enum CookieBannerHandlingMode { disabled, rejectAll, rejectOrAcceptAll }
+enum CookieBannerHandlingMode {
+ disabled,
+ rejectAll,
+ rejectOrAcceptAll,
+}
/// App links behavior mode - controls how external app links are handled
enum AppLinksMode {
/// Always open links in their native apps without prompting
always,
-
/// Prompt user before opening in app (with "Always open" checkbox)
ask,
-
/// Never open links in external apps, always use browser
never,
}
@@ -189,19 +202,15 @@ enum CustomCookiePolicy {
/// Total Cookie Protection - Dynamic First-Party Isolation (dFPI)
/// Most private option, isolates cookies per site
totalProtection,
-
/// Block cross-site and social media tracker cookies
/// Allows most cookies but blocks tracking cookies
crossSiteTrackers,
-
/// Block cookies from sites you haven't visited
/// Balances privacy with functionality
unvisited,
-
/// Block all third-party cookies
/// Only allows first-party cookies
thirdParty,
-
/// Block all cookies (may break many sites)
allCookies,
}
@@ -210,76 +219,107 @@ enum CustomCookiePolicy {
enum TrackingScope {
/// Apply to all browsing (normal + private)
all,
-
/// Apply only to private browsing tabs
privateOnly,
}
-enum DohSettingsMode { geckoDefault, increased, max, off }
+enum DohSettingsMode {
+ geckoDefault,
+ increased,
+ max,
+ off,
+}
/// Status that represents every state that a download can be in.
enum DownloadStatus {
/// Indicates that the download is in the first state after creation but not yet [DOWNLOADING].
initiated,
-
/// Indicates that an [INITIATED] download is now actively being downloaded.
downloading,
-
/// Indicates that the download that has been [DOWNLOADING] has been paused.
paused,
-
/// Indicates that the download that has been [DOWNLOADING] has been cancelled.
cancelled,
-
/// Indicates that the download that has been [DOWNLOADING] has moved to failed because
/// something unexpected has happened.
failed,
-
/// Indicates that the [DOWNLOADING] download has been completed.
completed,
}
-enum LogLevel { debug, info, warn, error }
+enum LogLevel {
+ debug,
+ info,
+ warn,
+ error,
+}
-enum SyncEngineValue { history, bookmarks, tabs }
+enum SyncEngineValue {
+ history,
+ bookmarks,
+ tabs,
+}
/// Type of ML model operation
-enum MlProgressType { downloading, loadingFromCache, runningInference }
+enum MlProgressType {
+ downloading,
+ loadingFromCache,
+ runningInference,
+}
/// Status of the ML operation
-enum MlProgressStatus { initiate, sizeEstimate, inProgress, done }
+enum MlProgressStatus {
+ initiate,
+ sizeEstimate,
+ inProgress,
+ done,
+}
/// Types of browsing data that can be cleared
enum ClearDataType {
/// Authentication sessions
authSessions,
-
/// All site data (cookies, storage, etc.)
allSiteData,
-
/// Cookies only
cookies,
-
/// Cache only
allCaches,
}
-enum GeckoFetchMethod { get, head, post, put, delete, connect, options, trace }
+enum GeckoFetchMethod {
+ get,
+ head,
+ post,
+ put,
+ delete,
+ connect,
+ options,
+ trace,
+}
-enum GeckoFetchRedircet { follow, manual }
+enum GeckoFetchRedircet {
+ follow,
+ manual,
+}
-enum GeckoFetchCookiePolicy { include, omit }
+enum GeckoFetchCookiePolicy {
+ include,
+ omit,
+}
-enum BookmarkNodeType { item, folder, separator }
+enum BookmarkNodeType {
+ item,
+ folder,
+ separator,
+}
/// Permission status for a site permission
enum SitePermissionStatus {
/// Permission has been granted
allowed,
-
/// Permission has been denied
blocked,
-
/// No decision has been made yet (ask to allow)
noDecision,
}
@@ -288,13 +328,10 @@ enum SitePermissionStatus {
enum AutoplayStatus {
/// Allow all autoplay (audible and inaudible)
allowed,
-
/// Block all autoplay
blocked,
-
/// Block audible autoplay only (allow inaudible)
blockAudible,
-
/// Allow autoplay on WiFi only
allowOnWifi,
}
@@ -304,21 +341,26 @@ enum AutoplayStatus {
/// @property downloadModel If the necessary models should be downloaded on request. If false, then
/// the translation will not complete and throw an exception if the models are not already available.
class TranslationOptions {
- TranslationOptions({required this.downloadModel});
+ TranslationOptions({
+ required this.downloadModel,
+ });
bool downloadModel;
List _toList() {
- return [downloadModel];
+ return [
+ downloadModel,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static TranslationOptions decode(Object result) {
result as List;
- return TranslationOptions(downloadModel: result[0]! as bool);
+ return TranslationOptions(
+ downloadModel: result[0]! as bool,
+ );
}
@override
@@ -335,7 +377,298 @@ class TranslationOptions {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
+}
+
+/// A language supported by the translation engine.
+class TranslationLanguage {
+ TranslationLanguage({
+ required this.code,
+ required this.localizedDisplayName,
+ });
+
+ String code;
+
+ String localizedDisplayName;
+
+ List _toList() {
+ return [
+ code,
+ localizedDisplayName,
+ ];
+ }
+
+ Object encode() {
+ return _toList(); }
+
+ static TranslationLanguage decode(Object result) {
+ result as List;
+ return TranslationLanguage(
+ code: result[0]! as String,
+ localizedDisplayName: result[1]! as String,
+ );
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(Object other) {
+ if (other is! TranslationLanguage || other.runtimeType != runtimeType) {
+ return false;
+ }
+ if (identical(this, other)) {
+ return true;
+ }
+ return _deepEquals(encode(), other.encode());
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode => Object.hashAll(_toList())
+;
+}
+
+/// Detected languages for a page.
+class TranslationDetectedLanguages {
+ TranslationDetectedLanguages({
+ this.documentLangTag,
+ this.supportedDocumentLang,
+ this.userPreferredLangTag,
+ });
+
+ String? documentLangTag;
+
+ bool? supportedDocumentLang;
+
+ String? userPreferredLangTag;
+
+ List _toList() {
+ return [
+ documentLangTag,
+ supportedDocumentLang,
+ userPreferredLangTag,
+ ];
+ }
+
+ Object encode() {
+ return _toList(); }
+
+ static TranslationDetectedLanguages decode(Object result) {
+ result as List;
+ return TranslationDetectedLanguages(
+ documentLangTag: result[0] as String?,
+ supportedDocumentLang: result[1] as bool?,
+ userPreferredLangTag: result[2] as String?,
+ );
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(Object other) {
+ if (other is! TranslationDetectedLanguages || other.runtimeType != runtimeType) {
+ return false;
+ }
+ if (identical(this, other)) {
+ return true;
+ }
+ return _deepEquals(encode(), other.encode());
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode => Object.hashAll(_toList())
+;
+}
+
+/// A from/to language pair for translation.
+class TranslationPair {
+ TranslationPair({
+ required this.fromLanguage,
+ required this.toLanguage,
+ });
+
+ String fromLanguage;
+
+ String toLanguage;
+
+ List _toList() {
+ return [
+ fromLanguage,
+ toLanguage,
+ ];
+ }
+
+ Object encode() {
+ return _toList(); }
+
+ static TranslationPair decode(Object result) {
+ result as List;
+ return TranslationPair(
+ fromLanguage: result[0]! as String,
+ toLanguage: result[1]! as String,
+ );
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(Object other) {
+ if (other is! TranslationPair || other.runtimeType != runtimeType) {
+ return false;
+ }
+ if (identical(this, other)) {
+ return true;
+ }
+ return _deepEquals(encode(), other.encode());
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode => Object.hashAll(_toList())
+;
+}
+
+/// Browser-level translation engine state (global).
+class TranslationEngineStateData {
+ TranslationEngineStateData({
+ this.isEngineSupported,
+ this.fromLanguages,
+ this.toLanguages,
+ });
+
+ bool? isEngineSupported;
+
+ List? fromLanguages;
+
+ List? toLanguages;
+
+ List _toList() {
+ return [
+ isEngineSupported,
+ fromLanguages,
+ toLanguages,
+ ];
+ }
+
+ Object encode() {
+ return _toList(); }
+
+ static TranslationEngineStateData decode(Object result) {
+ result as List;
+ return TranslationEngineStateData(
+ isEngineSupported: result[0] as bool?,
+ fromLanguages: (result[1] as List?)?.cast(),
+ toLanguages: (result[2] as List?)?.cast(),
+ );
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(Object other) {
+ if (other is! TranslationEngineStateData || other.runtimeType != runtimeType) {
+ return false;
+ }
+ if (identical(this, other)) {
+ return true;
+ }
+ return _deepEquals(encode(), other.encode());
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode => Object.hashAll(_toList())
+;
+}
+
+/// Per-tab translation state.
+class TabTranslationStateData {
+ TabTranslationStateData({
+ required this.tabId,
+ required this.isTranslated,
+ required this.isTranslateProcessing,
+ required this.isOfferTranslate,
+ required this.isExpectedTranslate,
+ this.detectedLanguageCode,
+ this.userPreferredLanguageCode,
+ this.requestedFromLanguage,
+ this.requestedToLanguage,
+ this.translationErrorName,
+ this.displayError,
+ });
+
+ String tabId;
+
+ bool isTranslated;
+
+ bool isTranslateProcessing;
+
+ bool isOfferTranslate;
+
+ bool isExpectedTranslate;
+
+ String? detectedLanguageCode;
+
+ String? userPreferredLanguageCode;
+
+ String? requestedFromLanguage;
+
+ String? requestedToLanguage;
+
+ String? translationErrorName;
+
+ bool? displayError;
+
+ List _toList() {
+ return [
+ tabId,
+ isTranslated,
+ isTranslateProcessing,
+ isOfferTranslate,
+ isExpectedTranslate,
+ detectedLanguageCode,
+ userPreferredLanguageCode,
+ requestedFromLanguage,
+ requestedToLanguage,
+ translationErrorName,
+ displayError,
+ ];
+ }
+
+ Object encode() {
+ return _toList(); }
+
+ static TabTranslationStateData decode(Object result) {
+ result as List;
+ return TabTranslationStateData(
+ tabId: result[0]! as String,
+ isTranslated: result[1]! as bool,
+ isTranslateProcessing: result[2]! as bool,
+ isOfferTranslate: result[3]! as bool,
+ isExpectedTranslate: result[4]! as bool,
+ detectedLanguageCode: result[5] as String?,
+ userPreferredLanguageCode: result[6] as String?,
+ requestedFromLanguage: result[7] as String?,
+ requestedToLanguage: result[8] as String?,
+ translationErrorName: result[9] as String?,
+ displayError: result[10] as bool?,
+ );
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(Object other) {
+ if (other is! TabTranslationStateData || other.runtimeType != runtimeType) {
+ return false;
+ }
+ if (identical(this, other)) {
+ return true;
+ }
+ return _deepEquals(encode(), other.encode());
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// Value type that represents the state of reader mode/view.
@@ -388,8 +721,7 @@ class ReaderState {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static ReaderState decode(Object result) {
result as List;
@@ -418,7 +750,8 @@ class ReaderState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// Parameters for adding a new tab.
@@ -468,8 +801,7 @@ class AddTabParams {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static AddTabParams decode(Object result) {
result as List;
@@ -482,8 +814,7 @@ class AddTabParams {
source: result[5]! as SourceValue,
private: result[6]! as bool,
historyMetadata: result[7] as HistoryMetadataKey?,
- additionalHeaders: (result[8] as Map?)
- ?.cast(),
+ additionalHeaders: (result[8] as Map?)?.cast(),
);
}
@@ -501,7 +832,8 @@ class AddTabParams {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// Details about the last playing media in this tab.
@@ -531,12 +863,15 @@ class LastMediaAccessState {
bool mediaSessionActive;
List _toList() {
- return [lastMediaUrl, lastMediaAccess, mediaSessionActive];
+ return [
+ lastMediaUrl,
+ lastMediaAccess,
+ mediaSessionActive,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static LastMediaAccessState decode(Object result) {
result as List;
@@ -561,7 +896,8 @@ class LastMediaAccessState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// Represents a set of history metadata values that uniquely identify a record. Note that
@@ -569,7 +905,11 @@ class LastMediaAccessState {
/// created, depending on the de-bouncing logic of the underlying storage i.e. recording history
/// metadata observations with the exact same values may be combined into a single record.
class HistoryMetadataKey {
- HistoryMetadataKey({required this.url, this.searchTerm, this.referrerUrl});
+ HistoryMetadataKey({
+ required this.url,
+ this.searchTerm,
+ this.referrerUrl,
+ });
/// A url of the page.
String url;
@@ -584,12 +924,15 @@ class HistoryMetadataKey {
String? referrerUrl;
List _toList() {
- return [url, searchTerm, referrerUrl];
+ return [
+ url,
+ searchTerm,
+ referrerUrl,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static HistoryMetadataKey decode(Object result) {
result as List;
@@ -614,25 +957,31 @@ class HistoryMetadataKey {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class PackageCategoryValue {
- PackageCategoryValue({required this.value});
+ PackageCategoryValue({
+ required this.value,
+ });
int value;
List _toList() {
- return [value];
+ return [
+ value,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static PackageCategoryValue decode(Object result) {
result as List;
- return PackageCategoryValue(value: result[0]! as int);
+ return PackageCategoryValue(
+ value: result[0]! as int,
+ );
}
@override
@@ -649,12 +998,16 @@ class PackageCategoryValue {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// Describes an external package.
class ExternalPackage {
- ExternalPackage({required this.packageId, required this.category});
+ ExternalPackage({
+ required this.packageId,
+ required this.category,
+ });
/// An Android package id.
String packageId;
@@ -663,12 +1016,14 @@ class ExternalPackage {
PackageCategoryValue category;
List _toList() {
- return [packageId, category];
+ return [
+ packageId,
+ category,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static ExternalPackage decode(Object result) {
result as List;
@@ -692,25 +1047,31 @@ class ExternalPackage {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class LoadUrlFlagsValue {
- LoadUrlFlagsValue({required this.value});
+ LoadUrlFlagsValue({
+ required this.value,
+ });
int value;
List _toList() {
- return [value];
+ return [
+ value,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static LoadUrlFlagsValue decode(Object result) {
result as List;
- return LoadUrlFlagsValue(value: result[0]! as int);
+ return LoadUrlFlagsValue(
+ value: result[0]! as int,
+ );
}
@override
@@ -727,23 +1088,29 @@ class LoadUrlFlagsValue {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class SourceValue {
- SourceValue({required this.id, this.caller});
+ SourceValue({
+ required this.id,
+ this.caller,
+ });
int id;
ExternalPackage? caller;
List _toList() {
- return [id, caller];
+ return [
+ id,
+ caller,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static SourceValue decode(Object result) {
result as List;
@@ -767,7 +1134,8 @@ class SourceValue {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// A tab that is no longer open and in the list of tabs, but that can be restored (recovered) at
@@ -862,8 +1230,7 @@ class TabState {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static TabState decode(Object result) {
result as List;
@@ -900,12 +1267,16 @@ class TabState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// A recoverable version of [TabState].
class RecoverableTab {
- RecoverableTab({this.engineSessionStateJson, required this.state});
+ RecoverableTab({
+ this.engineSessionStateJson,
+ required this.state,
+ });
/// The [EngineSessionState] needed for restoring the previous state of this tab.
String? engineSessionStateJson;
@@ -914,12 +1285,14 @@ class RecoverableTab {
TabState state;
List _toList() {
- return [engineSessionStateJson, state];
+ return [
+ engineSessionStateJson,
+ state,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static RecoverableTab decode(Object result) {
result as List;
@@ -943,12 +1316,16 @@ class RecoverableTab {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// A restored browser state, read from disk.
class RecoverableBrowserState {
- RecoverableBrowserState({required this.tabs, this.selectedTabId});
+ RecoverableBrowserState({
+ required this.tabs,
+ this.selectedTabId,
+ });
/// The list of restored tabs.
List tabs;
@@ -957,12 +1334,14 @@ class RecoverableBrowserState {
String? selectedTabId;
List _toList() {
- return [tabs, selectedTabId];
+ return [
+ tabs,
+ selectedTabId,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static RecoverableBrowserState decode(Object result) {
result as List;
@@ -986,7 +1365,8 @@ class RecoverableBrowserState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// A request to load an [Icon].
@@ -1013,12 +1393,18 @@ class IconRequest {
bool waitOnNetworkLoad;
List _toList() {
- return [url, size, resources, color, isPrivate, waitOnNetworkLoad];
+ return [
+ url,
+ size,
+ resources,
+ color,
+ isPrivate,
+ waitOnNetworkLoad,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static IconRequest decode(Object result) {
result as List;
@@ -1046,27 +1432,36 @@ class IconRequest {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class ResourceSize {
- ResourceSize({required this.height, required this.width});
+ ResourceSize({
+ required this.height,
+ required this.width,
+ });
int height;
int width;
List _toList() {
- return [height, width];
+ return [
+ height,
+ width,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static ResourceSize decode(Object result) {
result as List;
- return ResourceSize(height: result[0]! as int, width: result[1]! as int);
+ return ResourceSize(
+ height: result[0]! as int,
+ width: result[1]! as int,
+ );
}
@override
@@ -1083,7 +1478,8 @@ class ResourceSize {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// An icon resource that can be loaded.
@@ -1107,12 +1503,17 @@ class Resource {
bool maskable;
List _toList() {
- return [url, type, sizes, mimeType, maskable];
+ return [
+ url,
+ type,
+ sizes,
+ mimeType,
+ maskable,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static Resource decode(Object result) {
result as List;
@@ -1139,7 +1540,8 @@ class Resource {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
/// An [Icon] returned by [BrowserIcons] after processing an [IconRequest]
@@ -1164,12 +1566,16 @@ class IconResult {
bool maskable;
List _toList() {
- return [image, color, source, maskable];
+ return [
+ image,
+ color,
+ source,
+ maskable,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static IconResult decode(Object result) {
result as List;
@@ -1195,25 +1601,31 @@ class IconResult {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class CookiePartitionKey {
- CookiePartitionKey({required this.topLevelSite});
+ CookiePartitionKey({
+ required this.topLevelSite,
+ });
String topLevelSite;
List _toList() {
- return [topLevelSite];
+ return [
+ topLevelSite,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static CookiePartitionKey decode(Object result) {
result as List;
- return CookiePartitionKey(topLevelSite: result[0]! as String);
+ return CookiePartitionKey(
+ topLevelSite: result[0]! as String,
+ );
}
@override
@@ -1230,7 +1642,8 @@ class CookiePartitionKey {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class Cookie {
@@ -1295,8 +1708,7 @@ class Cookie {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static Cookie decode(Object result) {
result as List;
@@ -1331,7 +1743,8 @@ class Cookie {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class VisitInfo {
@@ -1372,8 +1785,7 @@ class VisitInfo {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static VisitInfo decode(Object result) {
result as List;
@@ -1402,27 +1814,36 @@ class VisitInfo {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class HistoryItem {
- HistoryItem({required this.url, required this.title});
+ HistoryItem({
+ required this.url,
+ required this.title,
+ });
String url;
String title;
List _toList() {
- return [url, title];
+ return [
+ url,
+ title,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static HistoryItem decode(Object result) {
result as List;
- return HistoryItem(url: result[0]! as String, title: result[1]! as String);
+ return HistoryItem(
+ url: result[0]! as String,
+ title: result[1]! as String,
+ );
}
@override
@@ -1439,7 +1860,8 @@ class HistoryItem {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class HistoryState {
@@ -1459,12 +1881,16 @@ class HistoryState {
bool canGoForward;
List _toList() {
- return [items, currentIndex, canGoBack, canGoForward];
+ return [
+ items,
+ currentIndex,
+ canGoBack,
+ canGoForward,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static HistoryState decode(Object result) {
result as List;
@@ -1490,11 +1916,15 @@ class HistoryState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class ReaderableState {
- ReaderableState({required this.readerable, required this.active});
+ ReaderableState({
+ required this.readerable,
+ required this.active,
+ });
/// Whether or not the current page can be transformed to
/// be displayed in a reader view.
@@ -1504,12 +1934,14 @@ class ReaderableState {
bool active;
List _toList() {
- return [readerable, active];
+ return [
+ readerable,
+ active,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static ReaderableState decode(Object result) {
result as List;
@@ -1533,7 +1965,8 @@ class ReaderableState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class SecurityInfoState {
@@ -1550,12 +1983,15 @@ class SecurityInfoState {
String issuer;
List _toList() {
- return [secure, host, issuer];
+ return [
+ secure,
+ host,
+ issuer,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static SecurityInfoState decode(Object result) {
result as List;
@@ -1580,7 +2016,8 @@ class SecurityInfoState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class TabContentState {
@@ -1633,8 +2070,7 @@ class TabContentState {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static TabContentState decode(Object result) {
result as List;
@@ -1666,7 +2102,8 @@ class TabContentState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class FindResultState {
@@ -1683,12 +2120,15 @@ class FindResultState {
bool isDoneCounting;
List _toList() {
- return [activeMatchOrdinal, numberOfMatches, isDoneCounting];
+ return [
+ activeMatchOrdinal,
+ numberOfMatches,
+ isDoneCounting,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static FindResultState decode(Object result) {
result as List;
@@ -1713,11 +2153,16 @@ class FindResultState {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class CustomSelectionAction {
- CustomSelectionAction({required this.id, required this.title, this.pattern});
+ CustomSelectionAction({
+ required this.id,
+ required this.title,
+ this.pattern,
+ });
String id;
@@ -1726,12 +2171,15 @@ class CustomSelectionAction {
SelectionPattern? pattern;
List _toList() {
- return [id, title, pattern];
+ return [
+ id,
+ title,
+ pattern,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static CustomSelectionAction decode(Object result) {
result as List;
@@ -1756,7 +2204,8 @@ class CustomSelectionAction {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class WebExtensionData {
@@ -1793,8 +2242,7 @@ class WebExtensionData {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static WebExtensionData decode(Object result) {
result as List;
@@ -1822,7 +2270,8 @@ class WebExtensionData {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class GeckoSuggestion {
@@ -1851,12 +2300,19 @@ class GeckoSuggestion {
Uint8List? icon;
List _toList() {
- return [id, type, score, title, description, editSuggestion, icon];
+ return [
+ id,
+ type,
+ score,
+ title,
+ description,
+ editSuggestion,
+ icon,
+ ];
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static GeckoSuggestion decode(Object result) {
result as List;
@@ -1885,7 +2341,8 @@ class GeckoSuggestion {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class TabContent {
@@ -1922,8 +2379,7 @@ class TabContent {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static TabContent decode(Object result) {
result as List;
@@ -1951,7 +2407,8 @@ class TabContent {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class ContentBlocking {
@@ -1980,8 +2437,7 @@ class ContentBlocking {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static ContentBlocking decode(Object result) {
result as List;
@@ -2007,7 +2463,8 @@ class ContentBlocking {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class DohSettings {
@@ -2036,8 +2493,7 @@ class DohSettings {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static DohSettings decode(Object result) {
result as List;
@@ -2063,7 +2519,8 @@ class DohSettings {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class GeckoEngineSettings {
@@ -2196,8 +2653,7 @@ class GeckoEngineSettings {
}
Object encode() {
- return _toList();
- }
+ return _toList(); }
static GeckoEngineSettings decode(Object result) {
result as List;
@@ -2208,8 +2664,7 @@ class GeckoEngineSettings {
globalPrivacyControlEnabled: result[3] as bool?,
preferredColorScheme: result[4] as ColorScheme?,
cookieBannerHandlingMode: result[5] as CookieBannerHandlingMode?,
- cookieBannerHandlingModePrivateBrowsing:
- result[6] as CookieBannerHandlingMode?,
+ cookieBannerHandlingModePrivateBrowsing: result[6] as CookieBannerHandlingMode?,
cookieBannerHandlingGlobalRules: result[7] as bool?,
cookieBannerHandlingGlobalRulesSubFrames: result[8] as bool?,
webContentIsolationStrategy: result[9] as WebContentIsolationStrategy?,
@@ -2247,7 +2702,8 @@ class GeckoEngineSettings {
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
- int get hashCode => Object.hashAll(_toList());
+ int get hashCode => Object.hashAll(_toList())
+;
}
class AutocompleteResult {
@@ -2270,12 +2726,17 @@ class AutocompleteResult {
int totalItems;
List _toList() {
- return [input, text, url, source, totalItems];
+ return