// // WebViewChannelDelegate.swift // flutter_inappwebview // // Created by Lorenzo Pichilli on 06/05/22. // import Foundation import WebKit import FlutterMacOS public class WebViewChannelDelegate: ChannelDelegate { private weak var webView: InAppWebView? public init(webView: InAppWebView, channel: FlutterMethodChannel) { super.init(channel: channel) self.webView = webView } public override func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { let arguments = call.arguments as? NSDictionary guard let method = WebViewChannelDelegateMethods.init(rawValue: call.method) else { result(FlutterMethodNotImplemented) return } switch method { case .getUrl: result(webView?.url?.absoluteString) break case .getTitle: result(webView?.title) break case .getProgress: result( (webView != nil) ? Int(webView!.estimatedProgress * 100) : nil ) break case .loadUrl: let urlRequest = arguments!["urlRequest"] as! [String:Any?] let allowingReadAccessTo = arguments!["allowingReadAccessTo"] as? String var allowingReadAccessToURL: URL? = nil if let allowingReadAccessTo = allowingReadAccessTo { allowingReadAccessToURL = URL(string: allowingReadAccessTo) } webView?.loadUrl(urlRequest: URLRequest.init(fromPluginMap: urlRequest), allowingReadAccessTo: allowingReadAccessToURL) result(true) break case .postUrl: if let webView = webView { let url = arguments!["url"] as! String let postData = arguments!["postData"] as! FlutterStandardTypedData webView.postUrl(url: URL(string: url)!, postData: postData.data) } result(true) break case .loadData: let data = arguments!["data"] as! String let mimeType = arguments!["mimeType"] as! String let encoding = arguments!["encoding"] as! String let baseUrl = URL(string: arguments!["baseUrl"] as! String)! let allowingReadAccessTo = arguments!["allowingReadAccessTo"] as? String var allowingReadAccessToURL: URL? = nil if let allowingReadAccessTo = allowingReadAccessTo { allowingReadAccessToURL = URL(string: allowingReadAccessTo) } webView?.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl, allowingReadAccessTo: allowingReadAccessToURL) result(true) break case .loadFile: let assetFilePath = arguments!["assetFilePath"] as! String do { try webView?.loadFile(assetFilePath: assetFilePath) } catch let error as NSError { result(FlutterError(code: "WebViewChannelDelegate", message: error.domain, details: nil)) return } result(true) break case .evaluateJavascript: if let webView = webView { let source = arguments!["source"] as! String let contentWorldMap = arguments!["contentWorld"] as? [String:Any?] if #available(macOS 11.0, *), let contentWorldMap = contentWorldMap { let contentWorld = WKContentWorld.fromMap(map: contentWorldMap, windowId: webView.windowId)! webView.evaluateJavascript(source: source, contentWorld: contentWorld) { (value) in result(value) } } else { webView.evaluateJavascript(source: source) { (value) in result(value) } } } else { result(nil) } break case .injectJavascriptFileFromUrl: let urlFile = arguments!["urlFile"] as! String let scriptHtmlTagAttributes = arguments!["scriptHtmlTagAttributes"] as? [String:Any?] webView?.injectJavascriptFileFromUrl(urlFile: urlFile, scriptHtmlTagAttributes: scriptHtmlTagAttributes) result(true) break case .injectCSSCode: let source = arguments!["source"] as! String webView?.injectCSSCode(source: source) result(true) break case .injectCSSFileFromUrl: let urlFile = arguments!["urlFile"] as! String let cssLinkHtmlTagAttributes = arguments!["cssLinkHtmlTagAttributes"] as? [String:Any?] webView?.injectCSSFileFromUrl(urlFile: urlFile, cssLinkHtmlTagAttributes: cssLinkHtmlTagAttributes) result(true) break case .reload: webView?.reload() result(true) break case .goBack: webView?.goBack() result(true) break case .canGoBack: result(webView?.canGoBack ?? false) break case .goForward: webView?.goForward() result(true) break case .canGoForward: result(webView?.canGoForward ?? false) break case .goBackOrForward: let steps = arguments!["steps"] as! Int webView?.goBackOrForward(steps: steps) result(true) break case .canGoBackOrForward: let steps = arguments!["steps"] as! Int result(webView?.canGoBackOrForward(steps: steps) ?? false) break case .stopLoading: webView?.stopLoading() result(true) break case .isLoading: result(webView?.isLoading ?? false) break case .takeScreenshot: if let webView = webView, #available(macOS 10.13, *) { let screenshotConfiguration = arguments!["screenshotConfiguration"] as? [String: Any?] webView.takeScreenshot(with: screenshotConfiguration, completionHandler: { (screenshot) -> Void in result(screenshot) }) } else { result(nil) } break case .setSettings: if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController { let inAppBrowserSettings = InAppBrowserSettings() let inAppBrowserSettingsMap = arguments!["settings"] as! [String: Any] let _ = inAppBrowserSettings.parse(settings: inAppBrowserSettingsMap) iabController.setSettings(newSettings: inAppBrowserSettings, newSettingsMap: inAppBrowserSettingsMap) } else { let inAppWebViewSettings = InAppWebViewSettings() let inAppWebViewSettingsMap = arguments!["settings"] as! [String: Any] let _ = inAppWebViewSettings.parse(settings: inAppWebViewSettingsMap) webView?.setSettings(newSettings: inAppWebViewSettings, newSettingsMap: inAppWebViewSettingsMap) } result(true) break case .getSettings: if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController { result(iabController.getSettings()) } else { result(webView?.getSettings()) } break case .close: if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController { iabController.close() result(true) } else { result(FlutterMethodNotImplemented) } break case .show: if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController { iabController.show() result(true) } else { result(FlutterMethodNotImplemented) } break case .hide: if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController { iabController.hide() result(true) } else { result(FlutterMethodNotImplemented) } break case .isHidden: if let iabController = webView?.inAppBrowserDelegate as? InAppBrowserWebViewController { result(iabController.isHidden) } else { result(FlutterMethodNotImplemented) } break case .getCopyBackForwardList: result(webView?.getCopyBackForwardList()) break case .findAll: if let webView = webView, let findInteractionController = webView.findInteractionController { let find = arguments!["find"] as! String findInteractionController.findAll(find: find, completionHandler: {(value, error) in if error != nil { result(FlutterError(code: "WebViewChannelDelegate", message: error?.localizedDescription, details: nil)) return } result(true) }) } else { result(false) } break case .findNext: if let webView = webView, let findInteractionController = webView.findInteractionController { let forward = arguments!["forward"] as! Bool findInteractionController.findNext(forward: forward, completionHandler: {(value, error) in if error != nil { result(FlutterError(code: "WebViewChannelDelegate", message: error?.localizedDescription, details: nil)) return } result(true) }) } else { result(false) } break case .clearMatches: if let webView = webView, let findInteractionController = webView.findInteractionController { findInteractionController.clearMatches(completionHandler: {(value, error) in if error != nil { result(FlutterError(code: "WebViewChannelDelegate", message: error?.localizedDescription, details: nil)) return } result(true) }) } else { result(false) } break case .clearCache: webView?.clearCache() result(true) break case .scrollTo: let x = arguments!["x"] as! Int let y = arguments!["y"] as! Int let animated = arguments!["animated"] as! Bool webView?.scrollTo(x: x, y: y, animated: animated) result(true) break case .scrollBy: let x = arguments!["x"] as! Int let y = arguments!["y"] as! Int let animated = arguments!["animated"] as! Bool webView?.scrollBy(x: x, y: y, animated: animated) result(true) break case .pauseTimers: webView?.pauseTimers() result(true) break case .resumeTimers: webView?.resumeTimers() result(true) break case .printCurrentPage: if let webView = webView { let settings = PrintJobSettings() if let settingsMap = arguments!["settings"] as? [String: Any?] { let _ = settings.parse(settings: settingsMap) } result(webView.printCurrentPage(settings: settings)) } else { result(nil) } break case .getContentHeight: webView?.getContentHeight { contentHeight, error in if let error = error { print(error) result(nil) return } result(contentHeight) } break case .getContentWidth: webView?.getContentWidth { contentWidth, error in if let error = error { print(error) result(nil) return } result(contentWidth) } break case .reloadFromOrigin: webView?.reloadFromOrigin() result(true) break case .getOriginalUrl: result(webView?.getOriginalUrl()?.absoluteString) break case .getZoomScale: result(nil) break case .hasOnlySecureContent: result(webView?.hasOnlySecureContent ?? false) break case .getSelectedText: if let webView = webView { webView.getSelectedText { (value, error) in if let err = error { print(err.localizedDescription) result("") return } result(value) } } else { result(nil) } break case .getScrollX: if let webView = webView { webView.getScrollX { scrollX, error in if let error = error { print(error) result(nil) return } result(scrollX) } } else { result(nil) } break case .getScrollY: if let webView = webView { webView.getScrollY { scrollY, error in if let error = error { print(error) result(nil) return } result(scrollY) } } else { result(nil) } break case .getCertificate: result(webView?.getCertificate()?.toMap()) break case .addUserScript: if let webView = webView { let userScriptMap = arguments!["userScript"] as! [String: Any?] let userScript = UserScript.fromMap(map: userScriptMap, windowId: webView.windowId)! webView.configuration.userContentController.addUserOnlyScript(userScript) webView.configuration.userContentController.sync(scriptMessageHandler: webView) } result(true) break case .removeUserScript: let index = arguments!["index"] as! Int let userScriptMap = arguments!["userScript"] as! [String: Any?] let userScript = UserScript.fromMap(map: userScriptMap, windowId: webView?.windowId)! webView?.configuration.userContentController.removeUserOnlyScript(at: index, injectionTime: userScript.injectionTime) result(true) break case .removeUserScriptsByGroupName: let groupName = arguments!["groupName"] as! String webView?.configuration.userContentController.removeUserOnlyScripts(with: groupName) result(true) break case .removeAllUserScripts: webView?.configuration.userContentController.removeAllUserOnlyScripts() result(true) break case .callAsyncJavaScript: if let webView = webView { if #available(macOS 11.0, *) { // on iOS 14.0, for some reason, it crashes let functionBody = arguments!["functionBody"] as! String let functionArguments = arguments!["arguments"] as! [String:Any] var contentWorld = WKContentWorld.page if let contentWorldMap = arguments!["contentWorld"] as? [String:Any?] { contentWorld = WKContentWorld.fromMap(map: contentWorldMap, windowId: webView.windowId)! } webView.callAsyncJavaScript(functionBody: functionBody, arguments: functionArguments, contentWorld: contentWorld) { (value) in result(value) } } else { let functionBody = arguments!["functionBody"] as! String let functionArguments = arguments!["arguments"] as! [String:Any] webView.callAsyncJavaScript(functionBody: functionBody, arguments: functionArguments) { (value) in result(value) } } } else { result(nil) } break case .createPdf: if let webView = webView, #available(macOS 11.0, *) { let configuration = arguments!["pdfConfiguration"] as? [String: Any?] webView.createPdf(configuration: configuration, completionHandler: { (pdf) -> Void in result(pdf) }) } else { result(nil) } break case .createWebArchiveData: if let webView = webView, #available(macOS 11.0, *) { webView.createWebArchiveData(dataCompletionHandler: { (webArchiveData) -> Void in result(webArchiveData) }) } else { result(nil) } break case .saveWebArchive: if let webView = webView, #available(macOS 11.0, *) { let filePath = arguments!["filePath"] as! String let autoname = arguments!["autoname"] as! Bool webView.saveWebArchive(filePath: filePath, autoname: autoname, completionHandler: { (path) -> Void in result(path) }) } else { result(nil) } break case .isSecureContext: if let webView = webView { webView.isSecureContext(completionHandler: { (isSecureContext) in result(isSecureContext) }) } else { result(false) } break case .createWebMessageChannel: if let webView = webView { let _ = webView.createWebMessageChannel { (webMessageChannel) in guard let webMessageChannel = webMessageChannel else { result(nil) return } result(webMessageChannel.toMap()) } } else { result(nil) } break case .postWebMessage: if let webView = webView { var message = WebMessage.fromMap(map: arguments!["message"] as! [String: Any?]) let targetOrigin = arguments!["targetOrigin"] as! String var ports: [WebMessagePort] = [] if let notConnectedPorts = message.ports { for notConnectedPort in notConnectedPorts { if let webMessageChannel = webView.webMessageChannels[notConnectedPort.webMessageChannelId] { ports.append(webMessageChannel.ports[Int(notConnectedPort.index)]) } } } message.ports = ports do { try webView.postWebMessage(message: message, targetOrigin: targetOrigin) { (_) in result(true) } } catch let error as NSError { result(FlutterError(code: "WebViewChannelDelegate", message: error.domain, details: nil)) } } else { result(false) } break case .addWebMessageListener: if let webView = webView, let plugin = webView.plugin { let webMessageListenerMap = arguments!["webMessageListener"] as! [String: Any?] let webMessageListener = WebMessageListener.fromMap(plugin: plugin, map: webMessageListenerMap)! do { try webView.addWebMessageListener(webMessageListener: webMessageListener) result(false) } catch let error as NSError { result(FlutterError(code: "WebViewChannelDelegate", message: error.domain, details: nil)) } } else { result(false) } break case .canScrollVertically: if let webView = webView { webView.canScrollVertically { canScrollVertically, error in if let error = error { print(error) result(false) return } result(canScrollVertically) } } else { result(false) } break case .canScrollHorizontally: if let webView = webView { webView.canScrollHorizontally { canScrollHorizontally, error in if let error = error { print(error) result(false) return } result(canScrollHorizontally) } } else { result(false) } break case .pauseAllMediaPlayback: if let webView = webView, #available(macOS 12.0 , *) { webView.pauseAllMediaPlayback(completionHandler: { () -> Void in result(true) }) } else { result(false) } break case .setAllMediaPlaybackSuspended: if let webView = webView, #available(macOS 12.0 , *) { let suspended = arguments!["suspended"] as! Bool webView.setAllMediaPlaybackSuspended(suspended, completionHandler: { () -> Void in result(true) }) } else { result(false) } break case .closeAllMediaPresentations: if let webView = self.webView, #available(macOS 11.3, *) { if #available(macOS 12.0, *) { webView.closeAllMediaPresentations { result(true) } } else { webView.closeAllMediaPresentations() result(true) } } else { result(false) } break case .requestMediaPlaybackState: if let webView = webView, #available(macOS 12.0, *) { webView.requestMediaPlaybackState(completionHandler: { (state) -> Void in result(state.rawValue) }) } else { result(nil) } break case .getMetaThemeColor: if let webView = webView, #available(macOS 12.0, *) { result(webView.themeColor?.hexString) } else { result(nil) } break case .isInFullscreen: if let webView = webView { if #available(macOS 13.0, *) { result(webView.fullscreenState == .inFullscreen) } else { result(webView.inFullscreen) } } else { result(false) } break case .getCameraCaptureState: if let webView = webView, #available(macOS 12.0, *) { result(webView.cameraCaptureState.rawValue) } else { result(nil) } break case .setCameraCaptureState: if let webView = webView, #available(macOS 12.0, *) { let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none webView.setCameraCaptureState(state) { result(true) } } else { result(false) } break case .getMicrophoneCaptureState: if let webView = webView, #available(macOS 12.0, *) { result(webView.microphoneCaptureState.rawValue) } else { result(nil) } break case .setMicrophoneCaptureState: if let webView = webView, #available(macOS 12.0, *) { let state = WKMediaCaptureState.init(rawValue: arguments!["state"] as! Int) ?? WKMediaCaptureState.none webView.setMicrophoneCaptureState(state) { result(true) } } else { result(false) } break case .loadSimulatedRequest: if let webView = webView, #available(macOS 12.0, *) { let request = URLRequest.init(fromPluginMap: arguments!["urlRequest"] as! [String:Any?]) let data = arguments!["data"] as! FlutterStandardTypedData var response: URLResponse? = nil if let urlResponse = arguments!["urlResponse"] as? [String:Any?] { response = URLResponse.init(fromPluginMap: urlResponse) } if let response = response { webView.loadSimulatedRequest(request, response: response, responseData: data.data) } else { webView.loadSimulatedRequest(request, responseHTML: String(decoding: data.data, as: UTF8.self)) } result(true) } else { result(false) } } } @available(*, deprecated, message: "Use FindInteractionChannelDelegate.onFindResultReceived instead.") public func onFindResultReceived(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Bool) { let arguments: [String : Any?] = [ "activeMatchOrdinal": activeMatchOrdinal, "numberOfMatches": numberOfMatches, "isDoneCounting": isDoneCounting ] channel?.invokeMethod("onFindResultReceived", arguments: arguments) } public func onLongPressHitTestResult(hitTestResult: HitTestResult) { channel?.invokeMethod("onLongPressHitTestResult", arguments: hitTestResult.toMap()) } public func onScrollChanged(x: Int, y: Int) { let arguments: [String: Any?] = ["x": x, "y": y] channel?.invokeMethod("onScrollChanged", arguments: arguments) } public func onDownloadStartRequest(request: DownloadStartRequest) { channel?.invokeMethod("onDownloadStartRequest", arguments: request.toMap()) } public func onCreateContextMenu(hitTestResult: HitTestResult) { channel?.invokeMethod("onCreateContextMenu", arguments: hitTestResult.toMap()) } public func onOverScrolled(x: Int, y: Int, clampedX: Bool, clampedY: Bool) { let arguments: [String: Any?] = ["x": x, "y": y, "clampedX": clampedX, "clampedY": clampedY] channel?.invokeMethod("onOverScrolled", arguments: arguments) } public func onContextMenuActionItemClicked(id: Any, title: String) { let arguments: [String: Any?] = [ "id": id, "iosId": id is Int64 ? String(id as! Int64) : id as! String, "androidId": nil, "title": title ] channel?.invokeMethod("onContextMenuActionItemClicked", arguments: arguments) } public func onHideContextMenu() { let arguments: [String: Any?] = [:] channel?.invokeMethod("onHideContextMenu", arguments: arguments) } public func onEnterFullscreen() { let arguments: [String: Any?] = [:] channel?.invokeMethod("onEnterFullscreen", arguments: arguments) } public func onExitFullscreen() { let arguments: [String: Any?] = [:] channel?.invokeMethod("onExitFullscreen", arguments: arguments) } public class JsAlertCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return JsAlertResponse.fromMap(map: obj as? [String:Any?]) } } } public func onJsAlert(url: URL?, message: String, isMainFrame: Bool, callback: JsAlertCallback) { if channel == nil { callback.defaultBehaviour(nil) return } let arguments: [String: Any?] = [ "url": url?.absoluteString, "message": message, "isMainFrame": isMainFrame ] channel?.invokeMethod("onJsAlert", arguments: arguments, callback: callback) } public class JsConfirmCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return JsConfirmResponse.fromMap(map: obj as? [String:Any?]) } } } public func onJsConfirm(url: URL?, message: String, isMainFrame: Bool, callback: JsConfirmCallback) { if channel == nil { callback.defaultBehaviour(nil) return } let arguments: [String: Any?] = [ "url": url?.absoluteString, "message": message, "isMainFrame": isMainFrame ] channel?.invokeMethod("onJsConfirm", arguments: arguments, callback: callback) } public class JsPromptCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return JsPromptResponse.fromMap(map: obj as? [String:Any?]) } } } public func onJsPrompt(url: URL?, message: String, defaultValue: String?, isMainFrame: Bool, callback: JsPromptCallback) { if channel == nil { callback.defaultBehaviour(nil) return } let arguments: [String: Any?] = [ "url": url?.absoluteString, "message": message, "defaultValue": defaultValue, "isMainFrame": isMainFrame ] channel?.invokeMethod("onJsPrompt", arguments: arguments, callback: callback) } public class CreateWindowCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return obj is Bool && obj as! Bool } } } public func onCreateWindow(createWindowAction: CreateWindowAction, callback: CreateWindowCallback) { if channel == nil { callback.defaultBehaviour(nil) return } channel?.invokeMethod("onCreateWindow", arguments: createWindowAction.toMap(), callback: callback) } public func onCloseWindow() { let arguments: [String: Any?] = [:] channel?.invokeMethod("onCloseWindow", arguments: arguments) } public func onConsoleMessage(message: String, messageLevel: Int) { let arguments: [String: Any?] = [ "message": message, "messageLevel": messageLevel ] channel?.invokeMethod("onConsoleMessage", arguments: arguments) } public func onProgressChanged(progress: Int) { let arguments: [String: Any?] = [ "progress": progress ] channel?.invokeMethod("onProgressChanged", arguments: arguments) } public func onTitleChanged(title: String?) { let arguments: [String: Any?] = [ "title": title ] channel?.invokeMethod("onTitleChanged", arguments: arguments) } public class PermissionRequestCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return PermissionResponse.fromMap(map: obj as? [String:Any?]) } } deinit { self.defaultBehaviour(nil) } } public func onPermissionRequest(request: PermissionRequest, callback: PermissionRequestCallback) { if channel == nil { callback.defaultBehaviour(nil) return } channel?.invokeMethod("onPermissionRequest", arguments: request.toMap(), callback: callback) } public class ShouldOverrideUrlLoadingCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in if let action = obj as? Int { return WKNavigationActionPolicy.init(rawValue: action) ?? WKNavigationActionPolicy.cancel } return WKNavigationActionPolicy.cancel } } deinit { self.defaultBehaviour(nil) } } public func shouldOverrideUrlLoading(navigationAction: WKNavigationAction, callback: ShouldOverrideUrlLoadingCallback) { if channel == nil { callback.defaultBehaviour(nil) return } channel?.invokeMethod("shouldOverrideUrlLoading", arguments: navigationAction.toMap(), callback: callback) } public func onLoadStart(url: String?) { let arguments: [String: Any?] = ["url": url] channel?.invokeMethod("onLoadStart", arguments: arguments) } public func onLoadStop(url: String?) { let arguments: [String: Any?] = ["url": url] channel?.invokeMethod("onLoadStop", arguments: arguments) } public func onUpdateVisitedHistory(url: String?, isReload: Bool?) { let arguments: [String: Any?] = [ "url": url, "isReload": nil ] channel?.invokeMethod("onUpdateVisitedHistory", arguments: arguments) } public func onReceivedError(request: WebResourceRequest, error: WebResourceError) { let arguments: [String: Any?] = [ "request": request.toMap(), "error": error.toMap() ] channel?.invokeMethod("onReceivedError", arguments: arguments) } public func onReceivedHttpError(request: WebResourceRequest, errorResponse: WebResourceResponse) { let arguments: [String: Any?] = [ "request": request.toMap(), "errorResponse": errorResponse.toMap() ] channel?.invokeMethod("onReceivedHttpError", arguments: arguments) } public class ReceivedHttpAuthRequestCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return HttpAuthResponse.fromMap(map: obj as? [String:Any?]) } } deinit { self.defaultBehaviour(nil) } } public func onReceivedHttpAuthRequest(challenge: HttpAuthenticationChallenge, callback: ReceivedHttpAuthRequestCallback) { if channel == nil { callback.defaultBehaviour(nil) return } // workaround for ProtectionSpace.toMap() SSL Certificate // https://github.com/pichillilorenzo/flutter_inappwebview/issues/1678 DispatchQueue.global(qos: .background).async { let arguments = challenge.toMap() DispatchQueue.main.async { [weak self] in if self?.channel == nil { callback.defaultBehaviour(nil) return } self?.channel?.invokeMethod("onReceivedHttpAuthRequest", arguments: arguments, callback: callback) } } } public class ReceivedServerTrustAuthRequestCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return ServerTrustAuthResponse.fromMap(map: obj as? [String:Any?]) } } deinit { self.defaultBehaviour(nil) } } public func onReceivedServerTrustAuthRequest(challenge: ServerTrustChallenge, callback: ReceivedServerTrustAuthRequestCallback) { if channel == nil { callback.defaultBehaviour(nil) return } // workaround for ProtectionSpace.toMap() SSL Certificate // https://github.com/pichillilorenzo/flutter_inappwebview/issues/1678 DispatchQueue.global(qos: .background).async { let arguments = challenge.toMap() DispatchQueue.main.async { [weak self] in if self?.channel == nil { callback.defaultBehaviour(nil) return } self?.channel?.invokeMethod("onReceivedServerTrustAuthRequest", arguments: arguments, callback: callback) } } } public class ReceivedClientCertRequestCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return ClientCertResponse.fromMap(map: obj as? [String:Any?]) } } deinit { self.defaultBehaviour(nil) } } public func onReceivedClientCertRequest(challenge: ClientCertChallenge, callback: ReceivedClientCertRequestCallback) { if channel == nil { callback.defaultBehaviour(nil) return } // workaround for ProtectionSpace.toMap() SSL Certificate // https://github.com/pichillilorenzo/flutter_inappwebview/issues/1678 DispatchQueue.global(qos: .background).async { let arguments = challenge.toMap() DispatchQueue.main.async { [weak self] in if self?.channel == nil { callback.defaultBehaviour(nil) return } self?.channel?.invokeMethod("onReceivedClientCertRequest", arguments: arguments, callback: callback) } } } public func onZoomScaleChanged(newScale: Float, oldScale: Float) { let arguments: [String: Any?] = [ "newScale": newScale, "oldScale": oldScale ] channel?.invokeMethod("onZoomScaleChanged", arguments: arguments) } public func onPageCommitVisible(url: String?) { let arguments: [String: Any?] = [ "url": url ] channel?.invokeMethod("onPageCommitVisible", arguments: arguments) } public class LoadResourceWithCustomSchemeCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return CustomSchemeResponse.fromMap(map: obj as? [String:Any?]) } } } public func onLoadResourceWithCustomScheme(request: WebResourceRequest, callback: LoadResourceWithCustomSchemeCallback) { if channel == nil { callback.defaultBehaviour(nil) return } let arguments: [String: Any?] = ["request": request.toMap()] channel?.invokeMethod("onLoadResourceWithCustomScheme", arguments: arguments, callback: callback) } public class CallJsHandlerCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return obj } } } public func onCallJsHandler(handlerName: String, args: String, callback: CallJsHandlerCallback) { if channel == nil { callback.defaultBehaviour(nil) return } let arguments: [String: Any?] = [ "handlerName": handlerName, "args": args ] channel?.invokeMethod("onCallJsHandler", arguments: arguments, callback: callback) } public class NavigationResponseCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in if let action = obj as? Int { return WKNavigationResponsePolicy.init(rawValue: action) ?? WKNavigationResponsePolicy.cancel } return WKNavigationResponsePolicy.cancel } } deinit { self.defaultBehaviour(nil) } } public func onNavigationResponse(navigationResponse: WKNavigationResponse, callback: NavigationResponseCallback) { if channel == nil { callback.defaultBehaviour(nil) return } channel?.invokeMethod("onNavigationResponse", arguments: navigationResponse.toMap(), callback: callback) } public class ShouldAllowDeprecatedTLSCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in if let action = obj as? Int { return action == 1 } return false } } deinit { self.defaultBehaviour(nil) } } public func shouldAllowDeprecatedTLS(challenge: URLAuthenticationChallenge, callback: ShouldAllowDeprecatedTLSCallback) { if channel == nil { callback.defaultBehaviour(nil) return } // workaround for ProtectionSpace.toMap() SSL Certificate // https://github.com/pichillilorenzo/flutter_inappwebview/issues/1678 DispatchQueue.global(qos: .background).async { let arguments = challenge.toMap() DispatchQueue.main.async { [weak self] in if self?.channel == nil { callback.defaultBehaviour(nil) return } self?.channel?.invokeMethod("shouldAllowDeprecatedTLS", arguments: arguments, callback: callback) } } } public func onWebContentProcessDidTerminate() { let arguments: [String: Any?] = [:] channel?.invokeMethod("onWebContentProcessDidTerminate", arguments: arguments) } public func onDidReceiveServerRedirectForProvisionalNavigation() { let arguments: [String: Any?] = [:] channel?.invokeMethod("onDidReceiveServerRedirectForProvisionalNavigation", arguments: arguments) } @available(macOS 12.0, *) public func onCameraCaptureStateChanged(oldState: WKMediaCaptureState?, newState: WKMediaCaptureState?) { let arguments = [ "oldState": oldState?.rawValue, "newState": newState?.rawValue ] channel?.invokeMethod("onCameraCaptureStateChanged", arguments: arguments) } @available(macOS 12.0, *) public func onMicrophoneCaptureStateChanged(oldState: WKMediaCaptureState?, newState: WKMediaCaptureState?) { let arguments = [ "oldState": oldState?.rawValue, "newState": newState?.rawValue ] channel?.invokeMethod("onMicrophoneCaptureStateChanged", arguments: arguments) } public class PrintRequestCallback: BaseCallbackResult { override init() { super.init() self.decodeResult = { (obj: Any?) in return obj is Bool && obj as! Bool } } } public func onPrintRequest(url: URL?, printJobId: String?, callback: PrintRequestCallback) { if channel == nil { callback.defaultBehaviour(nil) return } let arguments = [ "url": url?.absoluteString, "printJobId": printJobId, ] channel?.invokeMethod("onPrintRequest", arguments: arguments, callback: callback) } public override func dispose() { super.dispose() webView = nil } deinit { debugPrint("WebViewChannelDelegate - dealloc") dispose() } }