implemented container site assignements & improved routing

This commit is contained in:
Fabian Freund
2025-11-09 07:25:54 +01:00
parent da232bf8aa
commit 107b2a9aa6
45 changed files with 1104 additions and 264 deletions
@@ -12,6 +12,7 @@ import eu.weblibre.flutter_mozilla_components.feature.BrowserExtensionFeature
import eu.weblibre.flutter_mozilla_components.feature.MLEngineFeature
import eu.weblibre.flutter_mozilla_components.pigeons.BounceTrackingProtectionMode
import eu.weblibre.flutter_mozilla_components.pigeons.BrowserExtensionEvents
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents
import eu.weblibre.flutter_mozilla_components.pigeons.QueryParameterStripping
import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
@@ -81,7 +82,7 @@ object EngineProvider {
return runtime!!
}
fun createEngine(context: Context, defaultSettings: DefaultSettings, extensionEvents: BrowserExtensionEvents): Engine {
fun createEngine(context: Context, defaultSettings: DefaultSettings, extensionEvents: BrowserExtensionEvents, stateEvents: GeckoStateEvents): Engine {
Logger.debug("Creating Engine")
val runtime = getOrCreateRuntime(context)
@@ -89,7 +90,7 @@ object EngineProvider {
WebCompatFeature.install(it)
//CookieManagerFeature.install(it)
PrefManagerFeature.install(it)
ContainerProxyFeature.install(it)
ContainerProxyFeature.install(it, stateEvents)
BrowserExtensionFeature.install(it, extensionEvents)
MLEngineFeature.install(it)
}
@@ -25,6 +25,10 @@ class GeckoContainerProxyApiImpl : GeckoContainerProxyApi {
ContainerProxyFeature.scheduleRequest("removeContainerProxy", contextId)
}
override fun setSiteAssignments(assignments: Map<String, String>) {
ContainerProxyFeature.scheduleRequest("setSiteAssignments", JSONObject(assignments))
}
override fun healthcheck(callback: (Result<Boolean>) -> Unit) {
ContainerProxyFeature.scheduleRequestWithResponse("healthcheck", Unit, object :
ResultConsumer<JSONObject> {
@@ -114,7 +114,7 @@ class Core(
}
val engine: Engine by lazy {
EngineProvider.createEngine(context, engineSettings, extensionEvents)
EngineProvider.createEngine(context, engineSettings, extensionEvents, flutterEvents)
}
/**
@@ -158,7 +158,7 @@ class Core(
collectionUser = components.addonCollection.collectionUser,
collectionName = components.addonCollection.collectionName,
maxCacheAgeInMinutes = AMO_COLLECTION_MAX_CACHE_AGE
) else
) else
AMOAddonsProvider(
context,
client,
@@ -7,6 +7,9 @@
package eu.weblibre.flutter_mozilla_components.feature
import androidx.annotation.VisibleForTesting
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.pigeons.ContainerSiteAssignment
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoStateEvents
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
@@ -16,20 +19,27 @@ import mozilla.components.concept.engine.webextension.MessageHandler
import mozilla.components.concept.engine.webextension.Port
import mozilla.components.concept.engine.webextension.WebExtensionRuntime
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.ktx.android.org.json.tryGetString
import mozilla.components.support.webextensions.BuiltInWebExtensionController
import org.json.JSONObject
import org.mozilla.gecko.util.ThreadUtils.runOnUiThread
object ContainerProxyFeature {
private val logger = Logger("container_proxy")
private const val CONTAINER_PROXY_REPORTER_EXTENSION_ID = "container-proxy@weblibre.eu"
private const val CONTAINER_PROXY_REPORTER_EXTENSION_URL = "resource://android/assets/extensions/container_proxy/"
private const val CONTAINER_PROXY_REPORTER_EXTENSION_URL =
"resource://android/assets/extensions/container_proxy/"
private const val CONTAINER_PROXY_REPORTER_MESSAGING_ID = "containerProxy"
private var nextRequestId: Int = 0
private val requestHandlers = HashMap<Int, ResultConsumer<JSONObject>>()
private val mutex = Mutex()
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
@VisibleForTesting
// This is an internal var to make it mutable for unit testing purposes only
internal var extensionController = BuiltInWebExtensionController(
@@ -74,7 +84,9 @@ object ContainerProxyFeature {
}
}
private class ContainerProxyBackgroundMessageHandler() : MessageHandler {
private class ContainerProxyBackgroundMessageHandler(
private var events: GeckoStateEvents
) : MessageHandler {
override fun onPortMessage(message: Any, port: Port) {
runBlocking {
withContext(Dispatchers.Default) {
@@ -94,6 +106,25 @@ object ContainerProxyFeature {
message.getString("error")
)
}
} else if (type == "assignedSiteRequested") {
val requestId = messageJSON.getString("id")
val status = messageJSON.getString("status")
val details = messageJSON.getJSONObject("result")
if (status == "success") {
runOnUiThread {
events.onContainerSiteAssignment(
System.currentTimeMillis(),
ContainerSiteAssignment(
requestId = requestId,
tabId = components.core.store.state.selectedTabId,
originUrl = details.tryGetString("originUrl"),
url = details.getString("url"),
blocked = details.getBoolean("blocked")
)
) { _ -> }
}
}
}
}
}
@@ -108,9 +139,9 @@ object ContainerProxyFeature {
* @param productName a custom product name used to automatically label reports. Defaults to
* "android-components".
*/
fun install(runtime: WebExtensionRuntime) {
fun install(runtime: WebExtensionRuntime, events: GeckoStateEvents) {
extensionController.registerBackgroundMessageHandler(
ContainerProxyBackgroundMessageHandler()
ContainerProxyBackgroundMessageHandler(events)
)
extensionController.install(
runtime,
@@ -2277,6 +2277,46 @@ data class GeckoPref (
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
data class ContainerSiteAssignment (
val requestId: String,
val tabId: String? = null,
val originUrl: String? = null,
val url: String,
val blocked: Boolean
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): ContainerSiteAssignment {
val requestId = pigeonVar_list[0] as String
val tabId = pigeonVar_list[1] as String?
val originUrl = pigeonVar_list[2] as String?
val url = pigeonVar_list[3] as String
val blocked = pigeonVar_list[4] as Boolean
return ContainerSiteAssignment(requestId, tabId, originUrl, url, blocked)
}
}
fun toList(): List<Any?> {
return listOf(
requestId,
tabId,
originUrl,
url,
blocked,
)
}
override fun equals(other: Any?): Boolean {
if (other !is ContainerSiteAssignment) {
return false
}
if (this === other) {
return true
}
return GeckoPigeonUtils.deepEquals(toList(), other.toList()) }
override fun hashCode(): Int = toList().hashCode()
}
/** Generated class from Pigeon that represents data sent in messages. */
data class GeckoHeader (
val key: String,
@@ -2743,15 +2783,20 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
}
195.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoHeader.fromList(it)
ContainerSiteAssignment.fromList(it)
}
}
196.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchRequest.fromList(it)
GeckoHeader.fromList(it)
}
}
197.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchRequest.fromList(it)
}
}
198.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchResponse.fromList(it)
}
@@ -3025,18 +3070,22 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(194)
writeValue(stream, value.toList())
}
is GeckoHeader -> {
is ContainerSiteAssignment -> {
stream.write(195)
writeValue(stream, value.toList())
}
is GeckoFetchRequest -> {
is GeckoHeader -> {
stream.write(196)
writeValue(stream, value.toList())
}
is GeckoFetchResponse -> {
is GeckoFetchRequest -> {
stream.write(197)
writeValue(stream, value.toList())
}
is GeckoFetchResponse -> {
stream.write(198)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -4257,6 +4306,7 @@ interface GeckoContainerProxyApi {
fun setProxyPort(port: Long)
fun addContainerProxy(contextId: String)
fun removeContainerProxy(contextId: String)
fun setSiteAssignments(assignments: Map<String, String>)
fun healthcheck(callback: (Result<Boolean>) -> Unit)
companion object {
@@ -4322,6 +4372,24 @@ interface GeckoContainerProxyApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.setSiteAssignments$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val assignmentsArg = args[0] as Map<String, String>
val wrapped: List<Any?> = try {
api.setSiteAssignments(assignmentsArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.healthcheck$separatedMessageChannelSuffix", codec)
if (api != null) {
@@ -4744,6 +4812,23 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onContainerSiteAssignment(timestampArg: Long, detailsArg: ContainerSiteAssignment, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onContainerSiteAssignment$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(timestampArg, detailsArg)) {
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 GeckoLogging(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") {