From 73cbfce432d334295532fa233ca2c55ce237e696 Mon Sep 17 00:00:00 2001 From: Lawrence Chen <54008264+lawrencecchen@users.noreply.github.com> Date: Fri, 20 Feb 2026 20:53:59 -0800 Subject: [PATCH] Fix WKWebView JavaScript dialogs in browser panel --- Sources/Panels/BrowserPanel.swift | 77 +++++++++++++++++++ cmuxTests/CmuxWebViewKeyEquivalentTests.swift | 42 ++++++++++ 2 files changed, 119 insertions(+) diff --git a/Sources/Panels/BrowserPanel.swift b/Sources/Panels/BrowserPanel.swift index 873da962..12fa1c75 100644 --- a/Sources/Panels/BrowserPanel.swift +++ b/Sources/Panels/BrowserPanel.swift @@ -2245,6 +2245,25 @@ private class BrowserUIDelegate: NSObject, WKUIDelegate { var openInNewTab: ((URL) -> Void)? var requestNavigation: ((URL, BrowserInsecureHTTPNavigationIntent) -> Void)? + private func javaScriptDialogTitle(for webView: WKWebView) -> String { + if let absolute = webView.url?.absoluteString, !absolute.isEmpty { + return "The page at \(absolute) says:" + } + return "This page says:" + } + + private func presentDialog( + _ alert: NSAlert, + for webView: WKWebView, + completion: @escaping (NSApplication.ModalResponse) -> Void + ) { + if let window = webView.window { + alert.beginSheetModal(for: window, completionHandler: completion) + return + } + completion(alert.runModal()) + } + /// Returning nil tells WebKit not to open a new window. /// Cmd+click opens in a new tab; regular target=_blank navigates in-place. func webView( @@ -2282,4 +2301,62 @@ private class BrowserUIDelegate: NSObject, WKUIDelegate { completionHandler(result == .OK ? panel.urls : nil) } } + + func webView( + _ webView: WKWebView, + runJavaScriptAlertPanelWithMessage message: String, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping () -> Void + ) { + let alert = NSAlert() + alert.alertStyle = .informational + alert.messageText = javaScriptDialogTitle(for: webView) + alert.informativeText = message + alert.addButton(withTitle: "OK") + presentDialog(alert, for: webView) { _ in completionHandler() } + } + + func webView( + _ webView: WKWebView, + runJavaScriptConfirmPanelWithMessage message: String, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping (Bool) -> Void + ) { + let alert = NSAlert() + alert.alertStyle = .informational + alert.messageText = javaScriptDialogTitle(for: webView) + alert.informativeText = message + alert.addButton(withTitle: "OK") + alert.addButton(withTitle: "Cancel") + presentDialog(alert, for: webView) { response in + completionHandler(response == .alertFirstButtonReturn) + } + } + + func webView( + _ webView: WKWebView, + runJavaScriptTextInputPanelWithPrompt prompt: String, + defaultText: String?, + initiatedByFrame frame: WKFrameInfo, + completionHandler: @escaping (String?) -> Void + ) { + let alert = NSAlert() + alert.alertStyle = .informational + alert.messageText = javaScriptDialogTitle(for: webView) + alert.informativeText = prompt + alert.addButton(withTitle: "OK") + alert.addButton(withTitle: "Cancel") + + let field = NSTextField(frame: NSRect(x: 0, y: 0, width: 320, height: 24)) + field.stringValue = defaultText ?? "" + alert.accessoryView = field + + presentDialog(alert, for: webView) { response in + if response == .alertFirstButtonReturn { + completionHandler(field.stringValue) + } else { + completionHandler(nil) + } + } + } } diff --git a/cmuxTests/CmuxWebViewKeyEquivalentTests.swift b/cmuxTests/CmuxWebViewKeyEquivalentTests.swift index e27d3db3..548a979c 100644 --- a/cmuxTests/CmuxWebViewKeyEquivalentTests.swift +++ b/cmuxTests/CmuxWebViewKeyEquivalentTests.swift @@ -216,6 +216,48 @@ final class BrowserDeveloperToolsConfigurationTests: XCTestCase { } } +@MainActor +final class BrowserJavaScriptDialogDelegateTests: XCTestCase { + func testBrowserPanelUIDelegateImplementsJavaScriptDialogSelectors() { + let panel = BrowserPanel(workspaceId: UUID()) + guard let uiDelegate = panel.webView.uiDelegate as? NSObject else { + XCTFail("Expected BrowserPanel webView.uiDelegate to be an NSObject") + return + } + + XCTAssertTrue( + uiDelegate.responds( + to: #selector( + WKUIDelegate.webView( + _:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler: + ) + ) + ), + "Browser UI delegate must implement JavaScript alert handling" + ) + XCTAssertTrue( + uiDelegate.responds( + to: #selector( + WKUIDelegate.webView( + _:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler: + ) + ) + ), + "Browser UI delegate must implement JavaScript confirm handling" + ) + XCTAssertTrue( + uiDelegate.responds( + to: #selector( + WKUIDelegate.webView( + _:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler: + ) + ) + ), + "Browser UI delegate must implement JavaScript prompt handling" + ) + } +} + @MainActor final class BrowserDeveloperToolsVisibilityPersistenceTests: XCTestCase { private final class FakeInspector: NSObject {