Fix WKWebView JavaScript dialogs in browser panel

This commit is contained in:
Lawrence Chen 2026-02-20 20:53:59 -08:00
parent 5ae36fcb91
commit 73cbfce432
2 changed files with 119 additions and 0 deletions

View file

@ -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)
}
}
}
}

View file

@ -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 {