|
@@ -0,0 +1,132 @@
|
|
|
|
|
+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
|
|
|
|
|
+ }
|
|
|
|
|
+}
|