| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- import AppKit
- import WebKit
- final class InAppBrowserWindowManager {
- static let shared = InAppBrowserWindowManager()
- private var controllers: [InAppBrowserWindowController] = []
- private init() {}
- func open(url: URL, title: String? = nil) {
- let controller = InAppBrowserWindowController(initialURL: url, windowTitle: title)
- controllers.append(controller)
- controller.onClose = { [weak self, weak controller] in
- guard let self, let controller else { return }
- self.controllers.removeAll { $0 === controller }
- }
- controller.showWindow(nil)
- controller.window?.makeKeyAndOrderFront(nil)
- }
- }
- final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
- var onClose: (() -> Void)?
- private let webView: WKWebView
- private let initialURL: URL
- private var didLoadInitialURL = false
- init(initialURL: URL, windowTitle: String?) {
- self.initialURL = initialURL
- let config = WKWebViewConfiguration()
- config.defaultWebpagePreferences.allowsContentJavaScript = true
- self.webView = WKWebView(frame: .zero, configuration: config)
- let window = NSWindow(
- contentRect: NSRect(x: 180, y: 140, width: 1200, height: 800),
- styleMask: [.titled, .closable, .miniaturizable, .resizable],
- backing: .buffered,
- defer: false
- )
- window.title = windowTitle ?? "Browser"
- window.isReleasedWhenClosed = false
- window.contentView = webView
- super.init(window: window)
- window.delegate = self
- webView.uiDelegate = self
- webView.navigationDelegate = self
- webView.allowsBackForwardNavigationGestures = true
- webView.setValue(false, forKey: "drawsBackground")
- }
- @available(*, unavailable)
- required init?(coder: NSCoder) {
- nil
- }
- override func showWindow(_ sender: Any?) {
- super.showWindow(sender)
- guard !didLoadInitialURL else { return }
- didLoadInitialURL = true
- webView.load(URLRequest(url: initialURL))
- }
- func windowWillClose(_ notification: Notification) {
- onClose?()
- }
- }
- extension InAppBrowserWindowController: WKNavigationDelegate {
- func webView(
- _ webView: WKWebView,
- decidePolicyFor navigationAction: WKNavigationAction,
- decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
- ) {
- if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
- InAppBrowserWindowManager.shared.open(url: url)
- decisionHandler(.cancel)
- return
- }
- decisionHandler(.allow)
- }
- func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
- if let host = webView.url?.host, !host.isEmpty {
- window?.title = host
- }
- }
- func webView(
- _ webView: WKWebView,
- didFailProvisionalNavigation navigation: WKNavigation!,
- withError error: Error
- ) {
- showLoadError(error)
- }
- func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
- showLoadError(error)
- }
- private func showLoadError(_ error: Error) {
- let message = """
- <html>
- <body style="background:#111;color:#EEE;font-family:-apple-system, sans-serif;padding:24px;">
- <h2>Page failed to load</h2>
- <p>Could not open <b>\(initialURL.absoluteString)</b></p>
- <p>\(error.localizedDescription)</p>
- </body>
- </html>
- """
- webView.loadHTMLString(message, baseURL: nil)
- window?.title = "Load Error"
- }
- }
- extension InAppBrowserWindowController: WKUIDelegate {
- func webView(
- _ webView: WKWebView,
- createWebViewWith configuration: WKWebViewConfiguration,
- for navigationAction: WKNavigationAction,
- windowFeatures: WKWindowFeatures
- ) -> WKWebView? {
- guard let url = navigationAction.request.url else { return nil }
- InAppBrowserWindowManager.shared.open(url: url)
- return nil
- }
- }
|