From 2cd0948620b42309e78205828397e8f3443a3e16 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Wed, 10 Feb 2021 02:32:05 +0100 Subject: [PATCH] Added iosShouldAllowDeprecatedTLS iOS-specific WebView event --- CHANGELOG.md | 3 +- README.md | 2 ++ example/ios/Runner/Info.plist | 2 ++ ios/Classes/InAppWebView.swift | 44 ++++++++++++++++++++++++++ lib/src/headless_in_app_webview.dart | 8 ++++- lib/src/in_app_browser.dart | 14 ++++++-- lib/src/in_app_webview.dart | 8 ++++- lib/src/in_app_webview_controller.dart | 23 +++++++++++++- lib/src/types.dart | 31 ++++++++++++++++-- lib/src/webview.dart | 16 ++++++++-- 10 files changed, 141 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f408550..b4802b26 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ - Added `contentWorld` argument to `evaluateJavascript` WebView method - Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains`, `useOnNavigationResponse` iOS-specific WebView options - Added `handlesURLScheme`, `createPdf`, `createWebArchiveData` iOS-specific WebView methods -- Added `iosOnNavigationResponse` iOS-specific WebView events +- Added `iosOnNavigationResponse` and `iosShouldAllowDeprecatedTLS` iOS-specific WebView events - Added `iosAnimated` optional argument to `zoomBy` WebView method - Added `screenshotConfiguration` optional argument to `takeScreenshot` WebView method - Added `scriptHtmlTagAttributes` optional argument to `injectJavascriptFileFromUrl` WebView method @@ -50,6 +50,7 @@ - Added `callAsyncJavaScript` name to the list of javaScriptHandlerForbiddenNames - Changed `zoomBy` WebView method signature - Moved `saveWebArchive` WebView method from Android-specific to cross-platform +- Renamed `HttpAuthChallenge` to `URLAuthenticationChallenge` ## 4.0.0+4 diff --git a/README.md b/README.md index 97f46a5c..e1ffa9be 100755 --- a/README.md +++ b/README.md @@ -719,6 +719,8 @@ Event names that starts with `android` or `ios` are events platform-specific. * `androidOnReceivedLoginRequest`: Event fired when a request to automatically log in the user has been processed (available only on Android). * `iosOnWebContentProcessDidTerminate`: Invoked when the web view's web content process is terminated (available only on iOS). * `iosOnDidReceiveServerRedirectForProvisionalNavigation`: Called when a web view receives a server redirect (available only on iOS). +* `iosOnNavigationResponse`: Called when a web view asks for permission to navigate to new content after the response to the navigation request is known (available only on iOS). +* `iosShouldAllowDeprecatedTLS`: Called when a web view asks whether to continue with a connection that uses a deprecated version of TLS (v1.0 and v1.1) (available only on iOS). ### `ContextMenu` class diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index df18581f..d62ce85a 100755 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -28,6 +28,8 @@ NSAppTransportSecurity + NSAllowsArbitraryLoads + NSAllowsLocalNetworking diff --git a/ios/Classes/InAppWebView.swift b/ios/Classes/InAppWebView.swift index 072077ff..ae9e5cb8 100755 --- a/ios/Classes/InAppWebView.swift +++ b/ios/Classes/InAppWebView.swift @@ -3106,6 +3106,39 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi return windowWebView } + public func webView(_ webView: WKWebView, + authenticationChallenge challenge: URLAuthenticationChallenge, + shouldAllowDeprecatedTLS decisionHandler: @escaping (Bool) -> Void) { + shouldAllowDeprecatedTLS(challenge: challenge, result: {(result) -> Void in + if result is FlutterError { + print((result as! FlutterError).message ?? "") + } + else if (result as? NSObject) == FlutterMethodNotImplemented { + decisionHandler(false) + } + else { + var response: [String: Any] + if let r = result { + response = r as! [String: Any] + var action = response["action"] as? Int + action = action != nil ? action : 0; + switch action { + case 0: + decisionHandler(false) + break + case 1: + decisionHandler(true) + break + default: + decisionHandler(false) + } + return; + } + decisionHandler(false) + } + }) + } + public func webViewDidClose(_ webView: WKWebView) { let arguments: [String: Any?] = [:] channel?.invokeMethod("onCloseWindow", arguments: arguments) @@ -3396,6 +3429,17 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi channel?.invokeMethod("onReceivedClientCertRequest", arguments: arguments, result: result) } + public func shouldAllowDeprecatedTLS(challenge: URLAuthenticationChallenge, result: FlutterResult?) { + let arguments: [String: Any?] = [ + "host": challenge.protectionSpace.host, + "protocol": challenge.protectionSpace.protocol, + "realm": challenge.protectionSpace.realm, + "port": challenge.protectionSpace.port, + "previousFailureCount": challenge.previousFailureCount + ] + channel?.invokeMethod("shouldAllowDeprecatedTLS", arguments: arguments, result: result) + } + public func onJsAlert(frame: WKFrameInfo, message: String, result: FlutterResult?) { let arguments: [String: Any?] = [ "url": frame.request.url?.absoluteString, diff --git a/lib/src/headless_in_app_webview.dart b/lib/src/headless_in_app_webview.dart index aebc558e..8652d969 100644 --- a/lib/src/headless_in_app_webview.dart +++ b/lib/src/headless_in_app_webview.dart @@ -78,6 +78,7 @@ class HeadlessInAppWebView implements WebView { this.iosOnWebContentProcessDidTerminate, this.iosOnDidReceiveServerRedirectForProvisionalNavigation, this.iosOnNavigationResponse, + this.iosShouldAllowDeprecatedTLS, this.initialUrl, this.initialFile, this.initialData, @@ -196,6 +197,11 @@ class HeadlessInAppWebView implements WebView { IOSNavigationResponse navigationResponse)? iosOnNavigationResponse; + @override + final Future Function(InAppWebViewController controller, + URLAuthenticationChallenge challenge)? + iosShouldAllowDeprecatedTLS; + @override final Future Function( InAppWebViewController controller, AjaxRequest ajaxRequest)? @@ -293,7 +299,7 @@ class HeadlessInAppWebView implements WebView { @override final Future Function( - InAppWebViewController controller, HttpAuthChallenge challenge)? + InAppWebViewController controller, URLAuthenticationChallenge challenge)? onReceivedHttpAuthRequest; @override diff --git a/lib/src/in_app_browser.dart b/lib/src/in_app_browser.dart index 79b32f28..35db52d0 100755 --- a/lib/src/in_app_browser.dart +++ b/lib/src/in_app_browser.dart @@ -418,13 +418,13 @@ class InAppBrowser { ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. /// - ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge]. + ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [URLAuthenticationChallenge]. /// ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String) /// ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview Future? onReceivedHttpAuthRequest( - HttpAuthChallenge challenge) {} + URLAuthenticationChallenge challenge) {} ///Event fired when the WebView need to perform server trust authentication (certificate validation). ///The host application must return either [ServerTrustAuthResponse] instance with [ServerTrustAuthResponseAction.CANCEL] or [ServerTrustAuthResponseAction.PROCEED]. @@ -763,6 +763,16 @@ class InAppBrowser { Future? iosOnNavigationResponse(IOSNavigationResponse navigationResponse) {} + ///Called when a web view asks whether to continue with a connection that uses a deprecated version of TLS (v1.0 and v1.1). + /// + ///[challenge] represents the authentication challenge. + /// + ///**NOTE**: available only on iOS 14.0+. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/3601237-webview + Future? + iosShouldAllowDeprecatedTLS(URLAuthenticationChallenge challenge) {} + void throwIsAlreadyOpened({String message = ''}) { if (this.isOpened()) { throw Exception([ diff --git a/lib/src/in_app_webview.dart b/lib/src/in_app_webview.dart index 77a66bb8..abe84b18 100755 --- a/lib/src/in_app_webview.dart +++ b/lib/src/in_app_webview.dart @@ -90,6 +90,7 @@ class InAppWebView extends StatefulWidget implements WebView { this.iosOnWebContentProcessDidTerminate, this.iosOnDidReceiveServerRedirectForProvisionalNavigation, this.iosOnNavigationResponse, + this.iosShouldAllowDeprecatedTLS, this.gestureRecognizers, }) : super(key: key); @@ -157,6 +158,11 @@ class InAppWebView extends StatefulWidget implements WebView { IOSNavigationResponse navigationResponse)? iosOnNavigationResponse; + @override + final Future Function(InAppWebViewController controller, + URLAuthenticationChallenge challenge)? + iosShouldAllowDeprecatedTLS; + @override final Future Function( InAppWebViewController controller, AjaxRequest ajaxRequest)? @@ -263,7 +269,7 @@ class InAppWebView extends StatefulWidget implements WebView { @override final Future Function( - InAppWebViewController controller, HttpAuthChallenge challenge)? + InAppWebViewController controller, URLAuthenticationChallenge challenge)? onReceivedHttpAuthRequest; @override diff --git a/lib/src/in_app_webview_controller.dart b/lib/src/in_app_webview_controller.dart index b4441e7a..b615e79b 100644 --- a/lib/src/in_app_webview_controller.dart +++ b/lib/src/in_app_webview_controller.dart @@ -489,7 +489,7 @@ class InAppWebViewController { int previousFailureCount = call.arguments["previousFailureCount"]; var protectionSpace = ProtectionSpace( host: host, protocol: protocol, realm: realm, port: port); - var challenge = HttpAuthChallenge( + var challenge = URLAuthenticationChallenge( previousFailureCount: previousFailureCount, protectionSpace: protectionSpace); if (_webview != null && _webview!.onReceivedHttpAuthRequest != null) @@ -675,6 +675,27 @@ class InAppWebViewController { .iosOnNavigationResponse(iosOnNavigationResponse)) ?.toMap(); break; + case "shouldAllowDeprecatedTLS": + String host = call.arguments["host"]; + String protocol = call.arguments["protocol"]; + String? realm = call.arguments["realm"]; + int? port = call.arguments["port"]; + int previousFailureCount = call.arguments["previousFailureCount"]; + var protectionSpace = ProtectionSpace( + host: host, protocol: protocol, realm: realm, port: port); + var challenge = URLAuthenticationChallenge( + previousFailureCount: previousFailureCount, + protectionSpace: protectionSpace); + + if (_webview != null && _webview!.iosShouldAllowDeprecatedTLS != null) + return (await _webview!.iosShouldAllowDeprecatedTLS!( + this, challenge)) + ?.toMap(); + else if (_inAppBrowser != null) + return (await _inAppBrowser! + .iosShouldAllowDeprecatedTLS(challenge)) + ?.toMap(); + break; case "onLongPressHitTestResult": Map? hitTestResultMap = call.arguments["hitTestResult"]; diff --git a/lib/src/types.dart b/lib/src/types.dart index 0132c3bf..74f72420 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -995,14 +995,14 @@ class HttpAuthResponse { ///Class that represents the challenge of the [WebView.onReceivedHttpAuthRequest] event. ///It provides all the information about the challenge. -class HttpAuthChallenge { +class URLAuthenticationChallenge { ///A count of previous failed authentication attempts. int previousFailureCount; ///The protection space requiring authentication. ProtectionSpace? protectionSpace; - HttpAuthChallenge( + URLAuthenticationChallenge( {required this.previousFailureCount, required this.protectionSpace}); Map toMap() { @@ -5532,3 +5532,30 @@ class IOSNavigationResponseAction { }; } } + +///Class that is used by [WebView.iosShouldAllowDeprecatedTLS] event. +///It represents the policy to pass back to the decision handler. +class IOSShouldAllowDeprecatedTLSAction { + final int _value; + + const IOSShouldAllowDeprecatedTLSAction._internal(this._value); + + int toValue() => _value; + + ///Cancel the navigation. + static const CANCEL = const IOSShouldAllowDeprecatedTLSAction._internal(0); + + ///Allow the navigation to continue. + static const ALLOW = const IOSShouldAllowDeprecatedTLSAction._internal(1); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; + + Map toMap() { + return { + "action": _value, + }; + } +} diff --git a/lib/src/webview.dart b/lib/src/webview.dart index 251250a1..a2220735 100644 --- a/lib/src/webview.dart +++ b/lib/src/webview.dart @@ -218,13 +218,13 @@ abstract class WebView { ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. /// - ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge]. + ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [URLAuthenticationChallenge]. /// ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String) /// ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview final Future Function( - InAppWebViewController controller, HttpAuthChallenge challenge)? + InAppWebViewController controller, URLAuthenticationChallenge challenge)? onReceivedHttpAuthRequest; ///Event fired when the WebView need to perform server trust authentication (certificate validation). @@ -615,6 +615,17 @@ abstract class WebView { IOSNavigationResponse navigationResponse)? iosOnNavigationResponse; + ///Called when a web view asks whether to continue with a connection that uses a deprecated version of TLS (v1.0 and v1.1). + /// + ///[challenge] represents the authentication challenge. + /// + ///**NOTE**: available only on iOS 14.0+. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/3601237-webview + final Future Function(InAppWebViewController controller, + URLAuthenticationChallenge challenge)? + iosShouldAllowDeprecatedTLS; + ///Initial url that will be loaded. final String? initialUrl; @@ -692,6 +703,7 @@ abstract class WebView { this.iosOnWebContentProcessDidTerminate, this.iosOnDidReceiveServerRedirectForProvisionalNavigation, this.iosOnNavigationResponse, + this.iosShouldAllowDeprecatedTLS, this.initialUrl, this.initialFile, this.initialData,