diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 94ea0ff9f2a9..b4fc793df827 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.18.4 + +* Fixes crash when native `WKFrameInfo.request` is nil. + ## 3.18.3 * Fixes crash where the native `AuthenticationChallengeResponse` could not be found for auth diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/FrameInfoProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/FrameInfoProxyAPITests.swift index d7ec12258aa5..a0ece4a78be1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/FrameInfoProxyAPITests.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/FrameInfoProxyAPITests.swift @@ -28,6 +28,16 @@ class FrameInfoProxyAPITests: XCTestCase { XCTAssertEqual(value?.value, instance!.request) } + + @MainActor func testNilRequest() { + let registrar = TestProxyApiRegistrar() + let api = registrar.apiDelegate.pigeonApiWKFrameInfo(registrar) + + let instance = TestFrameInfoWithNilRequest() + let value = try? api.pigeonDelegate.request(pigeonApi: api, pigeonInstance: instance) + + XCTAssertNil(value) + } } class TestFrameInfo: WKFrameInfo { @@ -39,3 +49,6 @@ class TestFrameInfo: WKFrameInfo { return URLRequest(url: URL(string: "https://google.com")!) } } + +class TestFrameInfoWithNilRequest: WKFrameInfo { +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FrameInfoProxyAPIDelegate.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FrameInfoProxyAPIDelegate.swift index b14398e239b3..5fd11474f80d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FrameInfoProxyAPIDelegate.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FrameInfoProxyAPIDelegate.swift @@ -4,6 +4,18 @@ import WebKit +extension WKFrameInfo { + // It's possible that `WKFrameInfo.request` can be a nil value despite the Swift code considering + // it to be nonnull. This causes a crash when accessing the value with Swift. Accessing the value + // this way prevents the crash when the value is nil. + // + // See https://github.com/flutter/flutter/issues/163549 and https://developer.apple.com/forums/thread/77888. + var maybeRequest: URLRequest? { + return self.perform(#selector(getter:WKFrameInfo.request))?.takeUnretainedValue() + as! URLRequest? + } +} + /// ProxyApi implementation for `WKFrameInfo`. /// /// This class may handle instantiating native object instances that are attached to a Dart instance @@ -14,8 +26,12 @@ class FrameInfoProxyAPIDelegate: PigeonApiDelegateWKFrameInfo { } func request(pigeonApi: PigeonApiWKFrameInfo, pigeonInstance: WKFrameInfo) throws - -> URLRequestWrapper + -> URLRequestWrapper? { - return URLRequestWrapper(pigeonInstance.request) + let request = pigeonInstance.maybeRequest + if let request = request { + return URLRequestWrapper(request) + } + return nil } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebKitLibrary.g.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebKitLibrary.g.swift index d2b88d745e1b..177b66a2e6c6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebKitLibrary.g.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebKitLibrary.g.swift @@ -2125,7 +2125,7 @@ protocol PigeonApiDelegateWKFrameInfo { func isMainFrame(pigeonApi: PigeonApiWKFrameInfo, pigeonInstance: WKFrameInfo) throws -> Bool /// The frame’s current request. func request(pigeonApi: PigeonApiWKFrameInfo, pigeonInstance: WKFrameInfo) throws - -> URLRequestWrapper + -> URLRequestWrapper? } protocol PigeonApiProtocolWKFrameInfo { @@ -3993,8 +3993,15 @@ protocol PigeonApiProtocolWKNavigationDelegate { completion: @escaping (Result) -> Void) /// Asks the delegate to respond to an authentication challenge. /// - /// Returns a List with a `UrlSessionAuthChallengeDisposition` and a nullable - /// `URLCredential`. + /// This return value expects a List with: + /// + /// 1. `UrlSessionAuthChallengeDisposition` + /// 2. A nullable map to instantiate a `URLCredential`. The map structure is + /// [ + /// "user": "", + /// "password": "", + /// "persistence": , + /// ] func didReceiveAuthenticationChallenge( pigeonInstance pigeonInstanceArg: WKNavigationDelegate, webView webViewArg: WKWebView, challenge challengeArg: URLAuthenticationChallenge, @@ -4337,8 +4344,15 @@ final class PigeonApiWKNavigationDelegate: PigeonApiProtocolWKNavigationDelegate /// Asks the delegate to respond to an authentication challenge. /// - /// Returns a List with a `UrlSessionAuthChallengeDisposition` and a nullable - /// `URLCredential`. + /// This return value expects a List with: + /// + /// 1. `UrlSessionAuthChallengeDisposition` + /// 2. A nullable map to instantiate a `URLCredential`. The map structure is + /// [ + /// "user": "", + /// "password": "", + /// "persistence": , + /// ] func didReceiveAuthenticationChallenge( pigeonInstance pigeonInstanceArg: WKNavigationDelegate, webView webViewArg: WKWebView, challenge challengeArg: URLAuthenticationChallenge, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart index 428e45c2f571..ee91ba63f41f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart @@ -1756,7 +1756,7 @@ class WKFrameInfo extends NSObject { super.pigeon_binaryMessenger, super.pigeon_instanceManager, required this.isMainFrame, - required this.request, + this.request, super.observeValue, }) : super.pigeon_detached(); @@ -1765,7 +1765,7 @@ class WKFrameInfo extends NSObject { final bool isMainFrame; /// The frame’s current request. - final URLRequest request; + final URLRequest? request; static void pigeon_setUpMessageHandlers({ bool pigeon_clearHandlers = false, @@ -1773,7 +1773,7 @@ class WKFrameInfo extends NSObject { PigeonInstanceManager? pigeon_instanceManager, WKFrameInfo Function( bool isMainFrame, - URLRequest request, + URLRequest? request, )? pigeon_newInstance, }) { final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = @@ -1801,17 +1801,15 @@ class WKFrameInfo extends NSObject { assert(arg_isMainFrame != null, 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKFrameInfo.pigeon_newInstance was null, expected non-null bool.'); final URLRequest? arg_request = (args[2] as URLRequest?); - assert(arg_request != null, - 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKFrameInfo.pigeon_newInstance was null, expected non-null URLRequest.'); try { (pigeon_instanceManager ?? PigeonInstanceManager.instance) .addHostCreatedInstance( - pigeon_newInstance?.call(arg_isMainFrame!, arg_request!) ?? + pigeon_newInstance?.call(arg_isMainFrame!, arg_request) ?? WKFrameInfo.pigeon_detached( pigeon_binaryMessenger: pigeon_binaryMessenger, pigeon_instanceManager: pigeon_instanceManager, isMainFrame: arg_isMainFrame!, - request: arg_request!, + request: arg_request, ), arg_pigeon_instanceIdentifier!, ); @@ -4326,8 +4324,15 @@ class WKNavigationDelegate extends NSObject { /// Asks the delegate to respond to an authentication challenge. /// - /// Returns a List with a `UrlSessionAuthChallengeDisposition` and a nullable - /// `URLCredential`. + /// This return value expects a List with: + /// + /// 1. `UrlSessionAuthChallengeDisposition` + /// 2. A nullable map to instantiate a `URLCredential`. The map structure is + /// [ + /// "user": "", + /// "password": "", + /// "persistence": , + /// ] /// /// For the associated Native object to be automatically garbage collected, /// it is required that the implementation of this `Function` doesn't have a diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart index 4bf9d87aedf7..2ece1206dae8 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart @@ -235,7 +235,7 @@ class WebKitWebViewController extends PlatformWebViewController { final JavaScriptAlertDialogRequest request = JavaScriptAlertDialogRequest( message: message, - url: await frame.request.getUrl() ?? '', + url: await frame.request?.getUrl() ?? '', ); await callback.call(request); return; @@ -253,7 +253,7 @@ class WebKitWebViewController extends PlatformWebViewController { final JavaScriptConfirmDialogRequest request = JavaScriptConfirmDialogRequest( message: message, - url: await frame.request.getUrl() ?? '', + url: await frame.request?.getUrl() ?? '', ); final bool result = await callback.call(request); return result; @@ -274,7 +274,7 @@ class WebKitWebViewController extends PlatformWebViewController { final JavaScriptTextInputDialogRequest request = JavaScriptTextInputDialogRequest( message: prompt, - url: await frame.request.getUrl() ?? '', + url: await frame.request?.getUrl() ?? '', defaultText: defaultText); final String result = await callback.call(request); return result; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index c1dc26823a33..20b74cc72ade 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -448,7 +448,7 @@ abstract class WKFrameInfo extends NSObject { late bool isMainFrame; /// The frame’s current request. - late URLRequest request; + late URLRequest? request; } /// Information about an error condition including a domain, a domain-specific diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 518e0b3465cb..5a0e7ed115ae 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.18.3 +version: 3.18.4 environment: sdk: ^3.5.0