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)