diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index a4ea405f961..11dbc2c5a86 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.18.6 + +* Fixes `PlatformException` when calling `loadFlutterAsset` on macOS. +* Updates native wrapper with methods handling `SecTust` and `SecCertificate`. + ## 3.18.5 * Fixes crash when sending undefined message via JavaScript channel. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/GetTrustResultResponseProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/GetTrustResultResponseProxyAPITests.swift new file mode 100644 index 00000000000..0ab7203f063 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/GetTrustResultResponseProxyAPITests.swift @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import XCTest + +@testable import webview_flutter_wkwebview + +class GetTrustResultResponseProxyAPITests: XCTestCase { + func testResult() { + let registrar = TestProxyApiRegistrar() + let api = registrar.apiDelegate.pigeonApiGetTrustResultResponse(registrar) + + let instance = GetTrustResultResponse(result: SecTrustResultType.invalid, resultCode: -1) + let value = try? api.pigeonDelegate.result(pigeonApi: api, pigeonInstance: instance) + + XCTAssertEqual(value, DartSecTrustResultType.invalid) + } + + func testResultCode() { + let registrar = TestProxyApiRegistrar() + let api = registrar.apiDelegate.pigeonApiGetTrustResultResponse(registrar) + + let instance = GetTrustResultResponse(result: SecTrustResultType.invalid, resultCode: -1) + let value = try? api.pigeonDelegate.resultCode(pigeonApi: api, pigeonInstance: instance) + + XCTAssertEqual(value, -1) + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/SecCertificateProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/SecCertificateProxyAPITests.swift new file mode 100644 index 00000000000..7057b53e791 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/SecCertificateProxyAPITests.swift @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import XCTest + +@testable import webview_flutter_wkwebview + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +class SecCertificateProxyAPITests: XCTestCase { + func createDummyCertificate() -> SecCertificate { + let url = FlutterAssetManager().urlForAsset("assets/test_cert.der")! + let certificateData = NSData(contentsOf: url) + + return SecCertificateCreateWithData(nil, certificateData!)! + } + + func testCopyData() { + let registrar = TestProxyApiRegistrar() + let delegate = TestSecCertificateProxyAPIDelegate() + let api = PigeonApiSecCertificate(pigeonRegistrar: registrar, delegate: delegate) + + let value = try? api.pigeonDelegate.copyData( + pigeonApi: api, certificate: SecCertificateWrapper(value: createDummyCertificate())) + + XCTAssertEqual(value?.data, delegate.data) + } +} + +class TestSecCertificateProxyAPIDelegate: SecCertificateProxyAPIDelegate { + let data = Data() + + override func secCertificateCopyData(_ certificate: SecCertificate) -> CFData { + return data as CFData + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/SecTrustProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/SecTrustProxyAPITests.swift new file mode 100644 index 00000000000..ef92c27eb5f --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/SecTrustProxyAPITests.swift @@ -0,0 +1,133 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import XCTest + +@testable import webview_flutter_wkwebview + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +class SecTrustProxyAPITests: XCTestCase { + func createTrust(delegate: TestSecTrustProxyAPIDelegate) -> SecTrustWrapper { + var trust: SecTrust? + SecTrustCreateWithCertificates( + [delegate.createDummyCertificate()] as AnyObject, SecPolicyCreateBasicX509(), &trust) + + return SecTrustWrapper(value: trust!) + } + + func testEvaluateWithError() { + let registrar = TestProxyApiRegistrar() + let delegate = TestSecTrustProxyAPIDelegate() + let api = PigeonApiSecTrust(pigeonRegistrar: registrar, delegate: delegate) + + let expect = expectation(description: "Wait for setCookie.") + let trust = createTrust(delegate: delegate) + var resultValue: Bool? + + api.pigeonDelegate.evaluateWithError(pigeonApi: api, trust: trust) { result in + switch result { + case .success(let value): + resultValue = value + case .failure(_): + break + } + expect.fulfill() + } + + wait(for: [expect], timeout: 5.0) + XCTAssertEqual(resultValue, true) + } + + func testCopyExceptions() { + let registrar = TestProxyApiRegistrar() + let delegate = TestSecTrustProxyAPIDelegate() + let api = PigeonApiSecTrust(pigeonRegistrar: registrar, delegate: delegate) + + let trust = createTrust(delegate: delegate) + let value = try? api.pigeonDelegate.copyExceptions(pigeonApi: api, trust: trust) + + XCTAssertEqual(value?.data, Data()) + } + + func testSetExceptions() { + let registrar = TestProxyApiRegistrar() + let delegate = TestSecTrustProxyAPIDelegate() + let api = PigeonApiSecTrust(pigeonRegistrar: registrar, delegate: delegate) + + let trust = createTrust(delegate: delegate) + let value = try? api.pigeonDelegate.setExceptions( + pigeonApi: api, trust: trust, exceptions: FlutterStandardTypedData(bytes: Data())) + + XCTAssertEqual(value, false) + } + + func testGetTrustResult() { + let registrar = TestProxyApiRegistrar() + let delegate = TestSecTrustProxyAPIDelegate() + let api = PigeonApiSecTrust(pigeonRegistrar: registrar, delegate: delegate) + + let trust = createTrust(delegate: delegate) + let value = try? api.pigeonDelegate.getTrustResult(pigeonApi: api, trust: trust) + + XCTAssertEqual(value?.result, SecTrustResultType.invalid) + XCTAssertEqual(value?.resultCode, -1) + } + + func testCopyCertificateChain() { + let registrar = TestProxyApiRegistrar() + let delegate = TestSecTrustProxyAPIDelegate() + let api = PigeonApiSecTrust(pigeonRegistrar: registrar, delegate: delegate) + + let trust = createTrust(delegate: delegate) + let value = try? api.pigeonDelegate.copyCertificateChain(pigeonApi: api, trust: trust) + + XCTAssertEqual(value?.count, 1) + XCTAssertNotNil(value?.first?.value) + } +} + +class TestSecTrustProxyAPIDelegate: SecTrustProxyAPIDelegate { + func createDummyCertificate() -> SecCertificate { + let url = FlutterAssetManager().urlForAsset("assets/test_cert.der")! + let certificateData = NSData(contentsOf: url) + + return SecCertificateCreateWithData(nil, certificateData!)! + } + + override func secTrustEvaluateWithError( + _ trust: SecTrust, _ error: UnsafeMutablePointer? + ) -> Bool { + return true + } + + override func secTrustCopyExceptions(_ trust: SecTrust) -> CFData? { + return Data() as CFData + } + + override func secTrustSetExceptions(_ trust: SecTrust, _ exceptions: CFData?) -> Bool { + return false + } + + override func secTrustGetTrustResult( + _ trust: SecTrust, _ result: UnsafeMutablePointer + ) -> OSStatus { + result.pointee = SecTrustResultType.invalid + return -1 + } + + override func secTrustCopyCertificateChain(_ trust: SecTrust) -> CFArray? { + if #available(iOS 15.0, *) { + return [createDummyCertificate()] as CFArray + } + + return nil + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/TestProxyApiRegistrar.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/TestProxyApiRegistrar.swift index 98142d219fd..be7c1bde17d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/TestProxyApiRegistrar.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/TestProxyApiRegistrar.swift @@ -8,7 +8,9 @@ import XCTest class TestProxyApiRegistrar: ProxyAPIRegistrar { init() { - super.init(binaryMessenger: TestBinaryMessenger(), bundle: TestBundle()) + super.init( + binaryMessenger: TestBinaryMessenger(), + assetManager: FlutterAssetManager(bundle: TestBundle())) } override func dispatchOnMainThread( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/URLProtectionSpaceProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/URLProtectionSpaceProxyAPITests.swift index fcb9e9e4fb9..0ca297dd2f5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/URLProtectionSpaceProxyAPITests.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/Tests/URLProtectionSpaceProxyAPITests.swift @@ -56,4 +56,34 @@ class ProtectionSpaceProxyAPITests: XCTestCase { XCTAssertEqual(value, instance.authenticationMethod) } + func testGetServerTrust() { + let registrar = TestProxyApiRegistrar() + let api = registrar.apiDelegate.pigeonApiURLProtectionSpace(registrar) + + let instance = TestProtectionSpace( + host: "host", port: 23, protocol: "protocol", realm: "realm", authenticationMethod: "myMethod" + ) + let value = try? api.pigeonDelegate.getServerTrust(pigeonApi: api, pigeonInstance: instance) + + XCTAssertEqual(value!.value, instance.serverTrust) + } +} + +class TestProtectionSpace: URLProtectionSpace, @unchecked Sendable { + var serverTrustVal: SecTrust? + + override var serverTrust: SecTrust? { + if serverTrustVal == nil { + let url = FlutterAssetManager().urlForAsset("assets/test_cert.der")! + + let certificateData = NSData(contentsOf: url) + let dummyCertificate: SecCertificate! = SecCertificateCreateWithData(nil, certificateData!) + + var trust: SecTrust? + SecTrustCreateWithCertificates( + [dummyCertificate] as AnyObject, SecPolicyCreateBasicX509(), &trust) + serverTrustVal = trust! + } + return serverTrustVal + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FlutterAssetManager.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FlutterAssetManager.swift index 2c66d735a46..6d5aa3b2b72 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FlutterAssetManager.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/FlutterAssetManager.swift @@ -11,7 +11,35 @@ #endif open class FlutterAssetManager { - func lookupKeyForAsset(_ asset: String) -> String { + let bundle: Bundle + + init(bundle: Bundle = Bundle.main) { + self.bundle = bundle + } + + func lookupKeyForAsset(_ asset: String) -> String? { return FlutterDartProject.lookupKey(forAsset: asset) } + + func urlForAsset(_ asset: String) -> URL? { + let assetFilePath: String? = lookupKeyForAsset(asset) + + guard let assetFilePath = assetFilePath else { + return nil + } + + var url: URL? = bundle.url( + forResource: (assetFilePath as NSString).deletingPathExtension, + withExtension: (assetFilePath as NSString).pathExtension) + + #if os(macOS) + // See https://github.com/flutter/flutter/issues/135302 + // TODO(stuartmorgan): Remove this if the asset APIs are adjusted to work better for macOS. + if url == nil { + url = URL(string: assetFilePath, relativeTo: bundle.bundleURL) + } + #endif + + return url + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/GetTrustResultResponse.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/GetTrustResultResponse.swift new file mode 100644 index 00000000000..85393ad5be1 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/GetTrustResultResponse.swift @@ -0,0 +1,20 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Darwin +import Security + +/// Data class used to respond to `SecTrustGetTrustResult`. +/// +/// The native method needs to return two values, so this custom class is +/// created to support this. +class GetTrustResultResponse { + let result: SecTrustResultType + let resultCode: OSStatus + + init(result: SecTrustResultType, resultCode: OSStatus) { + self.result = result + self.resultCode = resultCode + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/GetTrustResultResponseProxyAPIDelegate.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/GetTrustResultResponseProxyAPIDelegate.swift new file mode 100644 index 00000000000..f6d5bd5807a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/GetTrustResultResponseProxyAPIDelegate.swift @@ -0,0 +1,42 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +/// ProxyApi implementation for `GetTrustResultResponse`. +/// +/// This class may handle instantiating native object instances that are attached to a Dart instance +/// or handle method calls on the associated native class or an instance of that class. +class GetTrustResultResponseProxyAPIDelegate: PigeonApiDelegateGetTrustResultResponse { + func result(pigeonApi: PigeonApiGetTrustResultResponse, pigeonInstance: GetTrustResultResponse) + throws -> DartSecTrustResultType + { + switch pigeonInstance.result { + case .unspecified: + return .unspecified + case .proceed: + return .proceed + case .deny: + return .deny + case .recoverableTrustFailure: + return .recoverableTrustFailure + case .fatalTrustFailure: + return .fatalTrustFailure + case .otherError: + return .otherError + case .invalid: + return .invalid + case .confirm: + return .confirm + @unknown default: + return .unknown + } + } + + func resultCode( + pigeonApi: PigeonApiGetTrustResultResponse, pigeonInstance: GetTrustResultResponse + ) throws -> Int64 { + return Int64(pigeonInstance.resultCode) + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/ProxyAPIRegistrar.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/ProxyAPIRegistrar.swift index 607001b2bc5..0c451567b62 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/ProxyAPIRegistrar.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/ProxyAPIRegistrar.swift @@ -16,11 +16,13 @@ import Foundation /// Implementation of `WebKitLibraryPigeonProxyApiRegistrar` that provides any additional resources needed by API implementations. open class ProxyAPIRegistrar: WebKitLibraryPigeonProxyApiRegistrar { - let assetManager = FlutterAssetManager() - let bundle: Bundle + let assetManager: FlutterAssetManager - init(binaryMessenger: FlutterBinaryMessenger, bundle: Bundle = Bundle.main) { - self.bundle = bundle + init( + binaryMessenger: FlutterBinaryMessenger, + assetManager: FlutterAssetManager = FlutterAssetManager() + ) { + self.assetManager = assetManager super.init(binaryMessenger: binaryMessenger, apiDelegate: ProxyAPIDelegate()) } @@ -280,4 +282,22 @@ class ProxyAPIDelegate: WebKitLibraryPigeonProxyApiDelegate { return PigeonApiWKWebpagePreferences( pigeonRegistrar: registrar, delegate: WebpagePreferencesProxyAPIDelegate()) } + + func pigeonApiGetTrustResultResponse(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) + -> PigeonApiGetTrustResultResponse + { + return PigeonApiGetTrustResultResponse( + pigeonRegistrar: registrar, delegate: GetTrustResultResponseProxyAPIDelegate()) + } + + func pigeonApiSecTrust(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) -> PigeonApiSecTrust { + return PigeonApiSecTrust(pigeonRegistrar: registrar, delegate: SecTrustProxyAPIDelegate()) + } + + func pigeonApiSecCertificate(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) + -> PigeonApiSecCertificate + { + return PigeonApiSecCertificate( + pigeonRegistrar: registrar, delegate: SecCertificateProxyAPIDelegate()) + } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecCertificateProxyAPIDelegate.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecCertificateProxyAPIDelegate.swift new file mode 100644 index 00000000000..3853154c90a --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecCertificateProxyAPIDelegate.swift @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// ProxyApi implementation for `SecCertificate`. +/// +/// This class may handle instantiating native object instances that are attached to a Dart instance +/// or handle method calls on the associated native class or an instance of that class. +class SecCertificateProxyAPIDelegate: PigeonApiDelegateSecCertificate { + func copyData(pigeonApi: PigeonApiSecCertificate, certificate: SecCertificateWrapper) throws + -> FlutterStandardTypedData + { + let data = secCertificateCopyData(certificate.value) + return FlutterStandardTypedData(bytes: data as Data) + } + + // Overridable for testing. + internal func secCertificateCopyData(_ certificate: SecCertificate) -> CFData { + return SecCertificateCopyData(certificate) + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecTrustProxyAPIDelegate.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecTrustProxyAPIDelegate.swift new file mode 100644 index 00000000000..69b91c6c969 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecTrustProxyAPIDelegate.swift @@ -0,0 +1,135 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// ProxyApi implementation for `SecTrust`. +/// +/// This class may handle instantiating native object instances that are attached to a Dart instance +/// or handle method calls on the associated native class or an instance of that class. +class SecTrustProxyAPIDelegate: PigeonApiDelegateSecTrust { + func evaluateWithError( + pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper, + completion: @escaping (Result) -> Void + ) { + /// `SecTrustEvaluateWithError` should not be called on main thread, so this calls the method on a background thread. + DispatchQueue.global().async { + var error: CFError? + let result = self.secTrustEvaluateWithError(trust.value, &error) + + DispatchQueue.main.async { + if let error = error { + completion( + Result.failure( + PigeonError( + code: CFErrorGetDomain(error) as String, + message: CFErrorCopyDescription(error) as String, details: nil))) + } else { + completion(Result.success(result)) + } + } + } + } + + func copyExceptions(pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper) throws + -> FlutterStandardTypedData? + { + let data = secTrustCopyExceptions(trust.value) + if let data = data { + return FlutterStandardTypedData(bytes: data as Data) + } + + return nil + } + + func setExceptions( + pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper, exceptions: FlutterStandardTypedData? + ) throws -> Bool { + let data: CFData? = exceptions != nil ? exceptions!.data as CFData : nil + return secTrustSetExceptions(trust.value, data) + } + + func getTrustResult(pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper) throws + -> GetTrustResultResponse + { + var result = SecTrustResultType.invalid + let status = secTrustGetTrustResult(trust.value, &result) + return GetTrustResultResponse(result: result, resultCode: status) + } + + func copyCertificateChain(pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper) throws + -> [SecCertificateWrapper]? + { + if #available(iOS 15.0, macOS 12.0, *) { + let array = secTrustCopyCertificateChain(trust.value) as Array? + if let array = array { + var certificateList: [SecCertificateWrapper] = [] + for certificate in array { + certificateList.append(SecCertificateWrapper(value: certificate as! SecCertificate)) + } + return certificateList + } + } else { + let count = secTrustGetCertificateCount(trust.value) + if count > 0 { + var certificateList: [SecCertificateWrapper] = [] + for index in 0..? + ) -> Bool { + return SecTrustEvaluateWithError(trust, error) + } + + // Overridable for testing. + internal func secTrustCopyExceptions(_ trust: SecTrust) -> CFData? { + return SecTrustCopyExceptions(trust) + } + + // Overridable for testing. + internal func secTrustSetExceptions(_ trust: SecTrust, _ exceptions: CFData?) -> Bool { + return SecTrustSetExceptions(trust, exceptions) + } + + // Overridable for testing. + internal func secTrustGetTrustResult( + _ trust: SecTrust, _ result: UnsafeMutablePointer + ) -> OSStatus { + return SecTrustGetTrustResult(trust, result) + } + + // Overridable for testing. + @available(iOS 15.0, macOS 12.0, *) + internal func secTrustCopyCertificateChain(_ trust: SecTrust) -> CFArray? { + return SecTrustCopyCertificateChain(trust) + } + + // Overridable for testing. + internal func secTrustGetCertificateCount(_ trust: SecTrust) -> CFIndex { + return SecTrustGetCertificateCount(trust) + } + + // Overridable for testing. + internal func secTrustGetCertificateAtIndex(_ trust: SecTrust, _ ix: CFIndex) -> SecCertificate? { + return SecTrustGetCertificateAtIndex(trust, ix) + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecWrappers.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecWrappers.swift new file mode 100644 index 00000000000..9dfd19b0dcc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/SecWrappers.swift @@ -0,0 +1,31 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import Security + +/// Wrapper for `SecTrust`. +/// +/// Corefoundation types don't support being casted in Swift and will always succeed +/// by default. This wrapper is used to make the class compatible with generated pigeon +/// code. All instances of `SecTrust`should be replaced with this. +class SecTrustWrapper { + let value: SecTrust + + init(value: SecTrust) { + self.value = value + } +} + +/// Wrapper for `SecCertificate`. +/// +/// Corefoundation types don't support being casted in Swift and will always succeed +/// by default. This wrapper is used to make the class compatible with generated pigeon +/// code. All instances of `SecCertificate`should be replaced with this. +class SecCertificateWrapper { + let value: SecCertificate + + init(value: SecCertificate) { + self.value = value + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/URLProtectionSpaceProxyAPIDelegate.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/URLProtectionSpaceProxyAPIDelegate.swift index d7c59c67f28..03e5dadcced 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/URLProtectionSpaceProxyAPIDelegate.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/URLProtectionSpaceProxyAPIDelegate.swift @@ -32,4 +32,14 @@ class URLProtectionSpaceProxyAPIDelegate: PigeonApiDelegateURLProtectionSpace { ) throws -> String? { return pigeonInstance.authenticationMethod } + + func getServerTrust(pigeonApi: PigeonApiURLProtectionSpace, pigeonInstance: URLProtectionSpace) + throws -> SecTrustWrapper? + { + if let serverTrust = pigeonInstance.serverTrust { + return SecTrustWrapper(value: serverTrust) + } + + 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 177b66a2e6c..9d35176ec17 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 @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v24.2.1), do not edit directly. +// Autogenerated from Pigeon (v25.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -492,6 +492,17 @@ protocol WebKitLibraryPigeonProxyApiDelegate { /// `WKWebpagePreferences` to the Dart `InstanceManager` and make calls to Dart. func pigeonApiWKWebpagePreferences(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) -> PigeonApiWKWebpagePreferences + /// An implementation of [PigeonApiGetTrustResultResponse] used to add a new Dart instance of + /// `GetTrustResultResponse` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiGetTrustResultResponse(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) + -> PigeonApiGetTrustResultResponse + /// An implementation of [PigeonApiSecTrust] used to add a new Dart instance of + /// `SecTrust` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiSecTrust(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) -> PigeonApiSecTrust + /// An implementation of [PigeonApiSecCertificate] used to add a new Dart instance of + /// `SecCertificate` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiSecCertificate(_ registrar: WebKitLibraryPigeonProxyApiRegistrar) + -> PigeonApiSecCertificate } extension WebKitLibraryPigeonProxyApiDelegate { @@ -591,6 +602,10 @@ open class WebKitLibraryPigeonProxyApiRegistrar { binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiURL(self)) PigeonApiWKWebpagePreferences.setUpMessageHandlers( binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiWKWebpagePreferences(self)) + PigeonApiSecTrust.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiSecTrust(self)) + PigeonApiSecCertificate.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiSecCertificate(self)) } func tearDown() { WebKitLibraryPigeonInstanceManagerApi.setUpMessageHandlers( @@ -620,6 +635,8 @@ open class WebKitLibraryPigeonProxyApiRegistrar { binaryMessenger: binaryMessenger, api: nil) PigeonApiURL.setUpMessageHandlers(binaryMessenger: binaryMessenger, api: nil) PigeonApiWKWebpagePreferences.setUpMessageHandlers(binaryMessenger: binaryMessenger, api: nil) + PigeonApiSecTrust.setUpMessageHandlers(binaryMessenger: binaryMessenger, api: nil) + PigeonApiSecCertificate.setUpMessageHandlers(binaryMessenger: binaryMessenger, api: nil) } } private class WebKitLibraryPigeonInternalProxyApiCodecReaderWriter: FlutterStandardReaderWriter { @@ -666,7 +683,7 @@ private class WebKitLibraryPigeonInternalProxyApiCodecReaderWriter: FlutterStand || value is NavigationActionPolicy || value is NavigationResponsePolicy || value is HttpCookiePropertyKey || value is NavigationType || value is PermissionDecision || value is MediaCaptureType || value is UrlSessionAuthChallengeDisposition - || value is UrlCredentialPersistence + || value is UrlCredentialPersistence || value is DartSecTrustResultType { super.writeValue(value) return @@ -1030,6 +1047,40 @@ private class WebKitLibraryPigeonInternalProxyApiCodecReaderWriter: FlutterStand return } + if let instance = value as? GetTrustResultResponse { + pigeonRegistrar.apiDelegate.pigeonApiGetTrustResultResponse(pigeonRegistrar) + .pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + + if let instance = value as? SecTrustWrapper { + pigeonRegistrar.apiDelegate.pigeonApiSecTrust(pigeonRegistrar).pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + + if let instance = value as? SecCertificateWrapper { + pigeonRegistrar.apiDelegate.pigeonApiSecCertificate(pigeonRegistrar).pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + if let instance = value as? NSObject { pigeonRegistrar.apiDelegate.pigeonApiNSObject(pigeonRegistrar).pigeonNewInstance( pigeonInstance: instance @@ -1335,6 +1386,31 @@ enum UrlCredentialPersistence: Int { case synchronizable = 3 } +/// Trust evaluation result codes. +/// +/// See https://developer.apple.com/documentation/security/sectrustresulttype?language=objc. +enum DartSecTrustResultType: Int { + /// The user did not specify a trust setting. + case unspecified = 0 + /// The user granted permission to trust the certificate for the purposes + /// designated in the specified policies. + case proceed = 1 + /// The user specified that the certificate should not be trusted. + case deny = 2 + /// Trust is denied, but recovery may be possible. + case recoverableTrustFailure = 3 + /// Trust is denied and no simple fix is available. + case fatalTrustFailure = 4 + /// A value that indicates a failure other than trust evaluation. + case otherError = 5 + /// An indication of an invalid setting or result. + case invalid = 6 + /// User confirmation is required before proceeding. + case confirm = 7 + /// The type is not recognized by this wrapper. + case unknown = 8 +} + private class WebKitLibraryPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { @@ -1422,6 +1498,12 @@ private class WebKitLibraryPigeonCodecReader: FlutterStandardReader { return UrlCredentialPersistence(rawValue: enumResultAsInt) } return nil + case 143: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return DartSecTrustResultType(rawValue: enumResultAsInt) + } + return nil default: return super.readValue(ofType: type) } @@ -1472,6 +1554,9 @@ private class WebKitLibraryPigeonCodecWriter: FlutterStandardWriter { } else if let value = value as? UrlCredentialPersistence { super.writeByte(142) super.writeValue(value.rawValue) + } else if let value = value as? DartSecTrustResultType { + super.writeByte(143) + super.writeValue(value.rawValue) } else { super.writeValue(value) } @@ -6581,6 +6666,9 @@ protocol PigeonApiDelegateURLProtectionSpace { func authenticationMethod( pigeonApi: PigeonApiURLProtectionSpace, pigeonInstance: URLProtectionSpace ) throws -> String? + /// A representation of the server’s SSL transaction state. + func getServerTrust(pigeonApi: PigeonApiURLProtectionSpace, pigeonInstance: URLProtectionSpace) + throws -> SecTrustWrapper? } protocol PigeonApiProtocolURLProtectionSpace { @@ -6621,6 +6709,8 @@ final class PigeonApiURLProtectionSpace: PigeonApiProtocolURLProtectionSpace { let realmArg = try! pigeonDelegate.realm(pigeonApi: self, pigeonInstance: pigeonInstance) let authenticationMethodArg = try! pigeonDelegate.authenticationMethod( pigeonApi: self, pigeonInstance: pigeonInstance) + let getServerTrustArg = try! pigeonDelegate.getServerTrust( + pigeonApi: self, pigeonInstance: pigeonInstance) let binaryMessenger = pigeonRegistrar.binaryMessenger let codec = pigeonRegistrar.codec let channelName: String = @@ -6628,7 +6718,10 @@ final class PigeonApiURLProtectionSpace: PigeonApiProtocolURLProtectionSpace { let channel = FlutterBasicMessageChannel( name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage( - [pigeonIdentifierArg, hostArg, portArg, realmArg, authenticationMethodArg] as [Any?] + [ + pigeonIdentifierArg, hostArg, portArg, realmArg, authenticationMethodArg, + getServerTrustArg, + ] as [Any?] ) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) @@ -6943,3 +7036,338 @@ final class PigeonApiWKWebpagePreferences: PigeonApiProtocolWKWebpagePreferences } } } +protocol PigeonApiDelegateGetTrustResultResponse { + /// The result code from the most recent trust evaluation. + func result(pigeonApi: PigeonApiGetTrustResultResponse, pigeonInstance: GetTrustResultResponse) + throws -> DartSecTrustResultType + /// A result code. + /// + /// See https://developer.apple.com/documentation/security/security-framework-result-codes?language=objc. + func resultCode( + pigeonApi: PigeonApiGetTrustResultResponse, pigeonInstance: GetTrustResultResponse + ) throws -> Int64 +} + +protocol PigeonApiProtocolGetTrustResultResponse { +} + +final class PigeonApiGetTrustResultResponse: PigeonApiProtocolGetTrustResultResponse { + unowned let pigeonRegistrar: WebKitLibraryPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateGetTrustResultResponse + ///An implementation of [NSObject] used to access callback methods + var pigeonApiNSObject: PigeonApiNSObject { + return pigeonRegistrar.apiDelegate.pigeonApiNSObject(pigeonRegistrar) + } + + init( + pigeonRegistrar: WebKitLibraryPigeonProxyApiRegistrar, + delegate: PigeonApiDelegateGetTrustResultResponse + ) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + ///Creates a Dart instance of GetTrustResultResponse and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: GetTrustResultResponse, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + } else if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(())) + } else { + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let resultArg = try! pigeonDelegate.result(pigeonApi: self, pigeonInstance: pigeonInstance) + let resultCodeArg = try! pigeonDelegate.resultCode( + pigeonApi: self, pigeonInstance: pigeonInstance) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.webview_flutter_wkwebview.GetTrustResultResponse.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg, resultArg, resultCodeArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + } +} +protocol PigeonApiDelegateSecTrust { + /// Evaluates trust for the specified certificate and policies. + func evaluateWithError( + pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper, + completion: @escaping (Result) -> Void) + /// Returns an opaque cookie containing exceptions to trust policies that will + /// allow future evaluations of the current certificate to succeed. + func copyExceptions(pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper) throws + -> FlutterStandardTypedData? + /// Sets a list of exceptions that should be ignored when the certificate is + /// evaluated. + func setExceptions( + pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper, exceptions: FlutterStandardTypedData? + ) throws -> Bool + /// Returns the result code from the most recent trust evaluation. + func getTrustResult(pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper) throws + -> GetTrustResultResponse + /// Certificates used to evaluate trust. + func copyCertificateChain(pigeonApi: PigeonApiSecTrust, trust: SecTrustWrapper) throws + -> [SecCertificateWrapper]? +} + +protocol PigeonApiProtocolSecTrust { +} + +final class PigeonApiSecTrust: PigeonApiProtocolSecTrust { + unowned let pigeonRegistrar: WebKitLibraryPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateSecTrust + ///An implementation of [NSObject] used to access callback methods + var pigeonApiNSObject: PigeonApiNSObject { + return pigeonRegistrar.apiDelegate.pigeonApiNSObject(pigeonRegistrar) + } + + init(pigeonRegistrar: WebKitLibraryPigeonProxyApiRegistrar, delegate: PigeonApiDelegateSecTrust) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + static func setUpMessageHandlers(binaryMessenger: FlutterBinaryMessenger, api: PigeonApiSecTrust?) + { + let codec: FlutterStandardMessageCodec = + api != nil + ? FlutterStandardMessageCodec( + readerWriter: WebKitLibraryPigeonInternalProxyApiCodecReaderWriter( + pigeonRegistrar: api!.pigeonRegistrar)) + : FlutterStandardMessageCodec.sharedInstance() + let evaluateWithErrorChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.evaluateWithError", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + evaluateWithErrorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let trustArg = args[0] as! SecTrustWrapper + api.pigeonDelegate.evaluateWithError(pigeonApi: api, trust: trustArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + evaluateWithErrorChannel.setMessageHandler(nil) + } + let copyExceptionsChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.copyExceptions", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + copyExceptionsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let trustArg = args[0] as! SecTrustWrapper + do { + let result = try api.pigeonDelegate.copyExceptions(pigeonApi: api, trust: trustArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + copyExceptionsChannel.setMessageHandler(nil) + } + let setExceptionsChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.setExceptions", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setExceptionsChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let trustArg = args[0] as! SecTrustWrapper + let exceptionsArg: FlutterStandardTypedData? = nilOrValue(args[1]) + do { + let result = try api.pigeonDelegate.setExceptions( + pigeonApi: api, trust: trustArg, exceptions: exceptionsArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + setExceptionsChannel.setMessageHandler(nil) + } + let getTrustResultChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.getTrustResult", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getTrustResultChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let trustArg = args[0] as! SecTrustWrapper + do { + let result = try api.pigeonDelegate.getTrustResult(pigeonApi: api, trust: trustArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getTrustResultChannel.setMessageHandler(nil) + } + let copyCertificateChainChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.copyCertificateChain", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + copyCertificateChainChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let trustArg = args[0] as! SecTrustWrapper + do { + let result = try api.pigeonDelegate.copyCertificateChain(pigeonApi: api, trust: trustArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + copyCertificateChainChannel.setMessageHandler(nil) + } + } + + ///Creates a Dart instance of SecTrust and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: SecTrustWrapper, completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + } else if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(())) + } else { + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + } +} +protocol PigeonApiDelegateSecCertificate { + /// Returns a DER representation of a certificate given a certificate object. + func copyData(pigeonApi: PigeonApiSecCertificate, certificate: SecCertificateWrapper) throws + -> FlutterStandardTypedData +} + +protocol PigeonApiProtocolSecCertificate { +} + +final class PigeonApiSecCertificate: PigeonApiProtocolSecCertificate { + unowned let pigeonRegistrar: WebKitLibraryPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateSecCertificate + ///An implementation of [NSObject] used to access callback methods + var pigeonApiNSObject: PigeonApiNSObject { + return pigeonRegistrar.apiDelegate.pigeonApiNSObject(pigeonRegistrar) + } + + init( + pigeonRegistrar: WebKitLibraryPigeonProxyApiRegistrar, delegate: PigeonApiDelegateSecCertificate + ) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + static func setUpMessageHandlers( + binaryMessenger: FlutterBinaryMessenger, api: PigeonApiSecCertificate? + ) { + let codec: FlutterStandardMessageCodec = + api != nil + ? FlutterStandardMessageCodec( + readerWriter: WebKitLibraryPigeonInternalProxyApiCodecReaderWriter( + pigeonRegistrar: api!.pigeonRegistrar)) + : FlutterStandardMessageCodec.sharedInstance() + let copyDataChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.webview_flutter_wkwebview.SecCertificate.copyData", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + copyDataChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let certificateArg = args[0] as! SecCertificateWrapper + do { + let result = try api.pigeonDelegate.copyData(pigeonApi: api, certificate: certificateArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + copyDataChannel.setMessageHandler(nil) + } + } + + ///Creates a Dart instance of SecCertificate and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: SecCertificateWrapper, completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + } else if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(())) + } else { + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.webview_flutter_wkwebview.SecCertificate.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + } +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewProxyAPIDelegate.swift b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewProxyAPIDelegate.swift index 8159a8a69ab..ca46bbb223f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewProxyAPIDelegate.swift +++ b/packages/webview_flutter/webview_flutter_wkwebview/darwin/webview_flutter_wkwebview/Sources/webview_flutter_wkwebview/WebViewProxyAPIDelegate.swift @@ -207,18 +207,16 @@ class WebViewProxyAPIDelegate: PigeonApiDelegateWKWebView, PigeonApiDelegateUIVi throws { let registrar = pigeonApi.pigeonRegistrar as! ProxyAPIRegistrar - let assetFilePath = registrar.assetManager.lookupKeyForAsset(key) - - let url = registrar.bundle.url( - forResource: (assetFilePath as NSString).deletingPathExtension, - withExtension: (assetFilePath as NSString).pathExtension) + let url = registrar.assetManager.urlForAsset(key) if let url = url { pigeonInstance.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent()) } else { + let assetFilePath = registrar.assetManager.lookupKeyForAsset(key) throw PigeonError( code: "FWFURLParsingError", - message: "Failed to find asset with filepath: `\(assetFilePath)`.", details: nil) + message: "Failed to find asset with filepath: `\(String(describing: assetFilePath))`.", + details: nil) } } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/test_cert.der b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/test_cert.der new file mode 100644 index 00000000000..87401bc4ad3 Binary files /dev/null and b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/test_cert.der differ diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart index 42d66dcf35a..91d99625748 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/integration_test/webview_flutter_test.dart @@ -238,6 +238,36 @@ Future main() async { expect(content.contains('flutter_test_header'), isTrue); }); + testWidgets('loadFlutterAsset successfully loads an HTML asset', + (WidgetTester tester) async { + final Completer pageFinished = Completer(); + + final PlatformWebViewController controller = PlatformWebViewController( + const PlatformWebViewControllerCreationParams(), + ); + final PlatformNavigationDelegate delegate = PlatformNavigationDelegate( + const PlatformNavigationDelegateCreationParams(), + ); + unawaited(delegate.setOnPageFinished((_) => pageFinished.complete())); + unawaited(controller.setPlatformNavigationDelegate(delegate)); + + await expectLater( + controller.loadFlutterAsset('assets/www/index.html'), + completes, + ); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return PlatformWebViewWidget( + PlatformWebViewWidgetCreationParams(controller: controller), + ).build(context); + }, + )); + + // Verify page also loads. + await pageFinished.future; + }); + testWidgets('JavascriptChannel', (WidgetTester tester) async { final Completer pageFinished = Completer(); final PlatformWebViewController controller = PlatformWebViewController( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj index 8d55bed2605..7b9f2bef87e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/project.pbxproj @@ -40,7 +40,10 @@ 8F1488FE2D2DE27000191744 /* HTTPCookieProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F1488C82D2DE27000191744 /* HTTPCookieProxyAPITests.swift */; }; 8F1488FF2D2DE27000191744 /* NavigationActionProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F1488CB2D2DE27000191744 /* NavigationActionProxyAPITests.swift */; }; 8F1489012D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F1489002D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift */; }; - 8FC45F712D5C0C1E004E03E8 /* WebpagePreferencesProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC45F702D5C0C1E004E03E8 /* WebpagePreferencesProxyAPITests.swift */; }; + 8FEC64852DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64812DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift */; }; + 8FEC64862DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64842DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift */; }; + 8FEC64872DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64822DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift */; }; + 8FEC64882DA2C6DC00C48569 /* SecTrustProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64832DA2C6DC00C48569 /* SecTrustProxyAPITests.swift */; }; 904EA421B6925EC8D52ABE1B /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 573E8F1472CA1578A7CBDB63 /* libPods-RunnerTests.a */; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; @@ -126,7 +129,10 @@ 8F1488E12D2DE27000191744 /* WebViewProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebViewProxyAPITests.swift; path = ../../darwin/Tests/WebViewProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8F1489002D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AuthenticationChallengeResponseProxyAPITests.swift; path = ../../darwin/Tests/AuthenticationChallengeResponseProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8F66D9D72D1362BE000835F9 /* RunnerTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RunnerTests-Bridging-Header.h"; sourceTree = ""; }; - 8FC45F702D5C0C1E004E03E8 /* WebpagePreferencesProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebpagePreferencesProxyAPITests.swift; sourceTree = ""; }; + 8FEC64812DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GetTrustResultResponseProxyAPITests.swift; path = ../../darwin/Tests/GetTrustResultResponseProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FEC64822DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SecCertificateProxyAPITests.swift; path = ../../darwin/Tests/SecCertificateProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FEC64832DA2C6DC00C48569 /* SecTrustProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SecTrustProxyAPITests.swift; path = ../../darwin/Tests/SecTrustProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FEC64842DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebpagePreferencesProxyAPITests.swift; path = ../../darwin/Tests/WebpagePreferencesProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -172,7 +178,10 @@ 68BDCAEA23C3F7CB00D9C032 /* RunnerTests */ = { isa = PBXGroup; children = ( - 8FC45F702D5C0C1E004E03E8 /* WebpagePreferencesProxyAPITests.swift */, + 8FEC64812DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift */, + 8FEC64822DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift */, + 8FEC64832DA2C6DC00C48569 /* SecTrustProxyAPITests.swift */, + 8FEC64842DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift */, 8F1489002D2DE91C00191744 /* AuthenticationChallengeResponseProxyAPITests.swift */, 8F1488C52D2DE27000191744 /* ErrorProxyAPITests.swift */, 8F1488C62D2DE27000191744 /* FrameInfoProxyAPITests.swift */, @@ -542,7 +551,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8FC45F712D5C0C1E004E03E8 /* WebpagePreferencesProxyAPITests.swift in Sources */, 8F1488E22D2DE27000191744 /* ScriptMessageHandlerProxyAPITests.swift in Sources */, 8F1488E32D2DE27000191744 /* TestProxyApiRegistrar.swift in Sources */, 8F1488E42D2DE27000191744 /* URLRequestProxyAPITests.swift in Sources */, @@ -558,6 +566,10 @@ 8F1488ED2D2DE27000191744 /* ErrorProxyAPITests.swift in Sources */, 8F1488EE2D2DE27000191744 /* NSObjectProxyAPITests.swift in Sources */, 8F1488EF2D2DE27000191744 /* NavigationResponseProxyAPITests.swift in Sources */, + 8FEC64852DA2C6DC00C48569 /* GetTrustResultResponseProxyAPITests.swift in Sources */, + 8FEC64862DA2C6DC00C48569 /* WebpagePreferencesProxyAPITests.swift in Sources */, + 8FEC64872DA2C6DC00C48569 /* SecCertificateProxyAPITests.swift in Sources */, + 8FEC64882DA2C6DC00C48569 /* SecTrustProxyAPITests.swift in Sources */, 8F1488F02D2DE27000191744 /* UserScriptProxyAPITests.swift in Sources */, 8F1488F12D2DE27000191744 /* URLProtectionSpaceProxyAPITests.swift in Sources */, 8F1488F22D2DE27000191744 /* WebViewConfigurationProxyAPITests.swift in Sources */, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/WebpagePreferencesProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/WebpagePreferencesProxyAPITests.swift deleted file mode 100644 index aa11b48ad1e..00000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/WebpagePreferencesProxyAPITests.swift +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import WebKit -import XCTest - -@testable import webview_flutter_wkwebview - -class WebpagePreferencesProxyAPITests: XCTestCase { - @available(iOS 14.0, macOS 11.0, *) - @MainActor func testSetAllowsContentJavaScript() { - let registrar = TestProxyApiRegistrar() - let api = registrar.apiDelegate.pigeonApiWKWebpagePreferences(registrar) - - let instance = WKWebpagePreferences() - let allow = true - try? api.pigeonDelegate.setAllowsContentJavaScript( - pigeonApi: api, pigeonInstance: instance, allow: allow) - - XCTAssertEqual(instance.allowsContentJavaScript, allow) - } -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj b/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj index 4fb9e94bb55..ddb499e5f26 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 60; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -28,7 +28,10 @@ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 6FBECA2F94D9B352000B75D7 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 418FB4EA49104BADE288A967 /* Pods_RunnerTests.framework */; }; 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; - 8FC45F732D5C0C37004E03E8 /* WebpagePreferencesProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC45F722D5C0C37004E03E8 /* WebpagePreferencesProxyAPITests.swift */; }; + 8FEC64952DA303E200C48569 /* SecCertificateProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64922DA303E200C48569 /* SecCertificateProxyAPITests.swift */; }; + 8FEC64962DA303E200C48569 /* WebpagePreferencesProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64942DA303E200C48569 /* WebpagePreferencesProxyAPITests.swift */; }; + 8FEC64972DA303E200C48569 /* SecTrustProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64932DA303E200C48569 /* SecTrustProxyAPITests.swift */; }; + 8FEC64982DA303E200C48569 /* GetTrustResultResponseProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEC64912DA303E200C48569 /* GetTrustResultResponseProxyAPITests.swift */; }; 8FF1FEA22D37201300A5E400 /* NSObjectProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF1FE8E2D37201300A5E400 /* NSObjectProxyAPITests.swift */; }; 8FF1FEA32D37201300A5E400 /* ScrollViewProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF1FE932D37201300A5E400 /* ScrollViewProxyAPITests.swift */; }; 8FF1FEA42D37201300A5E400 /* URLAuthenticationChallengeProxyAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FF1FE992D37201300A5E400 /* URLAuthenticationChallengeProxyAPITests.swift */; }; @@ -113,7 +116,10 @@ 69E635CFAEEB8242654D4BBC /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 7D83F970E1160B09690C7723 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - 8FC45F722D5C0C37004E03E8 /* WebpagePreferencesProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebpagePreferencesProxyAPITests.swift; sourceTree = ""; }; + 8FEC64912DA303E200C48569 /* GetTrustResultResponseProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GetTrustResultResponseProxyAPITests.swift; path = ../../darwin/Tests/GetTrustResultResponseProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FEC64922DA303E200C48569 /* SecCertificateProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SecCertificateProxyAPITests.swift; path = ../../darwin/Tests/SecCertificateProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FEC64932DA303E200C48569 /* SecTrustProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SecTrustProxyAPITests.swift; path = ../../darwin/Tests/SecTrustProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; + 8FEC64942DA303E200C48569 /* WebpagePreferencesProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WebpagePreferencesProxyAPITests.swift; path = ../../darwin/Tests/WebpagePreferencesProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FF1FE842D37201300A5E400 /* AuthenticationChallengeResponseProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AuthenticationChallengeResponseProxyAPITests.swift; path = ../../darwin/Tests/AuthenticationChallengeResponseProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FF1FE852D37201300A5E400 /* ErrorProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ErrorProxyAPITests.swift; path = ../../darwin/Tests/ErrorProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; 8FF1FE862D37201300A5E400 /* FrameInfoProxyAPITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FrameInfoProxyAPITests.swift; path = ../../darwin/Tests/FrameInfoProxyAPITests.swift; sourceTree = SOURCE_ROOT; }; @@ -176,7 +182,10 @@ 331C80D6294CF71000263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( - 8FC45F722D5C0C37004E03E8 /* WebpagePreferencesProxyAPITests.swift */, + 8FEC64912DA303E200C48569 /* GetTrustResultResponseProxyAPITests.swift */, + 8FEC64922DA303E200C48569 /* SecCertificateProxyAPITests.swift */, + 8FEC64932DA303E200C48569 /* SecTrustProxyAPITests.swift */, + 8FEC64942DA303E200C48569 /* WebpagePreferencesProxyAPITests.swift */, 8FF1FE842D37201300A5E400 /* AuthenticationChallengeResponseProxyAPITests.swift */, 8FF1FE852D37201300A5E400 /* ErrorProxyAPITests.swift */, 8FF1FE862D37201300A5E400 /* FrameInfoProxyAPITests.swift */, @@ -528,7 +537,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8FC45F732D5C0C37004E03E8 /* WebpagePreferencesProxyAPITests.swift in Sources */, 8FF1FEA22D37201300A5E400 /* NSObjectProxyAPITests.swift in Sources */, 8FF1FEA32D37201300A5E400 /* ScrollViewProxyAPITests.swift in Sources */, 8FF1FEA42D37201300A5E400 /* URLAuthenticationChallengeProxyAPITests.swift in Sources */, @@ -539,6 +547,10 @@ 8FF1FEA92D37201300A5E400 /* TestProxyApiRegistrar.swift in Sources */, 8FF1FEAA2D37201300A5E400 /* HTTPURLResponseProxyAPITests.swift in Sources */, 8FF1FEAB2D37201300A5E400 /* AuthenticationChallengeResponseProxyAPITests.swift in Sources */, + 8FEC64952DA303E200C48569 /* SecCertificateProxyAPITests.swift in Sources */, + 8FEC64962DA303E200C48569 /* WebpagePreferencesProxyAPITests.swift in Sources */, + 8FEC64972DA303E200C48569 /* SecTrustProxyAPITests.swift in Sources */, + 8FEC64982DA303E200C48569 /* GetTrustResultResponseProxyAPITests.swift in Sources */, 8FF1FEAC2D37201300A5E400 /* UIViewProxyAPITests.swift in Sources */, 8FF1FEAD2D37201300A5E400 /* WebViewConfigurationProxyAPITests.swift in Sources */, 8FF1FEAE2D37201300A5E400 /* NavigationActionProxyAPITests.swift in Sources */, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/macos/RunnerTests/WebpagePreferencesProxyAPITests.swift b/packages/webview_flutter/webview_flutter_wkwebview/example/macos/RunnerTests/WebpagePreferencesProxyAPITests.swift deleted file mode 100644 index aa11b48ad1e..00000000000 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/macos/RunnerTests/WebpagePreferencesProxyAPITests.swift +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import WebKit -import XCTest - -@testable import webview_flutter_wkwebview - -class WebpagePreferencesProxyAPITests: XCTestCase { - @available(iOS 14.0, macOS 11.0, *) - @MainActor func testSetAllowsContentJavaScript() { - let registrar = TestProxyApiRegistrar() - let api = registrar.apiDelegate.pigeonApiWKWebpagePreferences(registrar) - - let instance = WKWebpagePreferences() - let allow = true - try? api.pigeonDelegate.setAllowsContentJavaScript( - pigeonApi: api, pigeonInstance: instance, allow: allow) - - XCTAssertEqual(instance.allowsContentJavaScript, allow) - } -} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 051e60afa66..f8ed12551d5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -32,3 +32,5 @@ flutter: - assets/sample_video.mp4 - assets/www/index.html - assets/www/styles/style.css + # Test certificate used to create a test native `SecTrust`. + - assets/test_cert.der 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 ee91ba63f41..c94b4adb44b 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 @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v24.2.1), do not edit directly. +// Autogenerated from Pigeon (v25.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -199,6 +199,12 @@ class PigeonInstanceManager { URL.pigeon_setUpMessageHandlers(pigeon_instanceManager: instanceManager); WKWebpagePreferences.pigeon_setUpMessageHandlers( pigeon_instanceManager: instanceManager); + GetTrustResultResponse.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + SecTrust.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + SecCertificate.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); return instanceManager; } @@ -788,6 +794,39 @@ enum UrlCredentialPersistence { synchronizable, } +/// Trust evaluation result codes. +/// +/// See https://developer.apple.com/documentation/security/sectrustresulttype?language=objc. +enum DartSecTrustResultType { + /// The user did not specify a trust setting. + unspecified, + + /// The user granted permission to trust the certificate for the purposes + /// designated in the specified policies. + proceed, + + /// The user specified that the certificate should not be trusted. + deny, + + /// Trust is denied, but recovery may be possible. + recoverableTrustFailure, + + /// Trust is denied and no simple fix is available. + fatalTrustFailure, + + /// A value that indicates a failure other than trust evaluation. + otherError, + + /// An indication of an invalid setting or result. + invalid, + + /// User confirmation is required before proceeding. + confirm, + + /// The type is not recognized by this wrapper. + unknown, +} + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -837,6 +876,9 @@ class _PigeonCodec extends StandardMessageCodec { } else if (value is UrlCredentialPersistence) { buffer.putUint8(142); writeValue(buffer, value.index); + } else if (value is DartSecTrustResultType) { + buffer.putUint8(143); + writeValue(buffer, value.index); } else { super.writeValue(buffer, value); } @@ -889,6 +931,9 @@ class _PigeonCodec extends StandardMessageCodec { case 142: final int? value = readValue(buffer) as int?; return value == null ? null : UrlCredentialPersistence.values[value]; + case 143: + final int? value = readValue(buffer) as int?; + return value == null ? null : DartSecTrustResultType.values[value]; default: return super.readValueOfType(type, buffer); } @@ -7533,6 +7578,7 @@ class URLProtectionSpace extends NSObject { required this.port, this.realm, this.authenticationMethod, + this.getServerTrust, super.observeValue, }) : super.pigeon_detached(); @@ -7548,6 +7594,9 @@ class URLProtectionSpace extends NSObject { /// The authentication method used by the receiver. final String? authenticationMethod; + /// A representation of the server’s SSL transaction state. + final SecTrust? getServerTrust; + static void pigeon_setUpMessageHandlers({ bool pigeon_clearHandlers = false, BinaryMessenger? pigeon_binaryMessenger, @@ -7557,6 +7606,7 @@ class URLProtectionSpace extends NSObject { int port, String? realm, String? authenticationMethod, + SecTrust? getServerTrust, )? pigeon_newInstance, }) { final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = @@ -7588,11 +7638,12 @@ class URLProtectionSpace extends NSObject { 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.URLProtectionSpace.pigeon_newInstance was null, expected non-null int.'); final String? arg_realm = (args[3] as String?); final String? arg_authenticationMethod = (args[4] as String?); + final SecTrust? arg_getServerTrust = (args[5] as SecTrust?); try { (pigeon_instanceManager ?? PigeonInstanceManager.instance) .addHostCreatedInstance( pigeon_newInstance?.call(arg_host!, arg_port!, arg_realm, - arg_authenticationMethod) ?? + arg_authenticationMethod, arg_getServerTrust) ?? URLProtectionSpace.pigeon_detached( pigeon_binaryMessenger: pigeon_binaryMessenger, pigeon_instanceManager: pigeon_instanceManager, @@ -7600,6 +7651,7 @@ class URLProtectionSpace extends NSObject { port: arg_port!, realm: arg_realm, authenticationMethod: arg_authenticationMethod, + getServerTrust: arg_getServerTrust, ), arg_pigeon_instanceIdentifier!, ); @@ -7624,6 +7676,7 @@ class URLProtectionSpace extends NSObject { port: port, realm: realm, authenticationMethod: authenticationMethod, + getServerTrust: getServerTrust, observeValue: observeValue, ); } @@ -7964,3 +8017,489 @@ class WKWebpagePreferences extends NSObject { ); } } + +/// Data class used to respond to `SecTrust.getTrustResult`. +/// +/// The native method needs to return two values, so this custom class is +/// created to support this. +class GetTrustResultResponse extends NSObject { + /// Constructs [GetTrustResultResponse] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + GetTrustResultResponse.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.result, + required this.resultCode, + super.observeValue, + }) : super.pigeon_detached(); + + /// The result code from the most recent trust evaluation. + final DartSecTrustResultType result; + + /// A result code. + /// + /// See https://developer.apple.com/documentation/security/security-framework-result-codes?language=objc. + final int resultCode; + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + GetTrustResultResponse Function( + DartSecTrustResultType result, + int resultCode, + )? pigeon_newInstance, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.webview_flutter_wkwebview.GetTrustResultResponse.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.GetTrustResultResponse.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.GetTrustResultResponse.pigeon_newInstance was null, expected non-null int.'); + final DartSecTrustResultType? arg_result = + (args[1] as DartSecTrustResultType?); + assert(arg_result != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.GetTrustResultResponse.pigeon_newInstance was null, expected non-null DartSecTrustResultType.'); + final int? arg_resultCode = (args[2] as int?); + assert(arg_resultCode != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.GetTrustResultResponse.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call(arg_result!, arg_resultCode!) ?? + GetTrustResultResponse.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + result: arg_result!, + resultCode: arg_resultCode!, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + @override + GetTrustResultResponse pigeon_copy() { + return GetTrustResultResponse.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + result: result, + resultCode: resultCode, + observeValue: observeValue, + ); + } +} + +/// An object used to evaluate trust. +/// +/// See https://developer.apple.com/documentation/security/sectrust. +class SecTrust extends NSObject { + /// Constructs [SecTrust] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + SecTrust.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + super.observeValue, + }) : super.pigeon_detached(); + + late final _PigeonInternalProxyApiBaseCodec _pigeonVar_codecSecTrust = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + SecTrust Function()? pigeon_newInstance, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + SecTrust.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + /// Evaluates trust for the specified certificate and policies. + static Future evaluateWithError( + SecTrust trust, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.evaluateWithError'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([trust]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + /// Returns an opaque cookie containing exceptions to trust policies that will + /// allow future evaluations of the current certificate to succeed. + static Future copyExceptions( + SecTrust trust, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.copyExceptions'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([trust]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return (pigeonVar_replyList[0] as Uint8List?); + } + } + + /// Sets a list of exceptions that should be ignored when the certificate is + /// evaluated. + static Future setExceptions( + SecTrust trust, + Uint8List? exceptions, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.setExceptions'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([trust, exceptions]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + /// Returns the result code from the most recent trust evaluation. + static Future getTrustResult( + SecTrust trust, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.getTrustResult'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([trust]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as GetTrustResultResponse?)!; + } + } + + /// Certificates used to evaluate trust. + static Future?> copyCertificateChain( + SecTrust trust, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecTrust.copyCertificateChain'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([trust]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return (pigeonVar_replyList[0] as List?)?.cast(); + } + } + + @override + SecTrust pigeon_copy() { + return SecTrust.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + observeValue: observeValue, + ); + } +} + +/// An abstract Core Foundation-type object representing an X.509 certificate. +/// +/// See https://developer.apple.com/documentation/security/seccertificate. +class SecCertificate extends NSObject { + /// Constructs [SecCertificate] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + SecCertificate.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + super.observeValue, + }) : super.pigeon_detached(); + + late final _PigeonInternalProxyApiBaseCodec _pigeonVar_codecSecCertificate = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + SecCertificate Function()? pigeon_newInstance, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecCertificate.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.SecCertificate.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.SecCertificate.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + SecCertificate.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + /// Returns a DER representation of a certificate given a certificate object. + static Future copyData( + SecCertificate certificate, { + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.webview_flutter_wkwebview.SecCertificate.copyData'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([certificate]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as Uint8List?)!; + } + } + + @override + SecCertificate pigeon_copy() { + return SecCertificate.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + observeValue: observeValue, + ); + } +} 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 20b74cc72ad..3349c34105a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -340,6 +340,39 @@ enum UrlCredentialPersistence { synchronizable, } +/// Trust evaluation result codes. +/// +/// See https://developer.apple.com/documentation/security/sectrustresulttype?language=objc. +enum DartSecTrustResultType { + /// The user did not specify a trust setting. + unspecified, + + /// The user granted permission to trust the certificate for the purposes + /// designated in the specified policies. + proceed, + + /// The user specified that the certificate should not be trusted. + deny, + + /// Trust is denied, but recovery may be possible. + recoverableTrustFailure, + + /// Trust is denied and no simple fix is available. + fatalTrustFailure, + + /// A value that indicates a failure other than trust evaluation. + otherError, + + /// An indication of an invalid setting or result. + invalid, + + /// User confirmation is required before proceeding. + confirm, + + /// The type is not recognized by this wrapper. + unknown, +} + /// A URL load request that is independent of protocol or URL scheme. /// /// See https://developer.apple.com/documentation/foundation/urlrequest. @@ -1095,6 +1128,9 @@ abstract class URLProtectionSpace extends NSObject { /// The authentication method used by the receiver. late String? authenticationMethod; + + /// A representation of the server’s SSL transaction state. + late SecTrust? getServerTrust; } /// A challenge from a server requiring authentication from the client. @@ -1132,3 +1168,57 @@ abstract class WKWebpagePreferences extends NSObject { /// allowed to run. void setAllowsContentJavaScript(bool allow); } + +/// Data class used to respond to `SecTrust.getTrustResult`. +/// +/// The native method needs to return two values, so this custom class is +/// created to support this. +@ProxyApi() +abstract class GetTrustResultResponse extends NSObject { + /// The result code from the most recent trust evaluation. + late DartSecTrustResultType result; + + /// A result code. + /// + /// See https://developer.apple.com/documentation/security/security-framework-result-codes?language=objc. + late int resultCode; +} + +/// An object used to evaluate trust. +/// +/// See https://developer.apple.com/documentation/security/sectrust. +@ProxyApi(swiftOptions: SwiftProxyApiOptions(name: 'SecTrustWrapper')) +abstract class SecTrust extends NSObject { + /// Evaluates trust for the specified certificate and policies. + @static + @async + bool evaluateWithError(SecTrust trust); + + /// Returns an opaque cookie containing exceptions to trust policies that will + /// allow future evaluations of the current certificate to succeed. + @static + Uint8List? copyExceptions(SecTrust trust); + + /// Sets a list of exceptions that should be ignored when the certificate is + /// evaluated. + @static + bool setExceptions(SecTrust trust, Uint8List? exceptions); + + /// Returns the result code from the most recent trust evaluation. + @static + GetTrustResultResponse getTrustResult(SecTrust trust); + + /// Certificates used to evaluate trust. + @static + List? copyCertificateChain(SecTrust trust); +} + +/// An abstract Core Foundation-type object representing an X.509 certificate. +/// +/// See https://developer.apple.com/documentation/security/seccertificate. +@ProxyApi(swiftOptions: SwiftProxyApiOptions(name: 'SecCertificateWrapper')) +abstract class SecCertificate extends NSObject { + /// Returns a DER representation of a certificate given a certificate object. + @static + Uint8List copyData(SecCertificate certificate); +} diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 67f2f644cd6..73efba1dc8b 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.5 +version: 3.18.6 environment: sdk: ^3.5.0 @@ -32,7 +32,7 @@ dev_dependencies: flutter_test: sdk: flutter mockito: ^5.4.4 - pigeon: ^24.2.1 + pigeon: ^25.2.0 topics: - html