add initial sync of extensions

This commit is contained in:
Fabian Freund
2026-02-09 04:59:43 +01:00
parent fec33aa694
commit e0cce93ffb
9 changed files with 180 additions and 18 deletions
@@ -38,7 +38,7 @@ class Components(val profileApplicationContext: ProfileContext,
val logLevel: Log.Priority,
val contentBlocking: ContentBlocking,
val addonCollection: AddonCollection?,
private val addonEvents: GeckoAddonEvents,
val addonEvents: GeckoAddonEvents,
private val tabContentEvents: GeckoTabContentEvents,
private val extensionEvents: BrowserExtensionEvents
) {
@@ -14,6 +14,8 @@ import eu.weblibre.flutter_mozilla_components.pigeons.RecoverableBrowserState as
import eu.weblibre.flutter_mozilla_components.pigeons.RestoreLocation as PigeonRestoreLocation
import eu.weblibre.flutter_mozilla_components.pigeons.RecoverableTab as PigeonRecoverableTab
import eu.weblibre.flutter_mozilla_components.pigeons.SourceValue
import eu.weblibre.flutter_mozilla_components.pigeons.WebExtensionActionType
import eu.weblibre.flutter_mozilla_components.pigeons.WebExtensionData
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes
import eu.weblibre.flutter_mozilla_components.pigeons.FindResultState
@@ -178,6 +180,10 @@ class GeckoTabsApiImpl : GeckoTabsApi {
onHistoryStateChange: Boolean,
onFindResults: Boolean,
onThumbnailChange: Boolean,
onBrowserExtensionsChange: Boolean,
onPageExtensionsChange: Boolean,
onBrowserExtensionIcons: Boolean,
onPageExtensionIcons: Boolean,
) {
try {
val tabs = components.core.store.state.tabs.map { it.copy() }
@@ -274,11 +280,122 @@ class GeckoTabsApiImpl : GeckoTabsApi {
coroutineScope.launch { handleThumbnailChange(tab) }
}
}
// Sync extension events
if (onBrowserExtensionsChange || onPageExtensionsChange ||
onBrowserExtensionIcons || onPageExtensionIcons) {
syncExtensionEvents(
onBrowserExtensionsChange,
onPageExtensionsChange,
onBrowserExtensionIcons,
onPageExtensionIcons
)
}
} catch (e: Exception) {
logger.error("$TAG: Failed to sync events", e)
}
}
private fun syncExtensionEvents(
onBrowserExtensionsChange: Boolean,
onPageExtensionsChange: Boolean,
onBrowserExtensionIcons: Boolean,
onPageExtensionIcons: Boolean
) {
try {
val extensions = components.core.store.state.extensions.values.filter { it.enabled }
extensions.forEach { extension ->
val browserAction = extension.browserAction
val pageAction = extension.pageAction
// Sync browser action
if (browserAction != null) {
if (onBrowserExtensionsChange) {
val data = WebExtensionData(
extensionId = extension.id,
title = browserAction.title,
enabled = browserAction.enabled,
badgeText = browserAction.badgeText,
badgeTextColor = browserAction.badgeTextColor?.toLong(),
badgeBackgroundColor = browserAction.badgeBackgroundColor?.toLong(),
)
components.addonEvents.onUpsertWebExtensionAction(
System.currentTimeMillis(),
extension.id,
WebExtensionActionType.BROWSER,
data
) { }
}
if (onBrowserExtensionIcons) {
coroutineScope.launch {
try {
val icon = browserAction.loadIcon?.invoke(128)
icon?.let {
val imageBytes = icon.toWebPBytes()
withContext(Dispatchers.Main) {
components.addonEvents.onUpdateWebExtensionIcon(
System.currentTimeMillis(),
extension.id,
WebExtensionActionType.BROWSER,
imageBytes
) { }
}
}
} catch (e: Exception) {
logger.error("$TAG: Failed to load browser action icon for ${extension.id}", e)
}
}
}
}
// Sync page action
if (pageAction != null && pageAction.enabled == true) {
if (onPageExtensionsChange) {
val data = WebExtensionData(
extensionId = extension.id,
title = pageAction.title,
enabled = pageAction.enabled,
badgeText = pageAction.badgeText,
badgeTextColor = pageAction.badgeTextColor?.toLong(),
badgeBackgroundColor = pageAction.badgeBackgroundColor?.toLong(),
)
components.addonEvents.onUpsertWebExtensionAction(
System.currentTimeMillis(),
extension.id,
WebExtensionActionType.PAGE,
data
) { }
}
if (onPageExtensionIcons) {
coroutineScope.launch {
try {
val icon = pageAction.loadIcon?.invoke(128)
icon?.let {
val imageBytes = icon.toWebPBytes()
withContext(Dispatchers.Main) {
components.addonEvents.onUpdateWebExtensionIcon(
System.currentTimeMillis(),
extension.id,
WebExtensionActionType.PAGE,
imageBytes
) { }
}
}
} catch (e: Exception) {
logger.error("$TAG: Failed to load page action icon for ${extension.id}", e)
}
}
}
}
}
} catch (e: Exception) {
logger.error("$TAG: Failed to sync extension events", e)
}
}
override fun selectTab(tabId: String) {
try {
components.useCases.tabsUseCases.selectTab(tabId = tabId)
@@ -4344,7 +4344,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)
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 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, String>?): String
@@ -4390,8 +4390,12 @@ interface GeckoTabsApi {
val onHistoryStateChangeArg = args[6] as Boolean
val onFindResultsArg = args[7] as Boolean
val onThumbnailChangeArg = args[8] as Boolean
val onBrowserExtensionsChangeArg = args[9] as Boolean
val onPageExtensionsChangeArg = args[10] as Boolean
val onBrowserExtensionIconsArg = args[11] as Boolean
val onPageExtensionIconsArg = args[12] as Boolean
val wrapped: List<Any?> = try {
api.syncEvents(onSelectedTabChangeArg, onTabListChangeArg, onTabContentStateChangeArg, onIconChangeArg, onSecurityInfoStateChangeArg, onReaderableStateChangeArg, onHistoryStateChangeArg, onFindResultsArg, onThumbnailChangeArg)
api.syncEvents(onSelectedTabChangeArg, onTabListChangeArg, onTabContentStateChangeArg, onIconChangeArg, onSecurityInfoStateChangeArg, onReaderableStateChangeArg, onHistoryStateChangeArg, onFindResultsArg, onThumbnailChangeArg, onBrowserExtensionsChangeArg, onPageExtensionsChangeArg, onBrowserExtensionIconsArg, onPageExtensionIconsArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
@@ -25,6 +25,10 @@ class GeckoTabService {
bool onHistoryStateChange = false,
bool onFindResults = false,
bool onThumbnailChange = false,
bool onBrowserExtensionsChange = false,
bool onPageExtensionsChange = false,
bool onBrowserExtensionIcons = false,
bool onPageExtensionIcons = false,
}) {
return _api.syncEvents(
onSelectedTabChange: onSelectedTabChange,
@@ -36,6 +40,10 @@ class GeckoTabService {
onFindResults: onFindResults,
onThumbnailChange: onThumbnailChange,
onReaderableStateChange: false,
onBrowserExtensionsChange: onBrowserExtensionsChange,
onPageExtensionsChange: onPageExtensionsChange,
onBrowserExtensionIcons: onBrowserExtensionIcons,
onPageExtensionIcons: onPageExtensionIcons,
);
}
@@ -4935,14 +4935,14 @@ class GeckoTabsApi {
final String pigeonVar_messageChannelSuffix;
Future<void> syncEvents({required bool onSelectedTabChange, required bool onTabListChange, required bool onTabContentStateChange, required bool onIconChange, required bool onSecurityInfoStateChange, required bool onReaderableStateChange, required bool onHistoryStateChange, required bool onFindResults, required bool onThumbnailChange, }) async {
Future<void> syncEvents({required bool onSelectedTabChange, required bool onTabListChange, required bool onTabContentStateChange, required bool onIconChange, required bool onSecurityInfoStateChange, required bool onReaderableStateChange, required bool onHistoryStateChange, required bool onFindResults, required bool onThumbnailChange, required bool onBrowserExtensionsChange, required bool onPageExtensionsChange, required bool onBrowserExtensionIcons, required bool onPageExtensionIcons, }) async {
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTabsApi.syncEvents$pigeonVar_messageChannelSuffix';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[onSelectedTabChange, onTabListChange, onTabContentStateChange, onIconChange, onSecurityInfoStateChange, onReaderableStateChange, onHistoryStateChange, onFindResults, onThumbnailChange]);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[onSelectedTabChange, onTabListChange, onTabContentStateChange, onIconChange, onSecurityInfoStateChange, onReaderableStateChange, onHistoryStateChange, onFindResults, onThumbnailChange, onBrowserExtensionsChange, onPageExtensionsChange, onBrowserExtensionIcons, onPageExtensionIcons]);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
@@ -1113,6 +1113,10 @@ abstract class GeckoTabsApi {
required bool onHistoryStateChange,
required bool onFindResults,
required bool onThumbnailChange,
required bool onBrowserExtensionsChange,
required bool onPageExtensionsChange,
required bool onBrowserExtensionIcons,
required bool onPageExtensionIcons,
});
void selectTab({required String tabId});