fix proxy auto start issue

This commit is contained in:
Fabian Freund
2026-05-23 19:20:53 +02:00
parent 696ea84412
commit e34acf2777
20 changed files with 236 additions and 164 deletions
@@ -1186,7 +1186,7 @@ final class GroupedTabListItemsProvider
}
String _$groupedTabListItemsHash() =>
r'15106bfa42146270245c7178928a834237042c1c';
r'dbb510f22c00858fcbadfb94547d46841221fca4';
/// Grouped flat-list rendering for the list and grid views.
///
@@ -26,9 +26,7 @@ import 'package:weblibre/features/geckoview/features/tabs/data/models/container_
import 'package:weblibre/features/geckoview/features/tabs/data/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart';
import 'package:weblibre/features/proxy/data/proxy_connection.dart';
import 'package:weblibre/features/proxy/domain/repositories/container_proxy.dart';
import 'package:weblibre/features/proxy/domain/repositories/singbox_proxy_runtime.dart';
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
import 'package:weblibre/features/user/data/models/proxy_routing_settings.dart';
import 'package:weblibre/features/user/domain/repositories/proxy_routing_settings.dart';
@@ -43,25 +41,6 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
final _recomputeLock = Lock();
var _recomputeDirty = false;
Future<void> _ensureProxyConnectionAvailable(
ProxyConnectionId proxyConnectionId,
) async {
if (proxyConnectionId is! SingboxProxyConnectionId) return;
try {
await ref
.read(singboxProxyRuntimeRepositoryProvider.notifier)
.ensureProxyConnectionAvailable(proxyConnectionId);
} catch (error, stackTrace) {
logger.e(
'Failed to make proxy connection $proxyConnectionId available; '
'assigned traffic will remain blocked',
error: error,
stackTrace: stackTrace,
);
}
}
Future<void> _queueIsolatedProxyAliasesRecompute() async {
_recomputeDirty = true;
if (_recomputeLock.inLock) return;
@@ -187,7 +166,6 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
await containerProxy.clearContainerProxy('private');
} else {
await containerProxy.setContainerProxy('private', next.encode());
await _ensureProxyConnectionAvailable(next);
}
},
onError: (error, stackTrace) {
@@ -220,7 +198,6 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
'general',
proxyConnectionId.encode(),
);
await _ensureProxyConnectionAvailable(proxyConnectionId);
}
}
},
@@ -315,10 +292,6 @@ class ProxySettingsReplication extends _$ProxySettingsReplication {
}
try {
if (value != null) {
final proxyConnectionId = ProxyConnectionId.decode(value);
if (proxyConnectionId != null) {
await _ensureProxyConnectionAvailable(proxyConnectionId);
}
await repo.setContainerProxy(key, value);
} else {
await repo.clearContainerProxy(key);
@@ -58,14 +58,19 @@ import 'package:weblibre/features/geckoview/features/find_in_page/presentation/c
import 'package:weblibre/features/geckoview/features/find_in_page/presentation/widgets/find_in_page.dart';
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
import 'package:weblibre/features/geckoview/features/search/domain/providers/search_autofocus.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/proxy/data/proxy_connection.dart';
import 'package:weblibre/features/proxy/presentation/controllers/ensure_proxy_started.dart';
import 'package:weblibre/features/small_web/presentation/controllers/small_web_mode_controller.dart';
import 'package:weblibre/features/small_web/presentation/widgets/small_web_browser_overlay.dart';
import 'package:weblibre/features/sync/domain/repositories/sync.dart';
import 'package:weblibre/features/user/data/models/general_settings.dart';
import 'package:weblibre/features/user/data/models/proxy_routing_settings.dart';
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
import 'package:weblibre/features/user/domain/repositories/proxy_routing_settings.dart';
import 'package:weblibre/utils/move_to_background.dart';
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
@@ -230,6 +235,86 @@ class _TabBar extends HookConsumerWidget {
}
}
Future<ProxyConnectionId?> _proxyConnectionIdForLoadError(
WidgetRef ref, {
required String tabId,
required String? contextId,
}) async {
final routing = ref.read(proxyRoutingSettingsWithDefaultsProvider);
final tabState = ref.read(tabStateProvider(tabId));
if (contextId == 'private' || tabState?.tabMode is PrivateTabMode) {
return routing.privateTabsProxyConnectionId;
}
if (contextId != null && contextId.isNotEmpty && contextId != 'general') {
final isolatedContextProxyConnectionId =
await _isolatedContextProxyConnectionId(ref, contextId);
if (isolatedContextProxyConnectionId != null) {
return isolatedContextProxyConnectionId;
}
final contextContainer = await ref
.read(containerRepositoryProvider.notifier)
.getContainerByContextualIdentity(contextId);
final contextProxyConnectionId =
contextContainer?.metadata.proxyConnectionId;
if (contextProxyConnectionId != null) {
return contextProxyConnectionId;
}
}
final tabContainer = await ref
.read(tabDataRepositoryProvider.notifier)
.getTabContainerData(tabId);
final tabProxyConnectionId = tabContainer?.metadata.proxyConnectionId;
if (tabProxyConnectionId != null) {
return tabProxyConnectionId;
}
if (routing.regularTabsMode == ProxyRegularTabRoutingMode.all) {
return routing.regularTabsProxyConnectionId;
}
return null;
}
Future<ProxyConnectionId?> _isolatedContextProxyConnectionId(
WidgetRef 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 proxyIds =
containers
.where((container) => containerIds.contains(container.id))
.map((container) => container.metadata.proxyConnectionId?.encode())
.nonNulls
.toSet()
.toList()
..sort();
if (proxyIds.isEmpty) return null;
return ProxyConnectionId.decode(proxyIds.first);
}
typedef _PendingProxyLoadError = ({
String? contextId,
String errorType,
String? url,
});
class BrowserScreen extends HookConsumerWidget {
const BrowserScreen({super.key});
@@ -238,52 +323,89 @@ class BrowserScreen extends HookConsumerWidget {
final eventService = ref.watch(eventServiceProvider);
final viewportService = ref.watch(viewportServiceProvider);
final activeProxyPromptKeys = useRef(<String>{});
final pendingProxyLoadErrors = useRef(<String, _PendingProxyLoadError>{});
final selectedTabIdForProxyPrompt = ref.watch(selectedTabProvider);
Future<void> handleProxyLoadError({
required String tabId,
required String? contextId,
required String? url,
required String errorType,
}) async {
final promptKey = '$tabId:${url ?? errorType}';
if (!activeProxyPromptKeys.value.add(promptKey)) return;
try {
final proxyConnectionId = await _proxyConnectionIdForLoadError(
ref,
tabId: tabId,
contextId: contextId,
);
if (!context.mounted || proxyConnectionId == null) return;
final isProxyStarted = await ensureProxyStartedForConnection(
context,
ref,
proxyConnectionId,
);
if (isProxyStarted &&
context.mounted &&
ref.read(selectedTabProvider) == tabId) {
await ref.read(tabSessionProvider(tabId: tabId).notifier).reload();
}
} catch (error, stackTrace) {
logger.e(
'Failed to handle proxy load error',
error: error,
stackTrace: stackTrace,
);
} finally {
activeProxyPromptKeys.value.remove(promptKey);
}
}
useEffect(() {
final tabId = selectedTabIdForProxyPrompt;
if (tabId == null) return null;
final pending = pendingProxyLoadErrors.value.remove(tabId);
if (pending == null) return null;
unawaited(
handleProxyLoadError(
tabId: tabId,
contextId: pending.contextId,
url: pending.url,
errorType: pending.errorType,
),
);
return null;
}, [selectedTabIdForProxyPrompt]);
useOnStreamChange(
eventService.proxyLoadErrorEvents,
onData: (event) async {
final selectedTabId = ref.read(selectedTabProvider);
final tabId = event.tabId ?? selectedTabId;
if (tabId == null || tabId != selectedTabId) return;
if (tabId == null) return;
final promptKey = '$tabId:${event.url ?? event.errorType}';
if (!activeProxyPromptKeys.value.add(promptKey)) return;
try {
final contextId = event.contextId;
final contextContainer = contextId != null && contextId.isNotEmpty
? await ref
.read(containerRepositoryProvider.notifier)
.getContainerByContextualIdentity(contextId)
: null;
final container =
contextContainer ??
await ref
.read(tabDataRepositoryProvider.notifier)
.getTabContainerData(tabId);
if (!context.mounted || container == null) return;
final isProxyStarted = await ensureProxyStartedForContainer(
context,
ref,
container,
if (tabId != selectedTabId) {
pendingProxyLoadErrors.value[tabId] = (
contextId: event.contextId,
errorType: event.errorType,
url: event.url,
);
if (isProxyStarted &&
context.mounted &&
ref.read(selectedTabProvider) == tabId) {
await ref.read(tabSessionProvider(tabId: tabId).notifier).reload();
}
} catch (error, stackTrace) {
logger.e(
'Failed to handle proxy load error',
error: error,
stackTrace: stackTrace,
);
} finally {
activeProxyPromptKeys.value.remove(promptKey);
return;
}
await handleProxyLoadError(
tabId: tabId,
contextId: event.contextId,
url: event.url,
errorType: event.errorType,
);
},
);
@@ -272,7 +272,7 @@ final class InstallCurrentWebAppProvider
}
String _$installCurrentWebAppHash() =>
r'0e0013fac7b70441983f7af1e6db3c6470c23900';
r'6eeed094b9ea9ade946dab77c691f271c92f89e1';
/// Installs the current tab as a PWA, embedding profile and container context
/// in the shortcut intent so the PWA reopens with the same isolation.
@@ -460,7 +460,7 @@ final class InstallBasicShortcutProvider
}
String _$installBasicShortcutHash() =>
r'fcb5bec79f375b32a7a0953ad22859a0db222167';
r'7155266e635d0016e4d3ed407bc6d8af61434b0f';
/// Creates a basic bookmark shortcut on the home screen for the current tab.