intermediate prefs

This commit is contained in:
Fabian Freund
2025-01-30 10:46:38 +01:00
parent 78c17a70c5
commit 36acd46318
51 changed files with 3744 additions and 1719 deletions
@@ -3,7 +3,7 @@ version = "1.0-SNAPSHOT"
buildscript {
ext.kotlin_version = "1.9.22"
ext.mozillaComponentsVersion = '134.0.20241030214633'
ext.mozillaComponentsVersion = '136.0.20250127162202'
repositories {
google()
@@ -0,0 +1,119 @@
"use strict";
const { BackgroundTasksUtils } = ChromeUtils.importESModule(
"resource://gre/modules/BackgroundTasksUtils.sys.mjs"
);
const lazy = {};
ChromeUtils.defineLazyGetter(lazy, "log", () => {
let { ConsoleAPI } = ChromeUtils.importESModule(
"resource://gre/modules/Console.sys.mjs"
);
let consoleOptions = {
// tip: set maxLogLevel to "debug" and use log.debug() to create detailed
// messages during development. See LOG_LEVELS in Console.sys.mjs for details.
maxLogLevel: "error",
maxLogLevelPref: "app.update.background.loglevel",
prefix: "PrefsManager",
};
return new ConsoleAPI(consoleOptions);
});
var prefmanager = class extends ExtensionAPI {
getAPI(context) {
return {
experiments: {
prefmanager: {
async resetPrefs(prefNames) {
if (prefNames && prefNames.length > 0) {
for (const prefName of prefNames) {
Services.prefs.clearUserPref(prefName);
}
} else {
Services.prefs.resetPrefs();
}
},
async getPrefs(prefNames) {
const prefs = (prefNames && prefNames.length > 0)
? prefNames
: Services.prefs.getChildList("");
const result = {};
for (const prefName of prefs) {
try {
switch (Services.prefs.getPrefType(prefName)) {
case Services.prefs.PREF_BOOL:
result[prefName] = Services.prefs.getBoolPref(prefName);
break;
case Services.prefs.PREF_INT:
result[prefName] = Services.prefs.getIntPref(prefName);
break;
case Services.prefs.PREF_STRING:
result[prefName] = Services.prefs.getCharPref(prefName);
break;
default:
// Skip complex values or invalid preferences
continue;
}
} catch (e) {
lazy.log.error(`Error reading preference ${prefName}: ${e}`);
continue;
}
}
lazy.log.debug(`getAll: retrieved ${Object.keys(result).length} preferences`);
return result;
},
async parsePrefsAndApply(prefsFileContent, predicate = null) {
let prefs = {};
let addPref = (kind, name, value) => {
if (predicate && !predicate(name)) {
return;
}
prefs[name] = value;
};
Services.prefs.parsePrefsFromBuffer(
prefsFileContent,
{
onStringPref: addPref,
onIntPref: addPref,
onBoolPref: addPref,
onError(message) {
throw new Error(
`Error parsing preferences "${message}"`
);
},
},
);
await BackgroundTasksUtils.withProfileLock(profileLock => {
for (let [name, value] of Object.entries(prefs)) {
switch (typeof value) {
case "boolean":
Services.prefs.setBoolPref(name, value);
break;
case "number":
Services.prefs.setIntPref(name, value);
break;
case "string":
Services.prefs.setCharPref(name, value);
break;
default:
throw new Error(
`Pref from default profile with name "${name}" has unrecognized type`
);
}
}
});
lazy.log.debug(`applyPreferences: parsed prefs from buffer`, prefs);
return prefs;
}
}
}
};
}
};
@@ -0,0 +1,47 @@
'use strict';
const encoder = new TextEncoder();
const port = browser.runtime.connectNative("prefManager");
function sendJsonResultForRequest(id) {
return function (result) {
port.postMessage({
"id": id,
"status": "success",
"result": result
})
}
}
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 "parsePrefsAndApply":
browser.experiments.prefmanager.parsePrefsAndApply(encoder.encode(message["args"]), null)
.then(sendJsonResultForRequest(requestId))
.catch(sendErrorForRequest(requestId))
break
case "getPrefs":
browser.experiments.prefmanager.getPrefs(message["args"])
.then(sendJsonResultForRequest(requestId))
.catch(sendErrorForRequest(requestId))
break
case "resetPrefs":
browser.experiments.prefmanager.resetPrefs(message["args"])
.then(sendJsonResultForRequest(requestId))
.catch(sendErrorForRequest(requestId))
break
}
});
@@ -0,0 +1,39 @@
{
"manifest_version": 2,
"name": "Pref Manager",
"version": "1.0",
"description": "Manipulate GeckoView preferences",
"browser_specific_settings": {
"gecko": {
"id": "pref-manager@movenext.me"
}
},
"background": {
"scripts": [
"background.js"
]
},
"experiment_apis": {
"prefmanager": {
"schema": "schema.json",
"parent": {
"scopes": [
"addon_parent"
],
"paths": [
[
"experiments",
"prefmanager"
]
],
"script": "api.js"
}
}
},
"permissions": [
"nativeMessaging",
"nativeMessagingFromContent",
"geckoViewAddons",
"<all_urls>"
]
}
@@ -0,0 +1,78 @@
[
{
"namespace": "experiments.prefmanager",
"description": "Experimental API for updating Geckoview preferences",
"functions": [
{
"name": "getPrefs",
"type": "function",
"description": "Retrieves preferences and their values",
"async": true,
"parameters": [
{
"name": "prefNames",
"type": "array",
"optional": true,
"description": "Array of preference names to retrieve. If empty or null, retrieves all preferences",
"items": {
"type": "string"
}
}
],
"returns": {
"type": "object",
"description": "Object containing the preferences as key-value pairs",
"additionalProperties": {
"type": "any",
"description": "Preference values can be boolean, number, or string"
}
}
},
{
"name": "parsePrefsAndApply",
"type": "function",
"description": "Reads and sets preferences from an encoded preferences file content",
"async": true,
"parameters": [
{
"name": "prefsFileContent",
"type": "object",
"isInstanceOf": "Uint8Array",
"description": "Encoded content of the preferences file to parse (Uint8Array)"
},
{
"name": "predicate",
"type": "function",
"optional": true,
"description": "Optional filter function to determine which preferences to include"
}
],
"returns": {
"type": "object",
"description": "Object containing the parsed preferences as key-value pairs",
"additionalProperties": {
"type": "any",
"description": "Preference values can be boolean, number, or string"
}
}
},
{
"name": "resetPrefs",
"type": "function",
"description": "Resets multiple or all preferences to their default values",
"async": true,
"parameters": [
{
"name": "prefNames",
"type": "array",
"optional": true,
"description": "Array of preference names to reset. If empty or null, resets all preferences",
"items": {
"type": "string"
}
}
]
}
]
}
]
@@ -6,6 +6,7 @@ package eu.lensai.flutter_mozilla_components
import android.content.Context
import eu.lensai.flutter_mozilla_components.feature.CookieManagerFeature
import eu.lensai.flutter_mozilla_components.feature.PrefManagerFeature
import mozilla.components.browser.engine.gecko.GeckoEngine
import mozilla.components.browser.engine.gecko.fetch.GeckoViewFetchClient
import mozilla.components.concept.engine.DefaultSettings
@@ -31,6 +32,7 @@ object EngineProvider {
// About config it's no longer enabled by default
builder.aboutConfigEnabled(true)
builder.extensionsProcessEnabled(true)
builder.extensionsWebAPIEnabled(true)
builder.consoleOutput(true)
@@ -47,6 +49,7 @@ object EngineProvider {
return GeckoEngine(context, defaultSettings, runtime).also {
WebCompatFeature.install(it)
CookieManagerFeature.install(it)
PrefManagerFeature.install(it)
}
}
@@ -10,6 +10,7 @@ import eu.lensai.flutter_mozilla_components.api.GeckoCookieApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoEngineSettingsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoFindApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoIconsApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoPrefApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSelectionActionControllerImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSessionApiImpl
import eu.lensai.flutter_mozilla_components.api.GeckoSuggestionApiImpl
@@ -22,6 +23,7 @@ import eu.lensai.flutter_mozilla_components.pigeons.GeckoCookieApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoFindApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoIconsApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoPrefApi
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionController
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSelectionActionEvents
import eu.lensai.flutter_mozilla_components.pigeons.GeckoSessionApi
@@ -91,6 +93,7 @@ class FlutterMozillaComponentsPlugin: FlutterPlugin, ActivityAware {
GeckoTabsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoTabsApiImpl())
GeckoIconsApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoIconsApiImpl())
GeckoCookieApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoCookieApiImpl())
GeckoPrefApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoPrefApiImpl())
GeckoFindApi.setUp(_flutterPluginBinding.binaryMessenger, GeckoFindApiImpl())
GeckoSelectionActionController.setUp(_flutterPluginBinding.binaryMessenger, GeckoSelectionActionControllerImpl(
selectionActionDelegate
@@ -1,9 +1,19 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.GlobalComponents
import eu.lensai.flutter_mozilla_components.pigeons.ColorScheme
import eu.lensai.flutter_mozilla_components.pigeons.CookieBannerHandlingMode
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettings
import eu.lensai.flutter_mozilla_components.pigeons.GeckoEngineSettingsApi
import eu.lensai.flutter_mozilla_components.pigeons.HttpsOnlyMode
import eu.lensai.flutter_mozilla_components.pigeons.WebContentIsolationStrategy
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.feature.addons.logger
import mozilla.components.feature.session.SettingsUseCases
import mozilla.components.feature.session.TrackingProtectionUseCases
/**
* Implementation of GeckoEngineSettingsApi that manages engine-specific settings
@@ -17,13 +27,99 @@ class GeckoEngineSettingsApiImpl : GeckoEngineSettingsApi {
requireNotNull(GlobalComponents.components) { "Components not initialized" }
}
override fun javaScriptEnabled(state: Boolean) {
try {
components.core.engine.settings.javascriptEnabled = state
logger.debug("$TAG: JavaScript enabled state changed to: $state")
} catch (e: Exception) {
logger.error("$TAG: Failed to set JavaScript enabled state", e)
throw IllegalStateException("Failed to set JavaScript enabled state", e)
override fun setDefaultSettings(settings: GeckoEngineSettings) {
if(settings.javascriptEnabled != null) {
components.core.engineSettings.javascriptEnabled = settings.javascriptEnabled;
}
if(settings.trackingProtectionPolicy != null) {
components.core.engineSettings.trackingProtectionPolicy = when(settings.trackingProtectionPolicy) {
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.NONE -> TrackingProtectionPolicy.none()
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.RECOMMENDED -> TrackingProtectionPolicy.recommended()
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.STRICT -> TrackingProtectionPolicy.strict()
eu.lensai.flutter_mozilla_components.pigeons.TrackingProtectionPolicy.CUSTOM -> TODO()
}
}
if(settings.httpsOnlyMode != null) {
components.core.engineSettings.httpsOnlyMode = when(settings.httpsOnlyMode) {
HttpsOnlyMode.DISABLED -> Engine.HttpsOnlyMode.DISABLED
HttpsOnlyMode.PRIVATE_ONLY -> Engine.HttpsOnlyMode.ENABLED_PRIVATE_ONLY
HttpsOnlyMode.ENABLED -> Engine.HttpsOnlyMode.ENABLED
}
}
if(settings.globalPrivacyControlEnabled != null) {
components.core.engineSettings.globalPrivacyControlEnabled = settings.globalPrivacyControlEnabled;
}
if(settings.preferredColorScheme != null) {
components.core.engineSettings.preferredColorScheme = when(settings.preferredColorScheme) {
ColorScheme.SYSTEM -> PreferredColorScheme.System
ColorScheme.LIGHT -> PreferredColorScheme.Light
ColorScheme.DARK -> PreferredColorScheme.Dark
}
}
if(settings.cookieBannerHandlingMode != null) {
components.core.engineSettings.cookieBannerHandlingMode = when(settings.cookieBannerHandlingMode) {
CookieBannerHandlingMode.DISABLED -> EngineSession.CookieBannerHandlingMode.DISABLED
CookieBannerHandlingMode.REJECT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_ALL
CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL
}
}
if(settings.cookieBannerHandlingModePrivateBrowsing != null) {
components.core.engineSettings.cookieBannerHandlingModePrivateBrowsing = when(settings.cookieBannerHandlingModePrivateBrowsing) {
CookieBannerHandlingMode.DISABLED -> EngineSession.CookieBannerHandlingMode.DISABLED
CookieBannerHandlingMode.REJECT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_ALL
CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL -> EngineSession.CookieBannerHandlingMode.REJECT_OR_ACCEPT_ALL
}
}
if(settings.cookieBannerHandlingGlobalRules != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRules = settings.cookieBannerHandlingGlobalRules;
}
if(settings.cookieBannerHandlingGlobalRulesSubFrames != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRulesSubFrames = settings.cookieBannerHandlingGlobalRulesSubFrames;
}
if(settings.webContentIsolationStrategy != null) {
components.core.engineSettings.webContentIsolationStrategy = when(settings.webContentIsolationStrategy) {
WebContentIsolationStrategy.ISOLATE_NOTHING -> mozilla.components.concept.engine.fission.WebContentIsolationStrategy.ISOLATE_NOTHING
WebContentIsolationStrategy.ISOLATE_EVERYTHING -> mozilla.components.concept.engine.fission.WebContentIsolationStrategy.ISOLATE_EVERYTHING
WebContentIsolationStrategy.ISOLATE_HIGH_VALUE -> mozilla.components.concept.engine.fission.WebContentIsolationStrategy.ISOLATE_HIGH_VALUE
}
}
}
override fun updateRuntimeSettings(settings: GeckoEngineSettings) {
//First parse and set default values
setDefaultSettings(settings);
//Then copy default settings into runtime
if(settings.javascriptEnabled != null) {
components.core.engine.settings.javascriptEnabled = components.core.engineSettings.javascriptEnabled
}
if(settings.trackingProtectionPolicy != null) {
components.useCases.settingsUseCases.updateTrackingProtection(components.core.engineSettings.trackingProtectionPolicy!!)
components.useCases.sessionUseCases.reload()
}
if(settings.httpsOnlyMode != null) {
components.core.engineSettings.httpsOnlyMode = components.core.engineSettings.httpsOnlyMode
}
if(settings.globalPrivacyControlEnabled != null) {
components.core.engineSettings.globalPrivacyControlEnabled = components.core.engineSettings.globalPrivacyControlEnabled
}
if(settings.preferredColorScheme != null) {
components.core.engineSettings.preferredColorScheme = components.core.engineSettings.preferredColorScheme
}
if(settings.cookieBannerHandlingMode != null) {
components.core.engineSettings.cookieBannerHandlingMode = components.core.engineSettings.cookieBannerHandlingMode
}
if(settings.cookieBannerHandlingModePrivateBrowsing != null) {
components.core.engineSettings.cookieBannerHandlingModePrivateBrowsing = components.core.engineSettings.cookieBannerHandlingModePrivateBrowsing
}
if(settings.cookieBannerHandlingGlobalRules != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRules = components.core.engineSettings.cookieBannerHandlingGlobalRules
}
if(settings.cookieBannerHandlingGlobalRulesSubFrames != null) {
components.core.engineSettings.cookieBannerHandlingGlobalRulesSubFrames = components.core.engineSettings.cookieBannerHandlingGlobalRulesSubFrames
}
if(settings.webContentIsolationStrategy != null) {
components.core.engineSettings.webContentIsolationStrategy = components.core.engineSettings.webContentIsolationStrategy
}
}
}
@@ -0,0 +1,73 @@
package eu.lensai.flutter_mozilla_components.api
import eu.lensai.flutter_mozilla_components.feature.PrefManagerFeature
import eu.lensai.flutter_mozilla_components.feature.ResultConsumer
import eu.lensai.flutter_mozilla_components.pigeons.GeckoPrefApi
import org.json.JSONObject
import org.json.JSONArray
class GeckoPrefApiImpl : GeckoPrefApi {
private fun List<String>?.toJson(): JSONArray {
return JSONArray().apply {
this@toJson?.forEach { put(it) }
}
}
fun JSONObject.toMap(): Map<String, Any> {
val map = mutableMapOf<String, Any>()
val keys = this.keys()
while (keys.hasNext()) {
val key = keys.next()
when (val value = this.get(key)) {
is JSONObject -> map[key] = value.toMap()
// is JSONArray -> map[key] = value.toList()
// JSONObject.NULL -> map[key] = null
else -> map[key] = value
}
}
return map
}
override fun getPrefs(
preferenceFilter: List<String>?,
callback: (Result<Map<String, Any>>) -> Unit
) {
PrefManagerFeature.scheduleRequest("getPrefs", preferenceFilter.toJson(), object : ResultConsumer<JSONObject> {
override fun success(result: JSONObject) {
callback(Result.success(result.getJSONObject("result").toMap()))
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
}
})
}
override fun applyPrefs(prefBuffer: String, callback: (Result<Map<String, Any>>) -> Unit) {
PrefManagerFeature.scheduleRequest("parsePrefsAndApply", prefBuffer, object : ResultConsumer<JSONObject> {
override fun success(result: JSONObject) {
callback(Result.success(result.getJSONObject("result").toMap()))
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
callback(Result.failure(Exception("$errorCode $errorMessage $errorDetails")))
}
})
}
override fun resetPrefs(preferenceNames: List<String>?, callback: (Result<Unit>) -> Unit) {
PrefManagerFeature.scheduleRequest("resetPrefs", preferenceNames.toJson(), 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")))
}
})
}
}
@@ -2,6 +2,7 @@ package eu.lensai.flutter_mozilla_components.components
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import eu.lensai.flutter_mozilla_components.Components
import eu.lensai.flutter_mozilla_components.interceptor.AppRequestInterceptor
@@ -25,7 +26,10 @@ import mozilla.components.browser.thumbnails.ThumbnailsMiddleware
import mozilla.components.browser.thumbnails.storage.ThumbnailStorage
import mozilla.components.concept.engine.DefaultSettings
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.EngineSession
import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy
import mozilla.components.concept.engine.fission.WebContentIsolationStrategy
import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
import mozilla.components.concept.fetch.Client
import mozilla.components.feature.addons.AddonManager
import mozilla.components.feature.addons.amo.AMOAddonsProvider
@@ -54,20 +58,39 @@ class Core(private val context: Context,
PreferenceManager.getDefaultSharedPreferences(context)
}
private val engineSettings by lazy {
DefaultSettings().apply {
val engineSettings by lazy {
DefaultSettings(
//historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage)
requestInterceptor = AppRequestInterceptor(context)
remoteDebuggingEnabled = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_remote_debugging), false)
testingModeEnabled = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_testing_mode), false)
historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage)
trackingProtectionPolicy = createTrackingProtectionPolicy(prefs)
httpsOnlyMode = Engine.HttpsOnlyMode.ENABLED
globalPrivacyControlEnabled = prefs.getBoolean(
context.getPreferenceKey(R.string.pref_key_global_privacy_control),
false,
)
}
requestInterceptor = AppRequestInterceptor(context),
historyTrackingDelegate = HistoryDelegate(lazyHistoryStorage),
testingModeEnabled = false,
remoteDebuggingEnabled = false,
automaticFontSizeAdjustment = true,
fontInflationEnabled = true,
suspendMediaWhenInactive = false,
getDesktopMode = {
store.state.desktopMode
},
enterpriseRootsEnabled = false,
emailTrackerBlockingPrivateBrowsing = true,
// clearColor = ContextCompat.getColor(
// context,
// R.color.fx_mobile_layer_color_1,
// ),
trackingProtectionPolicy = createTrackingProtectionPolicy(TrackingProtectionPolicy.strict()),
//FP Protection is handled by trackingPolicy
//fingerprintingProtection
//fingerprintingProtectionPrivateBrowsing
httpsOnlyMode = Engine.HttpsOnlyMode.ENABLED,
globalPrivacyControlEnabled = true,
preferredColorScheme = PreferredColorScheme.Dark,
cookieBannerHandlingMode = EngineSession.CookieBannerHandlingMode.REJECT_ALL,
cookieBannerHandlingModePrivateBrowsing = EngineSession.CookieBannerHandlingMode.REJECT_ALL,
cookieBannerHandlingGlobalRules = true,
cookieBannerHandlingGlobalRulesSubFrames = true,
webContentIsolationStrategy = WebContentIsolationStrategy.ISOLATE_HIGH_VALUE
)
}
val engine: Engine by lazy {
@@ -136,7 +159,17 @@ class Core(private val context: Context,
// PromptMiddleware(),
SessionPrioritizationMiddleware(),
RecordingDevicesMiddleware(context, components.notificationsDelegate),
) + EngineMiddleware.create(engine),
) + EngineMiddleware.create(
engine,
// We are disabling automatic suspending of engine sessions under memory pressure.
// Instead we solely rely on GeckoView and the Android system to reclaim memory
// when needed. For details, see:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1752594
// https://github.com/mozilla-mobile/fenix/issues/12731
// https://github.com/mozilla-mobile/android-components/issues/11300
// https://github.com/mozilla-mobile/android-components/issues/11653
trimMemoryAutomatically = false,
)
).apply {
components.events.registerFlowEvents(this)
@@ -191,11 +224,10 @@ class Core(private val context: Context,
* @return the constructed tracking protection policy based on preferences.
*/
private fun createTrackingProtectionPolicy(
prefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context),
normalMode: Boolean = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_tracking_protection_normal), true),
privateMode: Boolean = prefs.getBoolean(context.getPreferenceKey(R.string.pref_key_tracking_protection_private), true),
trackingPolicy: EngineSession. TrackingProtectionPolicyForSessionTypes,
normalMode: Boolean = true,
privateMode: Boolean = true,
): TrackingProtectionPolicy {
val trackingPolicy = TrackingProtectionPolicy.recommended()
return when {
normalMode && privateMode -> trackingPolicy
normalMode && !privateMode -> trackingPolicy.forRegularSessionsOnly()
@@ -11,6 +11,7 @@ import mozilla.components.feature.app.links.AppLinksUseCases
import mozilla.components.feature.downloads.DownloadsUseCases
import mozilla.components.feature.session.SessionUseCases
import mozilla.components.feature.session.SettingsUseCases
import mozilla.components.feature.session.TrackingProtectionUseCases
import mozilla.components.feature.tabs.CustomTabsUseCases
import mozilla.components.feature.tabs.TabsUseCases
@@ -49,4 +50,6 @@ class UseCases(
val customTabsUseCases: CustomTabsUseCases by lazy { CustomTabsUseCases(store, sessionUseCases.loadUrl) }
val appLinksUseCases by lazy { AppLinksUseCases(context) }
//val trackingProtectionUseCases by lazy { TrackingProtectionUseCases(store, engine) }
}
@@ -0,0 +1,100 @@
package eu.lensai.flutter_mozilla_components.feature
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import mozilla.components.concept.engine.webextension.MessageHandler
import mozilla.components.concept.engine.webextension.Port
import mozilla.components.concept.engine.webextension.WebExtensionRuntime
import mozilla.components.support.base.log.logger.Logger
import mozilla.components.support.webextensions.WebExtensionController
import org.json.JSONObject
object PrefManagerFeature {
private val logger = Logger("pref-manager")
private const val PREF_MANAGER_REPORTER_EXTENSION_ID = "pref-manager@movenext.me"
private const val PREF_MANAGER_REPORTER_EXTENSION_URL = "resource://android/assets/extensions/pref_manager/"
private const val PREF_MANAGER_REPORTER_MESSAGING_ID = "prefManager"
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(
PREF_MANAGER_REPORTER_EXTENSION_ID,
PREF_MANAGER_REPORTER_EXTENSION_URL,
PREF_MANAGER_REPORTER_MESSAGING_ID,
)
fun scheduleRequest(command: String, args: Any, 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 PrefManagerReporterBackgroundMessageHandler() : 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(
"Pref 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(
PrefManagerReporterBackgroundMessageHandler(),
)
extensionController.install(
runtime,
onSuccess = {
logger.debug("Installed PrefManager webextension: ${it.id}")
},
onError = { throwable ->
logger.error("Failed to install PrefManager webextension: ", throwable)
},
)
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.7.2), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
@@ -168,6 +168,67 @@ enum class GeckoSuggestionType(val raw: Int) {
}
}
enum class TrackingProtectionPolicy(val raw: Int) {
NONE(0),
RECOMMENDED(1),
STRICT(2),
CUSTOM(3);
companion object {
fun ofRaw(raw: Int): TrackingProtectionPolicy? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class HttpsOnlyMode(val raw: Int) {
DISABLED(0),
PRIVATE_ONLY(1),
ENABLED(2);
companion object {
fun ofRaw(raw: Int): HttpsOnlyMode? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class ColorScheme(val raw: Int) {
SYSTEM(0),
LIGHT(1),
DARK(2);
companion object {
fun ofRaw(raw: Int): ColorScheme? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class CookieBannerHandlingMode(val raw: Int) {
DISABLED(0),
REJECT_ALL(1),
REJECT_OR_ACCEPT_ALL(2);
companion object {
fun ofRaw(raw: Int): CookieBannerHandlingMode? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class WebContentIsolationStrategy(val raw: Int) {
ISOLATE_NOTHING(0),
ISOLATE_EVERYTHING(1),
ISOLATE_HIGH_VALUE(2);
companion object {
fun ofRaw(raw: Int): WebContentIsolationStrategy? {
return values().firstOrNull { it.raw == raw }
}
}
}
/**
* Translation options that map to the Gecko Translations Options.
*
@@ -1051,6 +1112,51 @@ data class TabContent (
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class GeckoEngineSettings (
val javascriptEnabled: Boolean? = null,
val trackingProtectionPolicy: TrackingProtectionPolicy? = null,
val httpsOnlyMode: HttpsOnlyMode? = null,
val globalPrivacyControlEnabled: Boolean? = null,
val preferredColorScheme: ColorScheme? = null,
val cookieBannerHandlingMode: CookieBannerHandlingMode? = null,
val cookieBannerHandlingModePrivateBrowsing: CookieBannerHandlingMode? = null,
val cookieBannerHandlingGlobalRules: Boolean? = null,
val cookieBannerHandlingGlobalRulesSubFrames: Boolean? = null,
val webContentIsolationStrategy: WebContentIsolationStrategy? = null
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): GeckoEngineSettings {
val javascriptEnabled = pigeonVar_list[0] as Boolean?
val trackingProtectionPolicy = pigeonVar_list[1] as TrackingProtectionPolicy?
val httpsOnlyMode = pigeonVar_list[2] as HttpsOnlyMode?
val globalPrivacyControlEnabled = pigeonVar_list[3] as Boolean?
val preferredColorScheme = pigeonVar_list[4] as ColorScheme?
val cookieBannerHandlingMode = pigeonVar_list[5] as CookieBannerHandlingMode?
val cookieBannerHandlingModePrivateBrowsing = pigeonVar_list[6] as CookieBannerHandlingMode?
val cookieBannerHandlingGlobalRules = pigeonVar_list[7] as Boolean?
val cookieBannerHandlingGlobalRulesSubFrames = pigeonVar_list[8] as Boolean?
val webContentIsolationStrategy = pigeonVar_list[9] as WebContentIsolationStrategy?
return GeckoEngineSettings(javascriptEnabled, trackingProtectionPolicy, httpsOnlyMode, globalPrivacyControlEnabled, preferredColorScheme, cookieBannerHandlingMode, cookieBannerHandlingModePrivateBrowsing, cookieBannerHandlingGlobalRules, cookieBannerHandlingGlobalRulesSubFrames, webContentIsolationStrategy)
}
}
fun toList(): List<Any?> {
return listOf(
javascriptEnabled,
trackingProtectionPolicy,
httpsOnlyMode,
globalPrivacyControlEnabled,
preferredColorScheme,
cookieBannerHandlingMode,
cookieBannerHandlingModePrivateBrowsing,
cookieBannerHandlingGlobalRules,
cookieBannerHandlingGlobalRulesSubFrames,
webContentIsolationStrategy,
)
}
}
private open class GeckoPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
@@ -1095,140 +1201,170 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
}
}
137.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TranslationOptions.fromList(it)
return (readValue(buffer) as Long?)?.let {
TrackingProtectionPolicy.ofRaw(it.toInt())
}
}
138.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ReaderState.fromList(it)
return (readValue(buffer) as Long?)?.let {
HttpsOnlyMode.ofRaw(it.toInt())
}
}
139.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
LastMediaAccessState.fromList(it)
return (readValue(buffer) as Long?)?.let {
ColorScheme.ofRaw(it.toInt())
}
}
140.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
HistoryMetadataKey.fromList(it)
return (readValue(buffer) as Long?)?.let {
CookieBannerHandlingMode.ofRaw(it.toInt())
}
}
141.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
PackageCategoryValue.fromList(it)
return (readValue(buffer) as Long?)?.let {
WebContentIsolationStrategy.ofRaw(it.toInt())
}
}
142.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ExternalPackage.fromList(it)
TranslationOptions.fromList(it)
}
}
143.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
LoadUrlFlagsValue.fromList(it)
ReaderState.fromList(it)
}
}
144.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SourceValue.fromList(it)
LastMediaAccessState.fromList(it)
}
}
145.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabState.fromList(it)
HistoryMetadataKey.fromList(it)
}
}
146.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RecoverableTab.fromList(it)
PackageCategoryValue.fromList(it)
}
}
147.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
RecoverableBrowserState.fromList(it)
ExternalPackage.fromList(it)
}
}
148.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
IconRequest.fromList(it)
LoadUrlFlagsValue.fromList(it)
}
}
149.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ResourceSize.fromList(it)
SourceValue.fromList(it)
}
}
150.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
Resource.fromList(it)
TabState.fromList(it)
}
}
151.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
IconResult.fromList(it)
RecoverableTab.fromList(it)
}
}
152.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CookiePartitionKey.fromList(it)
RecoverableBrowserState.fromList(it)
}
}
153.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
Cookie.fromList(it)
IconRequest.fromList(it)
}
}
154.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
HistoryItem.fromList(it)
ResourceSize.fromList(it)
}
}
155.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
HistoryState.fromList(it)
Resource.fromList(it)
}
}
156.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ReaderableState.fromList(it)
IconResult.fromList(it)
}
}
157.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SecurityInfoState.fromList(it)
CookiePartitionKey.fromList(it)
}
}
158.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it)
Cookie.fromList(it)
}
}
159.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it)
HistoryItem.fromList(it)
}
}
160.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it)
HistoryState.fromList(it)
}
}
161.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
WebExtensionData.fromList(it)
ReaderableState.fromList(it)
}
}
162.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoSuggestion.fromList(it)
SecurityInfoState.fromList(it)
}
}
163.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContentState.fromList(it)
}
}
164.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
FindResultState.fromList(it)
}
}
165.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CustomSelectionAction.fromList(it)
}
}
166.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
WebExtensionData.fromList(it)
}
}
167.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoSuggestion.fromList(it)
}
}
168.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TabContent.fromList(it)
}
}
169.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoEngineSettings.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
@@ -1266,114 +1402,138 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(136)
writeValue(stream, value.raw)
}
is TranslationOptions -> {
is TrackingProtectionPolicy -> {
stream.write(137)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is ReaderState -> {
is HttpsOnlyMode -> {
stream.write(138)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is LastMediaAccessState -> {
is ColorScheme -> {
stream.write(139)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is HistoryMetadataKey -> {
is CookieBannerHandlingMode -> {
stream.write(140)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is PackageCategoryValue -> {
is WebContentIsolationStrategy -> {
stream.write(141)
writeValue(stream, value.toList())
writeValue(stream, value.raw)
}
is ExternalPackage -> {
is TranslationOptions -> {
stream.write(142)
writeValue(stream, value.toList())
}
is LoadUrlFlagsValue -> {
is ReaderState -> {
stream.write(143)
writeValue(stream, value.toList())
}
is SourceValue -> {
is LastMediaAccessState -> {
stream.write(144)
writeValue(stream, value.toList())
}
is TabState -> {
is HistoryMetadataKey -> {
stream.write(145)
writeValue(stream, value.toList())
}
is RecoverableTab -> {
is PackageCategoryValue -> {
stream.write(146)
writeValue(stream, value.toList())
}
is RecoverableBrowserState -> {
is ExternalPackage -> {
stream.write(147)
writeValue(stream, value.toList())
}
is IconRequest -> {
is LoadUrlFlagsValue -> {
stream.write(148)
writeValue(stream, value.toList())
}
is ResourceSize -> {
is SourceValue -> {
stream.write(149)
writeValue(stream, value.toList())
}
is Resource -> {
is TabState -> {
stream.write(150)
writeValue(stream, value.toList())
}
is IconResult -> {
is RecoverableTab -> {
stream.write(151)
writeValue(stream, value.toList())
}
is CookiePartitionKey -> {
is RecoverableBrowserState -> {
stream.write(152)
writeValue(stream, value.toList())
}
is Cookie -> {
is IconRequest -> {
stream.write(153)
writeValue(stream, value.toList())
}
is HistoryItem -> {
is ResourceSize -> {
stream.write(154)
writeValue(stream, value.toList())
}
is HistoryState -> {
is Resource -> {
stream.write(155)
writeValue(stream, value.toList())
}
is ReaderableState -> {
is IconResult -> {
stream.write(156)
writeValue(stream, value.toList())
}
is SecurityInfoState -> {
is CookiePartitionKey -> {
stream.write(157)
writeValue(stream, value.toList())
}
is TabContentState -> {
is Cookie -> {
stream.write(158)
writeValue(stream, value.toList())
}
is FindResultState -> {
is HistoryItem -> {
stream.write(159)
writeValue(stream, value.toList())
}
is CustomSelectionAction -> {
is HistoryState -> {
stream.write(160)
writeValue(stream, value.toList())
}
is WebExtensionData -> {
is ReaderableState -> {
stream.write(161)
writeValue(stream, value.toList())
}
is GeckoSuggestion -> {
is SecurityInfoState -> {
stream.write(162)
writeValue(stream, value.toList())
}
is TabContent -> {
is TabContentState -> {
stream.write(163)
writeValue(stream, value.toList())
}
is FindResultState -> {
stream.write(164)
writeValue(stream, value.toList())
}
is CustomSelectionAction -> {
stream.write(165)
writeValue(stream, value.toList())
}
is WebExtensionData -> {
stream.write(166)
writeValue(stream, value.toList())
}
is GeckoSuggestion -> {
stream.write(167)
writeValue(stream, value.toList())
}
is TabContent -> {
stream.write(168)
writeValue(stream, value.toList())
}
is GeckoEngineSettings -> {
stream.write(169)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -1433,7 +1593,8 @@ interface GeckoBrowserApi {
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoEngineSettingsApi {
fun javaScriptEnabled(state: Boolean)
fun setDefaultSettings(settings: GeckoEngineSettings)
fun updateRuntimeSettings(settings: GeckoEngineSettings)
companion object {
/** The codec used by GeckoEngineSettingsApi. */
@@ -1445,13 +1606,31 @@ interface GeckoEngineSettingsApi {
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoEngineSettingsApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.javaScriptEnabled$separatedMessageChannelSuffix", codec)
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.setDefaultSettings$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val stateArg = args[0] as Boolean
val settingsArg = args[0] as GeckoEngineSettings
val wrapped: List<Any?> = try {
api.javaScriptEnabled(stateArg)
api.setDefaultSettings(settingsArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.updateRuntimeSettings$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val settingsArg = args[0] as GeckoEngineSettings
val wrapped: List<Any?> = try {
api.updateRuntimeSettings(settingsArg)
listOf(null)
} catch (exception: Throwable) {
wrapError(exception)
@@ -2271,6 +2450,83 @@ interface GeckoIconsApi {
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoPrefApi {
fun getPrefs(preferenceFilter: List<String>?, callback: (Result<Map<String, Any>>) -> Unit)
fun applyPrefs(prefBuffer: String, callback: (Result<Map<String, Any>>) -> Unit)
fun resetPrefs(preferenceNames: List<String>?, callback: (Result<Unit>) -> Unit)
companion object {
/** The codec used by GeckoPrefApi. */
val codec: MessageCodec<Any?> by lazy {
GeckoPigeonCodec()
}
/** Sets up an instance of `GeckoPrefApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: GeckoPrefApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.getPrefs$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val preferenceFilterArg = args[0] as List<String>?
api.getPrefs(preferenceFilterArg) { result: Result<Map<String, Any>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.applyPrefs$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val prefBufferArg = args[0] as String
api.applyPrefs(prefBufferArg) { result: Result<Map<String, Any>> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.resetPrefs$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val preferenceNamesArg = args[0] as List<String>?
api.resetPrefs(preferenceNamesArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface GeckoCookieApi {
fun getCookie(firstPartyDomain: String?, name: String, partitionKey: CookiePartitionKey?, storeId: String?, url: String, callback: (Result<Cookie>) -> Unit)
fun getAllCookies(domain: String?, firstPartyDomain: String?, name: String?, partitionKey: CookiePartitionKey?, storeId: String?, url: String, callback: (Result<List<Cookie>>) -> Unit)
@@ -22,6 +22,8 @@
<string name="pref_key_tracking_protection_normal" translatable="false">pref_key_tracking_protection_normal</string>
<string name="pref_key_tracking_protection_private" translatable="false">pref_key_tracking_protection_private</string>
<string name="pref_key_global_privacy_control" translatable="false">pref_key_global_privacy_control</string>
<string name="pref_key_fingerprinting_protection" translatable="false">pref_key_fingerprinting_protection</string>
<string name="pref_key_enterprise_roots_enabled" translatable="false">pref_key_enterprise_roots_enabled</string>
<string name="pref_key_launch_external_app" translatable="false">pref_key_launch_external_app</string>
<string name="pref_key_override_amo_collection" translatable="false">pref_key_override_amo_collection</string>
<string name="pref_key_override_amo_user" translatable="false">pref_key_override_amo_user</string>
@@ -8,6 +8,7 @@ export 'src/domain/services/gecko_engine_settings.dart';
export 'src/domain/services/gecko_event.dart';
export 'src/domain/services/gecko_find_in_page.dart';
export 'src/domain/services/gecko_icon.dart';
export 'src/domain/services/gecko_pref.dart';
export 'src/domain/services/gecko_readerable.dart';
export 'src/domain/services/gecko_selection_action.dart';
export 'src/domain/services/gecko_session.dart';
@@ -17,10 +18,14 @@ export 'src/domain/services/gecko_tab_content.dart';
export 'src/geckoview_widget.dart';
export 'src/pigeons/gecko.g.dart'
show
ColorScheme,
CookieBannerHandlingMode,
CookieSameSiteStatus,
GeckoEngineSettings,
GeckoSuggestion,
GeckoSuggestionType,
HistoryMetadataKey,
HttpsOnlyMode,
IconSource,
IconType,
Resource,
@@ -28,5 +33,7 @@ export 'src/pigeons/gecko.g.dart'
SecurityInfoState,
TabContent,
TabContentState,
TrackingProtectionPolicy,
WebContentIsolationStrategy,
WebExtensionActionType,
WebExtensionData;
@@ -0,0 +1,32 @@
import 'package:flutter_mozilla_components/src/pigeons/gecko.g.dart';
final _apiInstance = GeckoPrefApi();
class GeckoPrefService {
Future<Map<String, Object>> getAllPrefs() {
return _apiInstance.getPrefs(null);
}
Future<Map<String, Object>> getPrefs(List<String> prefs) {
return _apiInstance.getPrefs(prefs);
}
Future<Map<String, Object>> applyPrefs(Map<String, Object> prefs) {
final buffer = prefs.entries.map((pref) {
final value = switch (pref.value) {
final bool x => '$x',
final int x => '$x',
final String x => '"$x"',
_ => throw Exception('Unknow pref type')
};
return 'user_pref("${pref.key}", $value);';
}).join();
return _apiInstance.applyPrefs(buffer);
}
Future<void> resetPrefs(List<String> prefs) {
return _apiInstance.resetPrefs(prefs);
}
}
@@ -1,4 +1,4 @@
// Autogenerated from Pigeon (v22.7.2), do not edit directly.
// Autogenerated from Pigeon (v22.7.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
@@ -94,6 +94,37 @@ enum GeckoSuggestionType {
history,
}
enum TrackingProtectionPolicy {
none,
recommended,
strict,
custom,
}
enum HttpsOnlyMode {
disabled,
privateOnly,
enabled,
}
enum ColorScheme {
system,
light,
dark,
}
enum CookieBannerHandlingMode {
disabled,
rejectAll,
rejectOrAcceptAll,
}
enum WebContentIsolationStrategy {
isolateNothing,
isolateEverything,
isolateHighValue,
}
/// Translation options that map to the Gecko Translations Options.
///
/// @property downloadModel If the necessary models should be downloaded on request. If false, then
@@ -1176,6 +1207,72 @@ class TabContent {
}
}
class GeckoEngineSettings {
GeckoEngineSettings({
this.javascriptEnabled,
this.trackingProtectionPolicy,
this.httpsOnlyMode,
this.globalPrivacyControlEnabled,
this.preferredColorScheme,
this.cookieBannerHandlingMode,
this.cookieBannerHandlingModePrivateBrowsing,
this.cookieBannerHandlingGlobalRules,
this.cookieBannerHandlingGlobalRulesSubFrames,
this.webContentIsolationStrategy,
});
bool? javascriptEnabled;
TrackingProtectionPolicy? trackingProtectionPolicy;
HttpsOnlyMode? httpsOnlyMode;
bool? globalPrivacyControlEnabled;
ColorScheme? preferredColorScheme;
CookieBannerHandlingMode? cookieBannerHandlingMode;
CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing;
bool? cookieBannerHandlingGlobalRules;
bool? cookieBannerHandlingGlobalRulesSubFrames;
WebContentIsolationStrategy? webContentIsolationStrategy;
Object encode() {
return <Object?>[
javascriptEnabled,
trackingProtectionPolicy,
httpsOnlyMode,
globalPrivacyControlEnabled,
preferredColorScheme,
cookieBannerHandlingMode,
cookieBannerHandlingModePrivateBrowsing,
cookieBannerHandlingGlobalRules,
cookieBannerHandlingGlobalRulesSubFrames,
webContentIsolationStrategy,
];
}
static GeckoEngineSettings decode(Object result) {
result as List<Object?>;
return GeckoEngineSettings(
javascriptEnabled: result[0] as bool?,
trackingProtectionPolicy: result[1] as TrackingProtectionPolicy?,
httpsOnlyMode: result[2] as HttpsOnlyMode?,
globalPrivacyControlEnabled: result[3] as bool?,
preferredColorScheme: result[4] as ColorScheme?,
cookieBannerHandlingMode: result[5] as CookieBannerHandlingMode?,
cookieBannerHandlingModePrivateBrowsing: result[6] as CookieBannerHandlingMode?,
cookieBannerHandlingGlobalRules: result[7] as bool?,
cookieBannerHandlingGlobalRulesSubFrames: result[8] as bool?,
webContentIsolationStrategy: result[9] as WebContentIsolationStrategy?,
);
}
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@@ -1208,87 +1305,105 @@ class _PigeonCodec extends StandardMessageCodec {
} else if (value is GeckoSuggestionType) {
buffer.putUint8(136);
writeValue(buffer, value.index);
} else if (value is TranslationOptions) {
} else if (value is TrackingProtectionPolicy) {
buffer.putUint8(137);
writeValue(buffer, value.encode());
} else if (value is ReaderState) {
writeValue(buffer, value.index);
} else if (value is HttpsOnlyMode) {
buffer.putUint8(138);
writeValue(buffer, value.encode());
} else if (value is LastMediaAccessState) {
writeValue(buffer, value.index);
} else if (value is ColorScheme) {
buffer.putUint8(139);
writeValue(buffer, value.encode());
} else if (value is HistoryMetadataKey) {
writeValue(buffer, value.index);
} else if (value is CookieBannerHandlingMode) {
buffer.putUint8(140);
writeValue(buffer, value.encode());
} else if (value is PackageCategoryValue) {
writeValue(buffer, value.index);
} else if (value is WebContentIsolationStrategy) {
buffer.putUint8(141);
writeValue(buffer, value.encode());
} else if (value is ExternalPackage) {
writeValue(buffer, value.index);
} else if (value is TranslationOptions) {
buffer.putUint8(142);
writeValue(buffer, value.encode());
} else if (value is LoadUrlFlagsValue) {
} else if (value is ReaderState) {
buffer.putUint8(143);
writeValue(buffer, value.encode());
} else if (value is SourceValue) {
} else if (value is LastMediaAccessState) {
buffer.putUint8(144);
writeValue(buffer, value.encode());
} else if (value is TabState) {
} else if (value is HistoryMetadataKey) {
buffer.putUint8(145);
writeValue(buffer, value.encode());
} else if (value is RecoverableTab) {
} else if (value is PackageCategoryValue) {
buffer.putUint8(146);
writeValue(buffer, value.encode());
} else if (value is RecoverableBrowserState) {
} else if (value is ExternalPackage) {
buffer.putUint8(147);
writeValue(buffer, value.encode());
} else if (value is IconRequest) {
} else if (value is LoadUrlFlagsValue) {
buffer.putUint8(148);
writeValue(buffer, value.encode());
} else if (value is ResourceSize) {
} else if (value is SourceValue) {
buffer.putUint8(149);
writeValue(buffer, value.encode());
} else if (value is Resource) {
} else if (value is TabState) {
buffer.putUint8(150);
writeValue(buffer, value.encode());
} else if (value is IconResult) {
} else if (value is RecoverableTab) {
buffer.putUint8(151);
writeValue(buffer, value.encode());
} else if (value is CookiePartitionKey) {
} else if (value is RecoverableBrowserState) {
buffer.putUint8(152);
writeValue(buffer, value.encode());
} else if (value is Cookie) {
} else if (value is IconRequest) {
buffer.putUint8(153);
writeValue(buffer, value.encode());
} else if (value is HistoryItem) {
} else if (value is ResourceSize) {
buffer.putUint8(154);
writeValue(buffer, value.encode());
} else if (value is HistoryState) {
} else if (value is Resource) {
buffer.putUint8(155);
writeValue(buffer, value.encode());
} else if (value is ReaderableState) {
} else if (value is IconResult) {
buffer.putUint8(156);
writeValue(buffer, value.encode());
} else if (value is SecurityInfoState) {
} else if (value is CookiePartitionKey) {
buffer.putUint8(157);
writeValue(buffer, value.encode());
} else if (value is TabContentState) {
} else if (value is Cookie) {
buffer.putUint8(158);
writeValue(buffer, value.encode());
} else if (value is FindResultState) {
} else if (value is HistoryItem) {
buffer.putUint8(159);
writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) {
} else if (value is HistoryState) {
buffer.putUint8(160);
writeValue(buffer, value.encode());
} else if (value is WebExtensionData) {
} else if (value is ReaderableState) {
buffer.putUint8(161);
writeValue(buffer, value.encode());
} else if (value is GeckoSuggestion) {
} else if (value is SecurityInfoState) {
buffer.putUint8(162);
writeValue(buffer, value.encode());
} else if (value is TabContent) {
} else if (value is TabContentState) {
buffer.putUint8(163);
writeValue(buffer, value.encode());
} else if (value is FindResultState) {
buffer.putUint8(164);
writeValue(buffer, value.encode());
} else if (value is CustomSelectionAction) {
buffer.putUint8(165);
writeValue(buffer, value.encode());
} else if (value is WebExtensionData) {
buffer.putUint8(166);
writeValue(buffer, value.encode());
} else if (value is GeckoSuggestion) {
buffer.putUint8(167);
writeValue(buffer, value.encode());
} else if (value is TabContent) {
buffer.putUint8(168);
writeValue(buffer, value.encode());
} else if (value is GeckoEngineSettings) {
buffer.putUint8(169);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -1322,59 +1437,76 @@ class _PigeonCodec extends StandardMessageCodec {
final int? value = readValue(buffer) as int?;
return value == null ? null : GeckoSuggestionType.values[value];
case 137:
return TranslationOptions.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : TrackingProtectionPolicy.values[value];
case 138:
return ReaderState.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : HttpsOnlyMode.values[value];
case 139:
return LastMediaAccessState.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : ColorScheme.values[value];
case 140:
return HistoryMetadataKey.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : CookieBannerHandlingMode.values[value];
case 141:
return PackageCategoryValue.decode(readValue(buffer)!);
final int? value = readValue(buffer) as int?;
return value == null ? null : WebContentIsolationStrategy.values[value];
case 142:
return ExternalPackage.decode(readValue(buffer)!);
return TranslationOptions.decode(readValue(buffer)!);
case 143:
return LoadUrlFlagsValue.decode(readValue(buffer)!);
return ReaderState.decode(readValue(buffer)!);
case 144:
return SourceValue.decode(readValue(buffer)!);
return LastMediaAccessState.decode(readValue(buffer)!);
case 145:
return TabState.decode(readValue(buffer)!);
return HistoryMetadataKey.decode(readValue(buffer)!);
case 146:
return RecoverableTab.decode(readValue(buffer)!);
return PackageCategoryValue.decode(readValue(buffer)!);
case 147:
return RecoverableBrowserState.decode(readValue(buffer)!);
return ExternalPackage.decode(readValue(buffer)!);
case 148:
return IconRequest.decode(readValue(buffer)!);
return LoadUrlFlagsValue.decode(readValue(buffer)!);
case 149:
return ResourceSize.decode(readValue(buffer)!);
return SourceValue.decode(readValue(buffer)!);
case 150:
return Resource.decode(readValue(buffer)!);
return TabState.decode(readValue(buffer)!);
case 151:
return IconResult.decode(readValue(buffer)!);
return RecoverableTab.decode(readValue(buffer)!);
case 152:
return CookiePartitionKey.decode(readValue(buffer)!);
return RecoverableBrowserState.decode(readValue(buffer)!);
case 153:
return Cookie.decode(readValue(buffer)!);
return IconRequest.decode(readValue(buffer)!);
case 154:
return HistoryItem.decode(readValue(buffer)!);
return ResourceSize.decode(readValue(buffer)!);
case 155:
return HistoryState.decode(readValue(buffer)!);
return Resource.decode(readValue(buffer)!);
case 156:
return ReaderableState.decode(readValue(buffer)!);
return IconResult.decode(readValue(buffer)!);
case 157:
return SecurityInfoState.decode(readValue(buffer)!);
return CookiePartitionKey.decode(readValue(buffer)!);
case 158:
return TabContentState.decode(readValue(buffer)!);
return Cookie.decode(readValue(buffer)!);
case 159:
return FindResultState.decode(readValue(buffer)!);
return HistoryItem.decode(readValue(buffer)!);
case 160:
return CustomSelectionAction.decode(readValue(buffer)!);
return HistoryState.decode(readValue(buffer)!);
case 161:
return WebExtensionData.decode(readValue(buffer)!);
return ReaderableState.decode(readValue(buffer)!);
case 162:
return GeckoSuggestion.decode(readValue(buffer)!);
return SecurityInfoState.decode(readValue(buffer)!);
case 163:
return TabContentState.decode(readValue(buffer)!);
case 164:
return FindResultState.decode(readValue(buffer)!);
case 165:
return CustomSelectionAction.decode(readValue(buffer)!);
case 166:
return WebExtensionData.decode(readValue(buffer)!);
case 167:
return GeckoSuggestion.decode(readValue(buffer)!);
case 168:
return TabContent.decode(readValue(buffer)!);
case 169:
return GeckoEngineSettings.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
@@ -1452,15 +1584,37 @@ class GeckoEngineSettingsApi {
final String pigeonVar_messageChannelSuffix;
Future<void> javaScriptEnabled(bool state) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.javaScriptEnabled$pigeonVar_messageChannelSuffix';
Future<void> setDefaultSettings(GeckoEngineSettings settings) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.setDefaultSettings$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?>[state]) as List<Object?>?;
await pigeonVar_channel.send(<Object?>[settings]) 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;
}
}
Future<void> updateRuntimeSettings(GeckoEngineSettings settings) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoEngineSettingsApi.updateRuntimeSettings$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?>[settings]) as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
@@ -2378,6 +2532,96 @@ class GeckoIconsApi {
}
}
class GeckoPrefApi {
/// Constructor for [GeckoPrefApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
GeckoPrefApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
final String pigeonVar_messageChannelSuffix;
Future<Map<String, Object>> getPrefs(List<String>? preferenceFilter) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.getPrefs$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?>[preferenceFilter]) 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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as Map<Object?, Object?>?)!.cast<String, Object>();
}
}
Future<Map<String, Object>> applyPrefs(String prefBuffer) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.applyPrefs$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?>[prefBuffer]) 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 if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as Map<Object?, Object?>?)!.cast<String, Object>();
}
}
Future<void> resetPrefs(List<String>? preferenceNames) async {
final String pigeonVar_channelName = 'dev.flutter.pigeon.flutter_mozilla_components.GeckoPrefApi.resetPrefs$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?>[preferenceNames]) 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 GeckoCookieApi {
/// Constructor for [GeckoCookieApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
@@ -539,6 +539,63 @@ class TabContent {
);
}
enum TrackingProtectionPolicy {
none,
recommended,
strict,
custom,
}
enum HttpsOnlyMode {
disabled,
privateOnly,
enabled,
}
enum ColorScheme {
system,
light,
dark,
}
enum CookieBannerHandlingMode {
disabled,
rejectAll,
rejectOrAcceptAll,
}
enum WebContentIsolationStrategy {
isolateNothing,
isolateEverything,
isolateHighValue,
}
class GeckoEngineSettings {
final bool? javascriptEnabled;
final TrackingProtectionPolicy? trackingProtectionPolicy;
final HttpsOnlyMode? httpsOnlyMode;
final bool? globalPrivacyControlEnabled;
final ColorScheme? preferredColorScheme;
final CookieBannerHandlingMode? cookieBannerHandlingMode;
final CookieBannerHandlingMode? cookieBannerHandlingModePrivateBrowsing;
final bool? cookieBannerHandlingGlobalRules;
final bool? cookieBannerHandlingGlobalRulesSubFrames;
final WebContentIsolationStrategy? webContentIsolationStrategy;
GeckoEngineSettings(
this.javascriptEnabled,
this.trackingProtectionPolicy,
this.httpsOnlyMode,
this.globalPrivacyControlEnabled,
this.preferredColorScheme,
this.cookieBannerHandlingMode,
this.cookieBannerHandlingModePrivateBrowsing,
this.cookieBannerHandlingGlobalRules,
this.cookieBannerHandlingGlobalRulesSubFrames,
this.webContentIsolationStrategy,
);
}
// /// Represents all the different supported types of data that can be found from long clicking
// /// an element.
// sealed class HitResult {
@@ -614,7 +671,8 @@ abstract class GeckoBrowserApi {
@HostApi()
abstract class GeckoEngineSettingsApi {
void javaScriptEnabled(bool state);
void setDefaultSettings(GeckoEngineSettings settings);
void updateRuntimeSettings(GeckoEngineSettings settings);
}
@HostApi()
@@ -804,6 +862,16 @@ abstract class GeckoIconsApi {
IconResult loadIcon(IconRequest request);
}
@HostApi()
abstract class GeckoPrefApi {
@async
Map<String, Object> getPrefs(List<String>? preferenceFilter);
@async
Map<String, Object> applyPrefs(String prefBuffer);
@async
void resetPrefs(List<String>? preferenceNames);
}
@HostApi()
abstract class GeckoCookieApi {
@async
@@ -17,7 +17,7 @@ dev_dependencies:
flutter_test:
sdk: flutter
lint: ^2.3.0
pigeon: ^22.7.3
pigeon: ^22.7.4
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"macos":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"windows":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]},{"name":"sqlite3_flutter_libs","dependencies":[]},{"name":"sqlite3_vec","dependencies":[]}],"date_created":"2025-01-22 22:25:30.421767","version":"3.27.1","swift_package_manager_enabled":false}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"integration_test","path":"/home/fafre/development/flutter/packages/integration_test/","native_build":true,"dependencies":[]},{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"macos":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]},{"name":"sqlite3_vec","path":"/home/fafre/development/repos/lensai/packages/sqlite3_vec/","native_build":true,"dependencies":[]}],"windows":[{"name":"sqlite3_flutter_libs","path":"/home/fafre/.pub-cache/hosted/pub.dev/sqlite3_flutter_libs-0.5.29/","native_build":true,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"integration_test","dependencies":[]},{"name":"sqlite3_flutter_libs","dependencies":[]},{"name":"sqlite3_vec","dependencies":[]}],"date_created":"2025-01-26 00:44:40.809594","version":"3.27.3","swift_package_manager_enabled":false}