Auto-refresh the captcha WebView when VK returns a limit-reached page, so the user no longer has to manually dismiss and re-trigger a connect cycle to get a working captcha.
How it works
When the WebView falls back for manual captcha solving, two independent detectors watch the loaded page:
- DOM heuristic — 2.5s after
DOMContentLoaded, inspectsdocument.body.innerTextfor limit markers (limit.*reached,лимит.*исчерп,try again later, etc.) and checks for interactive captcha selectors. Catches the case where the initial load already shows the limit screen. - API ERROR_LIMIT hook —
fetch/XHRwrappers in the WebView watch forcaptchaNotRobot.checkresponses containingstatus=ERROR_LIMIT. Catches the case where VK rate-limits after the user clicks the checkbox — the DOM heuristic has already firedstate:readyby then and would miss the dynamic switch.
Either trigger fires onCaptchaLimitDetected() on TunnelManager, which:
- Shows a
ProgressViewoverlay in the sheet (VK временно не отдаёт капчу — Ищем рабочую — попытка N из M). - Starts a 10s Timer that asks the extension for a fresh captcha URL (up to 6 attempts = 60s total).
- Updates flow back through
captchaImageURL→ SwiftUI rebind →CaptchaWKWebView.updateUIView→ WKWebView reload. JS fires again; if the new page is interactive,state:readystops the timer and hides the overlay. - Handles foreground resumption: iOS suspends
Timer.scheduledTimerwhile the app is backgrounded, so the foreground observer re-triggersonCaptchaLimitDetected()if the overlay was still visible on return — otherwise the progress spinner looked stuck after sleep/wake.
Logs from the WebView coordinator are now tunneled through the extension into vpn.log (via a new logFromCaptchaView hop on TunnelManager), so post-mortem log dumps include the full state machine trace.
Verified on device (iPhoneSE3, Wi-Fi)
vpn.wifi.4.log— 4 refresh attempts needed before VK returned a working captcha.vpn.wifi.5.log— API ERROR_LIMIT hook fired immediately, 1st attempt returned a working captcha. ~3s total recovery.vpn.wifi.6.log— overnight sleep (45m) → wake → auto-refresh machinery idle (not needed), existing reconnect path produced a fresh working URL within seconds of app foreground.
Build number 24 — skipping 23, which was consumed by the reverted APNs-through-VPN build. Apple doesn't allow reusing TestFlight build numbers.