Skip to content

Commit 1200e8d

Browse files
authored
Merge pull request #16 from magic-cucumber/feature-global-navigation-desktop
Implementing Global URL Change Event Listening on the Desktop Side
2 parents 1203fe8 + 9d3f5a0 commit 1200e8d

File tree

3 files changed

+108
-13
lines changed

3 files changed

+108
-13
lines changed

webview-compose/src/jvmMain/kotlin/io/github/kdroidfilter/webview/web/WebViewDesktop.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import androidx.compose.ui.awt.SwingPanel
1515
import io.github.kdroidfilter.webview.cookie.WryCookieManager
1616
import io.github.kdroidfilter.webview.jsbridge.WebViewJsBridge
1717
import io.github.kdroidfilter.webview.jsbridge.parseJsMessage
18+
import io.github.kdroidfilter.webview.request.WebRequest
19+
import io.github.kdroidfilter.webview.request.WebRequestInterceptResult
1820
import kotlinx.coroutines.delay
1921

2022
actual class WebViewFactoryParam(
@@ -120,6 +122,41 @@ actual fun ActualWebView(
120122
}
121123
}
122124

125+
DisposableEffect(nativeWebView) {
126+
val listener: (String) -> Boolean = a@{
127+
if (navigator.requestInterceptor == null) {
128+
return@a true
129+
}
130+
131+
val webRequest =
132+
WebRequest(
133+
url = it,
134+
headers = mutableMapOf(),
135+
isForMainFrame = true,
136+
isRedirect = true,
137+
method = "GET",
138+
)
139+
140+
return@a when (val interceptResult = navigator.requestInterceptor.onInterceptUrlRequest(webRequest, navigator)) {
141+
WebRequestInterceptResult.Allow -> true
142+
143+
WebRequestInterceptResult.Reject -> false
144+
145+
is WebRequestInterceptResult.Modify -> {
146+
interceptResult.request.let { modified ->
147+
navigator.stopLoading()
148+
navigator.loadUrl(modified.url, modified.headers)
149+
}
150+
false //no jump?
151+
}
152+
}
153+
}
154+
nativeWebView.addNavigateListener(listener)
155+
onDispose {
156+
nativeWebView.removeNavigateListener(listener)
157+
}
158+
}
159+
123160
SwingPanel(
124161
modifier = modifier,
125162
factory = {

wrywebview/src/main/kotlin/io/github/kdroidfilter/webview/wry/WryWebViewPanel.kt

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ class WryWebViewPanel(
3434
private var pendingBounds: Bounds? = null
3535
private var boundsTimer: Timer? = null
3636

37+
private val handlers = mutableListOf<(String) -> Boolean>()
38+
39+
private val handler = object : NavigationHandler {
40+
override fun handleNavigation(url: String): Boolean = handlers.any { it(url) }
41+
}
42+
3743
init {
3844
layout = BorderLayout()
3945
add(host, BorderLayout.CENTER)
@@ -71,6 +77,14 @@ class WryWebViewPanel(
7177
scheduleCreateIfNeeded()
7278
}
7379

80+
fun addNavigateListener(data: (String) -> Boolean) {
81+
handlers.add(data)
82+
}
83+
84+
fun removeNavigateListener(data: (String) -> Boolean) {
85+
handlers.remove(data)
86+
}
87+
7488
fun loadUrl(url: String) {
7589
loadUrl(url, emptyMap())
7690
}
@@ -332,9 +346,16 @@ class WryWebViewPanel(
332346
return try {
333347
webviewId =
334348
if (userAgent == null) {
335-
NativeBindings.createWebview(handleSnapshot, width, height, initialUrl)
349+
NativeBindings.createWebview(handleSnapshot, width, height, initialUrl, handler)
336350
} else {
337-
NativeBindings.createWebviewWithUserAgent(handleSnapshot, width, height, initialUrl, userAgent)
351+
NativeBindings.createWebviewWithUserAgent(
352+
handleSnapshot,
353+
width,
354+
height,
355+
initialUrl,
356+
userAgent,
357+
handler
358+
)
338359
}
339360
updateBounds()
340361
startGtkPumpIfNeeded()
@@ -350,6 +371,7 @@ class WryWebViewPanel(
350371
pendingHtml = null
351372
NativeBindings.loadHtml(id, html)
352373
}
374+
353375
urlWithHeaders != null && headers.isNotEmpty() -> {
354376
pendingUrlWithHeaders = null
355377
pendingHeaders = emptyMap()
@@ -370,9 +392,16 @@ class WryWebViewPanel(
370392
thread(name = "wry-webview-create", isDaemon = true) {
371393
val createdId = try {
372394
if (userAgent == null) {
373-
NativeBindings.createWebview(handleSnapshot, width, height, initialUrl)
395+
NativeBindings.createWebview(handleSnapshot, width, height, initialUrl, handler)
374396
} else {
375-
NativeBindings.createWebviewWithUserAgent(handleSnapshot, width, height, initialUrl, userAgent)
397+
NativeBindings.createWebviewWithUserAgent(
398+
handleSnapshot,
399+
width,
400+
height,
401+
initialUrl,
402+
userAgent,
403+
handler
404+
)
376405
}
377406
} catch (e: RuntimeException) {
378407
System.err.println("Failed to create Wry webview: ${e.message}")
@@ -406,11 +435,13 @@ class WryWebViewPanel(
406435
pendingHtml = null
407436
NativeBindings.loadHtml(createdId, html)
408437
}
438+
409439
urlWithHeaders != null && headers.isNotEmpty() -> {
410440
pendingUrlWithHeaders = null
411441
pendingHeaders = emptyMap()
412442
NativeBindings.loadUrlWithHeaders(createdId, urlWithHeaders, headers)
413443
}
444+
414445
pendingUrl != initialUrl -> {
415446
NativeBindings.loadUrl(createdId, pendingUrl)
416447
}
@@ -557,7 +588,13 @@ class WryWebViewPanel(
557588
}
558589
} else if (IS_MAC) {
559590
if (contentHandle != 0L && contentHandle != windowHandle) {
560-
log("resolveParentHandle skiko content=0x${contentHandle.toString(16)} window=0x${windowHandle.toString(16)} (macOS content)")
591+
log(
592+
"resolveParentHandle skiko content=0x${contentHandle.toString(16)} window=0x${
593+
windowHandle.toString(
594+
16
595+
)
596+
} (macOS content)"
597+
)
561598
return ParentHandle(contentHandle.toULong(), false)
562599
}
563600
if (windowHandle != 0L) {
@@ -570,7 +607,13 @@ class WryWebViewPanel(
570607
}
571608
} else {
572609
if (contentHandle != 0L) {
573-
log("resolveParentHandle skiko content=0x${contentHandle.toString(16)} window=0x${windowHandle.toString(16)}")
610+
log(
611+
"resolveParentHandle skiko content=0x${contentHandle.toString(16)} window=0x${
612+
windowHandle.toString(
613+
16
614+
)
615+
}"
616+
)
574617
return ParentHandle(contentHandle.toULong(), false)
575618
}
576619
if (windowHandle != 0L) {
@@ -642,8 +685,8 @@ class WryWebViewPanel(
642685
}
643686

644687
private object NativeBindings {
645-
fun createWebview(parentHandle: ULong, width: Int, height: Int, url: String): ULong {
646-
return io.github.kdroidfilter.webview.wry.createWebview(parentHandle, width, height, url)
688+
fun createWebview(parentHandle: ULong, width: Int, height: Int, url: String, handler: NavigationHandler): ULong {
689+
return io.github.kdroidfilter.webview.wry.createWebview(parentHandle, width, height, url, handler)
647690
}
648691

649692
fun createWebviewWithUserAgent(
@@ -652,13 +695,15 @@ private object NativeBindings {
652695
height: Int,
653696
url: String,
654697
userAgent: String,
698+
handler: NavigationHandler,
655699
): ULong {
656700
return io.github.kdroidfilter.webview.wry.createWebviewWithUserAgent(
657701
parentHandle,
658702
width,
659703
height,
660704
url,
661705
userAgent,
706+
handler,
662707
)
663708
}
664709

wrywebview/src/main/rust/lib.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ mod state;
1111
use std::str::FromStr;
1212
use std::sync::atomic::Ordering;
1313
use std::sync::Arc;
14-
use std::sync::OnceLock;
1514

1615
use wry::cookie::time::OffsetDateTime;
1716
use wry::cookie::{Cookie, Expiration, SameSite};
@@ -172,12 +171,19 @@ macro_rules! wry_log {
172171
// WebView Creation
173172
// ============================================================================
174173

174+
#[uniffi::export(callback_interface)]
175+
pub trait NavigationHandler: Send + Sync {
176+
/// Return true to allow navigation, false to cancel.
177+
fn handle_navigation(&self, url: String) -> bool;
178+
}
179+
175180
fn create_webview_inner(
176181
parent_handle: u64,
177182
width: i32,
178183
height: i32,
179184
url: String,
180185
user_agent: Option<String>,
186+
nav_handler: Option<Box<dyn NavigationHandler>>,
181187
) -> Result<u64, WebViewError> {
182188
let user_agent =
183189
user_agent.and_then(|ua| {
@@ -216,11 +222,16 @@ fn create_webview_inner(
216222

217223
let webview = builder
218224
.with_navigation_handler(move |new_url| {
225+
if let Some(handler) = &nav_handler {
226+
return handler.handle_navigation(new_url.to_string());
227+
}
228+
219229
wry_log!("[wrywebview] navigation_handler url={}", new_url);
220230
state_for_nav.is_loading.store(true, Ordering::SeqCst);
221231
if let Err(e) = state_for_nav.update_current_url(new_url.clone()) {
222232
wry_log!("[wrywebview] navigation_handler state update failed: {}", e);
223233
}
234+
224235
true
225236
})
226237
.with_on_page_load_handler(move |event, url| {
@@ -315,16 +326,17 @@ pub fn create_webview(
315326
width: i32,
316327
height: i32,
317328
url: String,
329+
nav_handler: Option<Box<dyn NavigationHandler>>
318330
) -> Result<u64, WebViewError> {
319331
#[cfg(target_os = "linux")]
320332
{
321333
return run_on_gtk_thread(move || {
322-
create_webview_inner(parent_handle, width, height, url, None)
334+
create_webview_inner(parent_handle, width, height, url, None, nav_handler)
323335
});
324336
}
325337

326338
#[cfg(not(target_os = "linux"))]
327-
run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, None))
339+
run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, None, nav_handler))
328340
}
329341

330342
#[uniffi::export]
@@ -334,16 +346,17 @@ pub fn create_webview_with_user_agent(
334346
height: i32,
335347
url: String,
336348
user_agent: Option<String>,
349+
nav_handler: Option<Box<dyn NavigationHandler>>
337350
) -> Result<u64, WebViewError> {
338351
#[cfg(target_os = "linux")]
339352
{
340353
return run_on_gtk_thread(move || {
341-
create_webview_inner(parent_handle, width, height, url, user_agent)
354+
create_webview_inner(parent_handle, width, height, url, user_agent, nav_handler)
342355
});
343356
}
344357

345358
#[cfg(not(target_os = "linux"))]
346-
run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, user_agent))
359+
run_on_main_thread(move || create_webview_inner(parent_handle, width, height, url, user_agent,nav_handler))
347360
}
348361

349362
// ============================================================================

0 commit comments

Comments
 (0)