Add proxy routing and sing-box support

This commit is contained in:
Fabian Freund
2026-05-22 18:16:31 +02:00
parent 51289f1266
commit a5974617aa
262 changed files with 32003 additions and 3962 deletions
@@ -10,6 +10,7 @@ import eu.weblibre.flutter_mozilla_components.feature.BrowserExtensionFeature
import eu.weblibre.flutter_mozilla_components.feature.ContainerProxyFeature
import eu.weblibre.flutter_mozilla_components.feature.ResultConsumer
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoContainerProxyApi
import eu.weblibre.flutter_mozilla_components.pigeons.GeckoProxySettings
import org.json.JSONObject
class GeckoContainerProxyApiImpl : GeckoContainerProxyApi {
@@ -25,6 +26,38 @@ class GeckoContainerProxyApiImpl : GeckoContainerProxyApi {
ContainerProxyFeature.scheduleRequest("removeContainerProxy", contextId)
}
override fun upsertProxy(proxy: GeckoProxySettings) {
ContainerProxyFeature.scheduleRequest("upsertProxy", proxy.toJson())
}
override fun removeProxy(proxyId: String) {
ContainerProxyFeature.scheduleRequest("removeProxy", proxyId)
}
override fun setContainerProxy(contextId: String, proxyId: String) {
ContainerProxyFeature.scheduleRequest(
"setContainerProxy",
JSONObject().apply {
put("contextId", contextId)
put("proxyId", proxyId)
}
)
}
override fun clearContainerProxy(contextId: String) {
ContainerProxyFeature.scheduleRequest("clearContainerProxy", contextId)
}
override fun removeContainerProxyRelation(contextId: String, proxyId: String) {
ContainerProxyFeature.scheduleRequest(
"removeContainerProxyRelation",
JSONObject().apply {
put("contextId", contextId)
put("proxyId", proxyId)
}
)
}
override fun setSiteAssignments(assignments: Map<String, String>) {
ContainerProxyFeature.scheduleRequest("setSiteAssignments", JSONObject(assignments))
}
@@ -42,4 +75,18 @@ class GeckoContainerProxyApiImpl : GeckoContainerProxyApi {
}
})
}
}
private fun GeckoProxySettings.toJson(): JSONObject {
return JSONObject().apply {
put("id", id)
put("title", title)
put("type", type)
put("host", host)
put("port", port)
username?.let { put("username", it) }
password?.let { put("password", it) }
put("proxyDNS", proxyDNS)
put("doNotProxyLocal", doNotProxyLocal)
}
}
}
@@ -12,9 +12,11 @@ import android.content.Intent
import android.net.Uri
import android.util.Log
import eu.weblibre.flutter_mozilla_components.GlobalComponents
import eu.weblibre.flutter_mozilla_components.ext.EventSequence
import eu.weblibre.flutter_mozilla_components.feature.InertExternalSchemes
import eu.weblibre.flutter_mozilla_components.feature.SandboxCaptureBridge
import eu.weblibre.flutter_mozilla_components.feature.SandboxCaptureRegistry
import eu.weblibre.flutter_mozilla_components.pigeons.ProxyLoadError
import mozilla.components.browser.errorpages.ErrorPages
import mozilla.components.browser.errorpages.ErrorType
import mozilla.components.browser.state.selector.findTabOrCustomTab
@@ -145,6 +147,21 @@ class AppRequestInterceptor(private val context: Context) : RequestInterceptor {
errorType: ErrorType,
uri: String?,
): RequestInterceptor.ErrorResponse {
if (errorType == ErrorType.ERROR_PROXY_CONNECTION_REFUSED ||
errorType == ErrorType.ERROR_UNKNOWN_PROXY_HOST
) {
val tab = components.core.store.state.findTabOrCustomTab(session)
components.flutterEvents.onProxyLoadError(
EventSequence.next(),
ProxyLoadError(
tabId = tab?.id,
contextId = tab?.contextId,
url = uri,
errorType = errorType.name,
)
) { _ -> }
}
val errorPage = ErrorPages.createUrlEncodedErrorPage(context, errorType, uri)
return RequestInterceptor.ErrorResponse(errorPage)
}
@@ -4573,6 +4573,72 @@ data class MlProgressData (
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class GeckoProxySettings (
val id: String,
val title: String,
val type: String,
val host: String,
val port: Long,
val username: String? = null,
val password: String? = null,
val proxyDNS: Boolean,
val doNotProxyLocal: Boolean
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): GeckoProxySettings {
val id = pigeonVar_list[0] as String
val title = pigeonVar_list[1] as String
val type = pigeonVar_list[2] as String
val host = pigeonVar_list[3] as String
val port = pigeonVar_list[4] as Long
val username = pigeonVar_list[5] as String?
val password = pigeonVar_list[6] as String?
val proxyDNS = pigeonVar_list[7] as Boolean
val doNotProxyLocal = pigeonVar_list[8] as Boolean
return GeckoProxySettings(id, title, type, host, port, username, password, proxyDNS, doNotProxyLocal)
}
}
fun toList(): List<Any?> {
return listOf(
id,
title,
type,
host,
port,
username,
password,
proxyDNS,
doNotProxyLocal,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as GeckoProxySettings
return GeckoPigeonUtils.deepEquals(this.id, other.id) && GeckoPigeonUtils.deepEquals(this.title, other.title) && GeckoPigeonUtils.deepEquals(this.type, other.type) && GeckoPigeonUtils.deepEquals(this.host, other.host) && GeckoPigeonUtils.deepEquals(this.port, other.port) && GeckoPigeonUtils.deepEquals(this.username, other.username) && GeckoPigeonUtils.deepEquals(this.password, other.password) && GeckoPigeonUtils.deepEquals(this.proxyDNS, other.proxyDNS) && GeckoPigeonUtils.deepEquals(this.doNotProxyLocal, other.doNotProxyLocal)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + GeckoPigeonUtils.deepHash(this.id)
result = 31 * result + GeckoPigeonUtils.deepHash(this.title)
result = 31 * result + GeckoPigeonUtils.deepHash(this.type)
result = 31 * result + GeckoPigeonUtils.deepHash(this.host)
result = 31 * result + GeckoPigeonUtils.deepHash(this.port)
result = 31 * result + GeckoPigeonUtils.deepHash(this.username)
result = 31 * result + GeckoPigeonUtils.deepHash(this.password)
result = 31 * result + GeckoPigeonUtils.deepHash(this.proxyDNS)
result = 31 * result + GeckoPigeonUtils.deepHash(this.doNotProxyLocal)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class ContainerSiteAssignment (
val requestId: String,
@@ -4623,6 +4689,52 @@ data class ContainerSiteAssignment (
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class ProxyLoadError (
val tabId: String? = null,
val contextId: String? = null,
val url: String? = null,
val errorType: String
)
{
companion object {
fun fromList(pigeonVar_list: List<Any?>): ProxyLoadError {
val tabId = pigeonVar_list[0] as String?
val contextId = pigeonVar_list[1] as String?
val url = pigeonVar_list[2] as String?
val errorType = pigeonVar_list[3] as String
return ProxyLoadError(tabId, contextId, url, errorType)
}
}
fun toList(): List<Any?> {
return listOf(
tabId,
contextId,
url,
errorType,
)
}
override fun equals(other: Any?): Boolean {
if (other == null || other.javaClass != javaClass) {
return false
}
if (this === other) {
return true
}
val other = other as ProxyLoadError
return GeckoPigeonUtils.deepEquals(this.tabId, other.tabId) && GeckoPigeonUtils.deepEquals(this.contextId, other.contextId) && GeckoPigeonUtils.deepEquals(this.url, other.url) && GeckoPigeonUtils.deepEquals(this.errorType, other.errorType)
}
override fun hashCode(): Int {
var result = javaClass.hashCode()
result = 31 * result + GeckoPigeonUtils.deepHash(this.tabId)
result = 31 * result + GeckoPigeonUtils.deepHash(this.contextId)
result = 31 * result + GeckoPigeonUtils.deepHash(this.url)
result = 31 * result + GeckoPigeonUtils.deepHash(this.errorType)
return result
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class GeckoHeader (
val key: String,
@@ -5995,75 +6107,85 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
}
235.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ContainerSiteAssignment.fromList(it)
GeckoProxySettings.fromList(it)
}
}
236.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoHeader.fromList(it)
ContainerSiteAssignment.fromList(it)
}
}
237.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchRequest.fromList(it)
ProxyLoadError.fromList(it)
}
}
238.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
GeckoFetchResponse.fromList(it)
GeckoHeader.fromList(it)
}
}
239.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
BookmarkNode.fromList(it)
GeckoFetchRequest.fromList(it)
}
}
240.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
BookmarkInfo.fromList(it)
GeckoFetchResponse.fromList(it)
}
}
241.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SitePermissions.fromList(it)
BookmarkNode.fromList(it)
}
}
242.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
TrackingProtectionException.fromList(it)
BookmarkInfo.fromList(it)
}
}
243.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
PwaIcon.fromList(it)
SitePermissions.fromList(it)
}
}
244.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ShareTargetFiles.fromList(it)
TrackingProtectionException.fromList(it)
}
}
245.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ShareTargetParams.fromList(it)
PwaIcon.fromList(it)
}
}
246.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ShareTarget.fromList(it)
ShareTargetFiles.fromList(it)
}
}
247.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ExternalApplicationResource.fromList(it)
ShareTargetParams.fromList(it)
}
}
248.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
PwaManifest.fromList(it)
ShareTarget.fromList(it)
}
}
249.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
ExternalApplicationResource.fromList(it)
}
}
250.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
PwaManifest.fromList(it)
}
}
251.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
SandboxCaptureEntry.fromList(it)
}
@@ -6497,66 +6619,74 @@ private open class GeckoPigeonCodec : StandardMessageCodec() {
stream.write(234)
writeValue(stream, value.toList())
}
is ContainerSiteAssignment -> {
is GeckoProxySettings -> {
stream.write(235)
writeValue(stream, value.toList())
}
is GeckoHeader -> {
is ContainerSiteAssignment -> {
stream.write(236)
writeValue(stream, value.toList())
}
is GeckoFetchRequest -> {
is ProxyLoadError -> {
stream.write(237)
writeValue(stream, value.toList())
}
is GeckoFetchResponse -> {
is GeckoHeader -> {
stream.write(238)
writeValue(stream, value.toList())
}
is BookmarkNode -> {
is GeckoFetchRequest -> {
stream.write(239)
writeValue(stream, value.toList())
}
is BookmarkInfo -> {
is GeckoFetchResponse -> {
stream.write(240)
writeValue(stream, value.toList())
}
is SitePermissions -> {
is BookmarkNode -> {
stream.write(241)
writeValue(stream, value.toList())
}
is TrackingProtectionException -> {
is BookmarkInfo -> {
stream.write(242)
writeValue(stream, value.toList())
}
is PwaIcon -> {
is SitePermissions -> {
stream.write(243)
writeValue(stream, value.toList())
}
is ShareTargetFiles -> {
is TrackingProtectionException -> {
stream.write(244)
writeValue(stream, value.toList())
}
is ShareTargetParams -> {
is PwaIcon -> {
stream.write(245)
writeValue(stream, value.toList())
}
is ShareTarget -> {
is ShareTargetFiles -> {
stream.write(246)
writeValue(stream, value.toList())
}
is ExternalApplicationResource -> {
is ShareTargetParams -> {
stream.write(247)
writeValue(stream, value.toList())
}
is PwaManifest -> {
is ShareTarget -> {
stream.write(248)
writeValue(stream, value.toList())
}
is SandboxCaptureEntry -> {
is ExternalApplicationResource -> {
stream.write(249)
writeValue(stream, value.toList())
}
is PwaManifest -> {
stream.write(250)
writeValue(stream, value.toList())
}
is SandboxCaptureEntry -> {
stream.write(251)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
@@ -8279,6 +8409,11 @@ interface GeckoContainerProxyApi {
fun setProxyPort(port: Long)
fun addContainerProxy(contextId: String)
fun removeContainerProxy(contextId: String)
fun upsertProxy(proxy: GeckoProxySettings)
fun removeProxy(proxyId: String)
fun setContainerProxy(contextId: String, proxyId: String)
fun clearContainerProxy(contextId: String)
fun removeContainerProxyRelation(contextId: String, proxyId: String)
fun setSiteAssignments(assignments: Map<String, String>)
fun healthcheck(callback: (Result<Boolean>) -> Unit)
@@ -8345,6 +8480,98 @@ interface GeckoContainerProxyApi {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.upsertProxy$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val proxyArg = args[0] as GeckoProxySettings
val wrapped: List<Any?> = try {
api.upsertProxy(proxyArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.removeProxy$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val proxyIdArg = args[0] as String
val wrapped: List<Any?> = try {
api.removeProxy(proxyIdArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.setContainerProxy$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val contextIdArg = args[0] as String
val proxyIdArg = args[1] as String
val wrapped: List<Any?> = try {
api.setContainerProxy(contextIdArg, proxyIdArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.clearContainerProxy$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val contextIdArg = args[0] as String
val wrapped: List<Any?> = try {
api.clearContainerProxy(contextIdArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.removeContainerProxyRelation$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val contextIdArg = args[0] as String
val proxyIdArg = args[1] as String
val wrapped: List<Any?> = try {
api.removeContainerProxyRelation(contextIdArg, proxyIdArg)
listOf(null)
} catch (exception: Throwable) {
GeckoPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.setSiteAssignments$separatedMessageChannelSuffix", codec)
if (api != null) {
@@ -8785,6 +9012,23 @@ class GeckoStateEvents(private val binaryMessenger: BinaryMessenger, private val
}
}
}
fun onProxyLoadError(sequenceArg: Long, detailsArg: ProxyLoadError, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName = "dev.flutter.pigeon.flutter_mozilla_components.GeckoStateEvents.onProxyLoadError$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(sequenceArg, detailsArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else {
callback(Result.success(Unit))
}
} else {
callback(Result.failure(GeckoPigeonUtils.createConnectionError(channelName)))
}
}
}
fun onMlProgress(sequenceArg: Long, progressArg: MlProgressData, callback: (Result<Unit>) -> Unit)
{
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
@@ -15,7 +15,7 @@ const privateIdentifier = 'firefox-private'
type DoNotProxy = never[]
export const doNotProxy: DoNotProxy = []
const emergencyBreak: Socks5ProxyInfo = {
export const emergencyBreak: Socks5ProxyInfo = {
type: ProxyType.Socks5,
host: 'emergency-break-proxy.localhost',
port: 1,
@@ -1,4 +1,4 @@
import { Socks5ProxySettings } from 'src/domain/ProxySettings';
import { ProxySettings, Socks5ProxySettings } from 'src/domain/ProxySettings';
import { Store } from '../store/Store'
import BackgroundMain from './BackgroundMain'
@@ -8,7 +8,16 @@ const store = new Store()
interface Message {
id: String | undefined;
action: 'setProxyPort' | 'addContainerProxy' | 'removeContainerProxy' | 'healthcheck' | 'setSiteAssignments';
action: 'setProxyPort' |
'addContainerProxy' |
'removeContainerProxy' |
'upsertProxy' |
'removeProxy' |
'setContainerProxy' |
'clearContainerProxy' |
'removeContainerProxyRelation' |
'healthcheck' |
'setSiteAssignments';
args: any;
}
@@ -42,6 +51,32 @@ port.onMessage.addListener((raw: unknown): void => {
store.removeContainerProxyRelation(message.args, "tor")
console.log('removed container relation ' + message.args)
break
case "upsertProxy": {
const proxy = ProxySettings.tryFromDao(message.args)
if (proxy === undefined) {
console.error('invalid proxy settings ' + JSON.stringify(message.args))
break
}
store.putProxy(proxy)
console.log('upsert proxy ' + message.args.id)
break
}
case "removeProxy":
store.deleteProxyById(message.args)
console.log('removed proxy ' + message.args)
break
case "setContainerProxy":
store.setContainerProxyRelation(message.args.contextId, message.args.proxyId)
console.log('set container relation ' + message.args.contextId + ' -> ' + message.args.proxyId)
break
case "clearContainerProxy":
store.clearContainerProxyRelation(message.args)
console.log('cleared container relation ' + message.args)
break
case "removeContainerProxyRelation":
store.removeContainerProxyRelation(message.args.contextId, message.args.proxyId)
console.log('removed container relation ' + message.args.contextId + ' -> ' + message.args.proxyId)
break
case "setSiteAssignments":
const entries = new Map(Object.entries(message.args))
console.log('set site assignments ' + JSON.stringify(message.args))
@@ -224,6 +224,10 @@ export class Store {
this.relations[cookieStoreId] = [proxyId]
}
clearContainerProxyRelation(cookieStoreId: string): void {
delete this.relations[cookieStoreId]
}
removeContainerProxyRelation(cookieStoreId: string, proxyId: string): void {
const currentRelations = this.relations[cookieStoreId] ?? []
this.relations[cookieStoreId] = currentRelations.filter(id => id !== proxyId)
@@ -1,4 +1,4 @@
import BackgroundMain, { doNotProxy } from '../../src/background/BackgroundMain'
import BackgroundMain, { doNotProxy, emergencyBreak } from '../../src/background/BackgroundMain'
import { Store } from '../../src/store/Store'
import { expect } from 'chai'
@@ -41,6 +41,16 @@ describe('BackgroundMain', function () {
expect(result).to.be.not.empty
})
it('should block if an assigned proxy no longer exists', async () => {
const isolatedStore = new Store()
const isolatedBackgroundMain = new BackgroundMain({ store: isolatedStore })
isolatedStore.setContainerProxyRelation('general', 'missing-proxy')
const result = await isolatedBackgroundMain.onRequest({ cookieStoreId: 'firefox-default', url: 'https://google.com', tabId: 0 })
expect(result).to.be.deep.equal([emergencyBreak])
})
it('should remove doNotProxyLocal flag from proxy settings if proxy is set up', async () => {
await givenSomeProxyIsSetUpForContainer({ containerId: 'firefox-default', host: undefined, doNotProxyLocal: undefined })
@@ -163,6 +163,15 @@ describe('Store', () => {
expect(relations.container1).to.be.deep.equal(['proxy1'])
expect(relations.container2).to.be.deep.equal(['proxy2'])
})
it('should keep a missing proxy relation distinguishable from no relation', () => {
const isolatedStore = new Store()
isolatedStore.setContainerProxyRelation('container1', 'deleted-proxy')
const result = isolatedStore.getProxiesForContainer('container1')
expect(result).to.be.deep.equal([])
})
})
describe('wildcard site assignments', function () {
@@ -69,6 +69,7 @@ export 'src/pigeons/gecko.g.dart'
GeckoDeleteBrowsingDataController,
GeckoEngineSettings,
GeckoFetchResponse,
GeckoProxySettings,
GeckoPref,
GeckoPublicSuffixListApi,
GeckoPwaApi,
@@ -93,6 +94,7 @@ export 'src/pigeons/gecko.g.dart'
MlProgressStatus,
MlProgressType,
PhoneHitResult,
ProxyLoadError,
PwaIcon,
PwaManifest,
QueryParameterStripping,
@@ -24,6 +24,26 @@ class GeckoContainerProxyService {
return _apiInstance.removeContainerProxy(contextId);
}
Future<void> upsertProxy(GeckoProxySettings proxy) {
return _apiInstance.upsertProxy(proxy);
}
Future<void> removeProxy(String proxyId) {
return _apiInstance.removeProxy(proxyId);
}
Future<void> setContainerProxy(String contextId, String proxyId) {
return _apiInstance.setContainerProxy(contextId, proxyId);
}
Future<void> clearContainerProxy(String contextId) {
return _apiInstance.clearContainerProxy(contextId);
}
Future<void> removeContainerProxyRelation(String contextId, String proxyId) {
return _apiInstance.removeContainerProxyRelation(contextId, proxyId);
}
Future<void> setSiteAssignments(Map<String, String> assignments) {
return _apiInstance.setSiteAssignments(assignments);
}
@@ -44,6 +44,7 @@ class GeckoEventService extends GeckoStateEvents {
// final _scrollEventSubject = PublishSubject<ScrollEvent>();
final _prefUpdateSubject = PublishSubject<GeckoPref>();
final _siteAssignementSubject = PublishSubject<ContainerSiteAssignment>();
final _proxyLoadErrorSubject = PublishSubject<ProxyLoadError>();
final _tabAddedSubject = PublishSubject<String>();
final _mlProgressSubject = PublishSubject<MlProgressData>();
@@ -71,6 +72,8 @@ class GeckoEventService extends GeckoStateEvents {
Stream<GeckoPref> get prefUpdateEvent => _prefUpdateSubject.stream;
Stream<ContainerSiteAssignment> get siteAssignementEvent =>
_siteAssignementSubject.stream;
Stream<ProxyLoadError> get proxyLoadErrorEvents =>
_proxyLoadErrorSubject.stream;
Stream<String> get tabAddedStream => _tabAddedSubject.stream;
Stream<MlProgressData> get mlProgressEvents => _mlProgressSubject.stream;
@@ -205,6 +208,11 @@ class GeckoEventService extends GeckoStateEvents {
);
}
@override
void onProxyLoadError(int sequence, ProxyLoadError details) {
_proxyLoadErrorSubject.addWhenMoreRecent(sequence, details.tabId, details);
}
@override
void onMlProgress(int sequence, MlProgressData progress) {
_mlProgressSubject.addWhenMoreRecent(sequence, null, progress);
@@ -266,6 +274,7 @@ class GeckoEventService extends GeckoStateEvents {
await _tabAddedSubject.close();
await _prefUpdateSubject.close();
await _siteAssignementSubject.close();
await _proxyLoadErrorSubject.close();
await _mlProgressSubject.close();
await _manifestUpdateSubject.close();
await _translationEngineSubject.close();
@@ -91,7 +91,10 @@ class GeckoHistoryService {
return _api.getVisited(urls);
}
Future<List<HistorySuggestion>> getSuggestions(String query, {int limit = 10}) {
Future<List<HistorySuggestion>> getSuggestions(
String query, {
int limit = 10,
}) {
return _api.getSuggestions(query, limit);
}
@@ -140,6 +143,8 @@ class GeckoHistoryService {
}
Future<void> deleteHistoryMetadataOlderThan(DateTime olderThan) {
return _api.deleteHistoryMetadataOlderThan(olderThan.millisecondsSinceEpoch);
return _api.deleteHistoryMetadataOlderThan(
olderThan.millisecondsSinceEpoch,
);
}
}
File diff suppressed because it is too large Load Diff
@@ -1856,11 +1856,40 @@ abstract class GeckoBrowserExtensionApi {
List<Object> getMarkdown(List<String> htmlList);
}
class GeckoProxySettings {
final String id;
final String title;
final String type;
final String host;
final int port;
final String? username;
final String? password;
final bool proxyDNS;
final bool doNotProxyLocal;
const GeckoProxySettings({
required this.id,
required this.title,
required this.type,
required this.host,
required this.port,
this.username,
this.password,
this.proxyDNS = true,
this.doNotProxyLocal = true,
});
}
@HostApi()
abstract class GeckoContainerProxyApi {
void setProxyPort(int port);
void addContainerProxy(String contextId);
void removeContainerProxy(String contextId);
void upsertProxy(GeckoProxySettings proxy);
void removeProxy(String proxyId);
void setContainerProxy(String contextId, String proxyId);
void clearContainerProxy(String contextId);
void removeContainerProxyRelation(String contextId, String proxyId);
void setSiteAssignments(Map<String, String> assignments);
@async
@@ -1930,6 +1959,20 @@ class ContainerSiteAssignment {
});
}
class ProxyLoadError {
final String? tabId;
final String? contextId;
final String? url;
final String errorType;
ProxyLoadError({
required this.tabId,
required this.contextId,
required this.url,
required this.errorType,
});
}
@FlutterApi()
abstract class GeckoStateEvents {
void onViewReadyStateChange(int sequence, bool state);
@@ -1960,6 +2003,8 @@ abstract class GeckoStateEvents {
void onContainerSiteAssignment(int sequence, ContainerSiteAssignment details);
void onProxyLoadError(int sequence, ProxyLoadError details);
void onMlProgress(int sequence, MlProgressData progress);
void onManifestUpdate(int sequence, String tabId, PwaManifest? manifest);