This commit is contained in:
Fabian Freund
2025-02-16 23:21:44 +01:00
parent ddb0f14059
commit 6368e30479
81 changed files with 20308 additions and 88 deletions
@@ -0,0 +1 @@
../../../../../javascript/container_proxy/dist/
@@ -5,6 +5,7 @@
package eu.lensai.flutter_mozilla_components
import android.content.Context
import eu.lensai.flutter_mozilla_components.feature.ContainerProxyFeature
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
import eu.lensai.flutter_mozilla_components.feature.PrefManagerFeature
import mozilla.components.browser.engine.gecko.GeckoEngine
@@ -50,6 +51,7 @@ object EngineProvider {
WebCompatFeature.install(it)
CookieManagerFeature.install(it)
PrefManagerFeature.install(it)
ContainerProxyFeature.install(it)
}
}
@@ -6,6 +6,7 @@ import androidx.fragment.app.FragmentActivity
import eu.lensai.flutter_mozilla_components.activities.NotificationActivity
import eu.lensai.flutter_mozilla_components.api.GeckoAddonsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoBrowserApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoContainerProxyApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoCookieApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoDeleteBrowsingDataControllerImpl
import eu.lensai.flutter_mozilla_components.api.GeckoEngineSettingsApiImpl
@@ -20,6 +21,7 @@ import eu.lensai.flutter_mozilla_components.feature.DefaultSelectionActionDelega
import eu.lensai.flutter_mozilla_components.pigeons.GeckoAddonEvents
import eu.lensai.flutter_mozilla_components.pigeons.GeckoAddonsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoContainerProxyApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoDeleteBrowsingDataController
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
@@ -46,6 +48,12 @@ import mozilla.components.support.base.log.sink.AndroidLogSink
/** FlutterMozillaComponentsPlugin */
class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
companion object {
private const val FRAGMENT_CONTAINER_ID = 0xBEEF
private var isGeckoInitialized = false
}
private val components by lazy {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
@@ -58,11 +66,18 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
private var isPlatformViewRegistered = false
private var pendingFragmentShow = false
init {
Log.addSink(AndroidLogSink())
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
synchronized(this) {
if(!isGeckoInitialized) {
Log.addSink(AndroidLogSink())
setupGeckoEngine(flutterPluginBinding)
isGeckoInitialized = true
}
}
}
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
private fun setupGeckoEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
_flutterPluginBinding = flutterPluginBinding
_flutterEvents = GeckoStateEvents(_flutterPluginBinding.binaryMessenger)
@@ -99,6 +114,7 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
GeckoIconsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoIconsApiImpl())
GeckoCookieApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoCookieApiImpl())
GeckoPrefApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoPrefApiImpl())
GeckoContainerProxyApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoContainerProxyApiImpl())
GeckoFindApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFindApiImpl())
GeckoSelectionActionController.setUp(_flutterPluginBinding.binaryMessenger, GeckoSelectionActionControllerImpl(
selectionActionDelegate
@@ -169,8 +185,4 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
isPlatformViewRegistered = false
pendingFragmentShow = false
}
companion object {
private const val FRAGMENT_CONTAINER_ID = 0xBEEF
}
}
@@ -0,0 +1,18 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.feature.ContainerProxyFeature
import eu.lensai.flutter_mozilla_components.pigeons.GeckoContainerProxyApi
class GeckoContainerProxyApiImpl : GeckoContainerProxyApi {
override fun setProxyPort(port: Long) {
ContainerProxyFeature.scheduleRequest("setProxyPort", port.toInt())
}
override fun addContainerProxy(contextId: String) {
ContainerProxyFeature.scheduleRequest("addContainerProxy", contextId)
}
override fun removeContainerProxy(contextId: String) {
ContainerProxyFeature.scheduleRequest("removeContainerProxy", contextId)
}
}
@@ -0,0 +1,67 @@
package eu.lensai.flutter_mozilla_components.feature
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
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.webextensions.WebExtensionController
import org.json.JSONObject
object ContainerProxyFeature {
private val logger = Logger("container_proxy")
private const val CONTAINER_PROXY_REPORTER_EXTENSION_ID = "container-proxy@lensai.eu"
private const val CONTAINER_PROXY_REPORTER_EXTENSION_URL = "resource://android/assets/extensions/container_proxy/"
private const val CONTAINER_PROXY_REPORTER_MESSAGING_ID = "containerProxy"
@VisibleForTesting
// This is an internal var to make it mutable for unit testing purposes only
internal var extensionController = WebExtensionController(
CONTAINER_PROXY_REPORTER_EXTENSION_ID,
CONTAINER_PROXY_REPORTER_EXTENSION_URL,
CONTAINER_PROXY_REPORTER_MESSAGING_ID,
)
fun scheduleRequest(command: String, args: Any) {
val message = JSONObject()
message.put("action", command);
message.put("args", args)
runBlocking {
withContext(Dispatchers.Default) {
extensionController.sendBackgroundMessage(message)
}
}
}
private class ContainerProxyBackgroundMessageHandler() : MessageHandler {
}
/**
* Installs the web extension in the runtime through the WebExtensionRuntime install method
*
* @param runtime a WebExtensionRuntime.
* @param productName a custom product name used to automatically label reports. Defaults to
* "android-components".
*/
fun install(runtime: WebExtensionRuntime) {
extensionController.registerBackgroundMessageHandler(
ContainerProxyBackgroundMessageHandler()
)
extensionController.install(
runtime,
onSuccess = {
logger.debug("Installed ContainerProxy webextension: ${it.id}")
},
onError = { throwable ->
logger.error("Failed to install ContainerProxy webextension: ", throwable)
},
)
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// Autogenerated from Pigeon (v24.1.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -2567,6 +2567,78 @@ interface GeckoPrefApi {
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoContainerProxyApi {
fun setProxyPort(port: Long)
fun addContainerProxy(contextId: String)
fun removeContainerProxy(contextId: String)
companion object {
/** The codec used by GeckoContainerProxyApi. */
val codec: MessageCodec<Any?> by lazy {
GeckoPigeonCodec()
}
/** Sets up an instance of `GeckoContainerProxyApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoContainerProxyApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.setProxyPort$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val portArg = args[0] as Long
val wrapped: List<Any?> = try {
api.setProxyPort(portArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.addContainerProxy$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val contextIdArg = args[0] as String
val wrapped: List<Any?> = try {
api.addContainerProxy(contextIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.removeContainerProxy$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val contextIdArg = args[0] as String
val wrapped: List<Any?> = try {
api.removeContainerProxy(contextIdArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoCookieApi {
fun getCookie(firstPartyDomain: String?, name: String, partitionKey: CookiePartitionKey?, storeId: String?, url: String, callback: (Result<Cookie>) -> Unit)
fun getAllCookies(domain: String?, firstPartyDomain: String?, name: String?, partitionKey: CookiePartitionKey?, storeId: String?, url: String, callback: (Result<List<Cookie>>) -> Unit)