[webview_flutter] Support for handling basic authentication requests (iOS) (#5455)
Adds the iOS implementation for basic http authentication.
This PR is part of a series of PRs that aim to close https://github.com/flutter/flutter/issues/83556.
The PR that contains all changes can be found at https://github.com/flutter/packages/pull/4140.
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
index d95a843..cc60d2d 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 3.10.0
+* Adds support for `PlatformNavigationDelegate.setOnHttpAuthRequest`.
* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.
## 3.9.4
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 64d7702..89ecad8 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
@@ -34,6 +34,15 @@
request.response.writeln('${request.headers}');
} else if (request.uri.path == '/favicon.ico') {
request.response.statusCode = HttpStatus.notFound;
+ } else if (request.uri.path == '/http-basic-authentication') {
+ final bool isAuthenticating = request.headers['Authorization'] != null;
+ if (isAuthenticating) {
+ request.response.writeln('Authorized');
+ } else {
+ request.response.headers
+ .add('WWW-Authenticate', 'Basic realm="Test realm"');
+ request.response.statusCode = HttpStatus.unauthorized;
+ }
} else {
fail('unexpected request: ${request.method} ${request.uri}');
}
@@ -43,6 +52,7 @@
final String primaryUrl = '$prefixUrl/hello.txt';
final String secondaryUrl = '$prefixUrl/secondary.txt';
final String headersUrl = '$prefixUrl/headers';
+ final String basicAuthUrl = '$prefixUrl/http-basic-authentication';
testWidgets(
'withWeakReferenceTo allows encapsulating class to be garbage collected',
@@ -1127,6 +1137,82 @@
});
});
+ testWidgets('can receive HTTP basic auth requests',
+ (WidgetTester tester) async {
+ final Completer<void> authRequested = Completer<void>();
+ final PlatformWebViewController controller = PlatformWebViewController(
+ const PlatformWebViewControllerCreationParams(),
+ );
+
+ final PlatformNavigationDelegate navigationDelegate =
+ PlatformNavigationDelegate(
+ const PlatformNavigationDelegateCreationParams(),
+ );
+ await navigationDelegate.setOnHttpAuthRequest(
+ (HttpAuthRequest request) => authRequested.complete());
+ await controller.setPlatformNavigationDelegate(navigationDelegate);
+
+ // Clear cache so that the auth request is always received and we don't get
+ // a cached response.
+ await controller.clearCache();
+
+ await tester.pumpWidget(
+ Builder(
+ builder: (BuildContext context) {
+ return PlatformWebViewWidget(
+ WebKitWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ },
+ ),
+ );
+
+ await controller.loadRequest(
+ LoadRequestParams(uri: Uri.parse(basicAuthUrl)),
+ );
+
+ await expectLater(authRequested.future, completes);
+ });
+
+ testWidgets('can reply to HTTP basic auth requests',
+ (WidgetTester tester) async {
+ final Completer<void> pageFinished = Completer<void>();
+ final PlatformWebViewController controller = PlatformWebViewController(
+ const PlatformWebViewControllerCreationParams(),
+ );
+
+ final PlatformNavigationDelegate navigationDelegate =
+ PlatformNavigationDelegate(
+ const PlatformNavigationDelegateCreationParams(),
+ );
+ await navigationDelegate.setOnPageFinished((_) => pageFinished.complete());
+ await navigationDelegate.setOnHttpAuthRequest(
+ (HttpAuthRequest request) => request.onProceed(
+ const WebViewCredential(user: 'user', password: 'password'),
+ ),
+ );
+ await controller.setPlatformNavigationDelegate(navigationDelegate);
+
+ // Clear cache so that the auth request is always received and we don't get
+ // a cached response.
+ await controller.clearCache();
+
+ await tester.pumpWidget(
+ Builder(
+ builder: (BuildContext context) {
+ return PlatformWebViewWidget(
+ WebKitWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ },
+ ),
+ );
+
+ await controller.loadRequest(
+ LoadRequestParams(uri: Uri.parse(basicAuthUrl)),
+ );
+
+ await expectLater(pageFinished.future, completes);
+ });
+
testWidgets('launches with gestureNavigationEnabled on iOS',
(WidgetTester tester) async {
final WebKitWebViewController controller = WebKitWebViewController(
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 32c0bf9..5b13eba 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
@@ -11,6 +11,9 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
8F4FF949299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */; };
8F4FF94B29AC223F000A6586 /* FWFURLTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F4FF94A29AC223F000A6586 /* FWFURLTests.m */; };
+ 8F562F902A56C02D00C2BED6 /* FWFURLCredentialHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F562F8F2A56C02D00C2BED6 /* FWFURLCredentialHostApiTests.m */; };
+ 8F562F922A56C04F00C2BED6 /* FWFURLProtectionSpaceHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F562F912A56C04F00C2BED6 /* FWFURLProtectionSpaceHostApiTests.m */; };
+ 8F562F942A56C07B00C2BED6 /* FWFURLAuthenticationChallengeHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F562F932A56C07B00C2BED6 /* FWFURLAuthenticationChallengeHostApiTests.m */; };
8F78EAAA2A02CB9100C2E520 /* FWFErrorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8F78EAA92A02CB9100C2E520 /* FWFErrorTests.m */; };
8FA6A87928062CD000A4B183 /* FWFInstanceManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */; };
8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */; };
@@ -81,6 +84,9 @@
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8F4FF948299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewFlutterWKWebViewExternalAPITests.m; sourceTree = "<group>"; };
8F4FF94A29AC223F000A6586 /* FWFURLTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFURLTests.m; sourceTree = "<group>"; };
+ 8F562F8F2A56C02D00C2BED6 /* FWFURLCredentialHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFURLCredentialHostApiTests.m; sourceTree = "<group>"; };
+ 8F562F912A56C04F00C2BED6 /* FWFURLProtectionSpaceHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFURLProtectionSpaceHostApiTests.m; sourceTree = "<group>"; };
+ 8F562F932A56C07B00C2BED6 /* FWFURLAuthenticationChallengeHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFURLAuthenticationChallengeHostApiTests.m; sourceTree = "<group>"; };
8F78EAA92A02CB9100C2E520 /* FWFErrorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFErrorTests.m; sourceTree = "<group>"; };
8FA6A87828062CD000A4B183 /* FWFInstanceManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFInstanceManagerTests.m; sourceTree = "<group>"; };
8FB79B5228134C3100C101D3 /* FWFWebViewHostApiTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FWFWebViewHostApiTests.m; sourceTree = "<group>"; };
@@ -167,6 +173,9 @@
8FB79B902820BAC700C101D3 /* FWFUIViewHostApiTests.m */,
8FB79B962821985200C101D3 /* FWFObjectHostApiTests.m */,
8F4FF94A29AC223F000A6586 /* FWFURLTests.m */,
+ 8F562F8F2A56C02D00C2BED6 /* FWFURLCredentialHostApiTests.m */,
+ 8F562F912A56C04F00C2BED6 /* FWFURLProtectionSpaceHostApiTests.m */,
+ 8F562F932A56C07B00C2BED6 /* FWFURLAuthenticationChallengeHostApiTests.m */,
8F78EAA92A02CB9100C2E520 /* FWFErrorTests.m */,
);
path = RunnerTests;
@@ -318,7 +327,7 @@
isa = PBXProject;
attributes = {
DefaultBuildSystemTypeForWorkspace = Original;
- LastUpgradeCheck = 1300;
+ LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
68BDCAE823C3F7CB00D9C032 = {
@@ -327,7 +336,6 @@
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
- DevelopmentTeam = 7624MWN53C;
};
F7151F73266057800028CB91 = {
CreatedOnToolsVersion = 12.5;
@@ -474,12 +482,15 @@
8FB79B852820A3A400C101D3 /* FWFUIDelegateHostApiTests.m in Sources */,
8FB79B972821985200C101D3 /* FWFObjectHostApiTests.m in Sources */,
8FB79B672820453400C101D3 /* FWFHTTPCookieStoreHostApiTests.m in Sources */,
+ 8F562F942A56C07B00C2BED6 /* FWFURLAuthenticationChallengeHostApiTests.m in Sources */,
8FB79B5328134C3100C101D3 /* FWFWebViewHostApiTests.m in Sources */,
8FB79B73282096B500C101D3 /* FWFScriptMessageHandlerHostApiTests.m in Sources */,
8FB79B7928209D1300C101D3 /* FWFUserContentControllerHostApiTests.m in Sources */,
+ 8F562F902A56C02D00C2BED6 /* FWFURLCredentialHostApiTests.m in Sources */,
8F4FF949299ADC2D000A6586 /* FWFWebViewFlutterWKWebViewExternalAPITests.m in Sources */,
8FB79B6B28204EE500C101D3 /* FWFWebsiteDataStoreHostApiTests.m in Sources */,
8FB79B8F2820BAB300C101D3 /* FWFScrollViewHostApiTests.m in Sources */,
+ 8F562F922A56C04F00C2BED6 /* FWFURLProtectionSpaceHostApiTests.m in Sources */,
8FB79B912820BAC700C101D3 /* FWFUIViewHostApiTests.m in Sources */,
8FB79B55281B24F600C101D3 /* FWFDataConvertersTests.m in Sources */,
8FB79B6D2820533B00C101D3 /* FWFWebViewConfigurationHostApiTests.m in Sources */,
@@ -689,6 +700,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 7624MWN53C;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -715,6 +727,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 7624MWN53C;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index cb713d7..cf07c46 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1300"
+ LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m
index 669234d..dc4cb3c 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFNavigationDelegateHostApiTests.m
@@ -213,4 +213,58 @@
webViewIdentifier:1
completion:OCMOCK_ANY]);
}
+
+- (void)testDidReceiveAuthenticationChallenge {
+ FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
+
+ FWFNavigationDelegate *mockDelegate = [self mockNavigationDelegateWithManager:instanceManager
+ identifier:0];
+ FWFNavigationDelegateFlutterApiImpl *mockFlutterAPI =
+ [self mockFlutterApiWithManager:instanceManager];
+
+ OCMStub([mockDelegate navigationDelegateAPI]).andReturn(mockFlutterAPI);
+
+ WKWebView *mockWebView = OCMClassMock([WKWebView class]);
+ [instanceManager addDartCreatedInstance:mockWebView withIdentifier:1];
+
+ NSURLAuthenticationChallenge *mockChallenge = OCMClassMock([NSURLAuthenticationChallenge class]);
+ NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"host"
+ port:0
+ protocol:nil
+ realm:@"realm"
+ authenticationMethod:nil];
+ OCMStub([mockChallenge protectionSpace]).andReturn(protectionSpace);
+ [instanceManager addDartCreatedInstance:mockChallenge withIdentifier:2];
+
+ NSURLCredential *credential = [NSURLCredential credentialWithUser:@"user"
+ password:@"password"
+ persistence:NSURLCredentialPersistenceNone];
+ [instanceManager addDartCreatedInstance:credential withIdentifier:5];
+
+ OCMStub([mockFlutterAPI
+ didReceiveAuthenticationChallengeForDelegateWithIdentifier:0
+ webViewIdentifier:1
+ challengeIdentifier:2
+ completion:
+ ([OCMArg
+ invokeBlockWithArgs:
+ [FWFAuthenticationChallengeResponse
+ makeWithDisposition:
+ FWFNSUrlSessionAuthChallengeDispositionCancelAuthenticationChallenge
+ credentialIdentifier:@(5)],
+ [NSNull null], nil])]);
+
+ NSURLSessionAuthChallengeDisposition __block callbackDisposition = -1;
+ NSURLCredential *__block callbackCredential;
+ [mockDelegate webView:mockWebView
+ didReceiveAuthenticationChallenge:mockChallenge
+ completionHandler:^(NSURLSessionAuthChallengeDisposition disposition,
+ NSURLCredential *credential) {
+ callbackDisposition = disposition;
+ callbackCredential = credential;
+ }];
+
+ XCTAssertEqual(callbackDisposition, NSURLSessionAuthChallengeCancelAuthenticationChallenge);
+ XCTAssertEqualObjects(callbackCredential, credential);
+}
@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLAuthenticationChallengeHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLAuthenticationChallengeHostApiTests.m
new file mode 100644
index 0000000..fc0edf9
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLAuthenticationChallengeHostApiTests.m
@@ -0,0 +1,47 @@
+// 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 Flutter;
+@import XCTest;
+@import webview_flutter_wkwebview;
+
+#import <OCMock/OCMock.h>
+
+@interface FWFURLAuthenticationChallengeHostApiTests : XCTestCase
+
+@end
+
+@implementation FWFURLAuthenticationChallengeHostApiTests
+- (void)testFlutterApiCreate {
+ FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
+ FWFURLAuthenticationChallengeFlutterApiImpl *flutterApi =
+ [[FWFURLAuthenticationChallengeFlutterApiImpl alloc]
+ initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
+ instanceManager:instanceManager];
+
+ flutterApi.api = OCMClassMock([FWFNSUrlAuthenticationChallengeFlutterApi class]);
+
+ NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"host"
+ port:0
+ protocol:nil
+ realm:@"realm"
+ authenticationMethod:nil];
+
+ NSURLAuthenticationChallenge *mockChallenge = OCMClassMock([NSURLAuthenticationChallenge class]);
+ OCMStub([mockChallenge protectionSpace]).andReturn(protectionSpace);
+
+ [flutterApi createWithInstance:mockChallenge
+ protectionSpace:protectionSpace
+ completion:^(FlutterError *error){
+
+ }];
+
+ long identifier = [instanceManager identifierWithStrongReferenceForInstance:mockChallenge];
+ long protectionSpaceIdentifier =
+ [instanceManager identifierWithStrongReferenceForInstance:protectionSpace];
+ OCMVerify([flutterApi.api createWithIdentifier:identifier
+ protectionSpaceIdentifier:protectionSpaceIdentifier
+ completion:OCMOCK_ANY]);
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLCredentialHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLCredentialHostApiTests.m
new file mode 100644
index 0000000..7f3aa34
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLCredentialHostApiTests.m
@@ -0,0 +1,35 @@
+// 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 Flutter;
+@import XCTest;
+@import webview_flutter_wkwebview;
+
+#import <OCMock/OCMock.h>
+
+@interface FWFURLCredentialHostApiTests : XCTestCase
+@end
+
+@implementation FWFURLCredentialHostApiTests
+- (void)testHostApiCreate {
+ FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
+
+ FWFURLCredentialHostApiImpl *hostApi = [[FWFURLCredentialHostApiImpl alloc]
+ initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
+ instanceManager:instanceManager];
+
+ FlutterError *error;
+ [hostApi createWithUserWithIdentifier:0
+ user:@"user"
+ password:@"password"
+ persistence:FWFNSUrlCredentialPersistencePermanent
+ error:&error];
+ XCTAssertNil(error);
+
+ NSURLCredential *credential = (NSURLCredential *)[instanceManager instanceForIdentifier:0];
+ XCTAssertEqualObjects(credential.user, @"user");
+ XCTAssertEqualObjects(credential.password, @"password");
+ XCTAssertEqual(credential.persistence, NSURLCredentialPersistencePermanent);
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLProtectionSpaceHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLProtectionSpaceHostApiTests.m
new file mode 100644
index 0000000..c5a6cdf
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFURLProtectionSpaceHostApiTests.m
@@ -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 Flutter;
+@import XCTest;
+@import webview_flutter_wkwebview;
+
+#import <OCMock/OCMock.h>
+
+@interface FWFURLProtectionSpaceHostApiTests : XCTestCase
+@end
+
+@implementation FWFURLProtectionSpaceHostApiTests
+- (void)testFlutterApiCreate {
+ FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init];
+ FWFURLProtectionSpaceFlutterApiImpl *flutterApi = [[FWFURLProtectionSpaceFlutterApiImpl alloc]
+ initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger))
+ instanceManager:instanceManager];
+
+ flutterApi.api = OCMClassMock([FWFNSUrlProtectionSpaceFlutterApi class]);
+
+ NSURLProtectionSpace *protectionSpace = [[NSURLProtectionSpace alloc] initWithHost:@"host"
+ port:0
+ protocol:nil
+ realm:@"realm"
+ authenticationMethod:nil];
+ [flutterApi createWithInstance:protectionSpace
+ host:@"host"
+ realm:@"realm"
+ authenticationMethod:@"method"
+ completion:^(FlutterError *error){
+
+ }];
+
+ long identifier = [instanceManager identifierWithStrongReferenceForInstance:protectionSpace];
+ OCMVerify([flutterApi.api createWithIdentifier:identifier
+ host:@"host"
+ realm:@"realm"
+ authenticationMethod:@"method"
+ completion:OCMOCK_ANY]);
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
index 8f3dfa8..c237018 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
@@ -162,6 +162,9 @@
})
..setOnUrlChange((UrlChange change) {
debugPrint('url change to ${change.url}');
+ })
+ ..setOnHttpAuthRequest((HttpAuthRequest request) {
+ openDialog(request);
}),
)
..addJavaScriptChannel(JavaScriptChannelParams(
@@ -220,6 +223,62 @@
child: const Icon(Icons.favorite),
);
}
+
+ Future<void> openDialog(HttpAuthRequest httpRequest) async {
+ final TextEditingController usernameTextController =
+ TextEditingController();
+ final TextEditingController passwordTextController =
+ TextEditingController();
+
+ return showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: Text('${httpRequest.host}: ${httpRequest.realm ?? '-'}'),
+ content: SingleChildScrollView(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: <Widget>[
+ TextField(
+ decoration: const InputDecoration(labelText: 'Username'),
+ autofocus: true,
+ controller: usernameTextController,
+ ),
+ TextField(
+ decoration: const InputDecoration(labelText: 'Password'),
+ controller: passwordTextController,
+ ),
+ ],
+ ),
+ ),
+ actions: <Widget>[
+ // Explicitly cancel the request on iOS as the OS does not emit new
+ // requests when a previous request is pending.
+ TextButton(
+ onPressed: () {
+ httpRequest.onCancel();
+ Navigator.of(context).pop();
+ },
+ child: const Text('Cancel'),
+ ),
+ TextButton(
+ onPressed: () {
+ httpRequest.onProceed(
+ WebViewCredential(
+ user: usernameTextController.text,
+ password: passwordTextController.text,
+ ),
+ );
+ Navigator.of(context).pop();
+ },
+ child: const Text('Authenticate'),
+ ),
+ ],
+ );
+ },
+ );
+ }
}
enum MenuOptions {
@@ -237,6 +296,7 @@
transparentBackground,
setCookie,
logExample,
+ basicAuthentication,
}
class SampleMenu extends StatelessWidget {
@@ -300,6 +360,9 @@
case MenuOptions.logExample:
_onLogExample();
break;
+ case MenuOptions.basicAuthentication:
+ _promptForUrl(context);
+ break;
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
@@ -360,6 +423,10 @@
value: MenuOptions.logExample,
child: Text('Log example'),
),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.basicAuthentication,
+ child: Text('Basic Authentication Example'),
+ ),
],
);
}
@@ -518,6 +585,41 @@
return webViewController.loadHtmlString(kLogExamplePage);
}
+
+ Future<void> _promptForUrl(BuildContext context) {
+ final TextEditingController urlTextController =
+ TextEditingController(text: 'https://');
+
+ return showDialog<String>(
+ context: context,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ title: const Text('Input URL to visit'),
+ content: TextField(
+ decoration: const InputDecoration(labelText: 'URL'),
+ autofocus: true,
+ controller: urlTextController,
+ ),
+ actions: <Widget>[
+ TextButton(
+ onPressed: () {
+ if (urlTextController.text.isNotEmpty) {
+ final Uri? uri = Uri.tryParse(urlTextController.text);
+ if (uri != null && uri.scheme.isNotEmpty) {
+ webViewController.loadRequest(
+ LoadRequestParams(uri: uri),
+ );
+ Navigator.pop(context);
+ }
+ }
+ },
+ child: const Text('Visit'),
+ ),
+ ],
+ );
+ },
+ );
+ }
}
class NavigationControls extends StatelessWidget {
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml
index e4066c0..6590404 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml
@@ -10,7 +10,7 @@
flutter:
sdk: flutter
path_provider: ^2.0.6
- webview_flutter_platform_interface: ^2.6.0
+ webview_flutter_platform_interface: ^2.7.0
webview_flutter_wkwebview:
# When depending on this package from a real application you should use:
# webview_flutter: ^x.y.z
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m
index c3d6699..e776726 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWebViewFlutterPlugin.m
@@ -13,6 +13,7 @@
#import "FWFScrollViewHostApi.h"
#import "FWFUIDelegateHostApi.h"
#import "FWFUIViewHostApi.h"
+#import "FWFURLCredentialHostApi.h"
#import "FWFURLHostApi.h"
#import "FWFUserContentControllerHostApi.h"
#import "FWFWebViewConfigurationHostApi.h"
@@ -105,6 +106,11 @@
[[FWFURLHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger
instanceManager:instanceManager]);
+ SetUpFWFNSUrlCredentialHostApi(
+ registrar.messenger,
+ [[FWFURLCredentialHostApiImpl alloc] initWithBinaryMessenger:registrar.messenger
+ instanceManager:instanceManager]);
+
FWFWebViewFactory *webviewFactory = [[FWFWebViewFactory alloc] initWithManager:instanceManager];
[registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"];
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h
index 005cecb..e9405f3 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.h
@@ -193,4 +193,26 @@
extern FWFWKMediaCaptureTypeData *FWFWKMediaCaptureTypeDataFromNativeWKMediaCaptureType(
WKMediaCaptureType type);
+/**
+ * Converts an FWFNSUrlSessionAuthChallengeDisposition to an NSURLSessionAuthChallengeDisposition.
+ *
+ * @param value The object containing information to create an NSURLSessionAuthChallengeDisposition.
+ *
+ * @return A NSURLSessionAuthChallengeDisposition or -1 if data could not be converted.
+ */
+extern NSURLSessionAuthChallengeDisposition
+FWFNativeNSURLSessionAuthChallengeDispositionFromFWFNSUrlSessionAuthChallengeDisposition(
+ FWFNSUrlSessionAuthChallengeDisposition value);
+
+/**
+ * Converts an FWFNSUrlCredentialPersistence to an NSURLCredentialPersistence.
+ *
+ * @param value The object containing information to create an NSURLCredentialPersistence.
+ *
+ * @return A NSURLCredentialPersistence or -1 if data could not be converted.
+ */
+extern NSURLCredentialPersistence
+FWFNativeNSURLCredentialPersistenceFromFWFNSUrlCredentialPersistence(
+ FWFNSUrlCredentialPersistence value);
+
NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m
index 3cbcea8..a4dda8b 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m
@@ -285,3 +285,36 @@
return nil;
}
+
+NSURLSessionAuthChallengeDisposition
+FWFNativeNSURLSessionAuthChallengeDispositionFromFWFNSUrlSessionAuthChallengeDisposition(
+ FWFNSUrlSessionAuthChallengeDisposition value) {
+ switch (value) {
+ case FWFNSUrlSessionAuthChallengeDispositionUseCredential:
+ return NSURLSessionAuthChallengeUseCredential;
+ case FWFNSUrlSessionAuthChallengeDispositionPerformDefaultHandling:
+ return NSURLSessionAuthChallengePerformDefaultHandling;
+ case FWFNSUrlSessionAuthChallengeDispositionCancelAuthenticationChallenge:
+ return NSURLSessionAuthChallengeCancelAuthenticationChallenge;
+ case FWFNSUrlSessionAuthChallengeDispositionRejectProtectionSpace:
+ return NSURLSessionAuthChallengeRejectProtectionSpace;
+ }
+
+ return -1;
+}
+
+NSURLCredentialPersistence FWFNativeNSURLCredentialPersistenceFromFWFNSUrlCredentialPersistence(
+ FWFNSUrlCredentialPersistence value) {
+ switch (value) {
+ case FWFNSUrlCredentialPersistenceNone:
+ return NSURLCredentialPersistenceNone;
+ case FWFNSUrlCredentialPersistenceSession:
+ return NSURLCredentialPersistenceForSession;
+ case FWFNSUrlCredentialPersistencePermanent:
+ return NSURLCredentialPersistencePermanent;
+ case FWFNSUrlCredentialPersistenceSynchronizable:
+ return NSURLCredentialPersistenceSynchronizable;
+ }
+
+ return -1;
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h
index 8469438..1e312a2 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h
@@ -264,6 +264,73 @@
- (instancetype)initWithValue:(FWFWKMediaCaptureType)value;
@end
+/// Responses to an authentication challenge.
+///
+/// See
+/// https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition?language=objc.
+typedef NS_ENUM(NSUInteger, FWFNSUrlSessionAuthChallengeDisposition) {
+ /// Use the specified credential, which may be nil.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengeusecredential?language=objc.
+ FWFNSUrlSessionAuthChallengeDispositionUseCredential = 0,
+ /// Use the default handling for the challenge as though this delegate method
+ /// were not implemented.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengeperformdefaulthandling?language=objc.
+ FWFNSUrlSessionAuthChallengeDispositionPerformDefaultHandling = 1,
+ /// Cancel the entire request.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengecancelauthenticationchallenge?language=objc.
+ FWFNSUrlSessionAuthChallengeDispositionCancelAuthenticationChallenge = 2,
+ /// Reject this challenge, and call the authentication delegate method again
+ /// with the next authentication protection space.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengerejectprotectionspace?language=objc.
+ FWFNSUrlSessionAuthChallengeDispositionRejectProtectionSpace = 3,
+};
+
+/// Wrapper for FWFNSUrlSessionAuthChallengeDisposition to allow for nullability.
+@interface FWFNSUrlSessionAuthChallengeDispositionBox : NSObject
+@property(nonatomic, assign) FWFNSUrlSessionAuthChallengeDisposition value;
+- (instancetype)initWithValue:(FWFNSUrlSessionAuthChallengeDisposition)value;
+@end
+
+/// Specifies how long a credential will be kept.
+typedef NS_ENUM(NSUInteger, FWFNSUrlCredentialPersistence) {
+ /// The credential should not be stored.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencenone?language=objc.
+ FWFNSUrlCredentialPersistenceNone = 0,
+ /// The credential should be stored only for this session.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistenceforsession?language=objc.
+ FWFNSUrlCredentialPersistenceSession = 1,
+ /// The credential should be stored in the keychain.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencepermanent?language=objc.
+ FWFNSUrlCredentialPersistencePermanent = 2,
+ /// The credential should be stored permanently in the keychain, and in
+ /// addition should be distributed to other devices based on the owning Apple
+ /// ID.
+ ///
+ /// See
+ /// https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencesynchronizable?language=objc.
+ FWFNSUrlCredentialPersistenceSynchronizable = 3,
+};
+
+/// Wrapper for FWFNSUrlCredentialPersistence to allow for nullability.
+@interface FWFNSUrlCredentialPersistenceBox : NSObject
+@property(nonatomic, assign) FWFNSUrlCredentialPersistence value;
+- (instancetype)initWithValue:(FWFNSUrlCredentialPersistence)value;
+@end
+
@class FWFNSKeyValueObservingOptionsEnumData;
@class FWFNSKeyValueChangeKeyEnumData;
@class FWFWKUserScriptInjectionTimeEnumData;
@@ -282,6 +349,7 @@
@class FWFWKSecurityOriginData;
@class FWFNSHttpCookieData;
@class FWFObjectOrIdentifier;
+@class FWFAuthenticationChallengeResponse;
@interface FWFNSKeyValueObservingOptionsEnumData : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
@@ -462,6 +530,15 @@
@property(nonatomic, assign) BOOL isIdentifier;
@end
+@interface FWFAuthenticationChallengeResponse : NSObject
+/// `init` unavailable to enforce nonnull fields, see the `make` class method.
+- (instancetype)init NS_UNAVAILABLE;
++ (instancetype)makeWithDisposition:(FWFNSUrlSessionAuthChallengeDisposition)disposition
+ credentialIdentifier:(nullable NSNumber *)credentialIdentifier;
+@property(nonatomic, assign) FWFNSUrlSessionAuthChallengeDisposition disposition;
+@property(nonatomic, strong, nullable) NSNumber *credentialIdentifier;
+@end
+
/// The codec used by FWFWKWebsiteDataStoreHostApi.
NSObject<FlutterMessageCodec> *FWFWKWebsiteDataStoreHostApiGetCodec(void);
@@ -711,6 +788,14 @@
completion:
(void (^)(FlutterError *_Nullable))
completion;
+- (void)didReceiveAuthenticationChallengeForDelegateWithIdentifier:(NSInteger)identifier
+ webViewIdentifier:(NSInteger)webViewIdentifier
+ challengeIdentifier:(NSInteger)challengeIdentifier
+ completion:
+ (void (^)(
+ FWFAuthenticationChallengeResponse
+ *_Nullable,
+ FlutterError *_Nullable))completion;
@end
/// The codec used by FWFNSObjectHostApi.
@@ -919,4 +1004,65 @@
completion:(void (^)(FlutterError *_Nullable))completion;
@end
+/// The codec used by FWFNSUrlCredentialHostApi.
+NSObject<FlutterMessageCodec> *FWFNSUrlCredentialHostApiGetCodec(void);
+
+/// Host API for `NSUrlCredential`.
+///
+/// This class may handle instantiating and adding native object instances that
+/// are attached to a Dart instance or handle method calls on the associated
+/// native class or an instance of the class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlcredential?language=objc.
+@protocol FWFNSUrlCredentialHostApi
+/// Create a new native instance and add it to the `InstanceManager`.
+- (void)createWithUserWithIdentifier:(NSInteger)identifier
+ user:(NSString *)user
+ password:(NSString *)password
+ persistence:(FWFNSUrlCredentialPersistence)persistence
+ error:(FlutterError *_Nullable *_Nonnull)error;
+@end
+
+extern void SetUpFWFNSUrlCredentialHostApi(id<FlutterBinaryMessenger> binaryMessenger,
+ NSObject<FWFNSUrlCredentialHostApi> *_Nullable api);
+
+/// The codec used by FWFNSUrlProtectionSpaceFlutterApi.
+NSObject<FlutterMessageCodec> *FWFNSUrlProtectionSpaceFlutterApiGetCodec(void);
+
+/// Flutter API for `NSUrlProtectionSpace`.
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlprotectionspace?language=objc.
+@interface FWFNSUrlProtectionSpaceFlutterApi : NSObject
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
+/// Create a new Dart instance and add it to the `InstanceManager`.
+- (void)createWithIdentifier:(NSInteger)identifier
+ host:(nullable NSString *)host
+ realm:(nullable NSString *)realm
+ authenticationMethod:(nullable NSString *)authenticationMethod
+ completion:(void (^)(FlutterError *_Nullable))completion;
+@end
+
+/// The codec used by FWFNSUrlAuthenticationChallengeFlutterApi.
+NSObject<FlutterMessageCodec> *FWFNSUrlAuthenticationChallengeFlutterApiGetCodec(void);
+
+/// Flutter API for `NSUrlAuthenticationChallenge`.
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+///
+/// See
+/// https://developer.apple.com/documentation/foundation/nsurlauthenticationchallenge?language=objc.
+@interface FWFNSUrlAuthenticationChallengeFlutterApi : NSObject
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger;
+/// Create a new Dart instance and add it to the `InstanceManager`.
+- (void)createWithIdentifier:(NSInteger)identifier
+ protectionSpaceIdentifier:(NSInteger)protectionSpaceIdentifier
+ completion:(void (^)(FlutterError *_Nullable))completion;
+@end
+
NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m
index 52fbbee..5d80178 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m
@@ -164,6 +164,31 @@
}
@end
+/// Responses to an authentication challenge.
+///
+/// See
+/// https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition?language=objc.
+@implementation FWFNSUrlSessionAuthChallengeDispositionBox
+- (instancetype)initWithValue:(FWFNSUrlSessionAuthChallengeDisposition)value {
+ self = [super init];
+ if (self) {
+ _value = value;
+ }
+ return self;
+}
+@end
+
+/// Specifies how long a credential will be kept.
+@implementation FWFNSUrlCredentialPersistenceBox
+- (instancetype)initWithValue:(FWFNSUrlCredentialPersistence)value {
+ self = [super init];
+ if (self) {
+ _value = value;
+ }
+ return self;
+}
+@end
+
static NSArray *wrapResult(id result, FlutterError *error) {
if (error) {
return @[
@@ -285,6 +310,12 @@
- (NSArray *)toList;
@end
+@interface FWFAuthenticationChallengeResponse ()
++ (FWFAuthenticationChallengeResponse *)fromList:(NSArray *)list;
++ (nullable FWFAuthenticationChallengeResponse *)nullableFromList:(NSArray *)list;
+- (NSArray *)toList;
+@end
+
@implementation FWFNSKeyValueObservingOptionsEnumData
+ (instancetype)makeWithValue:(FWFNSKeyValueObservingOptionsEnum)value {
FWFNSKeyValueObservingOptionsEnumData *pigeonResult =
@@ -727,6 +758,33 @@
}
@end
+@implementation FWFAuthenticationChallengeResponse
++ (instancetype)makeWithDisposition:(FWFNSUrlSessionAuthChallengeDisposition)disposition
+ credentialIdentifier:(nullable NSNumber *)credentialIdentifier {
+ FWFAuthenticationChallengeResponse *pigeonResult =
+ [[FWFAuthenticationChallengeResponse alloc] init];
+ pigeonResult.disposition = disposition;
+ pigeonResult.credentialIdentifier = credentialIdentifier;
+ return pigeonResult;
+}
++ (FWFAuthenticationChallengeResponse *)fromList:(NSArray *)list {
+ FWFAuthenticationChallengeResponse *pigeonResult =
+ [[FWFAuthenticationChallengeResponse alloc] init];
+ pigeonResult.disposition = [GetNullableObjectAtIndex(list, 0) integerValue];
+ pigeonResult.credentialIdentifier = GetNullableObjectAtIndex(list, 1);
+ return pigeonResult;
+}
++ (nullable FWFAuthenticationChallengeResponse *)nullableFromList:(NSArray *)list {
+ return (list) ? [FWFAuthenticationChallengeResponse fromList:list] : nil;
+}
+- (NSArray *)toList {
+ return @[
+ @(self.disposition),
+ self.credentialIdentifier ?: [NSNull null],
+ ];
+}
+@end
+
@interface FWFWKWebsiteDataStoreHostApiCodecReader : FlutterStandardReader
@end
@implementation FWFWKWebsiteDataStoreHostApiCodecReader
@@ -1684,14 +1742,16 @@
- (nullable id)readValueOfType:(UInt8)type {
switch (type) {
case 128:
- return [FWFNSErrorData fromList:[self readValue]];
+ return [FWFAuthenticationChallengeResponse fromList:[self readValue]];
case 129:
- return [FWFNSUrlRequestData fromList:[self readValue]];
+ return [FWFNSErrorData fromList:[self readValue]];
case 130:
- return [FWFWKFrameInfoData fromList:[self readValue]];
+ return [FWFNSUrlRequestData fromList:[self readValue]];
case 131:
- return [FWFWKNavigationActionData fromList:[self readValue]];
+ return [FWFWKFrameInfoData fromList:[self readValue]];
case 132:
+ return [FWFWKNavigationActionData fromList:[self readValue]];
+ case 133:
return [FWFWKNavigationActionPolicyEnumData fromList:[self readValue]];
default:
return [super readValueOfType:type];
@@ -1703,21 +1763,24 @@
@end
@implementation FWFWKNavigationDelegateFlutterApiCodecWriter
- (void)writeValue:(id)value {
- if ([value isKindOfClass:[FWFNSErrorData class]]) {
+ if ([value isKindOfClass:[FWFAuthenticationChallengeResponse class]]) {
[self writeByte:128];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) {
+ } else if ([value isKindOfClass:[FWFNSErrorData class]]) {
[self writeByte:129];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) {
+ } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) {
[self writeByte:130];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) {
+ } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) {
[self writeByte:131];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) {
[self writeByte:132];
[self writeValue:[value toList]];
+ } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) {
+ [self writeByte:133];
+ [self writeValue:[value toList]];
} else {
[super writeValue:value];
}
@@ -1934,6 +1997,40 @@
}
}];
}
+- (void)
+ didReceiveAuthenticationChallengeForDelegateWithIdentifier:(NSInteger)arg_identifier
+ webViewIdentifier:(NSInteger)arg_webViewIdentifier
+ challengeIdentifier:(NSInteger)arg_challengeIdentifier
+ completion:
+ (void (^)(FWFAuthenticationChallengeResponse
+ *_Nullable,
+ FlutterError *_Nullable))
+ completion {
+ FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+ messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview."
+ @"WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge"
+ binaryMessenger:self.binaryMessenger
+ codec:FWFWKNavigationDelegateFlutterApiGetCodec()];
+ [channel sendMessage:@[ @(arg_identifier), @(arg_webViewIdentifier), @(arg_challengeIdentifier) ]
+ reply:^(NSArray<id> *reply) {
+ if (reply != nil) {
+ if (reply.count > 1) {
+ completion(nil, [FlutterError errorWithCode:reply[0]
+ message:reply[1]
+ details:reply[2]]);
+ } else {
+ FWFAuthenticationChallengeResponse *output =
+ reply[0] == [NSNull null] ? nil : reply[0];
+ completion(output, nil);
+ }
+ } else {
+ completion(nil, [FlutterError
+ errorWithCode:@"channel-error"
+ message:@"Unable to establish connection on channel."
+ details:@""]);
+ }
+ }];
+}
@end
@interface FWFNSObjectHostApiCodecReader : FlutterStandardReader
@@ -2201,40 +2298,42 @@
- (nullable id)readValueOfType:(UInt8)type {
switch (type) {
case 128:
- return [FWFNSErrorData fromList:[self readValue]];
+ return [FWFAuthenticationChallengeResponse fromList:[self readValue]];
case 129:
- return [FWFNSHttpCookieData fromList:[self readValue]];
+ return [FWFNSErrorData fromList:[self readValue]];
case 130:
- return [FWFNSHttpCookiePropertyKeyEnumData fromList:[self readValue]];
+ return [FWFNSHttpCookieData fromList:[self readValue]];
case 131:
- return [FWFNSKeyValueChangeKeyEnumData fromList:[self readValue]];
+ return [FWFNSHttpCookiePropertyKeyEnumData fromList:[self readValue]];
case 132:
- return [FWFNSKeyValueObservingOptionsEnumData fromList:[self readValue]];
+ return [FWFNSKeyValueChangeKeyEnumData fromList:[self readValue]];
case 133:
- return [FWFNSUrlRequestData fromList:[self readValue]];
+ return [FWFNSKeyValueObservingOptionsEnumData fromList:[self readValue]];
case 134:
- return [FWFObjectOrIdentifier fromList:[self readValue]];
+ return [FWFNSUrlRequestData fromList:[self readValue]];
case 135:
- return [FWFWKAudiovisualMediaTypeEnumData fromList:[self readValue]];
+ return [FWFObjectOrIdentifier fromList:[self readValue]];
case 136:
- return [FWFWKFrameInfoData fromList:[self readValue]];
+ return [FWFWKAudiovisualMediaTypeEnumData fromList:[self readValue]];
case 137:
- return [FWFWKMediaCaptureTypeData fromList:[self readValue]];
+ return [FWFWKFrameInfoData fromList:[self readValue]];
case 138:
- return [FWFWKNavigationActionData fromList:[self readValue]];
+ return [FWFWKMediaCaptureTypeData fromList:[self readValue]];
case 139:
- return [FWFWKNavigationActionPolicyEnumData fromList:[self readValue]];
+ return [FWFWKNavigationActionData fromList:[self readValue]];
case 140:
- return [FWFWKPermissionDecisionData fromList:[self readValue]];
+ return [FWFWKNavigationActionPolicyEnumData fromList:[self readValue]];
case 141:
- return [FWFWKScriptMessageData fromList:[self readValue]];
+ return [FWFWKPermissionDecisionData fromList:[self readValue]];
case 142:
- return [FWFWKSecurityOriginData fromList:[self readValue]];
+ return [FWFWKScriptMessageData fromList:[self readValue]];
case 143:
- return [FWFWKUserScriptData fromList:[self readValue]];
+ return [FWFWKSecurityOriginData fromList:[self readValue]];
case 144:
- return [FWFWKUserScriptInjectionTimeEnumData fromList:[self readValue]];
+ return [FWFWKUserScriptData fromList:[self readValue]];
case 145:
+ return [FWFWKUserScriptInjectionTimeEnumData fromList:[self readValue]];
+ case 146:
return [FWFWKWebsiteDataTypeEnumData fromList:[self readValue]];
default:
return [super readValueOfType:type];
@@ -2246,60 +2345,63 @@
@end
@implementation FWFWKWebViewHostApiCodecWriter
- (void)writeValue:(id)value {
- if ([value isKindOfClass:[FWFNSErrorData class]]) {
+ if ([value isKindOfClass:[FWFAuthenticationChallengeResponse class]]) {
[self writeByte:128];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFNSHttpCookieData class]]) {
+ } else if ([value isKindOfClass:[FWFNSErrorData class]]) {
[self writeByte:129];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFNSHttpCookieData class]]) {
[self writeByte:130];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFNSKeyValueChangeKeyEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFNSHttpCookiePropertyKeyEnumData class]]) {
[self writeByte:131];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFNSKeyValueChangeKeyEnumData class]]) {
[self writeByte:132];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) {
+ } else if ([value isKindOfClass:[FWFNSKeyValueObservingOptionsEnumData class]]) {
[self writeByte:133];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFObjectOrIdentifier class]]) {
+ } else if ([value isKindOfClass:[FWFNSUrlRequestData class]]) {
[self writeByte:134];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFObjectOrIdentifier class]]) {
[self writeByte:135];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) {
+ } else if ([value isKindOfClass:[FWFWKAudiovisualMediaTypeEnumData class]]) {
[self writeByte:136];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKMediaCaptureTypeData class]]) {
+ } else if ([value isKindOfClass:[FWFWKFrameInfoData class]]) {
[self writeByte:137];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) {
+ } else if ([value isKindOfClass:[FWFWKMediaCaptureTypeData class]]) {
[self writeByte:138];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFWKNavigationActionData class]]) {
[self writeByte:139];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKPermissionDecisionData class]]) {
+ } else if ([value isKindOfClass:[FWFWKNavigationActionPolicyEnumData class]]) {
[self writeByte:140];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKScriptMessageData class]]) {
+ } else if ([value isKindOfClass:[FWFWKPermissionDecisionData class]]) {
[self writeByte:141];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKSecurityOriginData class]]) {
+ } else if ([value isKindOfClass:[FWFWKScriptMessageData class]]) {
[self writeByte:142];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) {
+ } else if ([value isKindOfClass:[FWFWKSecurityOriginData class]]) {
[self writeByte:143];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFWKUserScriptData class]]) {
[self writeByte:144];
[self writeValue:[value toList]];
- } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) {
+ } else if ([value isKindOfClass:[FWFWKUserScriptInjectionTimeEnumData class]]) {
[self writeByte:145];
[self writeValue:[value toList]];
+ } else if ([value isKindOfClass:[FWFWKWebsiteDataTypeEnumData class]]) {
+ [self writeByte:146];
+ [self writeValue:[value toList]];
} else {
[super writeValue:value];
}
@@ -3181,3 +3283,143 @@
}];
}
@end
+
+NSObject<FlutterMessageCodec> *FWFNSUrlCredentialHostApiGetCodec(void) {
+ static FlutterStandardMessageCodec *sSharedObject = nil;
+ sSharedObject = [FlutterStandardMessageCodec sharedInstance];
+ return sSharedObject;
+}
+
+void SetUpFWFNSUrlCredentialHostApi(id<FlutterBinaryMessenger> binaryMessenger,
+ NSObject<FWFNSUrlCredentialHostApi> *api) {
+ /// Create a new native instance and add it to the `InstanceManager`.
+ {
+ FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+ initWithName:
+ @"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser"
+ binaryMessenger:binaryMessenger
+ codec:FWFNSUrlCredentialHostApiGetCodec()];
+ if (api) {
+ NSCAssert([api respondsToSelector:@selector
+ (createWithUserWithIdentifier:user:password:persistence:error:)],
+ @"FWFNSUrlCredentialHostApi api (%@) doesn't respond to "
+ @"@selector(createWithUserWithIdentifier:user:password:persistence:error:)",
+ api);
+ [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+ NSArray *args = message;
+ NSInteger arg_identifier = [GetNullableObjectAtIndex(args, 0) integerValue];
+ NSString *arg_user = GetNullableObjectAtIndex(args, 1);
+ NSString *arg_password = GetNullableObjectAtIndex(args, 2);
+ FWFNSUrlCredentialPersistence arg_persistence =
+ [GetNullableObjectAtIndex(args, 3) integerValue];
+ FlutterError *error;
+ [api createWithUserWithIdentifier:arg_identifier
+ user:arg_user
+ password:arg_password
+ persistence:arg_persistence
+ error:&error];
+ callback(wrapResult(nil, error));
+ }];
+ } else {
+ [channel setMessageHandler:nil];
+ }
+ }
+}
+NSObject<FlutterMessageCodec> *FWFNSUrlProtectionSpaceFlutterApiGetCodec(void) {
+ static FlutterStandardMessageCodec *sSharedObject = nil;
+ sSharedObject = [FlutterStandardMessageCodec sharedInstance];
+ return sSharedObject;
+}
+
+@interface FWFNSUrlProtectionSpaceFlutterApi ()
+@property(nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;
+@end
+
+@implementation FWFNSUrlProtectionSpaceFlutterApi
+
+- (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)binaryMessenger {
+ self = [super init];
+ if (self) {
+ _binaryMessenger = binaryMessenger;
+ }
+ return self;
+}
+- (void)createWithIdentifier:(NSInteger)arg_identifier
+ host:(nullable NSString *)arg_host
+ realm:(nullable NSString *)arg_realm
+ authenticationMethod:(nullable NSString *)arg_authenticationMethod
+ completion:(void (^)(FlutterError *_Nullable))completion {
+ FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+ messageChannelWithName:
+ @"dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlProtectionSpaceFlutterApi.create"
+ binaryMessenger:self.binaryMessenger
+ codec:FWFNSUrlProtectionSpaceFlutterApiGetCodec()];
+ [channel
+ sendMessage:@[
+ @(arg_identifier), arg_host ?: [NSNull null], arg_realm ?: [NSNull null],
+ arg_authenticationMethod ?: [NSNull null]
+ ]
+ reply:^(NSArray<id> *reply) {
+ if (reply != nil) {
+ if (reply.count > 1) {
+ completion([FlutterError errorWithCode:reply[0]
+ message:reply[1]
+ details:reply[2]]);
+ } else {
+ completion(nil);
+ }
+ } else {
+ completion([FlutterError errorWithCode:@"channel-error"
+ message:@"Unable to establish connection on channel."
+ details:@""]);
+ }
+ }];
+}
+@end
+
+NSObject<FlutterMessageCodec> *FWFNSUrlAuthenticationChallengeFlutterApiGetCodec(void) {
+ static FlutterStandardMessageCodec *sSharedObject = nil;
+ sSharedObject = [FlutterStandardMessageCodec sharedInstance];
+ return sSharedObject;
+}
+
+@interface FWFNSUrlAuthenticationChallengeFlutterApi ()
+@property(nonatomic, strong) NSObject<FlutterBinaryMessenger> *binaryMessenger;
+@end
+
+@implementation FWFNSUrlAuthenticationChallengeFlutterApi
+
+- (instancetype)initWithBinaryMessenger:(NSObject<FlutterBinaryMessenger> *)binaryMessenger {
+ self = [super init];
+ if (self) {
+ _binaryMessenger = binaryMessenger;
+ }
+ return self;
+}
+- (void)createWithIdentifier:(NSInteger)arg_identifier
+ protectionSpaceIdentifier:(NSInteger)arg_protectionSpaceIdentifier
+ completion:(void (^)(FlutterError *_Nullable))completion {
+ FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+ messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview."
+ @"NSUrlAuthenticationChallengeFlutterApi.create"
+ binaryMessenger:self.binaryMessenger
+ codec:FWFNSUrlAuthenticationChallengeFlutterApiGetCodec()];
+ [channel
+ sendMessage:@[ @(arg_identifier), @(arg_protectionSpaceIdentifier) ]
+ reply:^(NSArray<id> *reply) {
+ if (reply != nil) {
+ if (reply.count > 1) {
+ completion([FlutterError errorWithCode:reply[0]
+ message:reply[1]
+ details:reply[2]]);
+ } else {
+ completion(nil);
+ }
+ } else {
+ completion([FlutterError errorWithCode:@"channel-error"
+ message:@"Unable to establish connection on channel."
+ details:@""]);
+ }
+ }];
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m
index 30035d0..db8c456 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFNavigationDelegateHostApi.m
@@ -4,9 +4,13 @@
#import "FWFNavigationDelegateHostApi.h"
#import "FWFDataConverters.h"
+#import "FWFURLAuthenticationChallengeHostApi.h"
#import "FWFWebViewConfigurationHostApi.h"
@interface FWFNavigationDelegateFlutterApiImpl ()
+// BinaryMessenger must be weak to prevent a circular reference with the host API it
+// references.
+@property(nonatomic, weak) id<FlutterBinaryMessenger> binaryMessenger;
// InstanceManager must be weak to prevent a circular reference with the object it stores.
@property(nonatomic, weak) FWFInstanceManager *instanceManager;
@end
@@ -16,6 +20,7 @@
instanceManager:(FWFInstanceManager *)instanceManager {
self = [self initWithBinaryMessenger:binaryMessenger];
if (self) {
+ _binaryMessenger = binaryMessenger;
_instanceManager = instanceManager;
}
return self;
@@ -102,6 +107,37 @@
webViewIdentifier:webViewIdentifier
completion:completion];
}
+
+- (void)
+ didReceiveAuthenticationChallengeForDelegate:(FWFNavigationDelegate *)instance
+ webView:(WKWebView *)webView
+ challenge:(NSURLAuthenticationChallenge *)challenge
+ completion:
+ (void (^)(FWFAuthenticationChallengeResponse *_Nullable,
+ FlutterError *_Nullable))completion {
+ NSInteger webViewIdentifier =
+ [self.instanceManager identifierWithStrongReferenceForInstance:webView];
+
+ FWFURLAuthenticationChallengeFlutterApiImpl *challengeApi =
+ [[FWFURLAuthenticationChallengeFlutterApiImpl alloc]
+ initWithBinaryMessenger:self.binaryMessenger
+ instanceManager:self.instanceManager];
+ [challengeApi createWithInstance:challenge
+ protectionSpace:challenge.protectionSpace
+ completion:^(FlutterError *error) {
+ NSAssert(!error, @"%@", error);
+ }];
+
+ [self
+ didReceiveAuthenticationChallengeForDelegateWithIdentifier:[self
+ identifierForDelegate:instance]
+ webViewIdentifier:webViewIdentifier
+ challengeIdentifier:
+ [self.instanceManager
+ identifierWithStrongReferenceForInstance:
+ challenge]
+ completion:completion];
+}
@end
@implementation FWFNavigationDelegate
@@ -144,8 +180,13 @@
completion:^(FWFWKNavigationActionPolicyEnumData *policy,
FlutterError *error) {
NSAssert(!error, @"%@", error);
- decisionHandler(
- FWFNativeWKNavigationActionPolicyFromEnumData(policy));
+ if (!error) {
+ decisionHandler(
+ FWFNativeWKNavigationActionPolicyFromEnumData(
+ policy));
+ } else {
+ decisionHandler(WKNavigationActionPolicyCancel);
+ }
}];
}
@@ -179,6 +220,40 @@
NSAssert(!error, @"%@", error);
}];
}
+
+- (void)webView:(WKWebView *)webView
+ didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
+ completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition,
+ NSURLCredential *_Nullable))completionHandler {
+ [self.navigationDelegateAPI
+ didReceiveAuthenticationChallengeForDelegate:self
+ webView:webView
+ challenge:challenge
+ completion:^(FWFAuthenticationChallengeResponse *response,
+ FlutterError *error) {
+ NSAssert(!error, @"%@", error);
+ if (!error) {
+ NSURLSessionAuthChallengeDisposition disposition =
+ FWFNativeNSURLSessionAuthChallengeDispositionFromFWFNSUrlSessionAuthChallengeDisposition(
+ response.disposition);
+
+ NSURLCredential *credential =
+ response.credentialIdentifier
+ ? (NSURLCredential *)[self.navigationDelegateAPI
+ .instanceManager
+ instanceForIdentifier:
+ response.credentialIdentifier
+ .longValue]
+ : nil;
+
+ completionHandler(disposition, credential);
+ } else {
+ completionHandler(
+ NSURLSessionAuthChallengeCancelAuthenticationChallenge,
+ nil);
+ }
+ }];
+}
@end
@interface FWFNavigationDelegateHostApiImpl ()
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h
new file mode 100644
index 0000000..6100b70
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.h
@@ -0,0 +1,33 @@
+// 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 <Flutter/Flutter.h>
+#import <Foundation/Foundation.h>
+#import "FWFGeneratedWebKitApis.h"
+#import "FWFInstanceManager.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Flutter API implementation for `NSURLAuthenticationChallenge`.
+ *
+ * This class may handle instantiating and adding Dart instances that are attached to a native
+ * instance or sending callback methods from an overridden native class.
+ */
+@interface FWFURLAuthenticationChallengeFlutterApiImpl : NSObject
+/**
+ * The Flutter API used to send messages back to Dart.
+ */
+@property FWFNSUrlAuthenticationChallengeFlutterApi *api;
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
+ instanceManager:(FWFInstanceManager *)instanceManager;
+/**
+ * Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`.
+ */
+- (void)createWithInstance:(NSURLAuthenticationChallenge *)instance
+ protectionSpace:(NSURLProtectionSpace *)protectionSpace
+ completion:(void (^)(FlutterError *_Nullable))completion;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.m
new file mode 100644
index 0000000..965952d
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLAuthenticationChallengeHostApi.m
@@ -0,0 +1,52 @@
+// 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 "FWFURLAuthenticationChallengeHostApi.h"
+#import "FWFURLProtectionSpaceHostApi.h"
+
+@interface FWFURLAuthenticationChallengeFlutterApiImpl ()
+// BinaryMessenger must be weak to prevent a circular reference with the host API it
+// references.
+@property(nonatomic, weak) id<FlutterBinaryMessenger> binaryMessenger;
+// InstanceManager must be weak to prevent a circular reference with the object it stores.
+@property(nonatomic, weak) FWFInstanceManager *instanceManager;
+@end
+
+@implementation FWFURLAuthenticationChallengeFlutterApiImpl
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
+ instanceManager:(FWFInstanceManager *)instanceManager {
+ self = [self init];
+ if (self) {
+ _binaryMessenger = binaryMessenger;
+ _instanceManager = instanceManager;
+ _api =
+ [[FWFNSUrlAuthenticationChallengeFlutterApi alloc] initWithBinaryMessenger:binaryMessenger];
+ }
+ return self;
+}
+
+- (void)createWithInstance:(NSURLAuthenticationChallenge *)instance
+ protectionSpace:(NSURLProtectionSpace *)protectionSpace
+ completion:(void (^)(FlutterError *_Nullable))completion {
+ if ([self.instanceManager containsInstance:instance]) {
+ return;
+ }
+
+ FWFURLProtectionSpaceFlutterApiImpl *protectionSpaceApi =
+ [[FWFURLProtectionSpaceFlutterApiImpl alloc] initWithBinaryMessenger:self.binaryMessenger
+ instanceManager:self.instanceManager];
+ [protectionSpaceApi createWithInstance:protectionSpace
+ host:protectionSpace.host
+ realm:protectionSpace.realm
+ authenticationMethod:protectionSpace.authenticationMethod
+ completion:^(FlutterError *error) {
+ NSAssert(!error, @"%@", error);
+ }];
+
+ [self.api createWithIdentifier:[self.instanceManager addHostCreatedInstance:instance]
+ protectionSpaceIdentifier:[self.instanceManager
+ identifierWithStrongReferenceForInstance:protectionSpace]
+ completion:completion];
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h
new file mode 100644
index 0000000..fe9b3d0
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.h
@@ -0,0 +1,24 @@
+// 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 <Flutter/Flutter.h>
+#import <Foundation/Foundation.h>
+#import "FWFDataConverters.h"
+#import "FWFGeneratedWebKitApis.h"
+#import "FWFInstanceManager.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Host API implementation for `NSURLCredential`.
+ *
+ * This class may handle instantiating and adding native object instances that are attached to a
+ * Dart instance or method calls on the associated native class or an instance of the class.
+ */
+@interface FWFURLCredentialHostApiImpl : NSObject <FWFNSUrlCredentialHostApi>
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
+ instanceManager:(FWFInstanceManager *)instanceManager;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.m
new file mode 100644
index 0000000..2b6955f
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLCredentialHostApi.m
@@ -0,0 +1,58 @@
+// 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 "FWFURLCredentialHostApi.h"
+
+@interface FWFURLCredentialHostApiImpl ()
+// BinaryMessenger must be weak to prevent a circular reference with the host API it
+// references.
+@property(nonatomic, weak) id<FlutterBinaryMessenger> binaryMessenger;
+// InstanceManager must be weak to prevent a circular reference with the object it stores.
+@property(nonatomic, weak) FWFInstanceManager *instanceManager;
+@end
+
+@implementation FWFURLCredentialHostApiImpl
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
+ instanceManager:(FWFInstanceManager *)instanceManager {
+ self = [self init];
+ if (self) {
+ _binaryMessenger = binaryMessenger;
+ _instanceManager = instanceManager;
+ }
+ return self;
+}
+
+- (void)createWithUserWithIdentifier:(NSInteger)identifier
+ user:(nonnull NSString *)user
+ password:(nonnull NSString *)password
+ persistence:(FWFNSUrlCredentialPersistence)persistence
+ error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
+ [self.instanceManager
+ addDartCreatedInstance:
+ [NSURLCredential
+ credentialWithUser:user
+ password:password
+ persistence:
+ FWFNativeNSURLCredentialPersistenceFromFWFNSUrlCredentialPersistence(
+ persistence)]
+ withIdentifier:identifier];
+}
+
+- (nullable NSURL *)credentialForIdentifier:(NSNumber *)identifier
+ error:
+ (FlutterError *_Nullable __autoreleasing *_Nonnull)error {
+ NSURL *instance = (NSURL *)[self.instanceManager instanceForIdentifier:identifier.longValue];
+
+ if (!instance) {
+ NSString *message =
+ [NSString stringWithFormat:@"InstanceManager does not contain an NSURL with identifier: %@",
+ identifier];
+ *error = [FlutterError errorWithCode:NSInternalInconsistencyException
+ message:message
+ details:nil];
+ }
+
+ return instance;
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h
new file mode 100644
index 0000000..5e57ab5
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.h
@@ -0,0 +1,35 @@
+// 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 <Flutter/Flutter.h>
+#import <Foundation/Foundation.h>
+#import "FWFGeneratedWebKitApis.h"
+#import "FWFInstanceManager.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+/**
+ * Flutter API implementation for `NSURLProtectionSpace`.
+ *
+ * This class may handle instantiating and adding Dart instances that are attached to a native
+ * instance or sending callback methods from an overridden native class.
+ */
+@interface FWFURLProtectionSpaceFlutterApiImpl : NSObject
+/**
+ * The Flutter API used to send messages back to Dart.
+ */
+@property FWFNSUrlProtectionSpaceFlutterApi *api;
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
+ instanceManager:(FWFInstanceManager *)instanceManager;
+/**
+ * Sends a message to Dart to create a new Dart instance and add it to the `InstanceManager`.
+ */
+- (void)createWithInstance:(NSURLProtectionSpace *)instance
+ host:(nullable NSString *)host
+ realm:(nullable NSString *)realm
+ authenticationMethod:(nullable NSString *)authenticationMethod
+ completion:(void (^)(FlutterError *_Nullable))completion;
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.m
new file mode 100644
index 0000000..fc2d163
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFURLProtectionSpaceHostApi.m
@@ -0,0 +1,36 @@
+// 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 "FWFURLProtectionSpaceHostApi.h"
+
+@interface FWFURLProtectionSpaceFlutterApiImpl ()
+// InstanceManager must be weak to prevent a circular reference with the object it stores.
+@property(nonatomic, weak) FWFInstanceManager *instanceManager;
+@end
+
+@implementation FWFURLProtectionSpaceFlutterApiImpl
+- (instancetype)initWithBinaryMessenger:(id<FlutterBinaryMessenger>)binaryMessenger
+ instanceManager:(FWFInstanceManager *)instanceManager {
+ self = [self init];
+ if (self) {
+ _instanceManager = instanceManager;
+ _api = [[FWFNSUrlProtectionSpaceFlutterApi alloc] initWithBinaryMessenger:binaryMessenger];
+ }
+ return self;
+}
+
+- (void)createWithInstance:(NSURLProtectionSpace *)instance
+ host:(nullable NSString *)host
+ realm:(nullable NSString *)realm
+ authenticationMethod:(nullable NSString *)authenticationMethod
+ completion:(void (^)(FlutterError *_Nullable))completion {
+ if (![self.instanceManager containsInstance:instance]) {
+ [self.api createWithIdentifier:[self.instanceManager addHostCreatedInstance:instance]
+ host:host
+ realm:realm
+ authenticationMethod:authenticationMethod
+ completion:completion];
+ }
+}
+@end
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h
index 2836829..6d97c07 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/webview-umbrella.h
@@ -16,7 +16,10 @@
#import "FWFScrollViewHostApi.h"
#import "FWFUIDelegateHostApi.h"
#import "FWFUIViewHostApi.h"
+#import "FWFURLAuthenticationChallengeHostApi.h"
+#import "FWFURLCredentialHostApi.h"
#import "FWFURLHostApi.h"
+#import "FWFURLProtectionSpaceHostApi.h"
#import "FWFUserContentControllerHostApi.h"
#import "FWFWebViewConfigurationHostApi.h"
#import "FWFWebViewFlutterWKWebViewExternalAPI.h"
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 ff08766..1c63220 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
@@ -202,6 +202,58 @@
unknown,
}
+/// Responses to an authentication challenge.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition?language=objc.
+enum NSUrlSessionAuthChallengeDisposition {
+ /// Use the specified credential, which may be nil.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengeusecredential?language=objc.
+ useCredential,
+
+ /// Use the default handling for the challenge as though this delegate method
+ /// were not implemented.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengeperformdefaulthandling?language=objc.
+ performDefaultHandling,
+
+ /// Cancel the entire request.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengecancelauthenticationchallenge?language=objc.
+ cancelAuthenticationChallenge,
+
+ /// Reject this challenge, and call the authentication delegate method again
+ /// with the next authentication protection space.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengerejectprotectionspace?language=objc.
+ rejectProtectionSpace,
+}
+
+/// Specifies how long a credential will be kept.
+enum NSUrlCredentialPersistence {
+ /// The credential should not be stored.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencenone?language=objc.
+ none,
+
+ /// The credential should be stored only for this session.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistenceforsession?language=objc.
+ session,
+
+ /// The credential should be stored in the keychain.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencepermanent?language=objc.
+ permanent,
+
+ /// The credential should be stored permanently in the keychain, and in
+ /// addition should be distributed to other devices based on the owning Apple
+ /// ID.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencesynchronizable?language=objc.
+ synchronizable,
+}
+
class NSKeyValueObservingOptionsEnumData {
NSKeyValueObservingOptionsEnumData({
required this.value,
@@ -684,6 +736,33 @@
}
}
+class AuthenticationChallengeResponse {
+ AuthenticationChallengeResponse({
+ required this.disposition,
+ this.credentialIdentifier,
+ });
+
+ NSUrlSessionAuthChallengeDisposition disposition;
+
+ int? credentialIdentifier;
+
+ Object encode() {
+ return <Object?>[
+ disposition.index,
+ credentialIdentifier,
+ ];
+ }
+
+ static AuthenticationChallengeResponse decode(Object result) {
+ result as List<Object?>;
+ return AuthenticationChallengeResponse(
+ disposition:
+ NSUrlSessionAuthChallengeDisposition.values[result[0]! as int],
+ credentialIdentifier: result[1] as int?,
+ );
+ }
+}
+
class _WKWebsiteDataStoreHostApiCodec extends StandardMessageCodec {
const _WKWebsiteDataStoreHostApiCodec();
@override
@@ -1576,21 +1655,24 @@
const _WKNavigationDelegateFlutterApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
- if (value is NSErrorData) {
+ if (value is AuthenticationChallengeResponse) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
- } else if (value is NSUrlRequestData) {
+ } else if (value is NSErrorData) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
- } else if (value is WKFrameInfoData) {
+ } else if (value is NSUrlRequestData) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
- } else if (value is WKNavigationActionData) {
+ } else if (value is WKFrameInfoData) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
- } else if (value is WKNavigationActionPolicyEnumData) {
+ } else if (value is WKNavigationActionData) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
+ } else if (value is WKNavigationActionPolicyEnumData) {
+ buffer.putUint8(133);
+ writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -1600,14 +1682,16 @@
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
- return NSErrorData.decode(readValue(buffer)!);
+ return AuthenticationChallengeResponse.decode(readValue(buffer)!);
case 129:
- return NSUrlRequestData.decode(readValue(buffer)!);
+ return NSErrorData.decode(readValue(buffer)!);
case 130:
- return WKFrameInfoData.decode(readValue(buffer)!);
+ return NSUrlRequestData.decode(readValue(buffer)!);
case 131:
- return WKNavigationActionData.decode(readValue(buffer)!);
+ return WKFrameInfoData.decode(readValue(buffer)!);
case 132:
+ return WKNavigationActionData.decode(readValue(buffer)!);
+ case 133:
return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -1641,6 +1725,9 @@
void webViewWebContentProcessDidTerminate(
int identifier, int webViewIdentifier);
+ Future<AuthenticationChallengeResponse> didReceiveAuthenticationChallenge(
+ int identifier, int webViewIdentifier, int challengeIdentifier);
+
static void setup(WKNavigationDelegateFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
@@ -1842,6 +1929,41 @@
});
}
}
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge',
+ codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge was null.');
+ final List<Object?> args = (message as List<Object?>?)!;
+ final int? arg_identifier = (args[0] as int?);
+ assert(arg_identifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge was null, expected non-null int.');
+ final int? arg_webViewIdentifier = (args[1] as int?);
+ assert(arg_webViewIdentifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge was null, expected non-null int.');
+ final int? arg_challengeIdentifier = (args[2] as int?);
+ assert(arg_challengeIdentifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKNavigationDelegateFlutterApi.didReceiveAuthenticationChallenge was null, expected non-null int.');
+ try {
+ final AuthenticationChallengeResponse output =
+ await api.didReceiveAuthenticationChallenge(arg_identifier!,
+ arg_webViewIdentifier!, arg_challengeIdentifier!);
+ return wrapResponse(result: output);
+ } on PlatformException catch (e) {
+ return wrapResponse(error: e);
+ } catch (e) {
+ return wrapResponse(
+ error: PlatformException(code: 'error', message: e.toString()));
+ }
+ });
+ }
+ }
}
}
@@ -2082,60 +2204,63 @@
const _WKWebViewHostApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
- if (value is NSErrorData) {
+ if (value is AuthenticationChallengeResponse) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
- } else if (value is NSHttpCookieData) {
+ } else if (value is NSErrorData) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
- } else if (value is NSHttpCookiePropertyKeyEnumData) {
+ } else if (value is NSHttpCookieData) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
- } else if (value is NSKeyValueChangeKeyEnumData) {
+ } else if (value is NSHttpCookiePropertyKeyEnumData) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
- } else if (value is NSKeyValueObservingOptionsEnumData) {
+ } else if (value is NSKeyValueChangeKeyEnumData) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
- } else if (value is NSUrlRequestData) {
+ } else if (value is NSKeyValueObservingOptionsEnumData) {
buffer.putUint8(133);
writeValue(buffer, value.encode());
- } else if (value is ObjectOrIdentifier) {
+ } else if (value is NSUrlRequestData) {
buffer.putUint8(134);
writeValue(buffer, value.encode());
- } else if (value is WKAudiovisualMediaTypeEnumData) {
+ } else if (value is ObjectOrIdentifier) {
buffer.putUint8(135);
writeValue(buffer, value.encode());
- } else if (value is WKFrameInfoData) {
+ } else if (value is WKAudiovisualMediaTypeEnumData) {
buffer.putUint8(136);
writeValue(buffer, value.encode());
- } else if (value is WKMediaCaptureTypeData) {
+ } else if (value is WKFrameInfoData) {
buffer.putUint8(137);
writeValue(buffer, value.encode());
- } else if (value is WKNavigationActionData) {
+ } else if (value is WKMediaCaptureTypeData) {
buffer.putUint8(138);
writeValue(buffer, value.encode());
- } else if (value is WKNavigationActionPolicyEnumData) {
+ } else if (value is WKNavigationActionData) {
buffer.putUint8(139);
writeValue(buffer, value.encode());
- } else if (value is WKPermissionDecisionData) {
+ } else if (value is WKNavigationActionPolicyEnumData) {
buffer.putUint8(140);
writeValue(buffer, value.encode());
- } else if (value is WKScriptMessageData) {
+ } else if (value is WKPermissionDecisionData) {
buffer.putUint8(141);
writeValue(buffer, value.encode());
- } else if (value is WKSecurityOriginData) {
+ } else if (value is WKScriptMessageData) {
buffer.putUint8(142);
writeValue(buffer, value.encode());
- } else if (value is WKUserScriptData) {
+ } else if (value is WKSecurityOriginData) {
buffer.putUint8(143);
writeValue(buffer, value.encode());
- } else if (value is WKUserScriptInjectionTimeEnumData) {
+ } else if (value is WKUserScriptData) {
buffer.putUint8(144);
writeValue(buffer, value.encode());
- } else if (value is WKWebsiteDataTypeEnumData) {
+ } else if (value is WKUserScriptInjectionTimeEnumData) {
buffer.putUint8(145);
writeValue(buffer, value.encode());
+ } else if (value is WKWebsiteDataTypeEnumData) {
+ buffer.putUint8(146);
+ writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -2145,40 +2270,42 @@
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
- return NSErrorData.decode(readValue(buffer)!);
+ return AuthenticationChallengeResponse.decode(readValue(buffer)!);
case 129:
- return NSHttpCookieData.decode(readValue(buffer)!);
+ return NSErrorData.decode(readValue(buffer)!);
case 130:
- return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!);
+ return NSHttpCookieData.decode(readValue(buffer)!);
case 131:
- return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!);
+ return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!);
case 132:
- return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!);
+ return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!);
case 133:
- return NSUrlRequestData.decode(readValue(buffer)!);
+ return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!);
case 134:
- return ObjectOrIdentifier.decode(readValue(buffer)!);
+ return NSUrlRequestData.decode(readValue(buffer)!);
case 135:
- return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!);
+ return ObjectOrIdentifier.decode(readValue(buffer)!);
case 136:
- return WKFrameInfoData.decode(readValue(buffer)!);
+ return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!);
case 137:
- return WKMediaCaptureTypeData.decode(readValue(buffer)!);
+ return WKFrameInfoData.decode(readValue(buffer)!);
case 138:
- return WKNavigationActionData.decode(readValue(buffer)!);
+ return WKMediaCaptureTypeData.decode(readValue(buffer)!);
case 139:
- return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!);
+ return WKNavigationActionData.decode(readValue(buffer)!);
case 140:
- return WKPermissionDecisionData.decode(readValue(buffer)!);
+ return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!);
case 141:
- return WKScriptMessageData.decode(readValue(buffer)!);
+ return WKPermissionDecisionData.decode(readValue(buffer)!);
case 142:
- return WKSecurityOriginData.decode(readValue(buffer)!);
+ return WKScriptMessageData.decode(readValue(buffer)!);
case 143:
- return WKUserScriptData.decode(readValue(buffer)!);
+ return WKSecurityOriginData.decode(readValue(buffer)!);
case 144:
- return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!);
+ return WKUserScriptData.decode(readValue(buffer)!);
case 145:
+ return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!);
+ case 146:
return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -3052,3 +3179,148 @@
}
}
}
+
+/// Host API for `NSUrlCredential`.
+///
+/// This class may handle instantiating and adding native object instances that
+/// are attached to a Dart instance or handle method calls on the associated
+/// native class or an instance of the class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlcredential?language=objc.
+class NSUrlCredentialHostApi {
+ /// Constructor for [NSUrlCredentialHostApi]. The [binaryMessenger] named argument is
+ /// available for dependency injection. If it is left null, the default
+ /// BinaryMessenger will be used which routes to the host platform.
+ NSUrlCredentialHostApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+ /// Create a new native instance and add it to the `InstanceManager`.
+ Future<void> createWithUser(int arg_identifier, String arg_user,
+ String arg_password, NSUrlCredentialPersistence arg_persistence) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser',
+ codec,
+ binaryMessenger: _binaryMessenger);
+ final List<Object?>? replyList = await channel.send(<Object?>[
+ arg_identifier,
+ arg_user,
+ arg_password,
+ arg_persistence.index
+ ]) as List<Object?>?;
+ if (replyList == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyList.length > 1) {
+ throw PlatformException(
+ code: replyList[0]! as String,
+ message: replyList[1] as String?,
+ details: replyList[2],
+ );
+ } else {
+ return;
+ }
+ }
+}
+
+/// Flutter API for `NSUrlProtectionSpace`.
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlprotectionspace?language=objc.
+abstract class NSUrlProtectionSpaceFlutterApi {
+ static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+ /// Create a new Dart instance and add it to the `InstanceManager`.
+ void create(int identifier, String? host, String? realm,
+ String? authenticationMethod);
+
+ static void setup(NSUrlProtectionSpaceFlutterApi? api,
+ {BinaryMessenger? binaryMessenger}) {
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlProtectionSpaceFlutterApi.create',
+ codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlProtectionSpaceFlutterApi.create was null.');
+ final List<Object?> args = (message as List<Object?>?)!;
+ final int? arg_identifier = (args[0] as int?);
+ assert(arg_identifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlProtectionSpaceFlutterApi.create was null, expected non-null int.');
+ final String? arg_host = (args[1] as String?);
+ final String? arg_realm = (args[2] as String?);
+ final String? arg_authenticationMethod = (args[3] as String?);
+ try {
+ api.create(
+ arg_identifier!, arg_host, arg_realm, arg_authenticationMethod);
+ return wrapResponse(empty: true);
+ } on PlatformException catch (e) {
+ return wrapResponse(error: e);
+ } catch (e) {
+ return wrapResponse(
+ error: PlatformException(code: 'error', message: e.toString()));
+ }
+ });
+ }
+ }
+ }
+}
+
+/// Flutter API for `NSUrlAuthenticationChallenge`.
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlauthenticationchallenge?language=objc.
+abstract class NSUrlAuthenticationChallengeFlutterApi {
+ static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+ /// Create a new Dart instance and add it to the `InstanceManager`.
+ void create(int identifier, int protectionSpaceIdentifier);
+
+ static void setup(NSUrlAuthenticationChallengeFlutterApi? api,
+ {BinaryMessenger? binaryMessenger}) {
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlAuthenticationChallengeFlutterApi.create',
+ codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ channel.setMessageHandler(null);
+ } else {
+ channel.setMessageHandler((Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlAuthenticationChallengeFlutterApi.create was null.');
+ final List<Object?> args = (message as List<Object?>?)!;
+ final int? arg_identifier = (args[0] as int?);
+ assert(arg_identifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlAuthenticationChallengeFlutterApi.create was null, expected non-null int.');
+ final int? arg_protectionSpaceIdentifier = (args[1] as int?);
+ assert(arg_protectionSpaceIdentifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlAuthenticationChallengeFlutterApi.create was null, expected non-null int.');
+ try {
+ api.create(arg_identifier!, arg_protectionSpaceIdentifier!);
+ return wrapResponse(empty: true);
+ } on PlatformException catch (e) {
+ return wrapResponse(error: e);
+ } catch (e) {
+ return wrapResponse(
+ error: PlatformException(code: 'error', message: e.toString()));
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart
index 3fe1a57..2647ed9 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation.dart
@@ -9,6 +9,9 @@
import '../common/weak_reference_utils.dart';
import 'foundation_api_impls.dart';
+export 'foundation_api_impls.dart'
+ show NSUrlSessionAuthChallengeDisposition, NSUrlCredentialPersistence;
+
/// The values that can be returned in a change map.
///
/// Wraps [NSKeyValueObservingOptions](https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions?language=objc).
@@ -387,3 +390,131 @@
);
}
}
+
+/// An authentication credential consisting of information specific to the type
+/// of credential and the type of persistent storage to use, if any.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlcredential?language=objc.
+class NSUrlCredential extends NSObject {
+ /// Creates a URL credential instance for internet password authentication
+ /// with a given user name and password, using a given persistence setting.
+ NSUrlCredential.withUser({
+ required String user,
+ required String password,
+ required NSUrlCredentialPersistence persistence,
+ @visibleForTesting super.binaryMessenger,
+ @visibleForTesting super.instanceManager,
+ }) : _urlCredentialApi = NSUrlCredentialHostApiImpl(
+ binaryMessenger: binaryMessenger, instanceManager: instanceManager),
+ super.detached() {
+ // Ensures Flutter Apis are setup.
+ FoundationFlutterApis.instance.ensureSetUp();
+ _urlCredentialApi.createWithUserFromInstances(
+ this,
+ user,
+ password,
+ persistence,
+ );
+ }
+
+ /// Instantiates a [NSUrlCredential] without creating and attaching to an
+ /// instance of the associated native class.
+ ///
+ /// This should only be used outside of tests by subclasses created by this
+ /// library or to create a copy for an [InstanceManager].
+ @protected
+ NSUrlCredential.detached({super.binaryMessenger, super.instanceManager})
+ : _urlCredentialApi = NSUrlCredentialHostApiImpl(
+ binaryMessenger: binaryMessenger, instanceManager: instanceManager),
+ super.detached();
+
+ final NSUrlCredentialHostApiImpl _urlCredentialApi;
+
+ @override
+ NSObject copy() {
+ return NSUrlCredential.detached(
+ binaryMessenger: _urlCredentialApi.binaryMessenger,
+ instanceManager: _urlCredentialApi.instanceManager,
+ );
+ }
+}
+
+/// A server or an area on a server, commonly referred to as a realm, that
+/// requires authentication.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlprotectionspace?language=objc.
+class NSUrlProtectionSpace extends NSObject {
+ /// Instantiates a [NSUrlProtectionSpace] without creating and attaching to an
+ /// instance of the associated native class.
+ ///
+ /// This should only be used outside of tests by subclasses created by this
+ /// library or to create a copy for an [InstanceManager].
+ @protected
+ NSUrlProtectionSpace.detached({
+ required this.host,
+ required this.realm,
+ required this.authenticationMethod,
+ super.binaryMessenger,
+ super.instanceManager,
+ }) : super.detached();
+
+ /// The receiver’s host.
+ final String? host;
+
+ /// The receiver’s authentication realm.
+ final String? realm;
+
+ /// The authentication method used by the receiver.
+ final String? authenticationMethod;
+
+ @override
+ NSUrlProtectionSpace copy() {
+ return NSUrlProtectionSpace.detached(
+ host: host,
+ realm: realm,
+ authenticationMethod: authenticationMethod,
+ );
+ }
+}
+
+/// The authentication method used by the receiver.
+class NSUrlAuthenticationMethod {
+ /// Use the default authentication method for a protocol.
+ static const String default_ = 'NSURLAuthenticationMethodDefault';
+
+ /// Use HTML form authentication for this protection space.
+ static const String htmlForm = 'NSURLAuthenticationMethodHTMLForm';
+
+ /// Use HTTP basic authentication for this protection space.
+ static const String httpBasic = 'NSURLAuthenticationMethodHTTPBasic';
+
+ /// Use HTTP digest authentication for this protection space.
+ static const String httpDigest = 'NSURLAuthenticationMethodHTTPDigest';
+}
+
+/// A challenge from a server requiring authentication from the client.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlauthenticationchallenge?language=objc.
+class NSUrlAuthenticationChallenge extends NSObject {
+ /// Instantiates a [NSUrlAuthenticationChallenge] without creating and
+ /// attaching to an instance of the associated native class.
+ ///
+ /// This should only be used outside of tests by subclasses created by this
+ /// library or to create a copy for an [InstanceManager].
+ @protected
+ NSUrlAuthenticationChallenge.detached({
+ required this.protectionSpace,
+ super.binaryMessenger,
+ super.instanceManager,
+ }) : super.detached();
+
+ /// The receiver’s protection space.
+ late final NSUrlProtectionSpace protectionSpace;
+
+ @override
+ NSUrlAuthenticationChallenge copy() {
+ return NSUrlAuthenticationChallenge.detached(
+ protectionSpace: protectionSpace,
+ );
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart
index 4f73c08..7bd1259 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/foundation/foundation_api_impls.dart
@@ -9,6 +9,9 @@
import '../common/web_kit.g.dart';
import 'foundation.dart';
+export '../common/web_kit.g.dart'
+ show NSUrlSessionAuthChallengeDisposition, NSUrlCredentialPersistence;
+
Iterable<NSKeyValueObservingOptionsEnumData>
_toNSKeyValueObservingOptionsEnumData(
Iterable<NSKeyValueObservingOptions> options,
@@ -56,6 +59,14 @@
url = NSUrlFlutterApiImpl(
binaryMessenger: binaryMessenger,
instanceManager: instanceManager,
+ ),
+ urlProtectionSpace = NSUrlProtectionSpaceFlutterApiImpl(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
+ ),
+ urlAuthenticationChallenge = NSUrlAuthenticationChallengeFlutterApiImpl(
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
);
static FoundationFlutterApis _instance = FoundationFlutterApis();
@@ -82,6 +93,14 @@
@visibleForTesting
final NSUrlFlutterApiImpl url;
+ /// Flutter Api for [NSUrlProtectionSpace].
+ @visibleForTesting
+ final NSUrlProtectionSpaceFlutterApiImpl urlProtectionSpace;
+
+ /// Flutter Api for [NSUrlAuthenticationChallenge].
+ @visibleForTesting
+ final NSUrlAuthenticationChallengeFlutterApiImpl urlAuthenticationChallenge;
+
/// Ensures all the Flutter APIs have been set up to receive calls from native code.
void ensureSetUp() {
if (!_hasBeenSetUp) {
@@ -90,6 +109,14 @@
binaryMessenger: _binaryMessenger,
);
NSUrlFlutterApi.setup(url, binaryMessenger: _binaryMessenger);
+ NSUrlProtectionSpaceFlutterApi.setup(
+ urlProtectionSpace,
+ binaryMessenger: _binaryMessenger,
+ );
+ NSUrlAuthenticationChallengeFlutterApi.setup(
+ urlAuthenticationChallenge,
+ binaryMessenger: _binaryMessenger,
+ );
_hasBeenSetUp = true;
}
}
@@ -249,3 +276,121 @@
);
}
}
+
+/// Host api implementation for [NSUrlCredential].
+class NSUrlCredentialHostApiImpl extends NSUrlCredentialHostApi {
+ /// Constructs an [NSUrlCredentialHostApiImpl].
+ NSUrlCredentialHostApiImpl({
+ this.binaryMessenger,
+ InstanceManager? instanceManager,
+ }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager,
+ super(binaryMessenger: binaryMessenger);
+
+ /// Sends binary data across the Flutter platform barrier.
+ ///
+ /// If it is null, the default BinaryMessenger will be used which routes to
+ /// the host platform.
+ final BinaryMessenger? binaryMessenger;
+
+ /// Maintains instances stored to communicate with Objective-C objects.
+ final InstanceManager instanceManager;
+
+ /// Calls [createWithUser] with the ids of the provided object instances.
+ Future<void> createWithUserFromInstances(
+ NSUrlCredential instance,
+ String user,
+ String password,
+ NSUrlCredentialPersistence persistence,
+ ) {
+ return createWithUser(
+ instanceManager.addDartCreatedInstance(instance),
+ user,
+ password,
+ persistence,
+ );
+ }
+}
+
+/// Flutter API implementation for [NSUrlProtectionSpace].
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+@protected
+class NSUrlProtectionSpaceFlutterApiImpl
+ implements NSUrlProtectionSpaceFlutterApi {
+ /// Constructs a [NSUrlProtectionSpaceFlutterApiImpl].
+ NSUrlProtectionSpaceFlutterApiImpl({
+ this.binaryMessenger,
+ InstanceManager? instanceManager,
+ }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager;
+
+ /// Receives binary data across the Flutter platform barrier.
+ ///
+ /// If it is null, the default BinaryMessenger will be used which routes to
+ /// the host platform.
+ final BinaryMessenger? binaryMessenger;
+
+ /// Maintains instances stored to communicate with native language objects.
+ final InstanceManager instanceManager;
+
+ @override
+ void create(
+ int identifier,
+ String? host,
+ String? realm,
+ String? authenticationMethod,
+ ) {
+ instanceManager.addHostCreatedInstance(
+ NSUrlProtectionSpace.detached(
+ host: host,
+ realm: realm,
+ authenticationMethod: authenticationMethod,
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
+ ),
+ identifier,
+ );
+ }
+}
+
+/// Flutter API implementation for [NSUrlAuthenticationChallenge].
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+@protected
+class NSUrlAuthenticationChallengeFlutterApiImpl
+ implements NSUrlAuthenticationChallengeFlutterApi {
+ /// Constructs a [NSUrlAuthenticationChallengeFlutterApiImpl].
+ NSUrlAuthenticationChallengeFlutterApiImpl({
+ this.binaryMessenger,
+ InstanceManager? instanceManager,
+ }) : instanceManager = instanceManager ?? NSObject.globalInstanceManager;
+
+ /// Receives binary data across the Flutter platform barrier.
+ ///
+ /// If it is null, the default BinaryMessenger will be used which routes to
+ /// the host platform.
+ final BinaryMessenger? binaryMessenger;
+
+ /// Maintains instances stored to communicate with native language objects.
+ final InstanceManager instanceManager;
+
+ @override
+ void create(
+ int identifier,
+ int protectionSpaceIdentifier,
+ ) {
+ instanceManager.addHostCreatedInstance(
+ NSUrlAuthenticationChallenge.detached(
+ protectionSpace: instanceManager.getInstanceWithWeakReference(
+ protectionSpaceIdentifier,
+ )!,
+ binaryMessenger: binaryMessenger,
+ instanceManager: instanceManager,
+ ),
+ identifier,
+ );
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart
index db9f41c..b5bfcd9 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart
@@ -830,6 +830,7 @@
this.didFailNavigation,
this.didFailProvisionalNavigation,
this.webViewWebContentProcessDidTerminate,
+ this.didReceiveAuthenticationChallenge,
super.observeValue,
super.binaryMessenger,
super.instanceManager,
@@ -855,6 +856,7 @@
this.didFailNavigation,
this.didFailProvisionalNavigation,
this.webViewWebContentProcessDidTerminate,
+ this.didReceiveAuthenticationChallenge,
super.observeValue,
super.binaryMessenger,
super.instanceManager,
@@ -901,6 +903,16 @@
/// {@macro webview_flutter_wkwebview.foundation.callbacks}
final void Function(WKWebView webView)? webViewWebContentProcessDidTerminate;
+ /// Called when the delegate needs a response to an authentication challenge.
+ final void Function(
+ WKWebView webView,
+ NSUrlAuthenticationChallenge challenge,
+ void Function(
+ NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential,
+ ) completionHandler,
+ )? didReceiveAuthenticationChallenge;
+
@override
WKNavigationDelegate copy() {
return WKNavigationDelegate.detached(
@@ -911,6 +923,7 @@
didFailProvisionalNavigation: didFailProvisionalNavigation,
webViewWebContentProcessDidTerminate:
webViewWebContentProcessDidTerminate,
+ didReceiveAuthenticationChallenge: didReceiveAuthenticationChallenge,
observeValue: observeValue,
binaryMessenger: _navigationDelegateApi.binaryMessenger,
instanceManager: _navigationDelegateApi.instanceManager,
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart
index ee545d4..bb86f2b 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:async';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
@@ -904,6 +906,53 @@
as WKWebView,
);
}
+
+ @override
+ Future<AuthenticationChallengeResponse> didReceiveAuthenticationChallenge(
+ int identifier,
+ int webViewIdentifier,
+ int challengeIdentifier,
+ ) async {
+ final void Function(
+ WKWebView webView,
+ NSUrlAuthenticationChallenge challenge,
+ void Function(
+ NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential,
+ ),
+ )? function = _getDelegate(identifier).didReceiveAuthenticationChallenge;
+
+ if (function == null) {
+ return AuthenticationChallengeResponse(
+ disposition: NSUrlSessionAuthChallengeDisposition.rejectProtectionSpace,
+ );
+ }
+
+ final Completer<AuthenticationChallengeResponse> responseCompleter =
+ Completer<AuthenticationChallengeResponse>();
+
+ function.call(
+ instanceManager.getInstanceWithWeakReference(webViewIdentifier)!
+ as WKWebView,
+ instanceManager.getInstanceWithWeakReference(challengeIdentifier)!
+ as NSUrlAuthenticationChallenge,
+ (
+ NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential,
+ ) {
+ responseCompleter.complete(
+ AuthenticationChallengeResponse(
+ disposition: disposition,
+ credentialIdentifier: credential != null
+ ? instanceManager.getIdentifier(credential)
+ : null,
+ ),
+ );
+ },
+ );
+
+ return responseCompleter.future;
+ }
}
/// Host api implementation for [WKWebView].
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart
index 68ce913..4b0c4cc 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_proxy.dart
@@ -71,6 +71,14 @@
void Function(WKWebView webView, NSError error)?
didFailProvisionalNavigation,
void Function(WKWebView webView)? webViewWebContentProcessDidTerminate,
+ void Function(
+ WKWebView webView,
+ NSUrlAuthenticationChallenge challenge,
+ void Function(
+ NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential,
+ ) completionHandler,
+ )? didReceiveAuthenticationChallenge,
}) createNavigationDelegate;
/// Constructs a [WKUIDelegate].
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart
index d15b282..62a4deb 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart
@@ -948,6 +948,54 @@
);
}
},
+ didReceiveAuthenticationChallenge: (
+ WKWebView webView,
+ NSUrlAuthenticationChallenge challenge,
+ void Function(
+ NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential,
+ ) completionHandler,
+ ) {
+ if (challenge.protectionSpace.authenticationMethod ==
+ NSUrlAuthenticationMethod.httpBasic) {
+ final void Function(HttpAuthRequest)? callback =
+ weakThis.target?._onHttpAuthRequest;
+ final String? host = challenge.protectionSpace.host;
+ final String? realm = challenge.protectionSpace.realm;
+
+ if (callback != null && host != null) {
+ callback(
+ HttpAuthRequest(
+ onProceed: (WebViewCredential credential) {
+ completionHandler(
+ NSUrlSessionAuthChallengeDisposition.useCredential,
+ NSUrlCredential.withUser(
+ user: credential.user,
+ password: credential.password,
+ persistence: NSUrlCredentialPersistence.session,
+ ),
+ );
+ },
+ onCancel: () {
+ completionHandler(
+ NSUrlSessionAuthChallengeDisposition
+ .cancelAuthenticationChallenge,
+ null,
+ );
+ },
+ host: host,
+ realm: realm,
+ ),
+ );
+ return;
+ }
+ }
+
+ completionHandler(
+ NSUrlSessionAuthChallengeDisposition.performDefaultHandling,
+ null,
+ );
+ },
);
}
@@ -960,6 +1008,7 @@
WebResourceErrorCallback? _onWebResourceError;
NavigationRequestCallback? _onNavigationRequest;
UrlChangeCallback? _onUrlChange;
+ HttpAuthRequestCallback? _onHttpAuthRequest;
@override
Future<void> setOnPageFinished(PageEventCallback onPageFinished) async {
@@ -994,6 +1043,13 @@
Future<void> setOnUrlChange(UrlChangeCallback onUrlChange) async {
_onUrlChange = onUrlChange;
}
+
+ @override
+ Future<void> setOnHttpAuthRequest(
+ HttpAuthRequestCallback onHttpAuthRequest,
+ ) async {
+ _onHttpAuthRequest = onHttpAuthRequest;
+ }
}
/// WebKit implementation of [PlatformWebViewPermissionRequest].
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 8e9e16f..c489469 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart
@@ -258,6 +258,58 @@
late WKMediaCaptureType value;
}
+/// Responses to an authentication challenge.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition?language=objc.
+enum NSUrlSessionAuthChallengeDisposition {
+ /// Use the specified credential, which may be nil.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengeusecredential?language=objc.
+ useCredential,
+
+ /// Use the default handling for the challenge as though this delegate method
+ /// were not implemented.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengeperformdefaulthandling?language=objc.
+ performDefaultHandling,
+
+ /// Cancel the entire request.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengecancelauthenticationchallenge?language=objc.
+ cancelAuthenticationChallenge,
+
+ /// Reject this challenge, and call the authentication delegate method again
+ /// with the next authentication protection space.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlsessionauthchallengedisposition/nsurlsessionauthchallengerejectprotectionspace?language=objc.
+ rejectProtectionSpace,
+}
+
+/// Specifies how long a credential will be kept.
+enum NSUrlCredentialPersistence {
+ /// The credential should not be stored.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencenone?language=objc.
+ none,
+
+ /// The credential should be stored only for this session.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistenceforsession?language=objc.
+ session,
+
+ /// The credential should be stored in the keychain.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencepermanent?language=objc.
+ permanent,
+
+ /// The credential should be stored permanently in the keychain, and in
+ /// addition should be distributed to other devices based on the owning Apple
+ /// ID.
+ ///
+ /// See https://developer.apple.com/documentation/foundation/nsurlcredentialpersistence/nsurlcredentialpersistencesynchronizable?language=objc.
+ synchronizable,
+}
+
/// Mirror of NSURLRequest.
///
/// See https://developer.apple.com/documentation/foundation/nsurlrequest?language=objc.
@@ -343,6 +395,11 @@
late bool isIdentifier;
}
+class AuthenticationChallengeResponse {
+ late NSUrlSessionAuthChallengeDisposition disposition;
+ late int? credentialIdentifier;
+}
+
/// Mirror of WKWebsiteDataStore.
///
/// See https://developer.apple.com/documentation/webkit/wkwebsitedatastore?language=objc.
@@ -582,6 +639,16 @@
int identifier,
int webViewIdentifier,
);
+
+ @async
+ @ObjCSelector(
+ 'didReceiveAuthenticationChallengeForDelegateWithIdentifier:webViewIdentifier:challengeIdentifier:',
+ )
+ AuthenticationChallengeResponse didReceiveAuthenticationChallenge(
+ int identifier,
+ int webViewIdentifier,
+ int challengeIdentifier,
+ );
}
/// Mirror of NSObject.
@@ -781,3 +848,57 @@
@ObjCSelector('createWithIdentifier:')
void create(int identifier);
}
+
+/// Host API for `NSUrlCredential`.
+///
+/// This class may handle instantiating and adding native object instances that
+/// are attached to a Dart instance or handle method calls on the associated
+/// native class or an instance of the class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlcredential?language=objc.
+@HostApi(dartHostTestHandler: 'TestNSUrlCredentialHostApi')
+abstract class NSUrlCredentialHostApi {
+ /// Create a new native instance and add it to the `InstanceManager`.
+ @ObjCSelector(
+ 'createWithUserWithIdentifier:user:password:persistence:',
+ )
+ void createWithUser(
+ int identifier,
+ String user,
+ String password,
+ NSUrlCredentialPersistence persistence,
+ );
+}
+
+/// Flutter API for `NSUrlProtectionSpace`.
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlprotectionspace?language=objc.
+@FlutterApi()
+abstract class NSUrlProtectionSpaceFlutterApi {
+ /// Create a new Dart instance and add it to the `InstanceManager`.
+ @ObjCSelector('createWithIdentifier:host:realm:authenticationMethod:')
+ void create(
+ int identifier,
+ String? host,
+ String? realm,
+ String? authenticationMethod,
+ );
+}
+
+/// Flutter API for `NSUrlAuthenticationChallenge`.
+///
+/// This class may handle instantiating and adding Dart instances that are
+/// attached to a native instance or receiving callback methods from an
+/// overridden native class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlauthenticationchallenge?language=objc.
+@FlutterApi()
+abstract class NSUrlAuthenticationChallengeFlutterApi {
+ /// Create a new Dart instance and add it to the `InstanceManager`.
+ @ObjCSelector('createWithIdentifier:protectionSpaceIdentifier:')
+ void create(int identifier, int protectionSpaceIdentifier);
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
index 4c7eca1..a7e5885 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
@@ -2,7 +2,7 @@
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.9.4
+version: 3.10.0
environment:
sdk: ">=3.0.0 <4.0.0"
@@ -20,7 +20,7 @@
flutter:
sdk: flutter
path: ^1.8.0
- webview_flutter_platform_interface: ^2.6.0
+ webview_flutter_platform_interface: ^2.7.0
dev_dependencies:
build_runner: ^2.1.5
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart
index 1541f02..a9b0157 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart
@@ -1199,60 +1199,63 @@
const _TestWKWebViewHostApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
- if (value is NSErrorData) {
+ if (value is AuthenticationChallengeResponse) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
- } else if (value is NSHttpCookieData) {
+ } else if (value is NSErrorData) {
buffer.putUint8(129);
writeValue(buffer, value.encode());
- } else if (value is NSHttpCookiePropertyKeyEnumData) {
+ } else if (value is NSHttpCookieData) {
buffer.putUint8(130);
writeValue(buffer, value.encode());
- } else if (value is NSKeyValueChangeKeyEnumData) {
+ } else if (value is NSHttpCookiePropertyKeyEnumData) {
buffer.putUint8(131);
writeValue(buffer, value.encode());
- } else if (value is NSKeyValueObservingOptionsEnumData) {
+ } else if (value is NSKeyValueChangeKeyEnumData) {
buffer.putUint8(132);
writeValue(buffer, value.encode());
- } else if (value is NSUrlRequestData) {
+ } else if (value is NSKeyValueObservingOptionsEnumData) {
buffer.putUint8(133);
writeValue(buffer, value.encode());
- } else if (value is ObjectOrIdentifier) {
+ } else if (value is NSUrlRequestData) {
buffer.putUint8(134);
writeValue(buffer, value.encode());
- } else if (value is WKAudiovisualMediaTypeEnumData) {
+ } else if (value is ObjectOrIdentifier) {
buffer.putUint8(135);
writeValue(buffer, value.encode());
- } else if (value is WKFrameInfoData) {
+ } else if (value is WKAudiovisualMediaTypeEnumData) {
buffer.putUint8(136);
writeValue(buffer, value.encode());
- } else if (value is WKMediaCaptureTypeData) {
+ } else if (value is WKFrameInfoData) {
buffer.putUint8(137);
writeValue(buffer, value.encode());
- } else if (value is WKNavigationActionData) {
+ } else if (value is WKMediaCaptureTypeData) {
buffer.putUint8(138);
writeValue(buffer, value.encode());
- } else if (value is WKNavigationActionPolicyEnumData) {
+ } else if (value is WKNavigationActionData) {
buffer.putUint8(139);
writeValue(buffer, value.encode());
- } else if (value is WKPermissionDecisionData) {
+ } else if (value is WKNavigationActionPolicyEnumData) {
buffer.putUint8(140);
writeValue(buffer, value.encode());
- } else if (value is WKScriptMessageData) {
+ } else if (value is WKPermissionDecisionData) {
buffer.putUint8(141);
writeValue(buffer, value.encode());
- } else if (value is WKSecurityOriginData) {
+ } else if (value is WKScriptMessageData) {
buffer.putUint8(142);
writeValue(buffer, value.encode());
- } else if (value is WKUserScriptData) {
+ } else if (value is WKSecurityOriginData) {
buffer.putUint8(143);
writeValue(buffer, value.encode());
- } else if (value is WKUserScriptInjectionTimeEnumData) {
+ } else if (value is WKUserScriptData) {
buffer.putUint8(144);
writeValue(buffer, value.encode());
- } else if (value is WKWebsiteDataTypeEnumData) {
+ } else if (value is WKUserScriptInjectionTimeEnumData) {
buffer.putUint8(145);
writeValue(buffer, value.encode());
+ } else if (value is WKWebsiteDataTypeEnumData) {
+ buffer.putUint8(146);
+ writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
@@ -1262,40 +1265,42 @@
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
- return NSErrorData.decode(readValue(buffer)!);
+ return AuthenticationChallengeResponse.decode(readValue(buffer)!);
case 129:
- return NSHttpCookieData.decode(readValue(buffer)!);
+ return NSErrorData.decode(readValue(buffer)!);
case 130:
- return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!);
+ return NSHttpCookieData.decode(readValue(buffer)!);
case 131:
- return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!);
+ return NSHttpCookiePropertyKeyEnumData.decode(readValue(buffer)!);
case 132:
- return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!);
+ return NSKeyValueChangeKeyEnumData.decode(readValue(buffer)!);
case 133:
- return NSUrlRequestData.decode(readValue(buffer)!);
+ return NSKeyValueObservingOptionsEnumData.decode(readValue(buffer)!);
case 134:
- return ObjectOrIdentifier.decode(readValue(buffer)!);
+ return NSUrlRequestData.decode(readValue(buffer)!);
case 135:
- return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!);
+ return ObjectOrIdentifier.decode(readValue(buffer)!);
case 136:
- return WKFrameInfoData.decode(readValue(buffer)!);
+ return WKAudiovisualMediaTypeEnumData.decode(readValue(buffer)!);
case 137:
- return WKMediaCaptureTypeData.decode(readValue(buffer)!);
+ return WKFrameInfoData.decode(readValue(buffer)!);
case 138:
- return WKNavigationActionData.decode(readValue(buffer)!);
+ return WKMediaCaptureTypeData.decode(readValue(buffer)!);
case 139:
- return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!);
+ return WKNavigationActionData.decode(readValue(buffer)!);
case 140:
- return WKPermissionDecisionData.decode(readValue(buffer)!);
+ return WKNavigationActionPolicyEnumData.decode(readValue(buffer)!);
case 141:
- return WKScriptMessageData.decode(readValue(buffer)!);
+ return WKPermissionDecisionData.decode(readValue(buffer)!);
case 142:
- return WKSecurityOriginData.decode(readValue(buffer)!);
+ return WKScriptMessageData.decode(readValue(buffer)!);
case 143:
- return WKUserScriptData.decode(readValue(buffer)!);
+ return WKSecurityOriginData.decode(readValue(buffer)!);
case 144:
- return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!);
+ return WKUserScriptData.decode(readValue(buffer)!);
case 145:
+ return WKUserScriptInjectionTimeEnumData.decode(readValue(buffer)!);
+ case 146:
return WKWebsiteDataTypeEnumData.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
@@ -2196,3 +2201,66 @@
}
}
}
+
+/// Host API for `NSUrlCredential`.
+///
+/// This class may handle instantiating and adding native object instances that
+/// are attached to a Dart instance or handle method calls on the associated
+/// native class or an instance of the class.
+///
+/// See https://developer.apple.com/documentation/foundation/nsurlcredential?language=objc.
+abstract class TestNSUrlCredentialHostApi {
+ static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding =>
+ TestDefaultBinaryMessengerBinding.instance;
+ static const MessageCodec<Object?> codec = StandardMessageCodec();
+
+ /// Create a new native instance and add it to the `InstanceManager`.
+ void createWithUser(int identifier, String user, String password,
+ NSUrlCredentialPersistence persistence);
+
+ static void setup(TestNSUrlCredentialHostApi? api,
+ {BinaryMessenger? binaryMessenger}) {
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser',
+ codec,
+ binaryMessenger: binaryMessenger);
+ if (api == null) {
+ _testBinaryMessengerBinding!.defaultBinaryMessenger
+ .setMockDecodedMessageHandler<Object?>(channel, null);
+ } else {
+ _testBinaryMessengerBinding!.defaultBinaryMessenger
+ .setMockDecodedMessageHandler<Object?>(channel,
+ (Object? message) async {
+ assert(message != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser was null.');
+ final List<Object?> args = (message as List<Object?>?)!;
+ final int? arg_identifier = (args[0] as int?);
+ assert(arg_identifier != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser was null, expected non-null int.');
+ final String? arg_user = (args[1] as String?);
+ assert(arg_user != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser was null, expected non-null String.');
+ final String? arg_password = (args[2] as String?);
+ assert(arg_password != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser was null, expected non-null String.');
+ final NSUrlCredentialPersistence? arg_persistence = args[3] == null
+ ? null
+ : NSUrlCredentialPersistence.values[args[3]! as int];
+ assert(arg_persistence != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.NSUrlCredentialHostApi.createWithUser was null, expected non-null NSUrlCredentialPersistence.');
+ try {
+ api.createWithUser(
+ arg_identifier!, arg_user!, arg_password!, arg_persistence!);
+ return wrapResponse(empty: true);
+ } on PlatformException catch (e) {
+ return wrapResponse(error: e);
+ } catch (e) {
+ return wrapResponse(
+ error: PlatformException(code: 'error', message: e.toString()));
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart
index ea2e37e..345c6fa 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.dart
@@ -17,6 +17,7 @@
@GenerateMocks(<Type>[
TestNSObjectHostApi,
+ TestNSUrlCredentialHostApi,
TestNSUrlHostApi,
])
void main() {
@@ -245,6 +246,102 @@
expect(instanceManager.getInstanceWithWeakReference(0), isA<NSUrl>());
});
});
+
+ group('NSUrlCredential', () {
+ tearDown(() {
+ TestNSUrlCredentialHostApi.setup(null);
+ });
+
+ test('HostApi createWithUser', () {
+ final MockTestNSUrlCredentialHostApi mockApi =
+ MockTestNSUrlCredentialHostApi();
+ TestNSUrlCredentialHostApi.setup(mockApi);
+
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ const String user = 'testString';
+ const String password = 'testString2';
+
+ const NSUrlCredentialPersistence persistence =
+ NSUrlCredentialPersistence.permanent;
+
+ final NSUrlCredential instance = NSUrlCredential.withUser(
+ user: user,
+ password: password,
+ persistence: persistence,
+ instanceManager: instanceManager,
+ );
+
+ verify(mockApi.createWithUser(
+ instanceManager.getIdentifier(instance),
+ user,
+ password,
+ persistence,
+ ));
+ });
+ });
+
+ group('NSUrlProtectionSpace', () {
+ test('FlutterAPI create', () {
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ final NSUrlProtectionSpaceFlutterApiImpl api =
+ NSUrlProtectionSpaceFlutterApiImpl(
+ instanceManager: instanceManager,
+ );
+
+ const int instanceIdentifier = 0;
+
+ api.create(
+ instanceIdentifier,
+ 'testString',
+ 'testString',
+ 'testAuthenticationMethod',
+ );
+
+ expect(
+ instanceManager.getInstanceWithWeakReference(instanceIdentifier),
+ isA<NSUrlProtectionSpace>(),
+ );
+ });
+ });
+
+ group('NSUrlAuthenticationChallenge', () {
+ test('FlutterAPI create', () {
+ final InstanceManager instanceManager = InstanceManager(
+ onWeakReferenceRemoved: (_) {},
+ );
+
+ final NSUrlAuthenticationChallengeFlutterApiImpl api =
+ NSUrlAuthenticationChallengeFlutterApiImpl(
+ instanceManager: instanceManager,
+ );
+
+ const int instanceIdentifier = 0;
+
+ const int protectionSpaceIdentifier = 1;
+ instanceManager.addHostCreatedInstance(
+ NSUrlProtectionSpace.detached(
+ host: null,
+ realm: null,
+ authenticationMethod: null,
+ instanceManager: instanceManager,
+ ),
+ protectionSpaceIdentifier,
+ );
+
+ api.create(instanceIdentifier, protectionSpaceIdentifier);
+
+ expect(
+ instanceManager.getInstanceWithWeakReference(instanceIdentifier),
+ isA<NSUrlAuthenticationChallenge>(),
+ );
+ });
+ });
});
test('NSError', () {
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart
index 29d9e63..ef23208 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/foundation/foundation_test.mocks.dart
@@ -78,6 +78,36 @@
);
}
+/// A class which mocks [TestNSUrlCredentialHostApi].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockTestNSUrlCredentialHostApi extends _i1.Mock
+ implements _i2.TestNSUrlCredentialHostApi {
+ MockTestNSUrlCredentialHostApi() {
+ _i1.throwOnMissingStub(this);
+ }
+
+ @override
+ void createWithUser(
+ int? identifier,
+ String? user,
+ String? password,
+ _i3.NSUrlCredentialPersistence? persistence,
+ ) =>
+ super.noSuchMethod(
+ Invocation.method(
+ #createWithUser,
+ [
+ identifier,
+ user,
+ password,
+ persistence,
+ ],
+ ),
+ returnValueForMissingStub: null,
+ );
+}
+
/// A class which mocks [TestNSUrlHostApi].
///
/// See the documentation for Mockito's code generation for more information.
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart
index 98a70de..2c95a52 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart
@@ -683,6 +683,63 @@
expect(argsCompleter.future, completion(<Object?>[webView]));
});
+
+ test('didReceiveAuthenticationChallenge', () async {
+ WebKitFlutterApis.instance = WebKitFlutterApis(
+ instanceManager: instanceManager,
+ );
+
+ const int credentialIdentifier = 3;
+ final NSUrlCredential credential = NSUrlCredential.detached(
+ instanceManager: instanceManager,
+ );
+ instanceManager.addHostCreatedInstance(
+ credential,
+ credentialIdentifier,
+ );
+
+ navigationDelegate = WKNavigationDelegate(
+ instanceManager: instanceManager,
+ didReceiveAuthenticationChallenge: (
+ WKWebView webView,
+ NSUrlAuthenticationChallenge challenge,
+ void Function(
+ NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential,
+ ) completionHandler,
+ ) {
+ completionHandler(
+ NSUrlSessionAuthChallengeDisposition.useCredential,
+ credential,
+ );
+ },
+ );
+
+ const int challengeIdentifier = 27;
+ instanceManager.addHostCreatedInstance(
+ NSUrlAuthenticationChallenge.detached(
+ protectionSpace: NSUrlProtectionSpace.detached(
+ host: null,
+ realm: null,
+ authenticationMethod: null,
+ ),
+ instanceManager: instanceManager,
+ ),
+ challengeIdentifier,
+ );
+
+ final AuthenticationChallengeResponse response = await WebKitFlutterApis
+ .instance.navigationDelegate
+ .didReceiveAuthenticationChallenge(
+ instanceManager.getIdentifier(navigationDelegate)!,
+ instanceManager.getIdentifier(webView)!,
+ challengeIdentifier,
+ );
+
+ expect(response.disposition,
+ NSUrlSessionAuthChallengeDisposition.useCredential);
+ expect(response.credentialIdentifier, credentialIdentifier);
+ });
});
group('WKWebView', () {
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart
index 4581c92..63d432c 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_navigation_delegate_test.dart
@@ -7,6 +7,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_wkwebview/src/common/web_kit.g.dart';
import 'package:webview_flutter_wkwebview/src/foundation/foundation.dart';
import 'package:webview_flutter_wkwebview/src/web_kit/web_kit.dart';
import 'package:webview_flutter_wkwebview/src/webkit_proxy.dart';
@@ -214,6 +215,44 @@
expect(callbackRequest.url, 'https://www.google.com');
expect(callbackRequest.isMainFrame, isFalse);
});
+
+ test('onHttpBasicAuthRequest emits host and realm', () {
+ final WebKitNavigationDelegate iosNavigationDelegate =
+ WebKitNavigationDelegate(
+ const WebKitNavigationDelegateCreationParams(
+ webKitProxy: WebKitProxy(
+ createNavigationDelegate: CapturingNavigationDelegate.new,
+ ),
+ ),
+ );
+
+ String? callbackHost;
+ String? callbackRealm;
+
+ iosNavigationDelegate.setOnHttpAuthRequest((HttpAuthRequest request) {
+ callbackHost = request.host;
+ callbackRealm = request.realm;
+ });
+
+ const String expectedHost = 'expectedHost';
+ const String expectedRealm = 'expectedRealm';
+
+ CapturingNavigationDelegate
+ .lastCreatedDelegate.didReceiveAuthenticationChallenge!(
+ WKWebView.detached(),
+ NSUrlAuthenticationChallenge.detached(
+ protectionSpace: NSUrlProtectionSpace.detached(
+ host: expectedHost,
+ realm: expectedRealm,
+ authenticationMethod: NSUrlAuthenticationMethod.httpBasic,
+ ),
+ ),
+ (NSUrlSessionAuthChallengeDisposition disposition,
+ NSUrlCredential? credential) {});
+
+ expect(callbackHost, expectedHost);
+ expect(callbackRealm, expectedRealm);
+ });
});
}
@@ -226,6 +265,7 @@
super.didFailProvisionalNavigation,
super.decidePolicyForNavigationAction,
super.webViewWebContentProcessDidTerminate,
+ super.didReceiveAuthenticationChallenge,
}) : super.detached() {
lastCreatedDelegate = this;
}
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart
index 46badc0..0bada40 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.dart
@@ -1366,6 +1366,7 @@
super.didFailProvisionalNavigation,
super.decidePolicyForNavigationAction,
super.webViewWebContentProcessDidTerminate,
+ super.didReceiveAuthenticationChallenge,
}) : super.detached() {
lastCreatedDelegate = this;
}