From e87c4539b35b13102bc601b642b43bd242bf746e Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Wed, 18 Feb 2026 09:11:37 +0100 Subject: [PATCH 1/8] Add proxy support in Rust lib and WryWebViewPanel --- .../github/kdroidfilter/webview/wry/Proxy.kt | 22 +++++++++++ .../webview/wry/WryWebViewPanel.kt | 19 +++++---- wrywebview/src/main/rust/lib.rs | 39 ++++++++++++++++--- 3 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt diff --git a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt new file mode 100644 index 0000000..2321e2b --- /dev/null +++ b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt @@ -0,0 +1,22 @@ +package io.github.kdroidfilter.webview.wry + +sealed class ProxyConfig { + abstract val host: String + abstract val port: Int + + data class Http( + override val host: String, + override val port: Int + ) : ProxyConfig() { + override fun toProxy(): Proxy.Http = Proxy.Http(Address(host, port.toUShort())) + } + + data class Socks5( + override val host: String, + override val port: Int + ) : ProxyConfig() { + override fun toProxy(): Proxy.Socks5 = Proxy.Socks5(Address(host, port.toUShort())) + } + + internal abstract fun toProxy(): Proxy +} diff --git a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt index 07a7e58..eeb73f1 100644 --- a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt +++ b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt @@ -9,12 +9,12 @@ import javax.swing.JPanel import javax.swing.SwingUtilities import javax.swing.Timer import kotlin.concurrent.thread -import kotlin.properties.Delegates class WryWebViewPanel( initialUrl: String, customUserAgent: String? = null, + proxyConfig: ProxyConfig? = null, private val bridgeLogger: (String) -> Unit = { System.err.println(it) } ) : JPanel() { private val host = SkikoInterop.createHost() @@ -23,6 +23,7 @@ class WryWebViewPanel( private var parentIsWindow: Boolean = false private var pendingUrl: String = initialUrl private val customUserAgent: String? = customUserAgent?.trim()?.takeIf { it.isNotEmpty() } + private val proxy: Proxy? = proxyConfig?.toProxy() private var pendingUrlWithHeaders: String? = null private var pendingHeaders: Map = emptyMap() private var pendingHtml: String? = null @@ -348,7 +349,7 @@ class WryWebViewPanel( return try { webviewId = if (userAgent == null) { - NativeBindings.createWebview(handleSnapshot, width, height, initialUrl, handler) + NativeBindings.createWebview(handleSnapshot, width, height, initialUrl, handler, proxy) } else { NativeBindings.createWebviewWithUserAgent( handleSnapshot, @@ -356,7 +357,8 @@ class WryWebViewPanel( height, initialUrl, userAgent, - handler + handler, + proxy ) } updateBounds() @@ -394,7 +396,7 @@ class WryWebViewPanel( thread(name = "wry-webview-create", isDaemon = true) { val createdId = try { if (userAgent == null) { - NativeBindings.createWebview(handleSnapshot, width, height, initialUrl, handler) + NativeBindings.createWebview(handleSnapshot, width, height, initialUrl, handler, proxy) } else { NativeBindings.createWebviewWithUserAgent( handleSnapshot, @@ -402,7 +404,8 @@ class WryWebViewPanel( height, initialUrl, userAgent, - handler + handler, + proxy ) } } catch (e: RuntimeException) { @@ -702,8 +705,8 @@ class WryWebViewPanel( private object NativeBindings { - fun createWebview(parentHandle: ULong, width: Int, height: Int, url: String, handler: NavigationHandler): ULong { - return io.github.kdroidfilter.webview.wry.createWebview(parentHandle, width, height, url, handler) + fun createWebview(parentHandle: ULong, width: Int, height: Int, url: String, handler: NavigationHandler, proxy: Proxy?): ULong { + return io.github.kdroidfilter.webview.wry.createWebview(parentHandle, width, height, url, handler, proxy) } fun createWebviewWithUserAgent( @@ -713,6 +716,7 @@ private object NativeBindings { url: String, userAgent: String, handler: NavigationHandler, + proxy: Proxy? ): ULong { return io.github.kdroidfilter.webview.wry.createWebviewWithUserAgent( parentHandle, @@ -721,6 +725,7 @@ private object NativeBindings { url, userAgent, handler, + proxy ) } diff --git a/wrywebview/src/main/rust/lib.rs b/wrywebview/src/main/rust/lib.rs index 6653810..6dbc7f7 100644 --- a/wrywebview/src/main/rust/lib.rs +++ b/wrywebview/src/main/rust/lib.rs @@ -16,8 +16,9 @@ use wry::cookie::time::OffsetDateTime; use wry::cookie::{Cookie, Expiration, SameSite}; use wry::http::header::HeaderName; use wry::http::{HeaderMap, HeaderValue}; -use wry::WebViewBuilder; - +use wry::{ProxyConfig, ProxyEndpoint, WebViewBuilder}; +use crate::Proxy::Http; +use crate::Proxy::Socks5; pub use error::WebViewError; use handle::{make_bounds, raw_window_handle_from, RawWindow}; @@ -68,6 +69,18 @@ pub struct WebViewCookie { pub is_http_only: Option, } +#[derive(uniffi::Enum)] +pub enum Proxy { + Http(Address), + Socks5(Address), +} + +#[derive(Debug, Clone, uniffi::Record)] +pub struct Address { + pub host: String, + pub port: u16 +} + fn header_map_from(headers: Vec) -> Result { let mut map = HeaderMap::new(); for header in headers { @@ -146,6 +159,13 @@ fn cookie_from_record(cookie: WebViewCookie) -> Result, WebViewE Ok(builder.build()) } +fn proxy_from_record(proxy: Proxy) -> ProxyConfig { + match proxy { + Http(addr) => ProxyConfig::Http(ProxyEndpoint { host: addr.host, port: addr.port.to_string() }), + Socks5(addr) => ProxyConfig::Socks5(ProxyEndpoint { host: (addr.host), port: (addr.port.to_string()) }) + } +} + use std::sync::atomic::AtomicBool; static LOG_ENABLED: AtomicBool = AtomicBool::new(false); @@ -216,6 +236,7 @@ fn create_webview_inner( url: String, user_agent: Option, nav_handler: Option>, + proxy_config: Option, ) -> Result { let user_agent = user_agent.and_then(|ua| { @@ -252,6 +273,10 @@ fn create_webview_inner( builder = builder.with_user_agent(ua); } + if let Some(proxy) = proxy_config { + builder = builder.with_proxy_config(proxy) + } + let webview = builder .with_navigation_handler(move |new_url| { if let Some(handler) = &nav_handler { @@ -358,12 +383,13 @@ pub fn create_webview( width: i32, height: i32, url: String, - nav_handler: Option> + nav_handler: Option>, + proxy: Option ) -> Result { #[cfg(target_os = "linux")] { return run_on_gtk_thread(move || { - create_webview_inner(parent_handle, width, height, url, None, nav_handler) + create_webview_inner(parent_handle, width, height, url, None, nav_handler, proxy.map(proxy_from_record)) }); } @@ -378,12 +404,13 @@ pub fn create_webview_with_user_agent( height: i32, url: String, user_agent: Option, - nav_handler: Option> + nav_handler: Option>, + proxy: Option ) -> Result { #[cfg(target_os = "linux")] { return run_on_gtk_thread(move || { - create_webview_inner(parent_handle, width, height, url, user_agent, nav_handler) + create_webview_inner(parent_handle, width, height, url, user_agent, nav_handler, proxy.map(proxy_from_record)) }); } From 0cdb300af121682e9e351a602af04ae7834770c4 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Wed, 18 Feb 2026 09:40:13 +0100 Subject: [PATCH 2/8] Add proxy support to Compose side Currently, the proxy won't be updated if it's changed after webview creation. --- .../webview/setting/PlatformWebSettings.kt | 1 + .../kdroidfilter/webview/setting/ProxyConfig.kt | 16 ++++++++++++++++ .../kdroidfilter/webview/web/WebViewDesktop.kt | 14 +++++++++++--- .../webview/wry/{Proxy.kt => JvmProxyConfig.kt} | 9 ++++++--- .../kdroidfilter/webview/wry/WryWebViewPanel.kt | 4 ++-- 5 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt rename wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/{Proxy.kt => JvmProxyConfig.kt} (81%) diff --git a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt index f9b44ec..864b519 100644 --- a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt +++ b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt @@ -14,6 +14,7 @@ sealed class PlatformWebSettings { data class DesktopWebSettings( var transparent: Boolean = true, + var proxyConfig: ProxyConfig? = null ) : PlatformWebSettings() data class IOSWebSettings( diff --git a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt new file mode 100644 index 0000000..4806857 --- /dev/null +++ b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt @@ -0,0 +1,16 @@ +package io.github.kdroidfilter.webview.setting + +sealed class ProxyConfig { + abstract val host: String + abstract val port: Int + + data class Http( + override val host: String, + override val port: Int + ) : ProxyConfig() + + data class Socks5( + override val host: String, + override val port: Int + ) : ProxyConfig() +} diff --git a/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt b/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt index 6c6ad79..452e232 100644 --- a/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt +++ b/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt @@ -17,18 +17,20 @@ import io.github.kdroidfilter.webview.jsbridge.WebViewJsBridge import io.github.kdroidfilter.webview.jsbridge.parseJsMessage import io.github.kdroidfilter.webview.request.WebRequest import io.github.kdroidfilter.webview.request.WebRequestInterceptResult +import io.github.kdroidfilter.webview.setting.ProxyConfig import kotlinx.coroutines.delay actual class WebViewFactoryParam( val state: WebViewState, val fileContent: String = "", val userAgent: String? = null, + val proxyConfig: ProxyConfig? = null ) actual fun defaultWebViewFactory(param: WebViewFactoryParam): NativeWebView = when (val content = param.state.content) { - is WebContent.Url -> NativeWebView(content.url, param.userAgent ?: param.state.webSettings.customUserAgentString) - else -> NativeWebView("about:blank", param.userAgent ?: param.state.webSettings.customUserAgentString) + is WebContent.Url -> NativeWebView(content.url, param.userAgent ?: param.state.webSettings.customUserAgentString, param.proxyConfig?.toJvmProxyConfig()) + else -> NativeWebView("about:blank", param.userAgent ?: param.state.webSettings.customUserAgentString, param.proxyConfig?.toJvmProxyConfig()) } @Composable @@ -44,6 +46,7 @@ actual fun ActualWebView( val currentOnDispose by rememberUpdatedState(onDispose) val scope = rememberCoroutineScope() + val proxyConfig = remember { state.webSettings.desktopWebSettings.proxyConfig } val desiredUserAgent = state.webSettings.customUserAgentString?.trim()?.takeIf { it.isNotEmpty() } var effectiveUserAgent by remember { mutableStateOf(desiredUserAgent) } @@ -55,7 +58,7 @@ actual fun ActualWebView( } key(effectiveUserAgent) { - val nativeWebView = remember(state, factory) { factory(WebViewFactoryParam(state, userAgent = effectiveUserAgent)) } + val nativeWebView = remember(state, factory) { factory(WebViewFactoryParam(state, userAgent = effectiveUserAgent, proxyConfig = proxyConfig)) } val desktopWebView = remember(nativeWebView, scope, webViewJsBridge) { @@ -174,3 +177,8 @@ actual fun ActualWebView( } } } + +internal fun ProxyConfig.toJvmProxyConfig(): io.github.kdroidfilter.webview.wry.JvmProxyConfig = when (this) { + is ProxyConfig.Http -> io.github.kdroidfilter.webview.wry.JvmProxyConfig.Http(host, port) + is ProxyConfig.Socks5 -> io.github.kdroidfilter.webview.wry.JvmProxyConfig.Socks5(host, port) +} \ No newline at end of file diff --git a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/JvmProxyConfig.kt similarity index 81% rename from wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt rename to wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/JvmProxyConfig.kt index 2321e2b..3ade578 100644 --- a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/Proxy.kt +++ b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/JvmProxyConfig.kt @@ -1,20 +1,23 @@ package io.github.kdroidfilter.webview.wry -sealed class ProxyConfig { +/** + * JVM-specific proxy config + */ +sealed class JvmProxyConfig { abstract val host: String abstract val port: Int data class Http( override val host: String, override val port: Int - ) : ProxyConfig() { + ) : JvmProxyConfig() { override fun toProxy(): Proxy.Http = Proxy.Http(Address(host, port.toUShort())) } data class Socks5( override val host: String, override val port: Int - ) : ProxyConfig() { + ) : JvmProxyConfig() { override fun toProxy(): Proxy.Socks5 = Proxy.Socks5(Address(host, port.toUShort())) } diff --git a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt index eeb73f1..a855403 100644 --- a/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt +++ b/wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt @@ -14,7 +14,7 @@ import kotlin.concurrent.thread class WryWebViewPanel( initialUrl: String, customUserAgent: String? = null, - proxyConfig: ProxyConfig? = null, + jvmProxyConfig: JvmProxyConfig? = null, private val bridgeLogger: (String) -> Unit = { System.err.println(it) } ) : JPanel() { private val host = SkikoInterop.createHost() @@ -23,7 +23,7 @@ class WryWebViewPanel( private var parentIsWindow: Boolean = false private var pendingUrl: String = initialUrl private val customUserAgent: String? = customUserAgent?.trim()?.takeIf { it.isNotEmpty() } - private val proxy: Proxy? = proxyConfig?.toProxy() + private val proxy: Proxy? = jvmProxyConfig?.toProxy() private var pendingUrlWithHeaders: String? = null private var pendingHeaders: Map = emptyMap() private var pendingHtml: String? = null From 310aadfc6771471ce7911822de79522580d98698 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Wed, 18 Feb 2026 09:57:40 +0100 Subject: [PATCH 3/8] Make proxy config mutable Now, the proxy config can be changed after webview creation. The webview will be recreated in this case. --- .../webview/setting/PlatformWebSettings.kt | 10 +++++++--- .../kdroidfilter/webview/web/WebViewDesktop.kt | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt index 864b519..6bf896d 100644 --- a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt +++ b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/PlatformWebSettings.kt @@ -1,5 +1,8 @@ package io.github.kdroidfilter.webview.setting +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.ui.graphics.Color /** @@ -13,9 +16,10 @@ sealed class PlatformWebSettings { ) : PlatformWebSettings() data class DesktopWebSettings( - var transparent: Boolean = true, - var proxyConfig: ProxyConfig? = null - ) : PlatformWebSettings() + var transparent: Boolean = true + ) : PlatformWebSettings() { + var proxyConfig: ProxyConfig? by mutableStateOf(null) + } data class IOSWebSettings( var opaque: Boolean = false, diff --git a/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt b/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt index 452e232..9ff4173 100644 --- a/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt +++ b/webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt @@ -46,7 +46,9 @@ actual fun ActualWebView( val currentOnDispose by rememberUpdatedState(onDispose) val scope = rememberCoroutineScope() - val proxyConfig = remember { state.webSettings.desktopWebSettings.proxyConfig } + val desiredProxyConfig = state.webSettings.desktopWebSettings.proxyConfig + var effectiveProxyConfig by remember { mutableStateOf(desiredProxyConfig) } + val desiredUserAgent = state.webSettings.customUserAgentString?.trim()?.takeIf { it.isNotEmpty() } var effectiveUserAgent by remember { mutableStateOf(desiredUserAgent) } @@ -57,8 +59,15 @@ actual fun ActualWebView( effectiveUserAgent = desiredUserAgent } - key(effectiveUserAgent) { - val nativeWebView = remember(state, factory) { factory(WebViewFactoryParam(state, userAgent = effectiveUserAgent, proxyConfig = proxyConfig)) } + LaunchedEffect(desiredProxyConfig) { + if (desiredProxyConfig == effectiveProxyConfig) return@LaunchedEffect + // Wry applies proxy config at creation time, so recreate the webview after a small debounce. + delay(400) + effectiveProxyConfig = desiredProxyConfig + } + + key(effectiveUserAgent, effectiveProxyConfig) { + val nativeWebView = remember(state, factory) { factory(WebViewFactoryParam(state, userAgent = effectiveUserAgent, proxyConfig = effectiveProxyConfig)) } val desktopWebView = remember(nativeWebView, scope, webViewJsBridge) { From e84f1615228499a3e3a1d31f1984edd92c75a9a8 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Wed, 18 Feb 2026 10:19:02 +0100 Subject: [PATCH 4/8] Add proxy option to demo app --- .../webview/demo/DemoToolsPanel.kt | 58 ++++++++++++++----- .../webview/setting/ProxyConfig.kt | 2 + 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/demo-shared/src/commonMain/kotlin/io/github/kdroidfilter/webview/demo/DemoToolsPanel.kt b/demo-shared/src/commonMain/kotlin/io/github/kdroidfilter/webview/demo/DemoToolsPanel.kt index a11ec9b..49d6558 100644 --- a/demo-shared/src/commonMain/kotlin/io/github/kdroidfilter/webview/demo/DemoToolsPanel.kt +++ b/demo-shared/src/commonMain/kotlin/io/github/kdroidfilter/webview/demo/DemoToolsPanel.kt @@ -1,21 +1,9 @@ package io.github.kdroidfilter.webview.demo -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.Card @@ -31,12 +19,17 @@ import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import composewebview.demo_shared.generated.resources.Res import io.github.kdroidfilter.webview.cookie.Cookie +import io.github.kdroidfilter.webview.setting.ProxyConfig import io.github.kdroidfilter.webview.util.KLogSeverity import io.github.kdroidfilter.webview.web.WebViewNavigator import io.github.kdroidfilter.webview.web.WebViewState @@ -87,6 +80,9 @@ internal fun DemoToolsPanel( onSetLogSeverity: (KLogSeverity) -> Unit, ) { val scope = rememberCoroutineScope() + val desktopWebSettings = webViewState.webSettings.desktopWebSettings + var proxyType by remember { mutableStateOf("None") } + var proxyText by remember { mutableStateOf(desktopWebSettings.proxyConfig?.toString() ?: "") } Surface(modifier = modifier, color = MaterialTheme.colorScheme.surface) { Column( modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(12.dp), @@ -375,6 +371,29 @@ internal fun DemoToolsPanel( singleLine = true, label = { Text("Custom User-Agent") }, ) + Spacer(Modifier.height(8.dp)) + OutlinedTextField( + value = proxyText, + onValueChange = { proxyText = it }, + singleLine = true, + label = { Text("Proxy (desktop only)") }, + ) + FlowRow(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + listOf("None", "HTTP", "SOCKS5").forEach { type -> + FilterChip( + selected = proxyType == type, + onClick = { proxyType = type }, + label = { Text(type) }, + ) + } + } + Button( + onClick = { + desktopWebSettings.proxyConfig = proxyText.ifBlank { null }?.parseProxyConfig(proxyType) + } + ){ + Text("Set proxy") + } } SectionCard(title = "Logs") { @@ -454,3 +473,14 @@ private fun inlineHtml(): String = """.trimIndent() + +private fun String.parseProxyConfig(proxyType: String): ProxyConfig? { + val host = substringBefore(":").ifBlank { null } ?: return null + val port = substringAfter(":").toIntOrNull() ?: return null + return when (proxyType) { + "None" -> null + "HTTP" -> ProxyConfig.Http(host, port) + "SOCKS5" -> ProxyConfig.Socks5(host, port) + else -> error("Invalid proxy type: $proxyType") + } +} \ No newline at end of file diff --git a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt index 4806857..c6f99a7 100644 --- a/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt +++ b/webview-compose/src/commonMain/kotlin/io/github/kdroidfilter/webview/setting/ProxyConfig.kt @@ -13,4 +13,6 @@ sealed class ProxyConfig { override val host: String, override val port: Int ) : ProxyConfig() + + override fun toString() = "$host:$port" } From bee466bd025661c2bb60e4865570967ca5656f63 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Wed, 18 Feb 2026 10:28:55 +0100 Subject: [PATCH 5/8] Add proxy docs to README --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 99bba6f..7747a67 100644 --- a/README.md +++ b/README.md @@ -222,13 +222,26 @@ Commands: state.webSettings.customUserAgentString = "MyApp/1.2.3" ``` -Desktop note: + + +### Proxy (desktop only) + +```kotlin +// HTTP CONNECT Proxy +state.webSettings.desktopWebSettings.proxyConfig = ProxyConfig.Http("proxy.tld", 8888) +// SOCKS5 Proxy +state.webSettings.desktopWebSettings.proxyConfig = ProxyConfig.Socks5("proxy.tld", 1080) +``` + +Proxy is only supported on Windows, macOS 14.0+ and Linux. + +Desktop note on both user-agent and proxy: * applied at creation time * changing it **recreates** the WebView (debounced) * JS context/history may be lost -👉 Set it early. +👉 Set them early. --- From 861c015438d9e09fd8b677cdd9ff4ce397f33e61 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Wed, 18 Feb 2026 10:35:55 +0100 Subject: [PATCH 6/8] Add mac-proxy feature for proxy support on macOS --- wrywebview/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrywebview/Cargo.toml b/wrywebview/Cargo.toml index 736708b..d11dd37 100644 --- a/wrywebview/Cargo.toml +++ b/wrywebview/Cargo.toml @@ -12,7 +12,7 @@ path = "src/main/rust/lib.rs" [dependencies] thiserror = "2.0.11" uniffi = "0.29.4" -wry = "0.53.5" +wry = { version = "0.53.5", features = ["mac-proxy"] } [profile.release] opt-level = "z" From d7c4d7f518239e1ae59f823d61e14c910da139a5 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Thu, 26 Feb 2026 20:43:18 +0100 Subject: [PATCH 7/8] Resolve build errors --- wrywebview/src/main/rust/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrywebview/src/main/rust/lib.rs b/wrywebview/src/main/rust/lib.rs index 6dbc7f7..bb529d7 100644 --- a/wrywebview/src/main/rust/lib.rs +++ b/wrywebview/src/main/rust/lib.rs @@ -394,7 +394,7 @@ pub fn create_webview( } #[cfg(not(target_os = "linux"))] - run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, None, nav_handler)) + run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, None, nav_handler, proxy.map(proxy_from_record))) } #[uniffi::export] @@ -415,7 +415,7 @@ pub fn create_webview_with_user_agent( } #[cfg(not(target_os = "linux"))] - run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, user_agent,nav_handler)) + run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, user_agent,nav_handler, proxy.map(proxy_from_record))) } // ============================================================================ From ed1ae26b847b2af4df3e8e771a21210fc155c8e6 Mon Sep 17 00:00:00 2001 From: bommbomm34 Date: Mon, 9 Mar 2026 20:06:07 +0100 Subject: [PATCH 8/8] Fix missing argument error in create_webview_inner --- wrywebview/src/main/rust/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrywebview/src/main/rust/lib.rs b/wrywebview/src/main/rust/lib.rs index 31ce30e..ad363dd 100644 --- a/wrywebview/src/main/rust/lib.rs +++ b/wrywebview/src/main/rust/lib.rs @@ -487,6 +487,7 @@ pub fn create_webview( width, height, url, user_agent, + proxy.map(proxy_from_record), data_directory, zoom, transparent, @@ -499,7 +500,6 @@ pub fn create_webview( autoplay, focused, nav_handler, - proxy ) ) }