setting to exclude proxy from global routing
This commit is contained in:
@@ -396,6 +396,19 @@ Future<bool> isTabTunneled(Ref ref, String? tabId) async {
|
|||||||
|
|
||||||
return containerData?.metadata.proxyConnectionId != null;
|
return containerData?.metadata.proxyConnectionId != null;
|
||||||
case ProxyRegularTabRoutingMode.all:
|
case ProxyRegularTabRoutingMode.all:
|
||||||
|
final containerData = await ref
|
||||||
|
.read(tabDataRepositoryProvider.notifier)
|
||||||
|
.getTabContainerData(tabState.id);
|
||||||
|
|
||||||
|
if (!ref.mounted) return false;
|
||||||
|
|
||||||
|
if (containerData?.metadata.proxyConnectionId != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (containerData?.metadata.bypassGlobalProxy == true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return proxyRoutingSettings.regularTabsProxyConnectionId != null;
|
return proxyRoutingSettings.regularTabsProxyConnectionId != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ final class IsTabTunneledProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$isTabTunneledHash() => r'cb3a3afe5b94f7e9c77d6a32266afbb8a225c259';
|
String _$isTabTunneledHash() => r'd36607ff812f71870af32ce59c2dbe898a9c1917';
|
||||||
|
|
||||||
final class IsTabTunneledFamily extends $Family
|
final class IsTabTunneledFamily extends $Family
|
||||||
with $FunctionalFamilyOverride<FutureOr<bool>, String?> {
|
with $FunctionalFamilyOverride<FutureOr<bool>, String?> {
|
||||||
|
|||||||
+116
-27
@@ -33,10 +33,45 @@ import 'package:weblibre/features/user/domain/repositories/proxy_routing_setting
|
|||||||
|
|
||||||
part 'proxy_settings_replication.g.dart';
|
part 'proxy_settings_replication.g.dart';
|
||||||
|
|
||||||
|
sealed class _ProxyAssignment with FastEquatable {
|
||||||
|
_ProxyAssignment();
|
||||||
|
|
||||||
|
factory _ProxyAssignment.inherit() = _InheritProxyAssignment;
|
||||||
|
|
||||||
|
factory _ProxyAssignment.direct(String scopeId) = _DirectProxyAssignment;
|
||||||
|
|
||||||
|
factory _ProxyAssignment.explicit(String proxyId) = _ExplicitProxyAssignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _InheritProxyAssignment extends _ProxyAssignment {
|
||||||
|
_InheritProxyAssignment();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get hashParameters => const ['inherit'];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _DirectProxyAssignment extends _ProxyAssignment {
|
||||||
|
final String scopeId;
|
||||||
|
|
||||||
|
_DirectProxyAssignment(this.scopeId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get hashParameters => [scopeId];
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _ExplicitProxyAssignment extends _ProxyAssignment {
|
||||||
|
final String proxyId;
|
||||||
|
|
||||||
|
_ExplicitProxyAssignment(this.proxyId);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get hashParameters => [proxyId];
|
||||||
|
}
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
class ProxySettingsReplication extends _$ProxySettingsReplication {
|
class ProxySettingsReplication extends _$ProxySettingsReplication {
|
||||||
var _isolatedProxyAssignments = <String, String>{};
|
var _isolatedProxyAssignments = <String, _ProxyAssignment>{};
|
||||||
var _appliedContainerProxies = <String, String?>{};
|
var _appliedContainerProxies = <String, _ProxyAssignment>{};
|
||||||
|
|
||||||
final _recomputeLock = Lock();
|
final _recomputeLock = Lock();
|
||||||
var _recomputeDirty = false;
|
var _recomputeDirty = false;
|
||||||
@@ -82,33 +117,71 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
|
|||||||
.read(containerRepositoryProvider.notifier)
|
.read(containerRepositoryProvider.notifier)
|
||||||
.getAllContainersWithCount();
|
.getAllContainersWithCount();
|
||||||
|
|
||||||
final containerProxyIds = <String, String>{
|
final containerAssignments = <String, _ProxyAssignment>{
|
||||||
for (final c in containers)
|
for (final c in containers)
|
||||||
if (c.metadata.proxyConnectionId case final proxyId?)
|
if (c.metadata.contextualIdentity case final contextId?)
|
||||||
c.id: proxyId.encode(),
|
c.id: switch (c.metadata.proxyConnectionId) {
|
||||||
|
final proxyId? => _ProxyAssignment.explicit(proxyId.encode()),
|
||||||
|
null when c.metadata.bypassGlobalProxy => _ProxyAssignment.direct(
|
||||||
|
contextId,
|
||||||
|
),
|
||||||
|
null => _ProxyAssignment.inherit(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
final newAssignments = <String, String>{};
|
final newAssignments = <String, _ProxyAssignment>{};
|
||||||
for (final entry in contextContainerMap.entries) {
|
for (final entry in contextContainerMap.entries) {
|
||||||
|
final assignments = entry.value
|
||||||
|
.map((containerId) => containerAssignments[containerId])
|
||||||
|
.nonNulls
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
if (assignments.isEmpty) continue;
|
||||||
|
|
||||||
final proxyIds =
|
final proxyIds =
|
||||||
entry.value
|
assignments
|
||||||
.map((containerId) => containerProxyIds[containerId])
|
.whereType<_ExplicitProxyAssignment>()
|
||||||
.nonNulls
|
.map((assignment) => assignment.proxyId)
|
||||||
.toSet()
|
.toSet()
|
||||||
.toList()
|
.toList()
|
||||||
..sort();
|
..sort();
|
||||||
|
final directScopeIds =
|
||||||
|
assignments
|
||||||
|
.whereType<_DirectProxyAssignment>()
|
||||||
|
.map((assignment) => assignment.scopeId)
|
||||||
|
.toSet()
|
||||||
|
.toList()
|
||||||
|
..sort();
|
||||||
|
final hasInheritedAssignment = assignments.any(
|
||||||
|
(assignment) => assignment is _InheritProxyAssignment,
|
||||||
|
);
|
||||||
|
|
||||||
if (proxyIds.isEmpty) continue;
|
final chosenAssignment = proxyIds.isNotEmpty
|
||||||
|
? _ProxyAssignment.explicit(proxyIds.first)
|
||||||
|
: directScopeIds.isNotEmpty && !hasInheritedAssignment
|
||||||
|
? _ProxyAssignment.direct(directScopeIds.first)
|
||||||
|
: _ProxyAssignment.inherit();
|
||||||
|
if (chosenAssignment is! _InheritProxyAssignment) {
|
||||||
|
newAssignments[entry.key] = chosenAssignment;
|
||||||
|
}
|
||||||
|
final chosenLabel = switch (chosenAssignment) {
|
||||||
|
_DirectProxyAssignment(:final scopeId) => 'direct:$scopeId',
|
||||||
|
_ExplicitProxyAssignment(:final proxyId) => proxyId,
|
||||||
|
_InheritProxyAssignment() => 'inherit',
|
||||||
|
};
|
||||||
|
|
||||||
newAssignments[entry.key] = proxyIds.first;
|
final distinctAssignmentCount =
|
||||||
|
proxyIds.length +
|
||||||
if (proxyIds.length > 1) {
|
directScopeIds.length +
|
||||||
|
(hasInheritedAssignment ? 1 : 0);
|
||||||
|
if (distinctAssignmentCount > 1) {
|
||||||
// Isolation contexts can hold multiple containers; if they disagree on
|
// Isolation contexts can hold multiple containers; if they disagree on
|
||||||
// a proxy connection the alias is forced to pick one. Surface this so
|
// routing, the alias is forced to pick one. Surface this so the user
|
||||||
// the user can split the containers across isolation contexts.
|
// can split the containers across isolation contexts.
|
||||||
logger.w(
|
logger.w(
|
||||||
'Isolation context ${entry.key} has containers with multiple '
|
'Isolation context ${entry.key} has containers with multiple '
|
||||||
'proxy connections (${proxyIds.join(', ')}); using ${proxyIds.first}',
|
'proxy routing assignments '
|
||||||
|
'(${[if (hasInheritedAssignment) 'inherit', ...directScopeIds.map((id) => 'direct:$id'), ...proxyIds].join(', ')}); using $chosenLabel',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,9 +199,7 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
|
|||||||
for (final MapEntry(:key, :value) in newAssignments.entries) {
|
for (final MapEntry(:key, :value) in newAssignments.entries) {
|
||||||
if (_isolatedProxyAssignments[key] == value) continue;
|
if (_isolatedProxyAssignments[key] == value) continue;
|
||||||
|
|
||||||
await ref
|
await _applyProxyAssignment(key, value);
|
||||||
.read(containerProxyRepositoryProvider.notifier)
|
|
||||||
.setContainerProxy(key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_isolatedProxyAssignments = newAssignments;
|
_isolatedProxyAssignments = newAssignments;
|
||||||
@@ -260,15 +331,22 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
|
|||||||
) async {
|
) async {
|
||||||
if (containers == null) return;
|
if (containers == null) return;
|
||||||
|
|
||||||
final desired = <String, String?>{};
|
final desired = <String, _ProxyAssignment>{};
|
||||||
for (final container in containers) {
|
for (final container in containers) {
|
||||||
final contextId = container.metadata.contextualIdentity;
|
final contextId = container.metadata.contextualIdentity;
|
||||||
if (contextId == null || contextId.isEmpty) continue;
|
if (contextId == null || contextId.isEmpty) continue;
|
||||||
desired[contextId] = container.metadata.proxyConnectionId?.encode();
|
final proxyConnectionId = container.metadata.proxyConnectionId;
|
||||||
|
desired[contextId] = proxyConnectionId != null
|
||||||
|
? _ProxyAssignment.explicit(proxyConnectionId.encode())
|
||||||
|
: container.metadata.bypassGlobalProxy
|
||||||
|
? _ProxyAssignment.direct(contextId)
|
||||||
|
: _ProxyAssignment.inherit();
|
||||||
}
|
}
|
||||||
|
|
||||||
final repo = ref.read(containerProxyRepositoryProvider.notifier);
|
final repo = ref.read(containerProxyRepositoryProvider.notifier);
|
||||||
final nextApplied = Map<String, String?>.from(_appliedContainerProxies);
|
final nextApplied = Map<String, _ProxyAssignment>.from(
|
||||||
|
_appliedContainerProxies,
|
||||||
|
);
|
||||||
|
|
||||||
for (final contextId in _appliedContainerProxies.keys.toSet().difference(
|
for (final contextId in _appliedContainerProxies.keys.toSet().difference(
|
||||||
desired.keys.toSet(),
|
desired.keys.toSet(),
|
||||||
@@ -291,11 +369,7 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (value != null) {
|
await _applyProxyAssignment(key, value);
|
||||||
await repo.setContainerProxy(key, value);
|
|
||||||
} else {
|
|
||||||
await repo.clearContainerProxy(key);
|
|
||||||
}
|
|
||||||
nextApplied[key] = value;
|
nextApplied[key] = value;
|
||||||
} catch (error, stackTrace) {
|
} catch (error, stackTrace) {
|
||||||
logger.e(
|
logger.e(
|
||||||
@@ -310,4 +384,19 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
|
|||||||
|
|
||||||
_appliedContainerProxies = nextApplied;
|
_appliedContainerProxies = nextApplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _applyProxyAssignment(
|
||||||
|
String contextId,
|
||||||
|
_ProxyAssignment assignment,
|
||||||
|
) async {
|
||||||
|
final repo = ref.read(containerProxyRepositoryProvider.notifier);
|
||||||
|
switch (assignment) {
|
||||||
|
case _ExplicitProxyAssignment(:final proxyId):
|
||||||
|
await repo.setContainerProxy(contextId, proxyId);
|
||||||
|
case _DirectProxyAssignment(:final scopeId):
|
||||||
|
await repo.setContainerDirectConnection(contextId, scopeId: scopeId);
|
||||||
|
case _InheritProxyAssignment():
|
||||||
|
await repo.clearContainerProxy(contextId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -42,7 +42,7 @@ final class ProxySettingsReplicationProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$proxySettingsReplicationHash() =>
|
String _$proxySettingsReplicationHash() =>
|
||||||
r'42dcd4ec7bc5a00804f729178b99c56e8c917850';
|
r'cdb624b7f7806a7ce4218da350253ed3d29986d0';
|
||||||
|
|
||||||
abstract class _$ProxySettingsReplication extends $Notifier<void> {
|
abstract class _$ProxySettingsReplication extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
|
|||||||
+33
-7
@@ -249,9 +249,9 @@ Future<ProxyConnectionId?> _proxyConnectionIdForLoadError(
|
|||||||
|
|
||||||
if (contextId != null && contextId.isNotEmpty && contextId != 'general') {
|
if (contextId != null && contextId.isNotEmpty && contextId != 'general') {
|
||||||
final isolatedContextProxyConnectionId =
|
final isolatedContextProxyConnectionId =
|
||||||
await _isolatedContextProxyConnectionId(ref, contextId);
|
await _isolatedContextProxyAssignment(ref, contextId);
|
||||||
if (isolatedContextProxyConnectionId != null) {
|
if (isolatedContextProxyConnectionId != null) {
|
||||||
return isolatedContextProxyConnectionId;
|
return isolatedContextProxyConnectionId.proxyConnectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
final contextContainer = await ref
|
final contextContainer = await ref
|
||||||
@@ -262,6 +262,9 @@ Future<ProxyConnectionId?> _proxyConnectionIdForLoadError(
|
|||||||
if (contextProxyConnectionId != null) {
|
if (contextProxyConnectionId != null) {
|
||||||
return contextProxyConnectionId;
|
return contextProxyConnectionId;
|
||||||
}
|
}
|
||||||
|
if (contextContainer?.metadata.bypassGlobalProxy == true) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final tabContainer = await ref
|
final tabContainer = await ref
|
||||||
@@ -271,6 +274,9 @@ Future<ProxyConnectionId?> _proxyConnectionIdForLoadError(
|
|||||||
if (tabProxyConnectionId != null) {
|
if (tabProxyConnectionId != null) {
|
||||||
return tabProxyConnectionId;
|
return tabProxyConnectionId;
|
||||||
}
|
}
|
||||||
|
if (tabContainer?.metadata.bypassGlobalProxy == true) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (routing.regularTabsMode == ProxyRegularTabRoutingMode.all) {
|
if (routing.regularTabsMode == ProxyRegularTabRoutingMode.all) {
|
||||||
return routing.regularTabsProxyConnectionId;
|
return routing.regularTabsProxyConnectionId;
|
||||||
@@ -279,7 +285,12 @@ Future<ProxyConnectionId?> _proxyConnectionIdForLoadError(
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ProxyConnectionId?> _isolatedContextProxyConnectionId(
|
typedef _LoadErrorProxyAssignment = ({
|
||||||
|
bool direct,
|
||||||
|
ProxyConnectionId? proxyConnectionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<_LoadErrorProxyAssignment?> _isolatedContextProxyAssignment(
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
String contextId,
|
String contextId,
|
||||||
) async {
|
) async {
|
||||||
@@ -296,17 +307,32 @@ Future<ProxyConnectionId?> _isolatedContextProxyConnectionId(
|
|||||||
final containers = await ref
|
final containers = await ref
|
||||||
.read(containerRepositoryProvider.notifier)
|
.read(containerRepositoryProvider.notifier)
|
||||||
.getAllContainersWithCount();
|
.getAllContainersWithCount();
|
||||||
|
final matchedContainers = containers.where(
|
||||||
|
(container) => containerIds.contains(container.id),
|
||||||
|
);
|
||||||
final proxyIds =
|
final proxyIds =
|
||||||
containers
|
matchedContainers
|
||||||
.where((container) => containerIds.contains(container.id))
|
|
||||||
.map((container) => container.metadata.proxyConnectionId?.encode())
|
.map((container) => container.metadata.proxyConnectionId?.encode())
|
||||||
.nonNulls
|
.nonNulls
|
||||||
.toSet()
|
.toSet()
|
||||||
.toList()
|
.toList()
|
||||||
..sort();
|
..sort();
|
||||||
|
|
||||||
if (proxyIds.isEmpty) return null;
|
if (proxyIds.isNotEmpty) {
|
||||||
return ProxyConnectionId.decode(proxyIds.first);
|
return (
|
||||||
|
direct: false,
|
||||||
|
proxyConnectionId: ProxyConnectionId.decode(proxyIds.first),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final matchedContainerList = matchedContainers.toList();
|
||||||
|
if (matchedContainerList.isNotEmpty &&
|
||||||
|
matchedContainerList.every(
|
||||||
|
(container) => container.metadata.bypassGlobalProxy,
|
||||||
|
)) {
|
||||||
|
return (direct: true, proxyConnectionId: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef _PendingProxyLoadError = ({
|
typedef _PendingProxyLoadError = ({
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ class ContainerMetadata with FastEquatable {
|
|||||||
@JsonKey(defaultValue: false)
|
@JsonKey(defaultValue: false)
|
||||||
final bool excludeFromIndex;
|
final bool excludeFromIndex;
|
||||||
|
|
||||||
|
@JsonKey(defaultValue: false)
|
||||||
|
final bool bypassGlobalProxy;
|
||||||
|
|
||||||
final List<Uri>? assignedSites;
|
final List<Uri>? assignedSites;
|
||||||
|
|
||||||
ContainerMetadata({
|
ContainerMetadata({
|
||||||
@@ -58,6 +61,7 @@ class ContainerMetadata with FastEquatable {
|
|||||||
required this.proxyConnectionId,
|
required this.proxyConnectionId,
|
||||||
required this.clearDataOnExit,
|
required this.clearDataOnExit,
|
||||||
required this.excludeFromIndex,
|
required this.excludeFromIndex,
|
||||||
|
required this.bypassGlobalProxy,
|
||||||
required this.assignedSites,
|
required this.assignedSites,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -67,6 +71,7 @@ class ContainerMetadata with FastEquatable {
|
|||||||
ProxyConnectionId? proxyConnectionId,
|
ProxyConnectionId? proxyConnectionId,
|
||||||
bool? clearDataOnExit,
|
bool? clearDataOnExit,
|
||||||
bool? excludeFromIndex,
|
bool? excludeFromIndex,
|
||||||
|
bool? bypassGlobalProxy,
|
||||||
List<Uri>? assignedSites,
|
List<Uri>? assignedSites,
|
||||||
}) : this(
|
}) : this(
|
||||||
iconData: iconData,
|
iconData: iconData,
|
||||||
@@ -74,6 +79,7 @@ class ContainerMetadata with FastEquatable {
|
|||||||
proxyConnectionId: proxyConnectionId,
|
proxyConnectionId: proxyConnectionId,
|
||||||
clearDataOnExit: clearDataOnExit ?? false,
|
clearDataOnExit: clearDataOnExit ?? false,
|
||||||
excludeFromIndex: excludeFromIndex ?? false,
|
excludeFromIndex: excludeFromIndex ?? false,
|
||||||
|
bypassGlobalProxy: bypassGlobalProxy ?? false,
|
||||||
assignedSites: assignedSites,
|
assignedSites: assignedSites,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -91,6 +97,7 @@ class ContainerMetadata with FastEquatable {
|
|||||||
proxyConnectionId,
|
proxyConnectionId,
|
||||||
clearDataOnExit,
|
clearDataOnExit,
|
||||||
excludeFromIndex,
|
excludeFromIndex,
|
||||||
|
bypassGlobalProxy,
|
||||||
assignedSites,
|
assignedSites,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ abstract class _$ContainerMetadataCWProxy {
|
|||||||
|
|
||||||
ContainerMetadata excludeFromIndex(bool excludeFromIndex);
|
ContainerMetadata excludeFromIndex(bool excludeFromIndex);
|
||||||
|
|
||||||
|
ContainerMetadata bypassGlobalProxy(bool bypassGlobalProxy);
|
||||||
|
|
||||||
ContainerMetadata assignedSites(List<Uri>? assignedSites);
|
ContainerMetadata assignedSites(List<Uri>? assignedSites);
|
||||||
|
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
@@ -32,6 +34,7 @@ abstract class _$ContainerMetadataCWProxy {
|
|||||||
ProxyConnectionId? proxyConnectionId,
|
ProxyConnectionId? proxyConnectionId,
|
||||||
bool clearDataOnExit,
|
bool clearDataOnExit,
|
||||||
bool excludeFromIndex,
|
bool excludeFromIndex,
|
||||||
|
bool bypassGlobalProxy,
|
||||||
List<Uri>? assignedSites,
|
List<Uri>? assignedSites,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -62,6 +65,10 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
|||||||
ContainerMetadata excludeFromIndex(bool excludeFromIndex) =>
|
ContainerMetadata excludeFromIndex(bool excludeFromIndex) =>
|
||||||
call(excludeFromIndex: excludeFromIndex);
|
call(excludeFromIndex: excludeFromIndex);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ContainerMetadata bypassGlobalProxy(bool bypassGlobalProxy) =>
|
||||||
|
call(bypassGlobalProxy: bypassGlobalProxy);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ContainerMetadata assignedSites(List<Uri>? assignedSites) =>
|
ContainerMetadata assignedSites(List<Uri>? assignedSites) =>
|
||||||
call(assignedSites: assignedSites);
|
call(assignedSites: assignedSites);
|
||||||
@@ -80,6 +87,7 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
|||||||
Object? proxyConnectionId = const $CopyWithPlaceholder(),
|
Object? proxyConnectionId = const $CopyWithPlaceholder(),
|
||||||
Object? clearDataOnExit = const $CopyWithPlaceholder(),
|
Object? clearDataOnExit = const $CopyWithPlaceholder(),
|
||||||
Object? excludeFromIndex = const $CopyWithPlaceholder(),
|
Object? excludeFromIndex = const $CopyWithPlaceholder(),
|
||||||
|
Object? bypassGlobalProxy = const $CopyWithPlaceholder(),
|
||||||
Object? assignedSites = const $CopyWithPlaceholder(),
|
Object? assignedSites = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return ContainerMetadata(
|
return ContainerMetadata(
|
||||||
@@ -107,6 +115,12 @@ class _$ContainerMetadataCWProxyImpl implements _$ContainerMetadataCWProxy {
|
|||||||
? _value.excludeFromIndex
|
? _value.excludeFromIndex
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: excludeFromIndex as bool,
|
: excludeFromIndex as bool,
|
||||||
|
bypassGlobalProxy:
|
||||||
|
bypassGlobalProxy == const $CopyWithPlaceholder() ||
|
||||||
|
bypassGlobalProxy == null
|
||||||
|
? _value.bypassGlobalProxy
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: bypassGlobalProxy as bool,
|
||||||
assignedSites: assignedSites == const $CopyWithPlaceholder()
|
assignedSites: assignedSites == const $CopyWithPlaceholder()
|
||||||
? _value.assignedSites
|
? _value.assignedSites
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
@@ -247,6 +261,7 @@ ContainerMetadata _$ContainerMetadataFromJson(Map<String, dynamic> json) =>
|
|||||||
),
|
),
|
||||||
clearDataOnExit: json['clearDataOnExit'] as bool? ?? false,
|
clearDataOnExit: json['clearDataOnExit'] as bool? ?? false,
|
||||||
excludeFromIndex: json['excludeFromIndex'] as bool? ?? false,
|
excludeFromIndex: json['excludeFromIndex'] as bool? ?? false,
|
||||||
|
bypassGlobalProxy: json['bypassGlobalProxy'] as bool? ?? false,
|
||||||
assignedSites: (json['assignedSites'] as List<dynamic>?)
|
assignedSites: (json['assignedSites'] as List<dynamic>?)
|
||||||
?.map((e) => Uri.parse(e as String))
|
?.map((e) => Uri.parse(e as String))
|
||||||
.toList(),
|
.toList(),
|
||||||
@@ -263,6 +278,7 @@ Map<String, dynamic> _$ContainerMetadataToJson(
|
|||||||
'proxyConnectionId': _proxyConnectionIdToJson(instance.proxyConnectionId),
|
'proxyConnectionId': _proxyConnectionIdToJson(instance.proxyConnectionId),
|
||||||
'clearDataOnExit': instance.clearDataOnExit,
|
'clearDataOnExit': instance.clearDataOnExit,
|
||||||
'excludeFromIndex': instance.excludeFromIndex,
|
'excludeFromIndex': instance.excludeFromIndex,
|
||||||
|
'bypassGlobalProxy': instance.bypassGlobalProxy,
|
||||||
'assignedSites': instance.assignedSites?.map((e) => e.toString()).toList(),
|
'assignedSites': instance.assignedSites?.map((e) => e.toString()).toList(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -195,8 +195,8 @@ Stream<List<SiteAssignment>> watchAllAssignedSites(Ref ref) {
|
|||||||
/// aliases for isolated contexts.
|
/// aliases for isolated contexts.
|
||||||
///
|
///
|
||||||
/// Returns a map from isolation context ID to the set of container IDs it
|
/// Returns a map from isolation context ID to the set of container IDs it
|
||||||
/// appears in. An isolation context needs a proxy alias if ANY of its
|
/// appears in. An isolation context needs an explicit routing alias if any
|
||||||
/// associated containers has a proxy connection assigned.
|
/// associated container has a proxy connection or bypasses global routing.
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
Stream<Map<String, Set<String>>> watchIsolatedContextContainerMap(Ref ref) {
|
Stream<Map<String, Set<String>>> watchIsolatedContextContainerMap(Ref ref) {
|
||||||
final db = ref.watch(tabDatabaseProvider);
|
final db = ref.watch(tabDatabaseProvider);
|
||||||
|
|||||||
@@ -1151,8 +1151,8 @@ String _$watchAllAssignedSitesHash() =>
|
|||||||
/// aliases for isolated contexts.
|
/// aliases for isolated contexts.
|
||||||
///
|
///
|
||||||
/// Returns a map from isolation context ID to the set of container IDs it
|
/// Returns a map from isolation context ID to the set of container IDs it
|
||||||
/// appears in. An isolation context needs a proxy alias if ANY of its
|
/// appears in. An isolation context needs an explicit routing alias if any
|
||||||
/// associated containers has a proxy connection assigned.
|
/// associated container has a proxy connection or bypasses global routing.
|
||||||
|
|
||||||
@ProviderFor(watchIsolatedContextContainerMap)
|
@ProviderFor(watchIsolatedContextContainerMap)
|
||||||
final watchIsolatedContextContainerMapProvider =
|
final watchIsolatedContextContainerMapProvider =
|
||||||
@@ -1163,8 +1163,8 @@ final watchIsolatedContextContainerMapProvider =
|
|||||||
/// aliases for isolated contexts.
|
/// aliases for isolated contexts.
|
||||||
///
|
///
|
||||||
/// Returns a map from isolation context ID to the set of container IDs it
|
/// Returns a map from isolation context ID to the set of container IDs it
|
||||||
/// appears in. An isolation context needs a proxy alias if ANY of its
|
/// appears in. An isolation context needs an explicit routing alias if any
|
||||||
/// associated containers has a proxy connection assigned.
|
/// associated container has a proxy connection or bypasses global routing.
|
||||||
|
|
||||||
final class WatchIsolatedContextContainerMapProvider
|
final class WatchIsolatedContextContainerMapProvider
|
||||||
extends
|
extends
|
||||||
@@ -1181,8 +1181,8 @@ final class WatchIsolatedContextContainerMapProvider
|
|||||||
/// aliases for isolated contexts.
|
/// aliases for isolated contexts.
|
||||||
///
|
///
|
||||||
/// Returns a map from isolation context ID to the set of container IDs it
|
/// Returns a map from isolation context ID to the set of container IDs it
|
||||||
/// appears in. An isolation context needs a proxy alias if ANY of its
|
/// appears in. An isolation context needs an explicit routing alias if any
|
||||||
/// associated containers has a proxy connection assigned.
|
/// associated container has a proxy connection or bypasses global routing.
|
||||||
WatchIsolatedContextContainerMapProvider._()
|
WatchIsolatedContextContainerMapProvider._()
|
||||||
: super(
|
: super(
|
||||||
from: null,
|
from: null,
|
||||||
|
|||||||
+28
@@ -99,6 +99,9 @@ class ContainerEditScreen extends HookConsumerWidget {
|
|||||||
final excludeFromIndex = useState(
|
final excludeFromIndex = useState(
|
||||||
initialContainer.metadata.excludeFromIndex,
|
initialContainer.metadata.excludeFromIndex,
|
||||||
);
|
);
|
||||||
|
final bypassGlobalProxy = useState(
|
||||||
|
initialContainer.metadata.bypassGlobalProxy,
|
||||||
|
);
|
||||||
final assignedSites = useState(initialContainer.metadata.assignedSites);
|
final assignedSites = useState(initialContainer.metadata.assignedSites);
|
||||||
final isPinned = useState(initialContainer.isPinned);
|
final isPinned = useState(initialContainer.isPinned);
|
||||||
|
|
||||||
@@ -122,6 +125,10 @@ class ContainerEditScreen extends HookConsumerWidget {
|
|||||||
clearDataOnExit:
|
clearDataOnExit:
|
||||||
clearDataOnExit.value && contextualIdentity.value != null,
|
clearDataOnExit.value && contextualIdentity.value != null,
|
||||||
excludeFromIndex: excludeFromIndex.value,
|
excludeFromIndex: excludeFromIndex.value,
|
||||||
|
bypassGlobalProxy:
|
||||||
|
contextualIdentity.value != null &&
|
||||||
|
proxyConnectionId.value == null &&
|
||||||
|
bypassGlobalProxy.value,
|
||||||
assignedSites: assignedSites.value,
|
assignedSites: assignedSites.value,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -242,6 +249,8 @@ class ContainerEditScreen extends HookConsumerWidget {
|
|||||||
final assignedSiteCount = assignedSites.value?.length ?? 0;
|
final assignedSiteCount = assignedSites.value?.length ?? 0;
|
||||||
final canPickProxy =
|
final canPickProxy =
|
||||||
_mode == _DialogMode.create || contextualIdentity.value != null;
|
_mode == _DialogMode.create || contextualIdentity.value != null;
|
||||||
|
final canBypassGlobalProxy =
|
||||||
|
contextualIdentity.value != null && proxyConnectionId.value == null;
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
canPop: container == comparison,
|
canPop: container == comparison,
|
||||||
@@ -404,6 +413,7 @@ class ContainerEditScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
if (!value) {
|
if (!value) {
|
||||||
proxyConnectionId.value = null;
|
proxyConnectionId.value = null;
|
||||||
|
bypassGlobalProxy.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!value && clearDataOnExit.value) {
|
if (!value && clearDataOnExit.value) {
|
||||||
@@ -473,14 +483,31 @@ class ContainerEditScreen extends HookConsumerWidget {
|
|||||||
proxyConnectionId.value = null;
|
proxyConnectionId.value = null;
|
||||||
if (createdTemporaryIdentity) {
|
if (createdTemporaryIdentity) {
|
||||||
contextualIdentity.value = null;
|
contextualIdentity.value = null;
|
||||||
|
bypassGlobalProxy.value = false;
|
||||||
}
|
}
|
||||||
case _ProxyPickerSelected(:final id):
|
case _ProxyPickerSelected(:final id):
|
||||||
proxyConnectionId.value = id;
|
proxyConnectionId.value = id;
|
||||||
|
bypassGlobalProxy.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
const Divider(height: 1, indent: 56),
|
const Divider(height: 1, indent: 56),
|
||||||
|
SwitchListTile.adaptive(
|
||||||
|
value:
|
||||||
|
canBypassGlobalProxy && bypassGlobalProxy.value,
|
||||||
|
title: const Text('Bypass Global Proxy'),
|
||||||
|
subtitle: const Text(
|
||||||
|
'Use the normal connection for this container when global routing is enabled',
|
||||||
|
),
|
||||||
|
secondary: const Icon(Icons.public),
|
||||||
|
onChanged: canBypassGlobalProxy
|
||||||
|
? (value) {
|
||||||
|
bypassGlobalProxy.value = value;
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
const Divider(height: 1, indent: 56),
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
value: clearDataOnExit.value,
|
value: clearDataOnExit.value,
|
||||||
title: const Text('Clear Data on Exit'),
|
title: const Text('Clear Data on Exit'),
|
||||||
@@ -661,6 +688,7 @@ class _ProxyConnectionPickerSheet extends StatelessWidget {
|
|||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: RadioGroup<ProxyConnectionId?>(
|
child: RadioGroup<ProxyConnectionId?>(
|
||||||
|
groupValue: selectedProxyConnectionId,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
Navigator.pop(
|
Navigator.pop(
|
||||||
context,
|
context,
|
||||||
|
|||||||
+7
@@ -266,6 +266,13 @@ class _ContainerCard extends HookConsumerWidget {
|
|||||||
isLoading: proxyOptionsLoading,
|
isLoading: proxyOptionsLoading,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (container.metadata.proxyConnectionId ==
|
||||||
|
null &&
|
||||||
|
container.metadata.bypassGlobalProxy)
|
||||||
|
const _ContainerInfoChip(
|
||||||
|
icon: Icons.public,
|
||||||
|
label: 'Direct',
|
||||||
|
),
|
||||||
if (container.metadata.clearDataOnExit)
|
if (container.metadata.clearDataOnExit)
|
||||||
const _ContainerInfoChip(
|
const _ContainerInfoChip(
|
||||||
icon: Icons.cleaning_services_outlined,
|
icon: Icons.cleaning_services_outlined,
|
||||||
|
|||||||
+7
@@ -322,6 +322,13 @@ class _SelectionContainerCard extends ConsumerWidget {
|
|||||||
isLoading: proxyOptionsLoading,
|
isLoading: proxyOptionsLoading,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (container.metadata.proxyConnectionId ==
|
||||||
|
null &&
|
||||||
|
container.metadata.bypassGlobalProxy)
|
||||||
|
const _SelectionInfoChip(
|
||||||
|
icon: Icons.public,
|
||||||
|
label: 'Direct',
|
||||||
|
),
|
||||||
if (container.metadata.clearDataOnExit)
|
if (container.metadata.clearDataOnExit)
|
||||||
const _SelectionInfoChip(
|
const _SelectionInfoChip(
|
||||||
icon: Icons.cleaning_services_outlined,
|
icon: Icons.cleaning_services_outlined,
|
||||||
|
|||||||
@@ -83,6 +83,16 @@ class ContainerProxyRepository extends _$ContainerProxyRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setContainerDirectConnection(
|
||||||
|
String contextId, {
|
||||||
|
required String scopeId,
|
||||||
|
}) {
|
||||||
|
return _runLocked(
|
||||||
|
body: () =>
|
||||||
|
_service.setContainerDirectConnection(contextId, scopeId: scopeId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> clearContainerProxy(String contextId) {
|
Future<void> clearContainerProxy(String contextId) {
|
||||||
return _runLocked(body: () => _service.clearContainerProxy(contextId));
|
return _runLocked(body: () => _service.clearContainerProxy(contextId));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class ContainerProxyRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$containerProxyRepositoryHash() =>
|
String _$containerProxyRepositoryHash() =>
|
||||||
r'08cd408a3ae96c859ed5c9d56d61cf7e6514415f';
|
r'ec3ec41b45b991623518f0aceb6ee7b21abb9112';
|
||||||
|
|
||||||
abstract class _$ContainerProxyRepository extends $Notifier<void> {
|
abstract class _$ContainerProxyRepository extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class _RegularTabsModeSection extends ConsumerWidget {
|
|||||||
value: ProxyRegularTabRoutingMode.all,
|
value: ProxyRegularTabRoutingMode.all,
|
||||||
title: Text('Global Routing'),
|
title: Text('Global Routing'),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
'Route every regular tab through the selected proxy.',
|
'Route regular tabs through the selected proxy unless a container bypasses it.',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import 'package:weblibre/extensions/uri.dart';
|
|||||||
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
|
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_mode.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_mode.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
|
||||||
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
|
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
|
||||||
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
||||||
@@ -35,6 +37,20 @@ import 'package:weblibre/features/user/domain/repositories/proxy_routing_setting
|
|||||||
|
|
||||||
part 'website_title.g.dart';
|
part 'website_title.g.dart';
|
||||||
|
|
||||||
|
sealed class _PageInfoProxyAssignment {
|
||||||
|
const _PageInfoProxyAssignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _PageInfoDirectAssignment extends _PageInfoProxyAssignment {
|
||||||
|
const _PageInfoDirectAssignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _PageInfoExplicitProxyAssignment extends _PageInfoProxyAssignment {
|
||||||
|
final ProxyConnectionId proxyConnectionId;
|
||||||
|
|
||||||
|
const _PageInfoExplicitProxyAssignment(this.proxyConnectionId);
|
||||||
|
}
|
||||||
|
|
||||||
@Riverpod()
|
@Riverpod()
|
||||||
class CompletePageInfo extends _$CompletePageInfo {
|
class CompletePageInfo extends _$CompletePageInfo {
|
||||||
@override
|
@override
|
||||||
@@ -91,12 +107,34 @@ Future<WebPageInfo> pageInfo(
|
|||||||
proxyRoutingSettingsWithDefaultsProvider,
|
proxyRoutingSettingsWithDefaultsProvider,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (containerData?.metadata.proxyConnectionId is TorProxyConnectionId ||
|
final isolatedAssignment = tabState.isolationContextId != null
|
||||||
(tabState.tabMode is! PrivateTabMode &&
|
? await _pageInfoAssignmentForIsolationContext(
|
||||||
proxyRoutingSettings.regularTabsMode ==
|
ref,
|
||||||
ProxyRegularTabRoutingMode.all &&
|
tabState.isolationContextId!,
|
||||||
proxyRoutingSettings.regularTabsProxyConnectionId
|
)
|
||||||
is TorProxyConnectionId) ||
|
: null;
|
||||||
|
|
||||||
|
final effectiveContainerProxyConnectionId = switch (isolatedAssignment) {
|
||||||
|
_PageInfoExplicitProxyAssignment(:final proxyConnectionId) =>
|
||||||
|
proxyConnectionId,
|
||||||
|
_PageInfoDirectAssignment() => null,
|
||||||
|
null => containerData?.metadata.proxyConnectionId,
|
||||||
|
};
|
||||||
|
final bypassesGlobalProxy =
|
||||||
|
isolatedAssignment is _PageInfoDirectAssignment ||
|
||||||
|
(isolatedAssignment == null &&
|
||||||
|
containerData?.metadata.bypassGlobalProxy == true);
|
||||||
|
final usesGlobalRegularTor =
|
||||||
|
tabState.tabMode is! PrivateTabMode &&
|
||||||
|
effectiveContainerProxyConnectionId == null &&
|
||||||
|
!bypassesGlobalProxy &&
|
||||||
|
proxyRoutingSettings.regularTabsMode ==
|
||||||
|
ProxyRegularTabRoutingMode.all &&
|
||||||
|
proxyRoutingSettings.regularTabsProxyConnectionId
|
||||||
|
is TorProxyConnectionId;
|
||||||
|
|
||||||
|
if (effectiveContainerProxyConnectionId is TorProxyConnectionId ||
|
||||||
|
usesGlobalRegularTor ||
|
||||||
(tabState.tabMode is PrivateTabMode &&
|
(tabState.tabMode is PrivateTabMode &&
|
||||||
proxyRoutingSettings.privateTabsProxyConnectionId
|
proxyRoutingSettings.privateTabsProxyConnectionId
|
||||||
is TorProxyConnectionId)) {
|
is TorProxyConnectionId)) {
|
||||||
@@ -125,6 +163,51 @@ Future<WebPageInfo> pageInfo(
|
|||||||
return result.value;
|
return result.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<_PageInfoProxyAssignment?> _pageInfoAssignmentForIsolationContext(
|
||||||
|
Ref ref,
|
||||||
|
String contextId,
|
||||||
|
) async {
|
||||||
|
final db = ref.read(tabDatabaseProvider);
|
||||||
|
final pairs = await db.tabDao.isolatedContextContainerPairs().get();
|
||||||
|
final containerIds = pairs
|
||||||
|
.where((pair) => pair.isolationContextId == contextId)
|
||||||
|
.map((pair) => pair.containerId)
|
||||||
|
.nonNulls
|
||||||
|
.toSet();
|
||||||
|
|
||||||
|
if (containerIds.isEmpty) return null;
|
||||||
|
|
||||||
|
final containers = await ref
|
||||||
|
.read(containerRepositoryProvider.notifier)
|
||||||
|
.getAllContainersWithCount();
|
||||||
|
final matchedContainers = containers.where(
|
||||||
|
(container) => containerIds.contains(container.id),
|
||||||
|
);
|
||||||
|
final proxyIds =
|
||||||
|
matchedContainers
|
||||||
|
.map((container) => container.metadata.proxyConnectionId?.encode())
|
||||||
|
.nonNulls
|
||||||
|
.toSet()
|
||||||
|
.toList()
|
||||||
|
..sort();
|
||||||
|
|
||||||
|
if (proxyIds.isNotEmpty) {
|
||||||
|
final proxyConnectionId = ProxyConnectionId.decode(proxyIds.first);
|
||||||
|
return proxyConnectionId != null
|
||||||
|
? _PageInfoExplicitProxyAssignment(proxyConnectionId)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
final matchedContainerList = matchedContainers.toList();
|
||||||
|
if (matchedContainerList.isNotEmpty &&
|
||||||
|
matchedContainerList.every(
|
||||||
|
(container) => container.metadata.bypassGlobalProxy,
|
||||||
|
)) {
|
||||||
|
return const _PageInfoDirectAssignment();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Riverpod()
|
@Riverpod()
|
||||||
AsyncValue<EquatableValue<Set<Uri>?>> websiteFeedProvider(
|
AsyncValue<EquatableValue<Set<Uri>?>> websiteFeedProvider(
|
||||||
Ref ref,
|
Ref ref,
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ final class PageInfoProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$pageInfoHash() => r'1dedc23d2420c950c7cec57500e99c5c221ed6e2';
|
String _$pageInfoHash() => r'680a8f0f0101c86946b4839d30d6f55307312cce';
|
||||||
|
|
||||||
final class PageInfoFamily extends $Family
|
final class PageInfoFamily extends $Family
|
||||||
with
|
with
|
||||||
|
|||||||
+170
-1
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter_tor/flutter_tor.dart';
|
import 'package:flutter_tor/flutter_tor.dart';
|
||||||
@@ -9,6 +10,8 @@ import 'package:weblibre/data/database/functions/lexo_rank_functions.dart';
|
|||||||
import 'package:weblibre/data/database/functions/url_functions.dart';
|
import 'package:weblibre/data/database/functions/url_functions.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/database/database.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/database/database.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_mode.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/data/entities/tab_source.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/site_assignment.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart';
|
||||||
@@ -82,12 +85,168 @@ void main() {
|
|||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test('bypassed containers sync explicit direct routing', () async {
|
||||||
|
final bypassedContainer = _container(
|
||||||
|
id: 'container-1',
|
||||||
|
contextId: 'context-a',
|
||||||
|
bypassGlobalProxy: true,
|
||||||
|
);
|
||||||
|
final db = TabDatabase(
|
||||||
|
NativeDatabase.memory(
|
||||||
|
setup: (database) {
|
||||||
|
registerLexorankFunctions(database);
|
||||||
|
registerUrlFunctions(database);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
final containerProxyRepository = _FakeContainerProxyRepository();
|
||||||
|
final containerRepository = _FakeContainerRepository([bypassedContainer]);
|
||||||
|
final container = ProviderContainer(
|
||||||
|
overrides: [
|
||||||
|
tabDatabaseProvider.overrideWith((ref) => db),
|
||||||
|
containerProxyRepositoryProvider.overrideWith(
|
||||||
|
() => containerProxyRepository,
|
||||||
|
),
|
||||||
|
containerRepositoryProvider.overrideWith(() => containerRepository),
|
||||||
|
torProxyServiceProvider.overrideWith(_FakeTorProxyService.new),
|
||||||
|
proxyRoutingSettingsWithDefaultsProvider.overrideWith(
|
||||||
|
(ref) => ProxyRoutingSettings.withDefaults(
|
||||||
|
regularTabsMode: ProxyRegularTabRoutingMode.all,
|
||||||
|
regularTabsProxyConnectionId: const TorProxyConnectionId(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
watchContainersWithCountProvider.overrideWith(
|
||||||
|
(ref) => Stream.value([bypassedContainer]),
|
||||||
|
),
|
||||||
|
watchAllAssignedSitesProvider.overrideWith(
|
||||||
|
(ref) => Stream.value(const <SiteAssignment>[]),
|
||||||
|
),
|
||||||
|
watchIsolatedContextContainerMapProvider.overrideWith(
|
||||||
|
(ref) => Stream.value(const <String, Set<String>>{}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
addTearDown(() async {
|
||||||
|
container.dispose();
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
final subscription = container.listen<void>(
|
||||||
|
proxySettingsReplicationProvider,
|
||||||
|
(previous, next) {},
|
||||||
|
fireImmediately: true,
|
||||||
|
);
|
||||||
|
addTearDown(subscription.close);
|
||||||
|
await pumpEventQueue();
|
||||||
|
|
||||||
|
expect(containerProxyRepository.setContainerDirectConnectionCalls, [
|
||||||
|
('context-a', 'context-a'),
|
||||||
|
]);
|
||||||
|
expect(containerProxyRepository.setContainerProxyCalls, [
|
||||||
|
('general', const TorProxyConnectionId().encode()),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'mixed bypassed and inherited isolated containers do not bypass globally',
|
||||||
|
() async {
|
||||||
|
final bypassedContainer = _container(
|
||||||
|
id: 'container-direct',
|
||||||
|
contextId: 'context-direct',
|
||||||
|
bypassGlobalProxy: true,
|
||||||
|
);
|
||||||
|
final inheritedContainer = _container(
|
||||||
|
id: 'container-inherit',
|
||||||
|
contextId: 'context-inherit',
|
||||||
|
);
|
||||||
|
final db = TabDatabase(
|
||||||
|
NativeDatabase.memory(
|
||||||
|
setup: (database) {
|
||||||
|
registerLexorankFunctions(database);
|
||||||
|
registerUrlFunctions(database);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await db.containerDao.addContainer(bypassedContainer);
|
||||||
|
await db.containerDao.addContainer(inheritedContainer);
|
||||||
|
await db.tabDao.insertTab(
|
||||||
|
'tab-direct',
|
||||||
|
source: TabSource.manual,
|
||||||
|
parentId: const Value(null),
|
||||||
|
containerId: const Value('container-direct'),
|
||||||
|
tabMode: Value(TabMode.isolated('isolation-a')),
|
||||||
|
);
|
||||||
|
await db.tabDao.insertTab(
|
||||||
|
'tab-inherit',
|
||||||
|
source: TabSource.manual,
|
||||||
|
parentId: const Value(null),
|
||||||
|
containerId: const Value('container-inherit'),
|
||||||
|
tabMode: Value(TabMode.isolated('isolation-a')),
|
||||||
|
);
|
||||||
|
final containerProxyRepository = _FakeContainerProxyRepository();
|
||||||
|
final containers = [bypassedContainer, inheritedContainer];
|
||||||
|
final containerRepository = _FakeContainerRepository(containers);
|
||||||
|
final container = ProviderContainer(
|
||||||
|
overrides: [
|
||||||
|
tabDatabaseProvider.overrideWith((ref) => db),
|
||||||
|
containerProxyRepositoryProvider.overrideWith(
|
||||||
|
() => containerProxyRepository,
|
||||||
|
),
|
||||||
|
containerRepositoryProvider.overrideWith(() => containerRepository),
|
||||||
|
torProxyServiceProvider.overrideWith(_FakeTorProxyService.new),
|
||||||
|
proxyRoutingSettingsWithDefaultsProvider.overrideWith(
|
||||||
|
(ref) => ProxyRoutingSettings.withDefaults(
|
||||||
|
regularTabsMode: ProxyRegularTabRoutingMode.all,
|
||||||
|
regularTabsProxyConnectionId: const TorProxyConnectionId(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
watchContainersWithCountProvider.overrideWith(
|
||||||
|
(ref) => Stream.value(containers),
|
||||||
|
),
|
||||||
|
watchAllAssignedSitesProvider.overrideWith(
|
||||||
|
(ref) => Stream.value(const <SiteAssignment>[]),
|
||||||
|
),
|
||||||
|
watchIsolatedContextContainerMapProvider.overrideWith(
|
||||||
|
(ref) => Stream.value(const {
|
||||||
|
'isolation-a': {'container-direct', 'container-inherit'},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
addTearDown(() async {
|
||||||
|
container.dispose();
|
||||||
|
await db.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
final subscription = container.listen<void>(
|
||||||
|
proxySettingsReplicationProvider,
|
||||||
|
(previous, next) {},
|
||||||
|
fireImmediately: true,
|
||||||
|
);
|
||||||
|
addTearDown(subscription.close);
|
||||||
|
await pumpEventQueue();
|
||||||
|
|
||||||
|
expect(containerProxyRepository.setContainerDirectConnectionCalls, [
|
||||||
|
('context-direct', 'context-direct'),
|
||||||
|
]);
|
||||||
|
expect(
|
||||||
|
containerProxyRepository.setContainerProxyCalls,
|
||||||
|
contains(('general', const TorProxyConnectionId().encode())),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
containerProxyRepository.setContainerDirectConnectionCalls,
|
||||||
|
isNot(contains(('isolation-a', 'context-direct'))),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContainerDataWithCount _container({
|
ContainerDataWithCount _container({
|
||||||
required String id,
|
required String id,
|
||||||
required String contextId,
|
required String contextId,
|
||||||
required ProxyConnectionId proxyConnectionId,
|
ProxyConnectionId? proxyConnectionId,
|
||||||
|
bool bypassGlobalProxy = false,
|
||||||
}) {
|
}) {
|
||||||
return ContainerDataWithCount(
|
return ContainerDataWithCount(
|
||||||
id: id,
|
id: id,
|
||||||
@@ -97,6 +256,7 @@ ContainerDataWithCount _container({
|
|||||||
metadata: ContainerMetadata.withDefaults(
|
metadata: ContainerMetadata.withDefaults(
|
||||||
contextualIdentity: contextId,
|
contextualIdentity: contextId,
|
||||||
proxyConnectionId: proxyConnectionId,
|
proxyConnectionId: proxyConnectionId,
|
||||||
|
bypassGlobalProxy: bypassGlobalProxy,
|
||||||
),
|
),
|
||||||
tabCount: 0,
|
tabCount: 0,
|
||||||
);
|
);
|
||||||
@@ -104,6 +264,7 @@ ContainerDataWithCount _container({
|
|||||||
|
|
||||||
class _FakeContainerProxyRepository extends ContainerProxyRepository {
|
class _FakeContainerProxyRepository extends ContainerProxyRepository {
|
||||||
final setContainerProxyCalls = <(String, String)>[];
|
final setContainerProxyCalls = <(String, String)>[];
|
||||||
|
final setContainerDirectConnectionCalls = <(String, String)>[];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setTorProxyPort(int? port) async {}
|
Future<void> setTorProxyPort(int? port) async {}
|
||||||
@@ -116,6 +277,14 @@ class _FakeContainerProxyRepository extends ContainerProxyRepository {
|
|||||||
setContainerProxyCalls.add((contextId, proxyId));
|
setContainerProxyCalls.add((contextId, proxyId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> setContainerDirectConnection(
|
||||||
|
String contextId, {
|
||||||
|
required String scopeId,
|
||||||
|
}) async {
|
||||||
|
setContainerDirectConnectionCalls.add((contextId, scopeId));
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> clearContainerProxy(String contextId) async {}
|
Future<void> clearContainerProxy(String contextId) async {}
|
||||||
|
|
||||||
|
|||||||
+3
@@ -9,6 +9,7 @@ void main() {
|
|||||||
final metadata = ContainerMetadata.withDefaults(
|
final metadata = ContainerMetadata.withDefaults(
|
||||||
proxyConnectionId: const SingboxProxyConnectionId('profile-1'),
|
proxyConnectionId: const SingboxProxyConnectionId('profile-1'),
|
||||||
clearDataOnExit: true,
|
clearDataOnExit: true,
|
||||||
|
bypassGlobalProxy: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
final json = metadata.toJson();
|
final json = metadata.toJson();
|
||||||
@@ -19,7 +20,9 @@ void main() {
|
|||||||
const SingboxProxyConnectionId('profile-1').encode(),
|
const SingboxProxyConnectionId('profile-1').encode(),
|
||||||
);
|
);
|
||||||
expect(json['clearDataOnExit'], isTrue);
|
expect(json['clearDataOnExit'], isTrue);
|
||||||
|
expect(json['bypassGlobalProxy'], isTrue);
|
||||||
expect(restored.proxyConnectionId, metadata.proxyConnectionId);
|
expect(restored.proxyConnectionId, metadata.proxyConnectionId);
|
||||||
|
expect(restored.bypassGlobalProxy, isTrue);
|
||||||
expect(restored.usesTorProxy, isFalse);
|
expect(restored.usesTorProxy, isFalse);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+10
@@ -44,6 +44,16 @@ class GeckoContainerProxyApiImpl : GeckoContainerProxyApi {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setContainerDirectConnection(contextId: String, scopeId: String) {
|
||||||
|
ContainerProxyFeature.scheduleRequest(
|
||||||
|
"setContainerDirectConnection",
|
||||||
|
JSONObject().apply {
|
||||||
|
put("contextId", contextId)
|
||||||
|
put("scopeId", scopeId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun clearContainerProxy(contextId: String) {
|
override fun clearContainerProxy(contextId: String) {
|
||||||
ContainerProxyFeature.scheduleRequest("clearContainerProxy", contextId)
|
ContainerProxyFeature.scheduleRequest("clearContainerProxy", contextId)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -8412,6 +8412,7 @@ interface GeckoContainerProxyApi {
|
|||||||
fun upsertProxy(proxy: GeckoProxySettings)
|
fun upsertProxy(proxy: GeckoProxySettings)
|
||||||
fun removeProxy(proxyId: String)
|
fun removeProxy(proxyId: String)
|
||||||
fun setContainerProxy(contextId: String, proxyId: String)
|
fun setContainerProxy(contextId: String, proxyId: String)
|
||||||
|
fun setContainerDirectConnection(contextId: String, scopeId: String)
|
||||||
fun clearContainerProxy(contextId: String)
|
fun clearContainerProxy(contextId: String)
|
||||||
fun removeContainerProxyRelation(contextId: String, proxyId: String)
|
fun removeContainerProxyRelation(contextId: String, proxyId: String)
|
||||||
fun setSiteAssignments(assignments: Map<String, String>)
|
fun setSiteAssignments(assignments: Map<String, String>)
|
||||||
@@ -8535,6 +8536,25 @@ interface GeckoContainerProxyApi {
|
|||||||
channel.setMessageHandler(null)
|
channel.setMessageHandler(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.setContainerDirectConnection$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val contextIdArg = args[0] as String
|
||||||
|
val scopeIdArg = args[1] as String
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
api.setContainerDirectConnection(contextIdArg, scopeIdArg)
|
||||||
|
listOf(null)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
GeckoPigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
run {
|
run {
|
||||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.clearContainerProxy$separatedMessageChannelSuffix", codec)
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.clearContainerProxy$separatedMessageChannelSuffix", codec)
|
||||||
if (api != null) {
|
if (api != null) {
|
||||||
|
|||||||
+10
@@ -14,6 +14,7 @@ interface Message {
|
|||||||
'upsertProxy' |
|
'upsertProxy' |
|
||||||
'removeProxy' |
|
'removeProxy' |
|
||||||
'setContainerProxy' |
|
'setContainerProxy' |
|
||||||
|
'setContainerDirectConnection' |
|
||||||
'clearContainerProxy' |
|
'clearContainerProxy' |
|
||||||
'removeContainerProxyRelation' |
|
'removeContainerProxyRelation' |
|
||||||
'healthcheck' |
|
'healthcheck' |
|
||||||
@@ -69,6 +70,15 @@ port.onMessage.addListener((raw: unknown): void => {
|
|||||||
store.setContainerProxyRelation(message.args.contextId, message.args.proxyId)
|
store.setContainerProxyRelation(message.args.contextId, message.args.proxyId)
|
||||||
console.log('set container relation ' + message.args.contextId + ' -> ' + message.args.proxyId)
|
console.log('set container relation ' + message.args.contextId + ' -> ' + message.args.proxyId)
|
||||||
break
|
break
|
||||||
|
case "setContainerDirectConnection":
|
||||||
|
if (typeof message.args === 'string') {
|
||||||
|
store.setContainerDirectRelation(message.args)
|
||||||
|
console.log('set container direct relation ' + message.args)
|
||||||
|
} else {
|
||||||
|
store.setContainerDirectRelation(message.args.contextId, message.args.scopeId)
|
||||||
|
console.log('set container direct relation ' + message.args.contextId + ' scoped to ' + message.args.scopeId)
|
||||||
|
}
|
||||||
|
break
|
||||||
case "clearContainerProxy":
|
case "clearContainerProxy":
|
||||||
store.clearContainerProxyRelation(message.args)
|
store.clearContainerProxyRelation(message.args)
|
||||||
console.log('cleared container relation ' + message.args)
|
console.log('cleared container relation ' + message.args)
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ interface WildcardAssignment {
|
|||||||
export class Store {
|
export class Store {
|
||||||
private proxies: ProxyDao[] = []
|
private proxies: ProxyDao[] = []
|
||||||
private relations: { [key: string]: string[] } = {}
|
private relations: { [key: string]: string[] } = {}
|
||||||
|
private directRelationScopes: { [key: string]: string } = {}
|
||||||
|
|
||||||
private siteAssignments: Map<string, string> = new Map<string, string>()
|
private siteAssignments: Map<string, string> = new Map<string, string>()
|
||||||
private wildcardAssignments: WildcardAssignment[] = []
|
private wildcardAssignments: WildcardAssignment[] = []
|
||||||
@@ -147,15 +148,35 @@ export class Store {
|
|||||||
return this.lookupAssignment(uri) !== undefined
|
return this.lookupAssignment(uri) !== undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasRelation(contextId: string): boolean {
|
||||||
|
return Object.prototype.hasOwnProperty.call(this.relations, contextId)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the effective proxy relation for a context ID, mirroring the
|
* Returns the effective proxy relation for a context ID, preserving the
|
||||||
* fallback logic in getProxiesForContainer: explicit relation first,
|
* difference between "no explicit relation" (undefined, may inherit) and
|
||||||
* then 'general' for non-private contexts, then empty.
|
* "explicit direct connection" ([]).
|
||||||
*/
|
*/
|
||||||
private getEffectiveRelation(contextId: string): string[] {
|
private getEffectiveRelation(contextId: string): string[] | undefined {
|
||||||
return this.relations[contextId]
|
if (this.hasRelation(contextId)) {
|
||||||
?? ((contextId !== 'private') ? this.relations['general'] : undefined)
|
return this.relations[contextId]
|
||||||
?? [];
|
}
|
||||||
|
if (contextId !== 'private' && this.hasRelation('general')) {
|
||||||
|
return this.relations['general']
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
private getEffectiveDirectScope(contextId: string): string | undefined {
|
||||||
|
if (this.hasRelation(contextId) && this.relations[contextId].length === 0) {
|
||||||
|
return this.directRelationScopes[contextId] ?? contextId
|
||||||
|
}
|
||||||
|
if (contextId !== 'private' &&
|
||||||
|
this.hasRelation('general') &&
|
||||||
|
this.relations['general'].length === 0) {
|
||||||
|
return this.directRelationScopes['general'] ?? 'general'
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
isSiteOriginInSameContext(uri: URL, contextId: string): boolean {
|
isSiteOriginInSameContext(uri: URL, contextId: string): boolean {
|
||||||
@@ -169,10 +190,19 @@ export class Store {
|
|||||||
const assignedRelation = this.getEffectiveRelation(assignedContextId);
|
const assignedRelation = this.getEffectiveRelation(assignedContextId);
|
||||||
const currentRelation = this.getEffectiveRelation(contextId);
|
const currentRelation = this.getEffectiveRelation(contextId);
|
||||||
|
|
||||||
// Only treat as equivalent if both have actual proxy relations —
|
const assignedDirectScope = this.getEffectiveDirectScope(assignedContextId)
|
||||||
// empty relations mean no proxy, and different non-proxied contexts
|
const currentDirectScope = this.getEffectiveDirectScope(contextId)
|
||||||
// should not be considered equivalent.
|
if (assignedDirectScope !== undefined || currentDirectScope !== undefined) {
|
||||||
if (assignedRelation.length > 0 &&
|
return assignedDirectScope !== undefined &&
|
||||||
|
assignedDirectScope === currentDirectScope
|
||||||
|
}
|
||||||
|
|
||||||
|
// Treat proxy-routed contexts as equivalent only when both resolve to an
|
||||||
|
// explicit non-direct relation. Direct relations are scoped above so two
|
||||||
|
// unrelated bypassed containers do not collapse into the same context.
|
||||||
|
if (assignedRelation !== undefined &&
|
||||||
|
currentRelation !== undefined &&
|
||||||
|
assignedRelation.length > 0 &&
|
||||||
assignedRelation.length === currentRelation.length &&
|
assignedRelation.length === currentRelation.length &&
|
||||||
assignedRelation.every((id, i) => id === currentRelation[i])) {
|
assignedRelation.every((id, i) => id === currentRelation[i])) {
|
||||||
return true;
|
return true;
|
||||||
@@ -222,10 +252,17 @@ export class Store {
|
|||||||
|
|
||||||
setContainerProxyRelation(cookieStoreId: string, proxyId: string): void {
|
setContainerProxyRelation(cookieStoreId: string, proxyId: string): void {
|
||||||
this.relations[cookieStoreId] = [proxyId]
|
this.relations[cookieStoreId] = [proxyId]
|
||||||
|
delete this.directRelationScopes[cookieStoreId]
|
||||||
|
}
|
||||||
|
|
||||||
|
setContainerDirectRelation(cookieStoreId: string, scopeId: string = cookieStoreId): void {
|
||||||
|
this.relations[cookieStoreId] = []
|
||||||
|
this.directRelationScopes[cookieStoreId] = scopeId
|
||||||
}
|
}
|
||||||
|
|
||||||
clearContainerProxyRelation(cookieStoreId: string): void {
|
clearContainerProxyRelation(cookieStoreId: string): void {
|
||||||
delete this.relations[cookieStoreId]
|
delete this.relations[cookieStoreId]
|
||||||
|
delete this.directRelationScopes[cookieStoreId]
|
||||||
}
|
}
|
||||||
|
|
||||||
removeContainerProxyRelation(cookieStoreId: string, proxyId: string): void {
|
removeContainerProxyRelation(cookieStoreId: string, proxyId: string): void {
|
||||||
|
|||||||
+30
@@ -172,6 +172,17 @@ describe('Store', () => {
|
|||||||
|
|
||||||
expect(result).to.be.deep.equal([])
|
expect(result).to.be.deep.equal([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should let an explicit direct relation bypass the general relation', () => {
|
||||||
|
const isolatedStore = new Store()
|
||||||
|
isolatedStore.putProxy(someProxyWith('global-proxy'))
|
||||||
|
isolatedStore.setContainerProxyRelation('general', 'global-proxy')
|
||||||
|
isolatedStore.setContainerDirectRelation('container1')
|
||||||
|
|
||||||
|
const result = isolatedStore.getProxiesForContainer('container1')
|
||||||
|
|
||||||
|
expect(result).to.be.null
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('wildcard site assignments', function () {
|
describe('wildcard site assignments', function () {
|
||||||
@@ -247,5 +258,24 @@ describe('Store', () => {
|
|||||||
const result = store.isSiteOriginInSameContext(new URL('https://blocked.example/another'), 'iso1_blocked')
|
const result = store.isSiteOriginInSameContext(new URL('https://blocked.example/another'), 'iso1_blocked')
|
||||||
expect(result).to.be.equal(false)
|
expect(result).to.be.equal(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should allow direct aliases scoped to the assigned container', async () => {
|
||||||
|
store.setSiteAssignments(new Map([['https://direct.example/path', 'container_direct']]))
|
||||||
|
await store.setContainerProxyRelation('general', 'proxy_global')
|
||||||
|
await store.setContainerDirectRelation('container_direct')
|
||||||
|
await store.setContainerDirectRelation('iso1_direct', 'container_direct')
|
||||||
|
|
||||||
|
const result = store.isSiteOriginInSameContext(new URL('https://direct.example/another'), 'iso1_direct')
|
||||||
|
expect(result).to.be.equal(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not allow unrelated direct containers as equivalent', async () => {
|
||||||
|
store.setSiteAssignments(new Map([['https://direct.example/path', 'container_direct']]))
|
||||||
|
await store.setContainerDirectRelation('container_direct')
|
||||||
|
await store.setContainerDirectRelation('other_direct')
|
||||||
|
|
||||||
|
const result = store.isSiteOriginInSameContext(new URL('https://direct.example/another'), 'other_direct')
|
||||||
|
expect(result).to.be.equal(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -36,6 +36,13 @@ class GeckoContainerProxyService {
|
|||||||
return _apiInstance.setContainerProxy(contextId, proxyId);
|
return _apiInstance.setContainerProxy(contextId, proxyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setContainerDirectConnection(
|
||||||
|
String contextId, {
|
||||||
|
required String scopeId,
|
||||||
|
}) {
|
||||||
|
return _apiInstance.setContainerDirectConnection(contextId, scopeId);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> clearContainerProxy(String contextId) {
|
Future<void> clearContainerProxy(String contextId) {
|
||||||
return _apiInstance.clearContainerProxy(contextId);
|
return _apiInstance.clearContainerProxy(contextId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8799,6 +8799,29 @@ class GeckoContainerProxyApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> setContainerDirectConnection(
|
||||||
|
String contextId,
|
||||||
|
String scopeId,
|
||||||
|
) async {
|
||||||
|
final pigeonVar_channelName =
|
||||||
|
'dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.setContainerDirectConnection$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(
|
||||||
|
<Object?>[contextId, scopeId],
|
||||||
|
);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
|
||||||
|
_extractReplyValueOrThrow(
|
||||||
|
pigeonVar_replyList,
|
||||||
|
pigeonVar_channelName,
|
||||||
|
isNullValid: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> clearContainerProxy(String contextId) async {
|
Future<void> clearContainerProxy(String contextId) async {
|
||||||
final pigeonVar_channelName =
|
final pigeonVar_channelName =
|
||||||
'dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.clearContainerProxy$pigeonVar_messageChannelSuffix';
|
'dev.flutter.pigeon.flutter_mozilla_components.GeckoContainerProxyApi.clearContainerProxy$pigeonVar_messageChannelSuffix';
|
||||||
|
|||||||
@@ -1888,6 +1888,7 @@ abstract class GeckoContainerProxyApi {
|
|||||||
void upsertProxy(GeckoProxySettings proxy);
|
void upsertProxy(GeckoProxySettings proxy);
|
||||||
void removeProxy(String proxyId);
|
void removeProxy(String proxyId);
|
||||||
void setContainerProxy(String contextId, String proxyId);
|
void setContainerProxy(String contextId, String proxyId);
|
||||||
|
void setContainerDirectConnection(String contextId, String scopeId);
|
||||||
void clearContainerProxy(String contextId);
|
void clearContainerProxy(String contextId);
|
||||||
void removeContainerProxyRelation(String contextId, String proxyId);
|
void removeContainerProxyRelation(String contextId, String proxyId);
|
||||||
void setSiteAssignments(Map<String, String> assignments);
|
void setSiteAssignments(Map<String, String> assignments);
|
||||||
|
|||||||
Reference in New Issue
Block a user