intermediate
This commit is contained in:
+85
@@ -0,0 +1,85 @@
|
||||
'use strict';
|
||||
|
||||
console.log("Connecting to native port...")
|
||||
const port = browser.runtime.connectNative("cookieManager");
|
||||
console.log("Native port connected!")
|
||||
|
||||
function cookieToMap(cookie) {
|
||||
let partitionKey = {}
|
||||
if (cookie.partitionKey) {
|
||||
partitionKey["topLevelSite"] = cookie.partitionKey.topLevelSite
|
||||
}
|
||||
|
||||
return {
|
||||
"domain": cookie.domain,
|
||||
"expirationDate": cookie.expirationDate,
|
||||
"firstPartyDomain": cookie.firstPartyDomain,
|
||||
"hostOnly": cookie.hostOnly,
|
||||
"httpOnly": cookie.httpOnly,
|
||||
"name": cookie.name,
|
||||
"partitionKey": partitionKey,
|
||||
"path": cookie.path,
|
||||
"secure": cookie.secure,
|
||||
"session": cookie.session,
|
||||
"sameSite": cookie.sameSite,
|
||||
"storeId": cookie.storeId,
|
||||
"value": cookie.value
|
||||
}
|
||||
}
|
||||
|
||||
function sendCookieResultForRequest(id) {
|
||||
return function (cookie) {
|
||||
port.postMessage({
|
||||
"id": id,
|
||||
"status": "success",
|
||||
"result": cookieToMap(cookie)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function sendCookieListResultForRequest(id) {
|
||||
return function (cookies) {
|
||||
port.postMessage({
|
||||
"id": id,
|
||||
"status": "success",
|
||||
"result": cookies.map((cookie) => cookieToMap(cookie))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function sendErrorForRequest(id) {
|
||||
return function (error) {
|
||||
console.error(error);
|
||||
port.postMessage({
|
||||
"id": id,
|
||||
"status": "error",
|
||||
"error": error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
port.onMessage.addListener(message => {
|
||||
let requestId = message["id"]
|
||||
switch (message["action"]) {
|
||||
case "get":
|
||||
browser.cookies.get(message["args"])
|
||||
.then(sendCookieResultForRequest(requestId))
|
||||
.catch(sendErrorForRequest(requestId))
|
||||
break
|
||||
case "getAll":
|
||||
browser.cookies.getAll(message["args"])
|
||||
.then(sendCookieListResultForRequest(requestId))
|
||||
.catch(sendErrorForRequest(requestId))
|
||||
break
|
||||
case "remove":
|
||||
browser.cookies.remove(message["args"])
|
||||
.then(sendCookieResultForRequest(requestId))
|
||||
.catch(sendErrorForRequest(requestId))
|
||||
break
|
||||
case "set":
|
||||
browser.cookies.set(message["args"])
|
||||
.then(sendCookieResultForRequest(requestId))
|
||||
.catch(sendErrorForRequest(requestId))
|
||||
break
|
||||
}
|
||||
});
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "cookie-manager",
|
||||
"version": "1.0",
|
||||
"description": "Cookie manager",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "cookie-manager@lensai.eu"
|
||||
}
|
||||
},
|
||||
"background": {
|
||||
"scripts": [
|
||||
"background.js"
|
||||
]
|
||||
},
|
||||
"permissions": [
|
||||
"nativeMessaging",
|
||||
"nativeMessagingFromContent",
|
||||
"geckoViewAddons",
|
||||
"cookies",
|
||||
"<all_urls>"
|
||||
]
|
||||
}
|
||||
+1
@@ -8,6 +8,7 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import mozilla.components.browser.thumbnails.BrowserThumbnails
|
||||
import mozilla.components.concept.engine.EngineView
|
||||
import mozilla.components.feature.media.fullscreen.MediaSessionFullscreenFeature
|
||||
|
||||
+120
-8
@@ -9,9 +9,18 @@ import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.lensai.flutter_mozilla_components.ext.toWebPBytes
|
||||
import eu.lensai.flutter_mozilla_components.middleware.FlutterEventMiddleware
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.HistoryItem
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.HistoryState
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.ReaderableState
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.SecurityInfoState
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.TabContentState
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.engine.gecko.GeckoEngine
|
||||
@@ -66,7 +75,7 @@ private const val DAY_IN_MINUTES = 24 * 60L
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@Suppress("LargeClass")
|
||||
open class DefaultComponents(private val applicationContext: Context) {
|
||||
open class DefaultComponents(private val applicationContext: Context, private val flutterEvents: GeckoStateEvents) {
|
||||
companion object {
|
||||
const val SAMPLE_BROWSER_PREFERENCES = "sample_browser_preferences"
|
||||
const val PREF_LAUNCH_EXTERNAL_APP = "sample_browser_launch_external_app"
|
||||
@@ -133,7 +142,7 @@ open class DefaultComponents(private val applicationContext: Context) {
|
||||
val store by lazy {
|
||||
BrowserStore(
|
||||
middleware = listOf(
|
||||
FlutterEventMiddleware(),
|
||||
FlutterEventMiddleware(flutterEvents),
|
||||
DownloadMiddleware(applicationContext, DownloadService::class.java),
|
||||
ReaderViewMiddleware(),
|
||||
ThumbnailsMiddleware(thumbnailStorage),
|
||||
@@ -145,12 +154,63 @@ open class DefaultComponents(private val applicationContext: Context) {
|
||||
) + EngineMiddleware.create(engine),
|
||||
).apply {
|
||||
this.flowScoped { flow ->
|
||||
flow.mapNotNull { state -> state.selectedTab }
|
||||
.distinctUntilChangedBy {
|
||||
it.content.url
|
||||
flow.map { state -> state.selectedTabId }
|
||||
.collect { tabId ->
|
||||
flutterEvents.onSelectedTabChange(
|
||||
tabId
|
||||
) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
this.flowScoped { flow ->
|
||||
flow.mapNotNull { state -> state.tabs }
|
||||
.filterChanged {
|
||||
it.content.icon
|
||||
}
|
||||
.collect { tab ->
|
||||
Log.d("URL_CHANGE", tab.content.url)
|
||||
val iconBytes = tab.content.icon?.toWebPBytes()
|
||||
flutterEvents.onIconChange(
|
||||
tab.id,
|
||||
iconBytes
|
||||
) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
this.flowScoped { flow ->
|
||||
flow.mapNotNull { state -> state.tabs }
|
||||
.filterChanged {
|
||||
it.content.securityInfo
|
||||
}
|
||||
.collect { tab ->
|
||||
flutterEvents.onSecurityInfoStateChange(
|
||||
tab.id,
|
||||
SecurityInfoState(
|
||||
tab.content.securityInfo.secure,
|
||||
tab.content.securityInfo.host,
|
||||
tab.content.securityInfo.issuer,
|
||||
)
|
||||
) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
this.flowScoped { flow ->
|
||||
flow.mapNotNull { state -> state.tabs }
|
||||
.filterChanged {
|
||||
it.readerState
|
||||
}
|
||||
.ifAnyChanged { arrayOf(
|
||||
it.readerState.readerable,
|
||||
it.readerState.active,
|
||||
)
|
||||
}
|
||||
.collect { tab ->
|
||||
flutterEvents.onReaderableStateChange(
|
||||
tab.id,
|
||||
ReaderableState(
|
||||
tab.readerState.readerable,
|
||||
tab.readerState.active,
|
||||
)
|
||||
) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,9 +219,61 @@ open class DefaultComponents(private val applicationContext: Context) {
|
||||
.filterChanged {
|
||||
it.content
|
||||
}
|
||||
.ifAnyChanged { arrayOf(it.content.url) }
|
||||
.ifAnyChanged { arrayOf(
|
||||
it.content.history,
|
||||
it.content.canGoBack,
|
||||
it.content.canGoForward,
|
||||
)
|
||||
}
|
||||
.collect { tab ->
|
||||
Log.d("URL_CHANGE2", tab.content.url)
|
||||
flutterEvents.onHistoryStateChange(
|
||||
tab.id,
|
||||
HistoryState(
|
||||
items = tab.content.history.items.map { item -> HistoryItem(
|
||||
url = item.uri,
|
||||
title = item.title
|
||||
) },
|
||||
currentIndex = tab.content.history.currentIndex.toLong(),
|
||||
canGoBack = tab.content.canGoBack,
|
||||
canGoForward = tab.content.canGoForward,
|
||||
)
|
||||
) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
this.flowScoped { flow ->
|
||||
flow.mapNotNull { state -> state.tabs.map {tab -> tab.id} }
|
||||
.distinctUntilChanged()
|
||||
.collect { tabs ->
|
||||
flutterEvents.onTabListChange(tabs) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
this.flowScoped { flow ->
|
||||
flow.mapNotNull { state -> state.tabs }
|
||||
.filterChanged {
|
||||
it.content
|
||||
}
|
||||
.ifAnyChanged { arrayOf(
|
||||
it.content.url,
|
||||
it.content.title,
|
||||
it.content.private,
|
||||
it.content.fullScreen,
|
||||
it.content.progress,
|
||||
it.content.loading) }
|
||||
.collect { tab ->
|
||||
flutterEvents.onTabContentStateChange(
|
||||
TabContentState(
|
||||
id = tab.id,
|
||||
contextId = tab.contextId,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
progress = tab.content.progress.toLong(),
|
||||
isPrivate = tab.content.private,
|
||||
isFullScreen = tab.content.fullScreen,
|
||||
isLoading = tab.content.loading
|
||||
)
|
||||
) { _ -> }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+16
-5
@@ -1,8 +1,5 @@
|
||||
package eu.lensai.flutter_mozilla_components
|
||||
|
||||
import GeckoBrowserApi
|
||||
import GeckoSessionApi
|
||||
import GeckoTabsApi
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
@@ -10,8 +7,17 @@ import androidx.annotation.NonNull
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoBrowserApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoCookieApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoIconsApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoSessionApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.api.GeckoTabsApiImpl
|
||||
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoIconsApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSessionApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTabsApi
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
@@ -34,7 +40,7 @@ import org.mozilla.geckoview.GeckoRuntimeSettings
|
||||
/**
|
||||
* Helper class for lazily instantiating components needed by the application.
|
||||
*/
|
||||
class Components(private val applicationContext: Context) : DefaultComponents(applicationContext) {
|
||||
class Components(private val applicationContext: Context, flutterEvents: GeckoStateEvents) : DefaultComponents(applicationContext, flutterEvents) {
|
||||
private val runtime by lazy {
|
||||
// Allow for exfiltrating Gecko metrics through the Glean SDK.
|
||||
val builder = GeckoRuntimeSettings.Builder().aboutConfigEnabled(true)
|
||||
@@ -56,6 +62,7 @@ class Components(private val applicationContext: Context) : DefaultComponents(ap
|
||||
// }
|
||||
|
||||
WebCompatFeature.install(it)
|
||||
CookieManagerFeature.install(it)
|
||||
//WebCompatReporterFeature.install(it)
|
||||
}
|
||||
}
|
||||
@@ -77,7 +84,9 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||
_flutterPluginBinding = flutterPluginBinding
|
||||
|
||||
GlobalComponents.setUp(flutterPluginBinding.applicationContext)
|
||||
val flutterEvents = GeckoStateEvents(_flutterPluginBinding.binaryMessenger)
|
||||
|
||||
GlobalComponents.setUp(flutterPluginBinding.applicationContext, flutterEvents)
|
||||
|
||||
val intent = Intent(flutterPluginBinding.applicationContext, NotificationActivity::class.java)
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
@@ -89,6 +98,8 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
|
||||
|
||||
GeckoSessionApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoSessionApiImpl())
|
||||
GeckoTabsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoTabsApiImpl())
|
||||
GeckoIconsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoIconsApiImpl())
|
||||
GeckoCookieApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoCookieApiImpl())
|
||||
}
|
||||
|
||||
private fun showNativeFragment() {
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
package eu.lensai.flutter_mozilla_components
|
||||
|
||||
import android.content.Context
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoStateEvents
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@@ -28,8 +29,8 @@ object GlobalComponents {
|
||||
.whenSessionsChange()
|
||||
}
|
||||
|
||||
fun setUp(applicationContext: Context) {
|
||||
val newComponents = Components(applicationContext)
|
||||
fun setUp(applicationContext: Context, flutterEvents: GeckoStateEvents) {
|
||||
val newComponents = Components(applicationContext, flutterEvents)
|
||||
|
||||
newComponents.crashReporter.install(applicationContext)
|
||||
Facts.registerProcessor(LogFactProcessor())
|
||||
|
||||
+2
-4
@@ -1,10 +1,8 @@
|
||||
package eu.lensai.flutter_mozilla_components.api
|
||||
|
||||
import GeckoBrowserApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoBrowserApi
|
||||
|
||||
class GeckoBrowserApiImpl(
|
||||
private val showFragmentCallback: () -> Unit
|
||||
) : GeckoBrowserApi {
|
||||
class GeckoBrowserApiImpl(private val showFragmentCallback: () -> Unit) : GeckoBrowserApi {
|
||||
override fun showNativeFragment() {
|
||||
showFragmentCallback.invoke();
|
||||
}
|
||||
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
package eu.lensai.flutter_mozilla_components.api
|
||||
|
||||
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
|
||||
import eu.lensai.flutter_mozilla_components.feature.ResultConsumer
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.Cookie
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.CookiePartitionKey
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.CookieSameSiteStatus
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
|
||||
import org.json.JSONObject
|
||||
|
||||
class GeckoCookieApiImpl : GeckoCookieApi {
|
||||
private fun toValueOrNull(json: JSONObject, key: String): Any? {
|
||||
if (!json.has(key)) {
|
||||
throw RuntimeException("Invalid map key")
|
||||
}
|
||||
|
||||
if (!json.isNull(key)) {
|
||||
return json.get(key)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun cookiePartitionKeyFromJSON(inputJSON: JSONObject): CookiePartitionKey {
|
||||
return CookiePartitionKey(
|
||||
topLevelSite = inputJSON.getString("topLevelSite")
|
||||
)
|
||||
}
|
||||
|
||||
private fun CookiePartitionKey.toJSON(): JSONObject {
|
||||
val json = JSONObject()
|
||||
json.put("topLevelSite", topLevelSite)
|
||||
return json
|
||||
}
|
||||
|
||||
private fun cookieFromJSON(inputJSON: JSONObject): Cookie {
|
||||
val partitionKeyRaw = toValueOrNull(inputJSON, "partitionKey") as JSONObject?
|
||||
val partitionKey = if (partitionKeyRaw == null || partitionKeyRaw.length() == 0) null
|
||||
else cookiePartitionKeyFromJSON(partitionKeyRaw)
|
||||
|
||||
return Cookie (
|
||||
domain = inputJSON.getString("domain"),
|
||||
expirationDate = (toValueOrNull(inputJSON, "expirationDate") as Int?)?.toLong(),
|
||||
firstPartyDomain = inputJSON.getString("firstPartyDomain"),
|
||||
hostOnly = inputJSON.getBoolean("hostOnly"),
|
||||
httpOnly = inputJSON.getBoolean("httpOnly"),
|
||||
name = inputJSON.getString("name"),
|
||||
partitionKey = partitionKey,
|
||||
path = inputJSON.getString("path"),
|
||||
secure = inputJSON.getBoolean("secure"),
|
||||
session = inputJSON.getBoolean("session"),
|
||||
sameSite = when(inputJSON.getString("sameSite")) {
|
||||
"no_restriction" -> CookieSameSiteStatus.NO_RESTRICTION
|
||||
"lax" -> CookieSameSiteStatus.LAX
|
||||
"strict" -> CookieSameSiteStatus.STRICT
|
||||
else -> CookieSameSiteStatus.UNSPECIFIED
|
||||
},
|
||||
storeId = inputJSON.getString("storeId"),
|
||||
value = inputJSON.getString("value")
|
||||
)
|
||||
}
|
||||
|
||||
override fun getCookie(
|
||||
firstPartyDomain: String?,
|
||||
name: String,
|
||||
partitionKey: CookiePartitionKey?,
|
||||
storeId: String?,
|
||||
url: String,
|
||||
callback: (Result<Cookie>) -> Unit
|
||||
) {
|
||||
val args = JSONObject()
|
||||
|
||||
if (firstPartyDomain == null) {
|
||||
args.put("firstPartyDomain", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("firstPartyDomain", firstPartyDomain)
|
||||
}
|
||||
args.put("name", name)
|
||||
|
||||
if (partitionKey == null) {
|
||||
args.put("partitionKey", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("partitionKey", partitionKey.toJSON())
|
||||
}
|
||||
|
||||
if (storeId == null) {
|
||||
args.put("storeId", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("storeId", storeId)
|
||||
}
|
||||
|
||||
args.put("url", url)
|
||||
|
||||
CookieManagerFeature.scheduleRequest("get", args, object: ResultConsumer<JSONObject> {
|
||||
override fun success(result: JSONObject) {
|
||||
callback(Result.success(cookieFromJSON(result.getJSONObject("result"))))
|
||||
}
|
||||
|
||||
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
|
||||
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun getAllCookies(
|
||||
domain: String?,
|
||||
firstPartyDomain: String?,
|
||||
name: String?,
|
||||
partitionKey: CookiePartitionKey?,
|
||||
storeId: String?,
|
||||
url: String,
|
||||
callback: (Result<List<Cookie>>) -> Unit
|
||||
) {
|
||||
val args = JSONObject()
|
||||
|
||||
if (domain == null) {
|
||||
args.put("domain", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("domain", domain)
|
||||
}
|
||||
|
||||
if (firstPartyDomain == null) {
|
||||
args.put("firstPartyDomain", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("firstPartyDomain", firstPartyDomain)
|
||||
}
|
||||
|
||||
args.put("name", name)
|
||||
|
||||
if (partitionKey == null) {
|
||||
args.put("partitionKey", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("partitionKey", partitionKey.toJSON())
|
||||
}
|
||||
|
||||
if (storeId == null) {
|
||||
args.put("storeId", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("storeId", storeId)
|
||||
}
|
||||
|
||||
args.put("url", url)
|
||||
|
||||
CookieManagerFeature.scheduleRequest("getAll", args, object: ResultConsumer<JSONObject> {
|
||||
override fun success(result: JSONObject) {
|
||||
val jsonArray = result.getJSONArray("result")
|
||||
val cookies: MutableList<Cookie> = mutableListOf()
|
||||
|
||||
repeat(jsonArray.length()) {
|
||||
index ->
|
||||
cookies.add(cookieFromJSON(jsonArray.getJSONObject(index)))
|
||||
}
|
||||
|
||||
callback(Result.success(cookies))
|
||||
}
|
||||
|
||||
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
|
||||
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun setCookie(
|
||||
domain: String?,
|
||||
expirationDate: Long?,
|
||||
firstPartyDomain: String?,
|
||||
httpOnly: Boolean?,
|
||||
name: String?,
|
||||
partitionKey: CookiePartitionKey?,
|
||||
path: String?,
|
||||
sameSite: CookieSameSiteStatus?,
|
||||
secure: Boolean?,
|
||||
storeId: String?,
|
||||
url: String,
|
||||
value: String?,
|
||||
callback: (Result<Unit>) -> Unit
|
||||
) {
|
||||
val args = JSONObject()
|
||||
|
||||
if (domain == null) {
|
||||
args.put("domain", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("domain", domain)
|
||||
}
|
||||
|
||||
if (expirationDate == null) {
|
||||
args.put("expirationDate", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("expirationDate", domain)
|
||||
}
|
||||
|
||||
if (firstPartyDomain == null) {
|
||||
args.put("firstPartyDomain", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("firstPartyDomain", firstPartyDomain)
|
||||
}
|
||||
|
||||
if (httpOnly == null) {
|
||||
args.put("httpOnly", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("httpOnly", httpOnly)
|
||||
}
|
||||
|
||||
if (name == null) {
|
||||
args.put("name", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("name", name)
|
||||
}
|
||||
|
||||
if (partitionKey == null) {
|
||||
args.put("partitionKey", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("partitionKey", partitionKey.toJSON())
|
||||
}
|
||||
|
||||
if (path == null) {
|
||||
args.put("path", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("path", path)
|
||||
}
|
||||
|
||||
if (sameSite == null) {
|
||||
args.put("sameSite", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("sameSite", when(sameSite) {
|
||||
CookieSameSiteStatus.NO_RESTRICTION -> "no_restriction"
|
||||
CookieSameSiteStatus.LAX -> "lax"
|
||||
CookieSameSiteStatus.STRICT -> "strict"
|
||||
CookieSameSiteStatus.UNSPECIFIED -> ""
|
||||
})
|
||||
}
|
||||
|
||||
if (secure == null) {
|
||||
args.put("secure", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("secure", secure)
|
||||
}
|
||||
|
||||
if (storeId == null) {
|
||||
args.put("storeId", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("storeId", storeId)
|
||||
}
|
||||
|
||||
args.put("url", url)
|
||||
|
||||
if (value == null) {
|
||||
args.put("value", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("value", value)
|
||||
}
|
||||
|
||||
CookieManagerFeature.scheduleRequest("set", args, object: ResultConsumer<JSONObject> {
|
||||
override fun success(result: JSONObject) {
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
|
||||
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
|
||||
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun removeCookie(
|
||||
firstPartyDomain: String?,
|
||||
name: String,
|
||||
partitionKey: CookiePartitionKey?,
|
||||
storeId: String?,
|
||||
url: String,
|
||||
callback: (Result<Unit>) -> Unit
|
||||
) {
|
||||
val args = JSONObject()
|
||||
|
||||
if (firstPartyDomain == null) {
|
||||
args.put("firstPartyDomain", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("firstPartyDomain", firstPartyDomain)
|
||||
}
|
||||
|
||||
args.put("name", name)
|
||||
|
||||
if (partitionKey == null) {
|
||||
args.put("partitionKey", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("partitionKey", partitionKey.toJSON())
|
||||
}
|
||||
|
||||
if (storeId == null) {
|
||||
args.put("storeId", JSONObject.NULL)
|
||||
} else {
|
||||
args.put("storeId", storeId)
|
||||
}
|
||||
|
||||
args.put("url", url)
|
||||
|
||||
CookieManagerFeature.scheduleRequest("remove", args, object: ResultConsumer<JSONObject> {
|
||||
override fun success(result: JSONObject) {
|
||||
callback(Result.success(Unit))
|
||||
}
|
||||
|
||||
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
|
||||
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package eu.lensai.flutter_mozilla_components.api
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import eu.lensai.flutter_mozilla_components.GlobalComponents
|
||||
import eu.lensai.flutter_mozilla_components.ext.toWebPBytes
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.*
|
||||
import kotlinx.coroutines.*
|
||||
import mozilla.components.browser.icons.BrowserIcons
|
||||
import mozilla.components.browser.icons.Icon
|
||||
import mozilla.components.concept.engine.manifest.Size as HtmlSize
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
typealias MozillaIconRequest = mozilla.components.browser.icons.IconRequest
|
||||
typealias MozillaIconSize = mozilla.components.browser.icons.IconRequest.Size
|
||||
typealias MozillaIconResource = mozilla.components.browser.icons.IconRequest.Resource
|
||||
typealias MozillaIconResourceType = mozilla.components.browser.icons.IconRequest.Resource.Type
|
||||
|
||||
class GeckoIconsApiImpl() : GeckoIconsApi {
|
||||
private val icons: BrowserIcons by lazy { GlobalComponents.components!!.icons }
|
||||
|
||||
override fun loadIcon(request: IconRequest, callback: (Result<IconResult>) -> Unit) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
runCatching {
|
||||
loadIconAsync(request.toMozillaIconRequest())
|
||||
}.fold(
|
||||
onSuccess = { result ->
|
||||
withContext(Dispatchers.Main) {
|
||||
callback(Result.success(result))
|
||||
}
|
||||
},
|
||||
onFailure = { error ->
|
||||
withContext(Dispatchers.Main) {
|
||||
callback(Result.failure(error))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadIconAsync(request: MozillaIconRequest): IconResult {
|
||||
val result = icons.loadIcon(request).await()
|
||||
val imageBytes = result.bitmap.toWebPBytes()
|
||||
return IconResult(
|
||||
image = imageBytes,
|
||||
maskable = result.maskable,
|
||||
color = result.color?.toLong(),
|
||||
source = result.source.toApiSource()
|
||||
)
|
||||
}
|
||||
|
||||
private fun IconRequest.toMozillaIconRequest(): MozillaIconRequest =
|
||||
MozillaIconRequest(
|
||||
url = url,
|
||||
size = size.toMozillaSize(),
|
||||
color = color?.toInt(),
|
||||
waitOnNetworkLoad = waitOnNetworkLoad,
|
||||
isPrivate = isPrivate,
|
||||
resources = resources.filterNotNull().map { it.toMozillaResource() }
|
||||
)
|
||||
|
||||
private fun IconSize.toMozillaSize(): MozillaIconSize = when (this) {
|
||||
IconSize.DEFAULT_SIZE -> MozillaIconSize.DEFAULT
|
||||
IconSize.LAUNCHER -> MozillaIconSize.LAUNCHER
|
||||
IconSize.LAUNCHER_ADAPTIVE -> MozillaIconSize.LAUNCHER_ADAPTIVE
|
||||
}
|
||||
|
||||
private fun Resource.toMozillaResource(): MozillaIconResource =
|
||||
MozillaIconResource(
|
||||
url = url,
|
||||
mimeType = mimeType,
|
||||
maskable = maskable,
|
||||
type = type.toMozillaType(),
|
||||
sizes = sizes.filterNotNull().map { HtmlSize(it.height.toInt(), it.width.toInt()) }
|
||||
)
|
||||
|
||||
private fun IconType.toMozillaType(): MozillaIconResourceType = when (this) {
|
||||
IconType.FAVICON -> MozillaIconResourceType.FAVICON
|
||||
IconType.APPLE_TOUCH_ICON -> MozillaIconResourceType.APPLE_TOUCH_ICON
|
||||
IconType.FLUID_ICON -> MozillaIconResourceType.FLUID_ICON
|
||||
IconType.IMAGE_SRC -> MozillaIconResourceType.IMAGE_SRC
|
||||
IconType.OPEN_GRAPH -> MozillaIconResourceType.OPENGRAPH
|
||||
IconType.TWITTER -> MozillaIconResourceType.TWITTER
|
||||
IconType.MICROSOFT_TILE -> MozillaIconResourceType.MICROSOFT_TILE
|
||||
IconType.TIPPY_TOP -> MozillaIconResourceType.TIPPY_TOP
|
||||
IconType.MANIFEST_ICON -> MozillaIconResourceType.MANIFEST_ICON
|
||||
}
|
||||
|
||||
private fun Icon.Source.toApiSource(): Source = when (this) {
|
||||
Icon.Source.GENERATOR -> Source.GENERATOR
|
||||
Icon.Source.DOWNLOAD -> Source.DOWNLOAD
|
||||
Icon.Source.INLINE -> Source.INLINE
|
||||
Icon.Source.MEMORY -> Source.MEMORY
|
||||
Icon.Source.DISK -> Source.DISK
|
||||
}
|
||||
}
|
||||
+15
-25
@@ -1,16 +1,16 @@
|
||||
package eu.lensai.flutter_mozilla_components.api
|
||||
|
||||
import GeckoSessionApi
|
||||
import LoadUrlFlagsValue
|
||||
import android.util.Log
|
||||
import eu.lensai.flutter_mozilla_components.GlobalComponents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSessionApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.LoadUrlFlagsValue
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.TranslationOptions as PigeonTranslationOptions
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.concept.engine.EngineSession
|
||||
import mozilla.components.concept.engine.translate.TranslationOptions
|
||||
import TranslationOptions as PigeonTranslationOptions
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
|
||||
class GeckoSessionApiImpl() : GeckoSessionApi {
|
||||
class GeckoSessionApiImpl : GeckoSessionApi {
|
||||
private val sessionUseCases: SessionUseCases by lazy { GlobalComponents.components!!.sessionUseCases }
|
||||
private val state: BrowserState by lazy { GlobalComponents.components!!.store.state }
|
||||
|
||||
@@ -33,21 +33,19 @@ class GeckoSessionApiImpl() : GeckoSessionApi {
|
||||
data = data,
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
mimeType = mimeType,
|
||||
encoding = encoding,
|
||||
encoding = encoding
|
||||
)
|
||||
}
|
||||
|
||||
override fun reload(tabId: String?, flags: LoadUrlFlagsValue) {
|
||||
sessionUseCases.reload(
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
flags = EngineSession.LoadUrlFlags.select(flags.value.toInt()),
|
||||
flags = EngineSession.LoadUrlFlags.select(flags.value.toInt())
|
||||
)
|
||||
}
|
||||
|
||||
override fun stopLoading(tabId: String?) {
|
||||
sessionUseCases.stopLoading(
|
||||
tabId = tabId
|
||||
)
|
||||
sessionUseCases.stopLoading(tabId = tabId ?: state.selectedTabId)
|
||||
}
|
||||
|
||||
override fun goBack(tabId: String?, userInteraction: Boolean) {
|
||||
@@ -74,26 +72,20 @@ class GeckoSessionApiImpl() : GeckoSessionApi {
|
||||
override fun requestDesktopSite(tabId: String?, enable: Boolean) {
|
||||
sessionUseCases.requestDesktopSite(
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
enable = enable,
|
||||
enable = enable
|
||||
)
|
||||
}
|
||||
|
||||
override fun exitFullscreen(tabId: String?) {
|
||||
sessionUseCases.exitFullscreen(
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
)
|
||||
sessionUseCases.exitFullscreen(tabId = tabId ?: state.selectedTabId)
|
||||
}
|
||||
|
||||
override fun saveToPdf(tabId: String?) {
|
||||
sessionUseCases.saveToPdf(
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
)
|
||||
sessionUseCases.saveToPdf(tabId = tabId ?: state.selectedTabId)
|
||||
}
|
||||
|
||||
override fun printContent(tabId: String?) {
|
||||
sessionUseCases.printContent(
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
)
|
||||
sessionUseCases.printContent(tabId = tabId ?: state.selectedTabId)
|
||||
}
|
||||
|
||||
override fun translate(
|
||||
@@ -106,18 +98,16 @@ class GeckoSessionApiImpl() : GeckoSessionApi {
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
fromLanguage = fromLanguage,
|
||||
toLanguage = toLanguage,
|
||||
options = if (options != null) TranslationOptions(downloadModel = options.downloadModel) else null,
|
||||
options = options?.let { TranslationOptions(downloadModel = it.downloadModel) }
|
||||
)
|
||||
}
|
||||
|
||||
override fun translateRestore(tabId: String?) {
|
||||
sessionUseCases.translateRestore(
|
||||
tabId = tabId ?: state.selectedTabId,
|
||||
)
|
||||
sessionUseCases.translateRestore(tabId = tabId ?: state.selectedTabId)
|
||||
}
|
||||
|
||||
override fun crashRecovery(tabIds: List<String>?) {
|
||||
if(tabIds != null) {
|
||||
if (tabIds != null) {
|
||||
sessionUseCases.crashRecovery.invoke(tabIds = tabIds)
|
||||
} else {
|
||||
sessionUseCases.crashRecovery.invoke()
|
||||
@@ -134,4 +124,4 @@ class GeckoSessionApiImpl() : GeckoSessionApi {
|
||||
lastAccess = lastAccess ?: System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-8
@@ -1,13 +1,14 @@
|
||||
package eu.lensai.flutter_mozilla_components.api
|
||||
|
||||
import GeckoTabsApi
|
||||
import HistoryMetadataKey as PigeonHistoryMetadataKey
|
||||
import LoadUrlFlagsValue
|
||||
import RecoverableBrowserState as PigeonRecoverableBrowserState
|
||||
import RestoreLocation as PigeonRestoreLocation
|
||||
import RecoverableTab as PigeonRecoverableTab
|
||||
import SourceValue
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.GeckoTabsApi
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.HistoryMetadataKey as PigeonHistoryMetadataKey
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.LoadUrlFlagsValue
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.RecoverableBrowserState as PigeonRecoverableBrowserState
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.RestoreLocation as PigeonRestoreLocation
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.RecoverableTab as PigeonRecoverableTab
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.SourceValue
|
||||
import eu.lensai.flutter_mozilla_components.GlobalComponents
|
||||
import eu.lensai.flutter_mozilla_components.pigeons.RestoreLocation
|
||||
import mozilla.components.browser.session.storage.RecoverableBrowserState
|
||||
import mozilla.components.browser.state.action.TabListAction
|
||||
import mozilla.components.browser.state.selector.findTab
|
||||
@@ -80,7 +81,11 @@ class GeckoTabsApiImpl() : GeckoTabsApi {
|
||||
}
|
||||
|
||||
private fun mapRestoreLocation(t: PigeonRestoreLocation) : TabListAction.RestoreAction.RestoreLocation {
|
||||
return TabListAction.RestoreAction.RestoreLocation.entries.first { it.ordinal == t.ordinal }
|
||||
return when(t) {
|
||||
RestoreLocation.BEGINNING -> TabListAction.RestoreAction.RestoreLocation.BEGINNING
|
||||
RestoreLocation.END -> TabListAction.RestoreAction.RestoreLocation.END
|
||||
RestoreLocation.AT_INDEX -> TabListAction.RestoreAction.RestoreLocation.AT_INDEX
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectTab(tabId: String) {
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package eu.lensai.flutter_mozilla_components.ext
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
fun Bitmap.toWebPBytes(): ByteArray {
|
||||
val stream = ByteArrayOutputStream()
|
||||
val compressFormat = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
Bitmap.CompressFormat.WEBP_LOSSLESS
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
Bitmap.CompressFormat.WEBP
|
||||
}
|
||||
compress(compressFormat, 100, stream)
|
||||
return stream.toByteArray()
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
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
|
||||
|
||||
interface ResultConsumer<T> {
|
||||
fun success(result: T)
|
||||
fun error(errorCode: String, errorMessage: String?, errorDetails: Any?)
|
||||
}
|
||||
|
||||
/**
|
||||
* A feature that enables users to report site issues to Mozilla's Web Compatibility team for
|
||||
* further diagnosis.
|
||||
*/
|
||||
object CookieManagerFeature {
|
||||
private val logger = Logger("cookie-manager")
|
||||
|
||||
private const val COOKIE_MANAGER_REPORTER_EXTENSION_ID = "cookie-manager@lensai.eu"
|
||||
private const val COOKIE_MANAGER_REPORTER_EXTENSION_URL = "resource://android/assets/extensions/cookie-manager/"
|
||||
private const val COOKIE_MANAGER_REPORTER_MESSAGING_ID = "cookieManager"
|
||||
|
||||
private var nextRequestId: Int = 0
|
||||
private val requestHandlers = HashMap<Int, ResultConsumer<JSONObject>>()
|
||||
private val mutex = Mutex()
|
||||
|
||||
@VisibleForTesting
|
||||
// This is an internal var to make it mutable for unit testing purposes only
|
||||
internal var extensionController = WebExtensionController(
|
||||
COOKIE_MANAGER_REPORTER_EXTENSION_ID,
|
||||
COOKIE_MANAGER_REPORTER_EXTENSION_URL,
|
||||
COOKIE_MANAGER_REPORTER_MESSAGING_ID,
|
||||
)
|
||||
|
||||
fun scheduleRequest(command: String, args: JSONObject, callback: ResultConsumer<JSONObject>) {
|
||||
val message = JSONObject()
|
||||
message.put("action", command);
|
||||
message.put("args", args)
|
||||
|
||||
runBlocking {
|
||||
withContext(Dispatchers.Default) {
|
||||
mutex.withLock {
|
||||
message.put("id", nextRequestId)
|
||||
|
||||
requestHandlers[nextRequestId] = callback
|
||||
|
||||
nextRequestId += 1
|
||||
|
||||
extensionController.sendBackgroundMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CookieManagerReporterBackgroundMessageHandler() : MessageHandler {
|
||||
override fun onPortMessage(message: Any, port: Port) {
|
||||
runBlocking {
|
||||
withContext(Dispatchers.Default) {
|
||||
mutex.withLock {
|
||||
val messageJSON = message as JSONObject;
|
||||
|
||||
val requestId = messageJSON.getInt("id")
|
||||
val status = messageJSON.getString("status")
|
||||
if (status == "success") {
|
||||
requestHandlers[requestId]?.success(message)
|
||||
} else {
|
||||
requestHandlers[requestId]?.error(
|
||||
"Cookie Manager",
|
||||
"Failed to perform operation",
|
||||
message.getString("error")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
CookieManagerReporterBackgroundMessageHandler(),
|
||||
)
|
||||
extensionController.install(
|
||||
runtime,
|
||||
onSuccess = {
|
||||
logger.debug("Installed CookieManager webextension: ${it.id}")
|
||||
},
|
||||
onError = { throwable ->
|
||||
logger.error("Failed to install CookieManager webextension: ", throwable)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
+9
-5
@@ -3,18 +3,24 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
package eu.lensai.flutter_mozilla_components.middleware
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.util.Log
|
||||
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.state.BrowserState
|
||||
import mozilla.components.lib.state.Middleware
|
||||
import mozilla.components.lib.state.MiddlewareContext
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* [Middleware] implementation for handling [ContentAction.UpdateThumbnailAction] and storing
|
||||
* the thumbnail to the disk cache.
|
||||
*/
|
||||
class FlutterEventMiddleware() : Middleware<BrowserState, BrowserAction> {
|
||||
class FlutterEventMiddleware(private val flutterEvents: GeckoStateEvents) : Middleware<BrowserState, BrowserAction> {
|
||||
@Suppress("ComplexMethod")
|
||||
override fun invoke(
|
||||
context: MiddlewareContext<BrowserState, BrowserAction>,
|
||||
@@ -23,10 +29,8 @@ class FlutterEventMiddleware() : Middleware<BrowserState, BrowserAction> {
|
||||
) {
|
||||
when (action) {
|
||||
is ContentAction.UpdateThumbnailAction -> {
|
||||
Log.d("UpdateThumbnailAction", action.sessionId)
|
||||
}
|
||||
is ContentAction.UpdateTitleAction -> {
|
||||
Log.d("UpdateTitleAction", action.title)
|
||||
val bytes = action.thumbnail.toWebPBytes()
|
||||
flutterEvents.onThumbnailChange(action.sessionId, bytes) { _ -> }
|
||||
}
|
||||
else -> {
|
||||
// no-op
|
||||
|
||||
+878
-31
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user