one step closer

This commit is contained in:
Fabian Freund
2024-10-22 07:27:37 +02:00
parent c4d93e8fe7
commit 8d351e55c0
92 changed files with 3829 additions and 2694 deletions
@@ -3,6 +3,8 @@ version = "1.0-SNAPSHOT"
buildscript {
ext.kotlin_version = "1.9.22"
ext.mozillaComponentsVersion = '131.0.3'
repositories {
google()
mavenCentral()
@@ -56,37 +58,36 @@ android {
}
dependencies {
implementation 'org.mozilla.components:support-utils:131.0b1'
implementation 'org.mozilla.components:support-ktx:131.0b1'
implementation 'org.mozilla.components:support-webextensions:131.0b1'
implementation 'org.mozilla.components:support-locale:131.0b1'
implementation 'org.mozilla.components:lib-fetch-httpurlconnection:131.0b1'
implementation 'org.mozilla.components:lib-crash:131.0b1'
implementation 'org.mozilla.components:lib-publicsuffixlist:131.0b1'
implementation 'org.mozilla.components:lib-dataprotect:131.0b1'
implementation 'org.mozilla.components:concept-engine:131.0b1'
implementation 'org.mozilla.components:concept-fetch:131.0b1'
implementation 'org.mozilla.components:browser-engine-gecko:131.0b1'
implementation 'org.mozilla.components:browser-state:131.0b1'
implementation 'org.mozilla.components:browser-session-storage:131.0b1'
implementation 'org.mozilla.components:browser-icons:131.0b1'
//implementation 'org.mozilla.components:browser-storage-sync:131.0b1'
implementation 'org.mozilla.components:browser-thumbnails:131.0b1'
implementation 'org.mozilla.components:feature-addons:131.0b1'
implementation 'org.mozilla.components:feature-app-links:131.0b1'
implementation 'org.mozilla.components:feature-autofill:131.0b1'
implementation 'org.mozilla.components:feature-downloads:131.0b1'
implementation 'org.mozilla.components:feature-media:131.0b1'
implementation 'org.mozilla.components:feature-tabs:131.0b1'
implementation 'org.mozilla.components:feature-prompts:131.0b1'
implementation 'org.mozilla.components:feature-session:131.0b1'
implementation 'org.mozilla.components:feature-readerview:131.0b1'
implementation 'org.mozilla.components:feature-privatemode:131.0b1'
implementation 'org.mozilla.components:feature-sitepermissions:131.0b1'
implementation 'org.mozilla.components:feature-webcompat:131.0b1'
implementation 'org.mozilla.components:feature-webnotifications:131.0b1'
implementation 'org.mozilla.components:service-digitalassetlinks:131.0b1'
implementation 'org.mozilla.components:ui-widgets:131.0b1'
implementation "org.mozilla.components:support-utils:$mozillaComponentsVersion"
implementation "org.mozilla.components:support-ktx:$mozillaComponentsVersion"
implementation "org.mozilla.components:support-webextensions:$mozillaComponentsVersion"
implementation "org.mozilla.components:support-locale:$mozillaComponentsVersion"
implementation "org.mozilla.components:lib-fetch-httpurlconnection:$mozillaComponentsVersion"
implementation "org.mozilla.components:lib-crash:$mozillaComponentsVersion"
implementation "org.mozilla.components:lib-publicsuffixlist:$mozillaComponentsVersion"
implementation "org.mozilla.components:lib-dataprotect:$mozillaComponentsVersion"
implementation "org.mozilla.components:concept-engine:$mozillaComponentsVersion"
implementation "org.mozilla.components:concept-fetch:$mozillaComponentsVersion"
implementation "org.mozilla.components:browser-engine-gecko:$mozillaComponentsVersion"
implementation "org.mozilla.components:browser-state:$mozillaComponentsVersion"
implementation "org.mozilla.components:browser-session-storage:$mozillaComponentsVersion"
implementation "org.mozilla.components:browser-icons:$mozillaComponentsVersion"
implementation "org.mozilla.components:browser-thumbnails:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-addons:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-app-links:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-autofill:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-downloads:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-media:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-tabs:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-prompts:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-session:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-readerview:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-privatemode:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-sitepermissions:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-webcompat:$mozillaComponentsVersion"
implementation "org.mozilla.components:feature-webnotifications:$mozillaComponentsVersion"
implementation "org.mozilla.components:service-digitalassetlinks:$mozillaComponentsVersion"
implementation "org.mozilla.components:ui-widgets:$mozillaComponentsVersion"
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
@@ -229,22 +229,6 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// consumeFlow(components.store) { flow ->
// flow.mapNotNull { state -> state.findCustomTabOrSelectedTab(sessionId) }
// .ifAnyChanged { tab ->
// arrayOf(
// tab.content.loading,
// tab.content.canGoBack,
// tab.content.canGoForward,
// )
// }
// .collect {
// binding.toolbar.invalidateActions()
// }
// }
}
@CallSuper
override fun onBackPressed(): Boolean =
listOf(sessionFeature).any { it.onBackPressed() }
@@ -31,9 +31,9 @@ class BrowserFragment(private val context: Context) : BaseBrowserFragment(), Use
override fun createEngine(components: Components): EngineView {
return components.engine.createView(context).apply {
selectionActionDelegate = DefaultSelectionActionDelegate(
components.selectionAction
)
// selectionActionDelegate = DefaultSelectionActionDelegate(
// components.selectionAction
// )
}
}
@@ -88,6 +88,7 @@ open class DefaultComponents(
}
var engineView: EngineView? = null
var engineReportedInitialized = false
val preferences: SharedPreferences =
applicationContext.getSharedPreferences(SAMPLE_BROWSER_PREFERENCES, Context.MODE_PRIVATE)
@@ -166,6 +167,7 @@ open class DefaultComponents(
.distinctUntilChanged()
.collect { tabId ->
flutterEvents.onSelectedTabChange(
System.currentTimeMillis(),
tabId
) { _ -> }
}
@@ -181,6 +183,7 @@ open class DefaultComponents(
.collect { tab ->
val iconBytes = tab.content.icon?.toWebPBytes()
flutterEvents.onIconChange(
System.currentTimeMillis(),
tab.id,
iconBytes
) { _ -> }
@@ -195,6 +198,7 @@ open class DefaultComponents(
.debounce { 50 }
.collect { tab ->
flutterEvents.onSecurityInfoStateChange(
System.currentTimeMillis(),
tab.id,
SecurityInfoState(
tab.content.securityInfo.secure,
@@ -218,6 +222,7 @@ open class DefaultComponents(
.debounce { 50 }
.collect { tab ->
flutterEvents.onReaderableStateChange(
System.currentTimeMillis(),
tab.id,
ReaderableState(
tab.readerState.readerable,
@@ -241,6 +246,7 @@ open class DefaultComponents(
.debounce { 50 }
.collect { tab ->
flutterEvents.onHistoryStateChange(
System.currentTimeMillis(),
tab.id,
HistoryState(
items = tab.content.history.items.map { item -> HistoryItem(
@@ -259,7 +265,7 @@ open class DefaultComponents(
flow.mapNotNull { state -> state.tabs.map {tab -> tab.id} }
.distinctUntilChanged()
.collect { tabs ->
flutterEvents.onTabListChange(tabs) { _ -> }
flutterEvents.onTabListChange(System.currentTimeMillis(), tabs) { _ -> }
}
}
@@ -280,6 +286,7 @@ open class DefaultComponents(
.collect { tab ->
logger.info("title: ${tab.content.title} ${tab.content.url}")
flutterEvents.onTabContentStateChange(
System.currentTimeMillis(),
TabContentState(
id = tab.id,
contextId = tab.contextId,
@@ -303,6 +310,7 @@ open class DefaultComponents(
.collect { tab ->
tab.content.findResults
flutterEvents.onFindResults(
System.currentTimeMillis(),
tab.id,
tab.content.findResults.map { result -> FindResultState(
activeMatchOrdinal = result.activeMatchOrdinal.toLong(),
@@ -40,7 +40,9 @@ private class NativeFragmentView(
override fun onFlutterViewAttached(flutterView: View) {
super.onFlutterViewAttached(flutterView)
flutterEvents.onFragmentReadyStateChange(true) { _ -> }
GlobalComponents.components!!.engineReportedInitialized = false;
flutterEvents.onViewReadyStateChange(System.currentTimeMillis(),true) { _ -> }
}
override fun getView(): View {
@@ -84,8 +84,6 @@ object GlobalComponents {
Logger.error("Failed to initialize web extension support", e)
}
newComponents.tabsUseCases.addTab.invoke("https://google.com", selectTab = true)
_components = newComponents
}
}
@@ -1,9 +1,21 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.GlobalComponents
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi
import mozilla.components.browser.state.action.SystemAction
import mozilla.components.feature.addons.logger
class GeckoBrowserApiImpl(private val showFragmentCallback: () -> Unit) : GeckoBrowserApi {
override fun showNativeFragment() {
showFragmentCallback.invoke();
}
override fun onTrimMemory(level: Long) {
logger.debug("onTrimMemory: $level")
val components = GlobalComponents.components!!
components.store.dispatch(SystemAction.LowMemoryAction(level.toInt()))
components.icons.onTrimMemory(level.toInt())
}
}
@@ -19,7 +19,13 @@ import eu.lensai.flutter_mozilla_components.pigeons.SecurityInfoState
import eu.lensai.flutter_mozilla_components.pigeons.TabContentState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.browser.session.storage.RecoverableBrowserState
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.selector.findTab
@@ -43,6 +49,7 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
private val engine: Engine by lazy { GlobalComponents.components!!.engine }
private val state: BrowserState by lazy { GlobalComponents.components!!.store.state }
private val thumbnailStorage: ThumbnailStorage by lazy { GlobalComponents.components!!.thumbnailStorage }
private val icons: BrowserIcons by lazy { GlobalComponents.components!!.icons }
private val events: GeckoStateEvents by lazy { GlobalComponents.components!!.flutterEvents }
private fun restoreSource(source: SourceValue ) : SessionState.Source {
@@ -115,19 +122,27 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
onFindResults: Boolean,
onThumbnailChange: Boolean,
) {
val tabs = state.tabs.map { x -> x.copy() }.toList()
val selectedTab = state.selectedTabId
if(onSelectedTabChange) {
events.onSelectedTabChange(
state.selectedTabId
System.currentTimeMillis(),
selectedTab
) { _ -> }
}
if(onTabListChange) {
events.onTabListChange(state.tabs.map {tab -> tab.id}) { _ -> }
events.onTabListChange(
System.currentTimeMillis(),
tabs.map {tab -> tab.id}
) { _ -> }
}
if(onTabContentStateChange) {
state.tabs.forEach { tab ->
tabs.forEach { tab ->
events.onTabContentStateChange(
System.currentTimeMillis(),
TabContentState(
id = tab.id,
contextId = tab.contextId,
@@ -143,21 +158,35 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
}
if(onIconChange) {
state.tabs.forEach { tab ->
tabs.forEach { tab ->
if(tab.content.icon != null) {
val iconBytes = tab.content.icon?.toWebPBytes()
events.onIconChange(
System.currentTimeMillis(),
tab.id,
iconBytes
) { _ -> }
} else {
CoroutineScope(Dispatchers.Default).launch {
val result = icons.loadIcon(IconRequest(url = tab.content.url)).await()
val iconBytes = result.bitmap.toWebPBytes()
runOnUiThread {
events.onIconChange(
System.currentTimeMillis(),
tab.id,
iconBytes
) { _ -> }
}
}
}
}
}
if(onSecurityInfoStateChange) {
state.tabs.forEach { tab ->
val iconBytes = tab.content.icon?.toWebPBytes()
tabs.forEach { tab ->
events.onSecurityInfoStateChange(
System.currentTimeMillis(),
tab.id,
SecurityInfoState(
tab.content.securityInfo.secure,
@@ -169,8 +198,9 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
}
if(onReaderableStateChange) {
state.tabs.forEach { tab ->
tabs.forEach { tab ->
events.onReaderableStateChange(
System.currentTimeMillis(),
tab.id,
ReaderableState(
tab.readerState.readerable,
@@ -181,8 +211,9 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
}
if(onHistoryStateChange) {
state.tabs.forEach { tab ->
tabs.forEach { tab ->
events.onHistoryStateChange(
System.currentTimeMillis(),
tab.id,
HistoryState(
items = tab.content.history.items.map { item -> HistoryItem(
@@ -198,8 +229,9 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
}
if(onFindResults) {
state.tabs.forEach { tab ->
tabs.forEach { tab ->
events.onFindResults(
System.currentTimeMillis(),
tab.id,
tab.content.findResults.map { result -> FindResultState(
activeMatchOrdinal = result.activeMatchOrdinal.toLong(),
@@ -211,13 +243,13 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
}
if(onThumbnailChange) {
state.tabs.forEach { tab ->
tabs.forEach { tab ->
CoroutineScope(Dispatchers.Default).launch {
val bitmap = thumbnailStorage.loadThumbnail(
ImageLoadRequest(
id = tab.id,
//TODO: make this configurable
size = 600,
size = 1024,
isPrivate = tab.content.private
)
).await()
@@ -225,7 +257,7 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
if(bitmap != null) {
val bytes = bitmap.toWebPBytes()
runOnUiThread {
events.onThumbnailChange(tab.id, bytes) { _ -> }
events.onThumbnailChange(System.currentTimeMillis(), tab.id, bytes) { _ -> }
}
}
}
@@ -4,6 +4,23 @@ import android.graphics.Bitmap
import android.os.Build
import java.io.ByteArrayOutputStream
fun Bitmap.resize(maxWidth: Int, maxHeight: Int): Bitmap {
var width = this.width
var height = this.height
val aspectRatio: Float = width.toFloat() / height.toFloat()
if (width > height) {
width = maxWidth
height = (width / aspectRatio).toInt()
} else {
height = maxHeight
width = (height * aspectRatio).toInt()
}
return Bitmap.createScaledBitmap(this, width, height, true)
}
fun Bitmap.toWebPBytes(): ByteArray {
val stream = ByteArrayOutputStream()
val compressFormat = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@@ -5,16 +5,22 @@ package eu.lensai.flutter_mozilla_components.middleware
import android.graphics.Bitmap
import android.util.Log
import eu.lensai.flutter_mozilla_components.GlobalComponents
import eu.lensai.flutter_mozilla_components.ext.resize
import eu.lensai.flutter_mozilla_components.ext.toWebPBytes
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.action.LastAccessAction
import mozilla.components.browser.state.action.ReaderAction
import mozilla.components.browser.state.action.TabListAction
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.feature.addons.logger
import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.MiddlewareContext
import org.mozilla.gecko.util.ThreadUtils.runOnUiThread
import java.io.ByteArrayOutputStream
import kotlin.reflect.typeOf
/**
@@ -30,13 +36,37 @@ class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Midd
) {
when (action) {
is ContentAction.UpdateThumbnailAction -> {
val bytes = action.thumbnail.toWebPBytes()
val resized = action.thumbnail.resize(maxWidth = 640, maxHeight = 480);
val bytes = resized.toWebPBytes()
runOnUiThread {
flutterEvents.onThumbnailChange(action.sessionId, bytes) { _ -> }
flutterEvents.onThumbnailChange(System.currentTimeMillis(), action.sessionId, bytes) { _ -> }
}
}
//UpdateReaderConnectRequiredAction seems to be the only event that is called predictable
//after a hot reload
is ReaderAction.UpdateReaderConnectRequiredAction -> {
if(!GlobalComponents.components!!.engineReportedInitialized) {
runOnUiThread {
flutterEvents.onEngineReadyStateChange(
System.currentTimeMillis(),
true
) { _ -> }
}
GlobalComponents.components!!.engineReportedInitialized = true
}
}
is TabListAction.AddTabAction -> {
runOnUiThread {
flutterEvents.onTabAdded(
System.currentTimeMillis(),
action.tab.id
) { _ -> }
}
}
else -> {
// no-op
//logger.debug("Event fired: " + action.javaClass.name)
}
}
next(action)
@@ -1158,6 +1158,7 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoBrowserApi {
fun showNativeFragment()
fun onTrimMemory(level: Long)
companion object {
/** The codec used by GeckoBrowserApi. */
@@ -1184,6 +1185,24 @@ interface GeckoBrowserApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.onTrimMemory$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val levelArg = args[0] as Long
val wrapped: List<Any?> = try {
api.onTrimMemory(levelArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
@@ -2153,12 +2172,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
GeckoPigeonCodec()
}
}
fun onFragmentReadyStateChange(stateArg: Boolean, callback: (Result<Unit>) -> Unit)
fun onViewReadyStateChange(timestampArg: Long, stateArg: Boolean, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFragmentReadyStateChange$separatedMessageChannelSuffix"
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(stateArg)) {
channel.send(listOf(timestampArg, stateArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2170,12 +2189,46 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onTabListChange(tabIdsArg: List<String>, callback: (Result<Unit>) -> Unit)
fun onEngineReadyStateChange(timestampArg: Long, stateArg: Boolean, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(timestampArg, stateArg)) {
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(createConnectionError(channelName)))
}
}
}
fun onTabAdded(timestampArg: Long, tabIdArg: String, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(timestampArg, tabIdArg)) {
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(createConnectionError(channelName)))
}
}
}
fun onTabListChange(timestampArg: Long, tabIdsArg: List<String>, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(tabIdsArg)) {
channel.send(listOf(timestampArg, tabIdsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2187,12 +2240,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onSelectedTabChange(idArg: String?, callback: (Result<Unit>) -> Unit)
fun onSelectedTabChange(timestampArg: Long, idArg: String?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSelectedTabChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg)) {
channel.send(listOf(timestampArg, idArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2204,12 +2257,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onTabContentStateChange(stateArg: TabContentState, callback: (Result<Unit>) -> Unit)
fun onTabContentStateChange(timestampArg: Long, stateArg: TabContentState, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(stateArg)) {
channel.send(listOf(timestampArg, stateArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2221,12 +2274,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onHistoryStateChange(idArg: String, stateArg: HistoryState, callback: (Result<Unit>) -> Unit)
fun onHistoryStateChange(timestampArg: Long, idArg: String, stateArg: HistoryState, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, stateArg)) {
channel.send(listOf(timestampArg, idArg, stateArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2238,12 +2291,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onReaderableStateChange(idArg: String, stateArg: ReaderableState, callback: (Result<Unit>) -> Unit)
fun onReaderableStateChange(timestampArg: Long, idArg: String, stateArg: ReaderableState, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, stateArg)) {
channel.send(listOf(timestampArg, idArg, stateArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2255,12 +2308,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onSecurityInfoStateChange(idArg: String, stateArg: SecurityInfoState, callback: (Result<Unit>) -> Unit)
fun onSecurityInfoStateChange(timestampArg: Long, idArg: String, stateArg: SecurityInfoState, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, stateArg)) {
channel.send(listOf(timestampArg, idArg, stateArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2272,12 +2325,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onIconChange(idArg: String, bytesArg: ByteArray?, callback: (Result<Unit>) -> Unit)
fun onIconChange(timestampArg: Long, idArg: String, bytesArg: ByteArray?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, bytesArg)) {
channel.send(listOf(timestampArg, idArg, bytesArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2289,12 +2342,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onThumbnailChange(idArg: String, bytesArg: ByteArray?, callback: (Result<Unit>) -> Unit)
fun onThumbnailChange(timestampArg: Long, idArg: String, bytesArg: ByteArray?, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, bytesArg)) {
channel.send(listOf(timestampArg, idArg, bytesArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -2306,12 +2359,12 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onFindResults(idArg: String, resultsArg: List<FindResultState>, callback: (Result<Unit>) -> Unit)
fun onFindResults(timestampArg: Long, idArg: String, resultsArg: List<FindResultState>, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(idArg, resultsArg)) {
channel.send(listOf(timestampArg, idArg, resultsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
@@ -1,5 +1,20 @@
package eu.lensai.flutter_mozilla_components_example
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterFragmentActivity()
class MainActivity: FlutterFragmentActivity() {
private val CHANNEL = "me.movenext.flutter_mozilla_components/trim_memory"
private lateinit var channel: MethodChannel
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
channel.invokeMethod("onTrimMemory", level)
}
}
@@ -10,4 +10,8 @@ class GeckoBrowserService {
Future<void> showNativeFragment() {
return _api.showNativeFragment();
}
Future<void> onTrimMemory(int level) {
return _api.onTrimMemory(level);
}
}
@@ -12,22 +12,31 @@ typedef ThumbnailEvent = ({String tabId, Uint8List? bytes});
typedef FindResultsEvent = ({String tabId, List<FindResultState> results});
class GeckoEventService extends GeckoStateEvents {
final _lastEventTimes = <Subject, Map<dynamic, int>>{};
// Stream controllers
final _fragmentStateSubject = BehaviorSubject.seeded(false);
final _viewStateSubject = BehaviorSubject.seeded(false);
final _engineStateSubject = BehaviorSubject.seeded(false);
final _tabListSubject = BehaviorSubject<List<String>>();
final _selectedTabSubject = BehaviorSubject<String?>();
final _tabContentSubject = BehaviorSubject<TabContentState>();
final _historySubject = BehaviorSubject<HistoryEvent>();
final _readerableSubject = BehaviorSubject<ReaderableEvent>();
final _securityInfoSubject = BehaviorSubject<SecurityInfoEvent>();
final _iconSubject = BehaviorSubject<IconEvent>();
final _thumbnailSubject = BehaviorSubject<ThumbnailEvent>();
final _findResultsSubject = BehaviorSubject<FindResultsEvent>();
final _tabContentSubject = ReplaySubject<TabContentState>();
final _historySubject = ReplaySubject<HistoryEvent>();
final _securityInfoSubject = ReplaySubject<SecurityInfoEvent>();
final _readerableSubject = ReplaySubject<ReaderableEvent>();
final _iconSubject = PublishSubject<IconEvent>();
final _thumbnailSubject = PublishSubject<ThumbnailEvent>();
final _findResultsSubject = PublishSubject<FindResultsEvent>();
final _tabAddedSubject = PublishSubject<String>();
// Event streams
Stream<bool> get fragmentReadyStateEvents => _fragmentStateSubject.stream;
Stream<List<String>> get tabListEvents => _tabListSubject.stream;
Stream<String?> get selectedTabEvents => _selectedTabSubject.stream;
ValueStream<bool> get viewReadyStateEvents => _viewStateSubject.stream;
ValueStream<bool> get engineReadyStateEvents => _engineStateSubject.stream;
ValueStream<List<String>> get tabListEvents => _tabListSubject.stream;
ValueStream<String?> get selectedTabEvents => _selectedTabSubject.stream;
Stream<TabContentState> get tabContentEvents => _tabContentSubject.stream;
Stream<HistoryEvent> get historyEvents => _historySubject.stream;
Stream<ReaderableEvent> get readerableEvents => _readerableSubject.stream;
@@ -37,55 +46,124 @@ class GeckoEventService extends GeckoStateEvents {
Stream<ThumbnailEvent> get thumbnailEvents => _thumbnailSubject.stream;
Stream<FindResultsEvent> get findResultsEvent => _findResultsSubject.stream;
Stream<String> get tabAddedStream => _tabAddedSubject.stream;
void _addWhenMoreRecent<T>(
Subject<T> subject,
int timestamp,
dynamic identifier,
T value,
) {
_lastEventTimes[subject] ??= {};
if ((_lastEventTimes[subject]?[identifier] ?? 0) < timestamp) {
_lastEventTimes[subject]![identifier] = timestamp;
subject.add(value);
}
}
@override
void onFragmentReadyStateChange(bool state) {
_fragmentStateSubject.add(state);
void onViewReadyStateChange(int timestamp, bool state) {
_addWhenMoreRecent(_viewStateSubject, timestamp, null, state);
}
@override
void onEngineReadyStateChange(int timestamp, bool state) {
_addWhenMoreRecent(_engineStateSubject, timestamp, null, state);
}
// Overridden methods
@override
void onTabListChange(List<String?> tabIds) {
_tabListSubject.add(tabIds.nonNulls.toList());
void onTabListChange(int timestamp, List<String?> tabIds) {
_addWhenMoreRecent(
_tabListSubject,
timestamp,
null,
tabIds.nonNulls.toList(),
);
}
@override
void onSelectedTabChange(String? id) {
_selectedTabSubject.add(id);
void onSelectedTabChange(int timestamp, String? id) {
_addWhenMoreRecent(_selectedTabSubject, timestamp, id, id);
}
@override
void onTabContentStateChange(TabContentState state) {
_tabContentSubject.add(state);
void onTabContentStateChange(int timestamp, TabContentState state) {
_addWhenMoreRecent(_tabContentSubject, timestamp, state.id, state);
}
@override
void onHistoryStateChange(String id, HistoryState state) {
_historySubject.add((tabId: id, history: state));
void onHistoryStateChange(int timestamp, String id, HistoryState state) {
_addWhenMoreRecent(
_historySubject,
timestamp,
id,
(tabId: id, history: state),
);
}
@override
void onReaderableStateChange(String id, ReaderableState state) {
_readerableSubject.add((tabId: id, readerable: state));
void onReaderableStateChange(
int timestamp,
String id,
ReaderableState state,
) {
_addWhenMoreRecent(
_readerableSubject,
timestamp,
id,
(tabId: id, readerable: state),
);
}
@override
void onSecurityInfoStateChange(String id, SecurityInfoState state) {
_securityInfoSubject.add((tabId: id, securityInfo: state));
void onSecurityInfoStateChange(
int timestamp,
String id,
SecurityInfoState state,
) {
_addWhenMoreRecent(
_securityInfoSubject,
timestamp,
id,
(tabId: id, securityInfo: state),
);
}
@override
void onIconChange(String id, Uint8List? bytes) {
_iconSubject.add((tabId: id, bytes: bytes));
void onIconChange(int timestamp, String id, Uint8List? bytes) {
_addWhenMoreRecent(_iconSubject, timestamp, id, (tabId: id, bytes: bytes));
}
@override
void onThumbnailChange(String id, Uint8List? bytes) {
_thumbnailSubject.add((tabId: id, bytes: bytes));
void onThumbnailChange(int timestamp, String id, Uint8List? bytes) {
_addWhenMoreRecent(
_thumbnailSubject,
timestamp,
id,
(tabId: id, bytes: bytes),
);
}
@override
void onFindResults(String id, List<FindResultState?> results) {
_findResultsSubject.add((tabId: id, results: results.nonNulls.toList()));
void onFindResults(int timestamp, String id, List<FindResultState?> results) {
_addWhenMoreRecent(
_findResultsSubject,
timestamp,
id,
(tabId: id, results: results.nonNulls.toList()),
);
}
@override
void onTabAdded(int timestamp, String tabId) {
_addWhenMoreRecent(
_tabAddedSubject,
timestamp,
null,
tabId,
);
}
GeckoEventService.setUp({
@@ -109,5 +187,6 @@ class GeckoEventService extends GeckoStateEvents {
unawaited(_iconSubject.close());
unawaited(_thumbnailSubject.close());
unawaited(_findResultsSubject.close());
unawaited(_tabAddedSubject.close());
}
}
@@ -11,14 +11,14 @@ class GeckoTabService {
GeckoTabService({GeckoTabsApi? api}) : _api = api ?? _apiInstance;
Future<void> syncEvents({
bool onSelectedTabChange = true,
bool onTabListChange = true,
bool onTabContentStateChange = true,
bool onIconChange = true,
bool onSecurityInfoStateChange = true,
bool onHistoryStateChange = true,
bool onFindResults = true,
bool onThumbnailChange = true,
bool onSelectedTabChange = false,
bool onTabListChange = false,
bool onTabContentStateChange = false,
bool onIconChange = false,
bool onSecurityInfoStateChange = false,
bool onHistoryStateChange = false,
bool onFindResults = false,
bool onThumbnailChange = false,
}) {
return _api.syncEvents(
onSelectedTabChange: onSelectedTabChange,
@@ -8,11 +8,35 @@ import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mozilla_components/src/domain/services/gecko_browser.dart';
class GeckoView extends StatelessWidget {
class GeckoView extends StatefulWidget {
final FutureOr<void> Function()? preInitializationStep;
const GeckoView({super.key, this.preInitializationStep});
@override
State<GeckoView> createState() => _GeckoViewState();
}
class _GeckoViewState extends State<GeckoView> {
static const platform =
MethodChannel('me.movenext.flutter_mozilla_components/trim_memory');
final browserService = GeckoBrowserService();
@override
void initState() {
super.initState();
_setupMethodCallHandler();
}
void _setupMethodCallHandler() {
platform.setMethodCallHandler((MethodCall call) async {
if (call.method == 'onTrimMemory') {
await browserService.onTrimMemory(call.arguments as int);
}
});
}
@override
Widget build(BuildContext context) {
return PlatformViewLink(
@@ -39,8 +63,14 @@ class GeckoView extends StatelessWidget {
params.onPlatformViewCreated(value);
SchedulerBinding.instance.addPostFrameCallback((_) async {
await preInitializationStep?.call();
await GeckoBrowserService().showNativeFragment();
await widget.preInitializationStep?.call();
await Future.delayed(
//Wait for two more frames just to be sure view has been initialized
Duration(milliseconds: ((1000 / 60) * 2).toInt()),
).whenComplete(() async {
await browserService.showNativeFragment();
});
});
})
// ignore: discarded_futures
@@ -1187,6 +1187,28 @@ class GeckoBrowserApi {
return;
}
}
Future<void> onTrimMemory(int level) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoBrowserApi.onTrimMemory$pigeonVar_messageChannelSuffix';
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final List<Object?>? pigeonVar_replyList =
await pigeonVar_channel.send(<Object?>[level]) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else {
return;
}
}
}
class GeckoEngineSettingsApi {
@@ -2243,44 +2265,107 @@ class GeckoCookieApi {
abstract class GeckoStateEvents {
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
void onFragmentReadyStateChange(bool state);
void onViewReadyStateChange(int timestamp, bool state);
void onTabListChange(List<String> tabIds);
void onEngineReadyStateChange(int timestamp, bool state);
void onSelectedTabChange(String? id);
void onTabAdded(int timestamp, String tabId);
void onTabContentStateChange(TabContentState state);
void onTabListChange(int timestamp, List<String> tabIds);
void onHistoryStateChange(String id, HistoryState state);
void onSelectedTabChange(int timestamp, String? id);
void onReaderableStateChange(String id, ReaderableState state);
void onTabContentStateChange(int timestamp, TabContentState state);
void onSecurityInfoStateChange(String id, SecurityInfoState state);
void onHistoryStateChange(int timestamp, String id, HistoryState state);
void onIconChange(String id, Uint8List? bytes);
void onReaderableStateChange(int timestamp, String id, ReaderableState state);
void onThumbnailChange(String id, Uint8List? bytes);
void onSecurityInfoStateChange(int timestamp, String id, SecurityInfoState state);
void onFindResults(String id, List<FindResultState> results);
void onIconChange(int timestamp, String id, Uint8List? bytes);
void onThumbnailChange(int timestamp, String id, Uint8List? bytes);
void onFindResults(int timestamp, String id, List<FindResultState> results);
static void setUp(GeckoStateEvents? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) {
messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFragmentReadyStateChange$messageChannelSuffix', pigeonChannelCodec,
'dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFragmentReadyStateChange was null.');
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final bool? arg_state = (args[0] as bool?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange was null, expected non-null int.');
final bool? arg_state = (args[1] as bool?);
assert(arg_state != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFragmentReadyStateChange was null, expected non-null bool.');
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onViewReadyStateChange was null, expected non-null bool.');
try {
api.onFragmentReadyStateChange(arg_state!);
api.onViewReadyStateChange(arg_timestamp!, arg_state!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange was null, expected non-null int.');
final bool? arg_state = (args[1] as bool?);
assert(arg_state != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onEngineReadyStateChange was null, expected non-null bool.');
try {
api.onEngineReadyStateChange(arg_timestamp!, arg_state!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
} catch (e) {
return wrapResponse(error: PlatformException(code: 'error', message: e.toString()));
}
});
}
}
{
final BasicMessageChannel<Object?> pigeonVar_channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded$messageChannelSuffix', pigeonChannelCodec,
binaryMessenger: binaryMessenger);
if (api == null) {
pigeonVar_channel.setMessageHandler(null);
} else {
pigeonVar_channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded was null, expected non-null int.');
final String? arg_tabId = (args[1] as String?);
assert(arg_tabId != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabAdded was null, expected non-null String.');
try {
api.onTabAdded(arg_timestamp!, arg_tabId!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2301,11 +2386,14 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final List<String>? arg_tabIds = (args[0] as List<Object?>?)?.cast<String>();
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange was null, expected non-null int.');
final List<String>? arg_tabIds = (args[1] as List<Object?>?)?.cast<String>();
assert(arg_tabIds != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabListChange was null, expected non-null List<String>.');
try {
api.onTabListChange(arg_tabIds!);
api.onTabListChange(arg_timestamp!, arg_tabIds!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2326,9 +2414,12 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSelectedTabChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSelectedTabChange was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
try {
api.onSelectedTabChange(arg_id);
api.onSelectedTabChange(arg_timestamp!, arg_id);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2349,11 +2440,14 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final TabContentState? arg_state = (args[0] as TabContentState?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange was null, expected non-null int.');
final TabContentState? arg_state = (args[1] as TabContentState?);
assert(arg_state != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onTabContentStateChange was null, expected non-null TabContentState.');
try {
api.onTabContentStateChange(arg_state!);
api.onTabContentStateChange(arg_timestamp!, arg_state!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2374,14 +2468,17 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null, expected non-null String.');
final HistoryState? arg_state = (args[1] as HistoryState?);
final HistoryState? arg_state = (args[2] as HistoryState?);
assert(arg_state != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onHistoryStateChange was null, expected non-null HistoryState.');
try {
api.onHistoryStateChange(arg_id!, arg_state!);
api.onHistoryStateChange(arg_timestamp!, arg_id!, arg_state!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2402,14 +2499,17 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null, expected non-null String.');
final ReaderableState? arg_state = (args[1] as ReaderableState?);
final ReaderableState? arg_state = (args[2] as ReaderableState?);
assert(arg_state != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onReaderableStateChange was null, expected non-null ReaderableState.');
try {
api.onReaderableStateChange(arg_id!, arg_state!);
api.onReaderableStateChange(arg_timestamp!, arg_id!, arg_state!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2430,14 +2530,17 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null, expected non-null String.');
final SecurityInfoState? arg_state = (args[1] as SecurityInfoState?);
final SecurityInfoState? arg_state = (args[2] as SecurityInfoState?);
assert(arg_state != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onSecurityInfoStateChange was null, expected non-null SecurityInfoState.');
try {
api.onSecurityInfoStateChange(arg_id!, arg_state!);
api.onSecurityInfoStateChange(arg_timestamp!, arg_id!, arg_state!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2458,12 +2561,15 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onIconChange was null, expected non-null String.');
final Uint8List? arg_bytes = (args[1] as Uint8List?);
final Uint8List? arg_bytes = (args[2] as Uint8List?);
try {
api.onIconChange(arg_id!, arg_bytes);
api.onIconChange(arg_timestamp!, arg_id!, arg_bytes);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2484,12 +2590,15 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onThumbnailChange was null, expected non-null String.');
final Uint8List? arg_bytes = (args[1] as Uint8List?);
final Uint8List? arg_bytes = (args[2] as Uint8List?);
try {
api.onThumbnailChange(arg_id!, arg_bytes);
api.onThumbnailChange(arg_timestamp!, arg_id!, arg_bytes);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -2510,14 +2619,17 @@ abstract class GeckoStateEvents {
assert(message != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null.');
final List<Object?> args = (message as List<Object?>?)!;
final String? arg_id = (args[0] as String?);
final int? arg_timestamp = (args[0] as int?);
assert(arg_timestamp != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null, expected non-null int.');
final String? arg_id = (args[1] as String?);
assert(arg_id != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null, expected non-null String.');
final List<FindResultState>? arg_results = (args[1] as List<Object?>?)?.cast<FindResultState>();
final List<FindResultState>? arg_results = (args[2] as List<Object?>?)?.cast<FindResultState>();
assert(arg_results != null,
'Argument for dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onFindResults was null, expected non-null List<FindResultState>.');
try {
api.onFindResults(arg_id!, arg_results!);
api.onFindResults(arg_timestamp!, arg_id!, arg_results!);
return wrapResponse(empty: true);
} on PlatformException catch (e) {
return wrapResponse(error: e);
@@ -539,6 +539,7 @@ class FindResultState {
@HostApi()
abstract class GeckoBrowserApi {
void showNativeFragment();
void onTrimMemory(int level);
}
@HostApi()
@@ -782,19 +783,26 @@ abstract class GeckoCookieApi {
@FlutterApi()
abstract class GeckoStateEvents {
void onFragmentReadyStateChange(bool state);
void onViewReadyStateChange(int timestamp, bool state);
void onEngineReadyStateChange(int timestamp, bool state);
void onTabListChange(List<String> tabIds);
void onSelectedTabChange(String? id);
void onTabAdded(int timestamp, String tabId);
void onTabContentStateChange(TabContentState state);
void onHistoryStateChange(String id, HistoryState state);
void onReaderableStateChange(String id, ReaderableState state);
void onSecurityInfoStateChange(String id, SecurityInfoState state);
void onIconChange(String id, Uint8List? bytes);
void onThumbnailChange(String id, Uint8List? bytes);
void onTabListChange(int timestamp, List<String> tabIds);
void onSelectedTabChange(int timestamp, String? id);
void onFindResults(String id, List<FindResultState> results);
void onTabContentStateChange(int timestamp, TabContentState state);
void onHistoryStateChange(int timestamp, String id, HistoryState state);
void onReaderableStateChange(int timestamp, String id, ReaderableState state);
void onSecurityInfoStateChange(
int timestamp,
String id,
SecurityInfoState state,
);
void onIconChange(int timestamp, String id, Uint8List? bytes);
void onThumbnailChange(int timestamp, String id, Uint8List? bytes);
void onFindResults(int timestamp, String id, List<FindResultState> results);
}
@HostApi()