add initial sync of extensions
This commit is contained in:
@@ -21,7 +21,6 @@ import 'dart:async';
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:weblibre/core/logger.dart';
|
import 'package:weblibre/core/logger.dart';
|
||||||
import 'package:weblibre/domain/entities/equatable_image.dart';
|
import 'package:weblibre/domain/entities/equatable_image.dart';
|
||||||
@@ -61,12 +60,12 @@ class WebExtensionsState extends _$WebExtensionsState {
|
|||||||
title: data.title,
|
title: data.title,
|
||||||
enabled: data.enabled ?? current.enabled,
|
enabled: data.enabled ?? current.enabled,
|
||||||
badgeText: data.badgeText,
|
badgeText: data.badgeText,
|
||||||
badgeTextColor: data.badgeTextColor.mapNotNull(
|
badgeTextColor: data.badgeTextColor != null
|
||||||
(color) => Color(color),
|
? Color(data.badgeTextColor!)
|
||||||
),
|
: null,
|
||||||
badgeBackgroundColor: data.badgeBackgroundColor.mapNotNull(
|
badgeBackgroundColor: data.badgeBackgroundColor != null
|
||||||
(color) => Color(color),
|
? Color(data.badgeBackgroundColor!)
|
||||||
),
|
: null,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (state.containsKey(extensionId)) {
|
if (state.containsKey(extensionId)) {
|
||||||
@@ -152,6 +151,40 @@ class WebExtensionsState extends _$WebExtensionsState {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Sync extension events after engine is ready and listeners are set up
|
||||||
|
// This ensures we get the current state even if we missed initial events
|
||||||
|
ref.listen(
|
||||||
|
fireImmediately: true,
|
||||||
|
engineReadyStateProvider,
|
||||||
|
(previous, next) async {
|
||||||
|
if (next) {
|
||||||
|
try {
|
||||||
|
await GeckoTabService().syncEvents(
|
||||||
|
onBrowserExtensionsChange:
|
||||||
|
actionType == WebExtensionActionType.browser,
|
||||||
|
onPageExtensionsChange: actionType == WebExtensionActionType.page,
|
||||||
|
onBrowserExtensionIcons:
|
||||||
|
actionType == WebExtensionActionType.browser,
|
||||||
|
onPageExtensionIcons: actionType == WebExtensionActionType.page,
|
||||||
|
);
|
||||||
|
} catch (e, s) {
|
||||||
|
logger.w(
|
||||||
|
'Failed to sync extension events for $actionType',
|
||||||
|
error: e,
|
||||||
|
stackTrace: s,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: (error, stackTrace) {
|
||||||
|
logger.e(
|
||||||
|
'Error listening to engineReadyStateProvider',
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
ref.onDispose(() async {
|
ref.onDispose(() async {
|
||||||
// Dispose all cached images
|
// Dispose all cached images
|
||||||
_imageCache.clear();
|
_imageCache.clear();
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ final class WebExtensionsStateProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$webExtensionsStateHash() =>
|
String _$webExtensionsStateHash() =>
|
||||||
r'28cd9c99bc167fa1a051eccf3d23917a417e54ea';
|
r'13cbbea409b5b643eb6766689f6b23a4d1701204';
|
||||||
|
|
||||||
final class WebExtensionsStateFamily extends $Family
|
final class WebExtensionsStateFamily extends $Family
|
||||||
with
|
with
|
||||||
|
|||||||
+1
-5
@@ -37,11 +37,7 @@ class ExtensionBadgeIcon extends StatelessWidget {
|
|||||||
textColor: state.badgeTextColor,
|
textColor: state.badgeTextColor,
|
||||||
backgroundColor: state.badgeBackgroundColor,
|
backgroundColor: state.badgeBackgroundColor,
|
||||||
child: RepaintBoundary(
|
child: RepaintBoundary(
|
||||||
child: SafeRawImage(
|
child: SafeRawImage(image: state.icon, width: 24, height: 24),
|
||||||
image: state.icon,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -38,7 +38,7 @@ class Components(val profileApplicationContext: ProfileContext,
|
|||||||
val logLevel: Log.Priority,
|
val logLevel: Log.Priority,
|
||||||
val contentBlocking: ContentBlocking,
|
val contentBlocking: ContentBlocking,
|
||||||
val addonCollection: AddonCollection?,
|
val addonCollection: AddonCollection?,
|
||||||
private val addonEvents: GeckoAddonEvents,
|
val addonEvents: GeckoAddonEvents,
|
||||||
private val tabContentEvents: GeckoTabContentEvents,
|
private val tabContentEvents: GeckoTabContentEvents,
|
||||||
private val extensionEvents: BrowserExtensionEvents
|
private val extensionEvents: BrowserExtensionEvents
|
||||||
) {
|
) {
|
||||||
|
|||||||
+117
@@ -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.RestoreLocation as PigeonRestoreLocation
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.RecoverableTab as PigeonRecoverableTab
|
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.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.GlobalComponents
|
||||||
import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes
|
import eu.weblibre.flutter_mozilla_components.ext.toWebPBytes
|
||||||
import eu.weblibre.flutter_mozilla_components.pigeons.FindResultState
|
import eu.weblibre.flutter_mozilla_components.pigeons.FindResultState
|
||||||
@@ -178,6 +180,10 @@ class GeckoTabsApiImpl : GeckoTabsApi {
|
|||||||
onHistoryStateChange: Boolean,
|
onHistoryStateChange: Boolean,
|
||||||
onFindResults: Boolean,
|
onFindResults: Boolean,
|
||||||
onThumbnailChange: Boolean,
|
onThumbnailChange: Boolean,
|
||||||
|
onBrowserExtensionsChange: Boolean,
|
||||||
|
onPageExtensionsChange: Boolean,
|
||||||
|
onBrowserExtensionIcons: Boolean,
|
||||||
|
onPageExtensionIcons: Boolean,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
val tabs = components.core.store.state.tabs.map { it.copy() }
|
val tabs = components.core.store.state.tabs.map { it.copy() }
|
||||||
@@ -274,11 +280,122 @@ class GeckoTabsApiImpl : GeckoTabsApi {
|
|||||||
coroutineScope.launch { handleThumbnailChange(tab) }
|
coroutineScope.launch { handleThumbnailChange(tab) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync extension events
|
||||||
|
if (onBrowserExtensionsChange || onPageExtensionsChange ||
|
||||||
|
onBrowserExtensionIcons || onPageExtensionIcons) {
|
||||||
|
syncExtensionEvents(
|
||||||
|
onBrowserExtensionsChange,
|
||||||
|
onPageExtensionsChange,
|
||||||
|
onBrowserExtensionIcons,
|
||||||
|
onPageExtensionIcons
|
||||||
|
)
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.error("$TAG: Failed to sync events", e)
|
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) {
|
override fun selectTab(tabId: String) {
|
||||||
try {
|
try {
|
||||||
components.useCases.tabsUseCases.selectTab(tabId = tabId)
|
components.useCases.tabsUseCases.selectTab(tabId = tabId)
|
||||||
|
|||||||
+6
-2
@@ -4344,7 +4344,7 @@ interface GeckoSessionApi {
|
|||||||
}
|
}
|
||||||
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
|
||||||
interface GeckoTabsApi {
|
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 selectTab(tabId: String)
|
||||||
fun removeTab(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
|
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 onHistoryStateChangeArg = args[6] as Boolean
|
||||||
val onFindResultsArg = args[7] as Boolean
|
val onFindResultsArg = args[7] as Boolean
|
||||||
val onThumbnailChangeArg = args[8] 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 {
|
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)
|
listOf(null)
|
||||||
} catch (exception: Throwable) {
|
} catch (exception: Throwable) {
|
||||||
GeckoPigeonUtils.wrapError(exception)
|
GeckoPigeonUtils.wrapError(exception)
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ class GeckoTabService {
|
|||||||
bool onHistoryStateChange = false,
|
bool onHistoryStateChange = false,
|
||||||
bool onFindResults = false,
|
bool onFindResults = false,
|
||||||
bool onThumbnailChange = false,
|
bool onThumbnailChange = false,
|
||||||
|
bool onBrowserExtensionsChange = false,
|
||||||
|
bool onPageExtensionsChange = false,
|
||||||
|
bool onBrowserExtensionIcons = false,
|
||||||
|
bool onPageExtensionIcons = false,
|
||||||
}) {
|
}) {
|
||||||
return _api.syncEvents(
|
return _api.syncEvents(
|
||||||
onSelectedTabChange: onSelectedTabChange,
|
onSelectedTabChange: onSelectedTabChange,
|
||||||
@@ -36,6 +40,10 @@ class GeckoTabService {
|
|||||||
onFindResults: onFindResults,
|
onFindResults: onFindResults,
|
||||||
onThumbnailChange: onThumbnailChange,
|
onThumbnailChange: onThumbnailChange,
|
||||||
onReaderableStateChange: false,
|
onReaderableStateChange: false,
|
||||||
|
onBrowserExtensionsChange: onBrowserExtensionsChange,
|
||||||
|
onPageExtensionsChange: onPageExtensionsChange,
|
||||||
|
onBrowserExtensionIcons: onBrowserExtensionIcons,
|
||||||
|
onPageExtensionIcons: onPageExtensionIcons,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4935,14 +4935,14 @@ class GeckoTabsApi {
|
|||||||
|
|
||||||
final String pigeonVar_messageChannelSuffix;
|
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_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoTabsApi.syncEvents$pigeonVar_messageChannelSuffix';
|
||||||
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
pigeonVar_channelName,
|
pigeonVar_channelName,
|
||||||
pigeonChannelCodec,
|
pigeonChannelCodec,
|
||||||
binaryMessenger: pigeonVar_binaryMessenger,
|
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?>?;
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
if (pigeonVar_replyList == null) {
|
if (pigeonVar_replyList == null) {
|
||||||
throw _createConnectionError(pigeonVar_channelName);
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
|||||||
@@ -1113,6 +1113,10 @@ abstract class GeckoTabsApi {
|
|||||||
required bool onHistoryStateChange,
|
required bool onHistoryStateChange,
|
||||||
required bool onFindResults,
|
required bool onFindResults,
|
||||||
required bool onThumbnailChange,
|
required bool onThumbnailChange,
|
||||||
|
required bool onBrowserExtensionsChange,
|
||||||
|
required bool onPageExtensionsChange,
|
||||||
|
required bool onBrowserExtensionIcons,
|
||||||
|
required bool onPageExtensionIcons,
|
||||||
});
|
});
|
||||||
|
|
||||||
void selectTab({required String tabId});
|
void selectTab({required String tabId});
|
||||||
|
|||||||
Reference in New Issue
Block a user