Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.24.3

* Adds support to get failing url from DNS errors on iOS 26+.

## 3.24.2

* Fixes dartdoc comments that accidentally used HTML.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,27 @@ class FrameInfoProxyAPITests: XCTestCase {
let registrar = TestProxyApiRegistrar()
let api = registrar.apiDelegate.pigeonApiWKFrameInfo(registrar)

let instance: TestFrameInfo? = TestFrameInfo()
let value = try? api.pigeonDelegate.isMainFrame(pigeonApi: api, pigeonInstance: instance!)
let instance = TestFrameInfo.instance
let value = try? api.pigeonDelegate.isMainFrame(pigeonApi: api, pigeonInstance: instance)

XCTAssertEqual(value, instance!.isMainFrame)
XCTAssertEqual(value, instance.isMainFrame)
}

@MainActor func testRequest() {
let registrar = TestProxyApiRegistrar()
let api = registrar.apiDelegate.pigeonApiWKFrameInfo(registrar)

let instance: TestFrameInfo? = TestFrameInfo()
let value = try? api.pigeonDelegate.request(pigeonApi: api, pigeonInstance: instance!)
let instance = TestFrameInfo.instance
let value = try? api.pigeonDelegate.request(pigeonApi: api, pigeonInstance: instance)

XCTAssertEqual(value?.value, instance!.request)
XCTAssertEqual(value?.value, instance.request)
}

@MainActor func testNilRequest() {
let registrar = TestProxyApiRegistrar()
let api = registrar.apiDelegate.pigeonApiWKFrameInfo(registrar)

let instance = TestFrameInfoWithNilRequest()
let instance = TestFrameInfoWithNilRequest.instance
let value = try? api.pigeonDelegate.request(pigeonApi: api, pigeonInstance: instance)
// On macOS 15.5+, `WKFrameInfo.request` returns with an empty URLRequest.
// Previously it would return nil so accept either.
Expand All @@ -46,6 +46,14 @@ class FrameInfoProxyAPITests: XCTestCase {
}

class TestFrameInfo: WKFrameInfo {
// Global test instance of `WKFrameInfo`. Using a static instance prevents a crash when
// a `WKFrameInfo` is deallocated during a test on iOS 26+.
static let instance = TestFrameInfo()

private override init() {

}

override var isMainFrame: Bool {
return true
}
Expand All @@ -56,4 +64,11 @@ class TestFrameInfo: WKFrameInfo {
}

class TestFrameInfoWithNilRequest: WKFrameInfo {
// Global test instance of `WKFrameInfo` with a nil URLRequest. Using a static instance prevents a
// crash when a `WKFrameInfo` is deallocated during a test on iOS 26+.
static let instance = TestFrameInfoWithNilRequest()

private override init() {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class NavigationActionProxyAPITests: XCTestCase {
}

class TestNavigationAction: WKNavigationAction {
let internalTargetFrame = TestFrameInfo()
let internalTargetFrame = TestFrameInfo.instance

override var request: URLRequest {
return URLRequest(url: URL(string: "http://google.com")!)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,7 @@ class TestWebView: WKWebView {
}

class TestURLAuthenticationChallengeSender: NSObject, URLAuthenticationChallengeSender,
@unchecked
Sendable
@unchecked Sendable
{
func use(_ credential: URLCredential, for challenge: URLAuthenticationChallenge) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class UIDelegateProxyAPITests: XCTestCase {
let instance = UIDelegateImpl(api: api, registrar: registrar)
let webView = WKWebView(frame: .zero)
let origin = SecurityOriginProxyAPITests.testSecurityOrigin
let frame = TestFrameInfo()
let frame = TestFrameInfo.instance
let type: WKMediaCaptureType = .camera

var resultDecision: WKPermissionDecision?
Expand All @@ -64,7 +64,7 @@ class UIDelegateProxyAPITests: XCTestCase {
let instance = UIDelegateImpl(api: api, registrar: registrar)
let webView = WKWebView(frame: .zero)
let message = "myString"
let frame = TestFrameInfo()
let frame = TestFrameInfo.instance

instance.webView(webView, runJavaScriptAlertPanelWithMessage: message, initiatedByFrame: frame)
{
Expand All @@ -79,7 +79,7 @@ class UIDelegateProxyAPITests: XCTestCase {
let instance = UIDelegateImpl(api: api, registrar: registrar)
let webView = WKWebView(frame: .zero)
let message = "myString"
let frame = TestFrameInfo()
let frame = TestFrameInfo.instance

var confirmedResult: Bool?
let callbackExpectation = expectation(description: "Wait for callback.")
Expand All @@ -103,7 +103,7 @@ class UIDelegateProxyAPITests: XCTestCase {
let webView = WKWebView(frame: .zero)
let prompt = "myString"
let defaultText = "myString3"
let frame = TestFrameInfo()
let frame = TestFrameInfo.instance

var inputResult: String?
let callbackExpectation = expectation(description: "Wait for callback.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class NSErrorUserInfoKey {
/// See https://developer.apple.com/documentation/foundation/nsurlerrorfailingurlstringerrorkey?language=objc.
static const String NSURLErrorFailingURLStringError =
'NSErrorFailingURLStringKey';

/// The URL which caused a load to fail.
///
/// See https://developer.apple.com/documentation/foundation/nsurlerrorfailingurlerrorkey.
static const String NSURLErrorFailingURLErrorKey = 'NSErrorFailingURLKey';
}

/// The authentication method used by the receiver.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1254,17 +1254,22 @@ class WebKitNavigationDelegate extends PlatformNavigationDelegate {
);
}
},
didFailProvisionalNavigation: (_, __, NSError error) {
didFailProvisionalNavigation: (_, __, NSError error) async {
var url =
error.userInfo[NSErrorUserInfoKey.NSURLErrorFailingURLStringError]
as String?;

// On iOS 26+, the error is stored with `NSURLErrorFailingURLErrorKey`.
if (url == null) {
final nativeURL =
error.userInfo[NSErrorUserInfoKey.NSURLErrorFailingURLErrorKey]
as URL?;
url = await nativeURL?.getAbsoluteString();
}

if (weakThis.target?._onWebResourceError != null) {
weakThis.target!._onWebResourceError!(
WebKitWebResourceError._(
error,
isForMainFrame: true,
url:
error.userInfo[NSErrorUserInfoKey
.NSURLErrorFailingURLStringError]
as String?,
),
WebKitWebResourceError._(error, isForMainFrame: true, url: url),
);
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.24.2
version: 3.24.3

environment:
sdk: ^3.9.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,55 @@ void main() {
expect(callbackError.isForMainFrame, true);
});

test(
'onWebResourceError can receive DNS errors from didFailProvisionalNavigation',
() async {
PigeonOverrides.wKNavigationDelegate_new =
CapturingNavigationDelegate.new;
final webKitDelegate = WebKitNavigationDelegate(
const WebKitNavigationDelegateCreationParams(),
);

late final WebKitWebResourceError callbackError;
void onWebResourceError(WebResourceError error) {
callbackError = error as WebKitWebResourceError;
}

await webKitDelegate.setOnWebResourceError(onWebResourceError);

CapturingNavigationDelegate
.lastCreatedDelegate
.didFailProvisionalNavigation!(
WKNavigationDelegate.pigeon_detached(
decidePolicyForNavigationAction: (_, __, ___) async {
return NavigationActionPolicy.cancel;
},
decidePolicyForNavigationResponse: (_, __, ___) async {
return NavigationResponsePolicy.cancel;
},
didReceiveAuthenticationChallenge: (_, __, ___) async {
return AuthenticationChallengeResponse.pigeon_detached(
disposition:
UrlSessionAuthChallengeDisposition.performDefaultHandling,
);
},
),
WKWebView.pigeon_detached(),
NSError.pigeon_detached(
code: WKErrorCode.webViewInvalidated,
domain: 'domain',
userInfo: const <String, Object?>{
NSErrorUserInfoKey.NSURLErrorFailingURLStringError:
'www.flutter.dev',
NSErrorUserInfoKey.NSLocalizedDescription: 'my desc',
},
),
);

expect(callbackError.url, 'www.flutter.dev');
},
);

test(
'onWebResourceError from webViewWebContentProcessDidTerminate',
() async {
Expand Down
Loading