From 37a600f0465f73ccd8ed5fbfc88a1d3a9577b164 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Wed, 29 Jul 2026 12:30:59 -0700 Subject: [PATCH] fix(ios): always invoke WebKit completion handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WKWebView wraps every decision/completion handler in a CompletionHandlerCallChecker whose destructor raises an ObjC exception if the handler was never called. Under NativeScript the block is owned by a JS wrapper, so the exception is thrown from inside a later V8 GC and aborts the app with SIGABRT far away from the code that dropped the handler. Three paths never called their handler: - decidePolicyForNavigationAction: the Cancel call sat inside an `if (Trace.isEnabled())` block, so cancelling a navigation from shouldOverrideUrlLoading leaked the handler in every build with tracing off. The `!url` early return leaked it too. - runJavaScriptAlert/Confirm/TextInputPanel: `_webAlert`/`_webConfirm`/ `_webPrompt` return false when the app registers no listener, and the missing-owner path returned early — both left the handler uncalled, so any page calling alert()/confirm()/prompt() could abort the app. --- src/webview/index.ios.ts | 45 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/webview/index.ios.ts b/src/webview/index.ios.ts index 2d5169e..56713f6 100644 --- a/src/webview/index.ios.ts +++ b/src/webview/index.ios.ts @@ -643,6 +643,8 @@ export class WKNavigationDelegateNotaImpl extends NSObject implements WKNavigati Trace.write(`webViewDecidePolicyForNavigationActionDecisionHandler: "${url}"`, WebViewTraceCategory, Trace.messageType.info); } if (!url) { + decisionHandler(WKNavigationActionPolicy.Cancel); + return; } @@ -683,8 +685,11 @@ export class WKNavigationDelegateNotaImpl extends NSObject implements WKNavigati WebViewTraceCategory, Trace.messageType.info ); - decisionHandler(WKNavigationActionPolicy.Cancel); } + // WebKit raises an ObjC exception when an unused decision handler is deallocated, + // so every code path must call it exactly once regardless of tracing. + decisionHandler(WKNavigationActionPolicy.Cancel); + return; } decisionHandler(WKNavigationActionPolicy.Allow); @@ -817,18 +822,20 @@ export class WKUIDelegateNotaImpl extends NSObject implements WKUIDelegate { */ public webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler(webView: WKWebView, message: string, frame: WKFrameInfo, completionHandler: () => void): void { const owner = this.owner.get(); - if (!owner) { - return; - } let gotResponse = false; - owner._webAlert(message, () => { + const respond = () => { if (!gotResponse) { + gotResponse = true; completionHandler(); } + }; - gotResponse = true; - }); + // _webAlert returns false when nothing is listening, so respond ourselves + // rather than leaving the handler uncalled. + if (!owner || !owner._webAlert(message, respond)) { + respond(); + } } /** @@ -841,18 +848,18 @@ export class WKUIDelegateNotaImpl extends NSObject implements WKUIDelegate { completionHandler: (confirmed: boolean) => void ): void { const owner = this.owner.get(); - if (!owner) { - return; - } let gotResponse = false; - owner._webConfirm(message, (confirmed: boolean) => { + const respond = (confirmed = false) => { if (!gotResponse) { + gotResponse = true; completionHandler(confirmed); } + }; - gotResponse = true; - }); + if (!owner || !owner._webConfirm(message, respond)) { + respond(); + } } /** @@ -866,18 +873,18 @@ export class WKUIDelegateNotaImpl extends NSObject implements WKUIDelegate { completionHandler: (response: string) => void ): void { const owner = this.owner.get(); - if (!owner) { - return; - } let gotResponse = false; - owner._webPrompt(message, defaultText, (response: string) => { + const respond = (response: string = null) => { if (!gotResponse) { + gotResponse = true; completionHandler(response); } + }; - gotResponse = true; - }); + if (!owner || !owner._webPrompt(message, defaultText, respond)) { + respond(); + } } webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures(