[webview_flutter_wkwebview] Add javascript panel interface for wkwebview (#5795)
* There are cases where Web calls System Popup with javascript on webview_flutter
* At this time, the message comes in the WKUIDelegate part in iOS.
* https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview
* https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview
* Related issue: https://github.com/flutter/flutter/issues/30358#issuecomment-1645347616
* Related Interface PR: https://github.com/flutter/packages/pull/5670
* The PR that contains all changes can be found at https://github.com/flutter/packages/pull/4704
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
index 5a97e3e..0d899a5 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 3.11.0
+
+* Adds support to show JavaScript dialog. See `PlatformWebViewController.setOnJavaScriptAlertDialog`, `PlatformWebViewController.setOnJavaScriptConfirmDialog` and `PlatformWebViewController.setOnJavaScriptTextInputDialog`.
+
## 3.10.3
* Adds a check that throws an `ArgumentError` when `WebKitWebViewController.addJavaScriptChannel`
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 89ecad8..9d95849 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
@@ -1303,6 +1303,98 @@
},
);
+ testWidgets('can receive JavaScript alert dialogs',
+ (WidgetTester tester) async {
+ final PlatformWebViewController controller = PlatformWebViewController(
+ const PlatformWebViewControllerCreationParams(),
+ );
+
+ final Completer<String> alertMessage = Completer<String>();
+ unawaited(controller.setOnJavaScriptAlertDialog(
+ (JavaScriptAlertDialogRequest request) async {
+ alertMessage.complete(request.message);
+ },
+ ));
+
+ unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
+ unawaited(
+ controller.loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl))),
+ );
+
+ await tester.pumpWidget(Builder(
+ builder: (BuildContext context) {
+ return PlatformWebViewWidget(
+ PlatformWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ },
+ ));
+
+ await controller.runJavaScript('alert("alert message")');
+ await expectLater(alertMessage.future, completion('alert message'));
+ });
+
+ testWidgets('can receive JavaScript confirm dialogs',
+ (WidgetTester tester) async {
+ final PlatformWebViewController controller = PlatformWebViewController(
+ const PlatformWebViewControllerCreationParams(),
+ );
+
+ final Completer<String> confirmMessage = Completer<String>();
+ unawaited(controller.setOnJavaScriptConfirmDialog(
+ (JavaScriptConfirmDialogRequest request) async {
+ confirmMessage.complete(request.message);
+ return true;
+ },
+ ));
+
+ unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
+ unawaited(
+ controller.loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl))),
+ );
+
+ await tester.pumpWidget(Builder(
+ builder: (BuildContext context) {
+ return PlatformWebViewWidget(
+ PlatformWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ },
+ ));
+
+ await controller.runJavaScript('confirm("confirm message")');
+ await expectLater(confirmMessage.future, completion('confirm message'));
+ });
+
+ testWidgets('can receive JavaScript prompt dialogs',
+ (WidgetTester tester) async {
+ final PlatformWebViewController controller = PlatformWebViewController(
+ const PlatformWebViewControllerCreationParams(),
+ );
+
+ unawaited(controller.setOnJavaScriptTextInputDialog(
+ (JavaScriptTextInputDialogRequest request) async {
+ return 'return message';
+ },
+ ));
+
+ unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
+ unawaited(
+ controller.loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl))),
+ );
+
+ await tester.pumpWidget(Builder(
+ builder: (BuildContext context) {
+ return PlatformWebViewWidget(
+ PlatformWebViewWidgetCreationParams(controller: controller),
+ ).build(context);
+ },
+ ));
+
+ final Object promptResponse = await controller.runJavaScriptReturningResult(
+ 'prompt("input message", "default text")',
+ );
+ expect(promptResponse, 'return message');
+ });
+
group('Logging', () {
testWidgets('can receive console log messages',
(WidgetTester tester) async {
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 cf80637..116f4bb 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart
@@ -108,6 +108,38 @@
</html>
''';
+const String kAlertTestPage = '''
+<!DOCTYPE html>
+<html>
+ <head>
+ <script type = "text/javascript">
+ function showAlert(text) {
+ alert(text);
+ }
+
+ function showConfirm(text) {
+ var result = confirm(text);
+ alert(result);
+ }
+
+ function showPrompt(text, defaultText) {
+ var inputString = prompt('Enter input', 'Default text');
+ alert(inputString);
+ }
+ </script>
+ </head>
+
+ <body>
+ <p> Click the following button to see the effect </p>
+ <form>
+ <input type = "button" value = "Alert" onclick = "showAlert('Test Alert');" />
+ <input type = "button" value = "Confirm" onclick = "showConfirm('Test Confirm');" />
+ <input type = "button" value = "Prompt" onclick = "showPrompt('Test Prompt', 'Default Value');" />
+ </form>
+ </body>
+</html>
+''';
+
class WebViewExample extends StatefulWidget {
const WebViewExample({super.key, this.cookieManager});
@@ -297,6 +329,7 @@
setCookie,
logExample,
basicAuthentication,
+ javaScriptAlert,
}
class SampleMenu extends StatelessWidget {
@@ -348,6 +381,8 @@
_onLogExample();
case MenuOptions.basicAuthentication:
_promptForUrl(context);
+ case MenuOptions.javaScriptAlert:
+ _onJavaScriptAlertExample(context);
}
},
itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
@@ -412,6 +447,10 @@
value: MenuOptions.basicAuthentication,
child: Text('Basic Authentication Example'),
),
+ const PopupMenuItem<MenuOptions>(
+ value: MenuOptions.javaScriptAlert,
+ child: Text('JavaScript Alert Example'),
+ ),
],
);
}
@@ -536,6 +575,28 @@
return webViewController.loadHtmlString(kTransparentBackgroundPage);
}
+ Future<void> _onJavaScriptAlertExample(BuildContext context) {
+ webViewController.setOnJavaScriptAlertDialog(
+ (JavaScriptAlertDialogRequest request) async {
+ await _showAlert(context, request.message);
+ });
+
+ webViewController.setOnJavaScriptConfirmDialog(
+ (JavaScriptConfirmDialogRequest request) async {
+ final bool result = await _showConfirm(context, request.message);
+ return result;
+ });
+
+ webViewController.setOnJavaScriptTextInputDialog(
+ (JavaScriptTextInputDialogRequest request) async {
+ final String result =
+ await _showTextInput(context, request.message, request.defaultText);
+ return result;
+ });
+
+ return webViewController.loadHtmlString(kAlertTestPage);
+ }
+
Widget _getCookieList(String cookies) {
if (cookies == '""') {
return Container();
@@ -605,6 +666,65 @@
},
);
}
+
+ Future<void> _showAlert(BuildContext context, String message) async {
+ return showDialog<void>(
+ context: context,
+ builder: (BuildContext ctx) {
+ return AlertDialog(
+ content: Text(message),
+ actions: <Widget>[
+ TextButton(
+ onPressed: () {
+ Navigator.of(ctx).pop();
+ },
+ child: const Text('OK'))
+ ],
+ );
+ });
+ }
+
+ Future<bool> _showConfirm(BuildContext context, String message) async {
+ return await showDialog<bool>(
+ context: context,
+ builder: (BuildContext ctx) {
+ return AlertDialog(
+ content: Text(message),
+ actions: <Widget>[
+ TextButton(
+ onPressed: () {
+ Navigator.of(ctx).pop(false);
+ },
+ child: const Text('Cancel')),
+ TextButton(
+ onPressed: () {
+ Navigator.of(ctx).pop(true);
+ },
+ child: const Text('OK')),
+ ],
+ );
+ }) ??
+ false;
+ }
+
+ Future<String> _showTextInput(
+ BuildContext context, String message, String? defaultText) async {
+ return await showDialog<String>(
+ context: context,
+ builder: (BuildContext ctx) {
+ return AlertDialog(
+ content: Text(message),
+ actions: <Widget>[
+ TextButton(
+ onPressed: () {
+ Navigator.of(ctx).pop('Text test');
+ },
+ child: const Text('Enter')),
+ ],
+ );
+ }) ??
+ '';
+ }
}
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 900520f..5d323b7 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.7.0
+ webview_flutter_platform_interface: ^2.9.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/FWFDataConverters.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m
index a4dda8b..9a5cc86 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFDataConverters.m
@@ -167,7 +167,7 @@
FWFNSUrlRequestData *FWFNSUrlRequestDataFromNativeNSURLRequest(NSURLRequest *request) {
return [FWFNSUrlRequestData
- makeWithUrl:request.URL.absoluteString
+ makeWithUrl:request.URL.absoluteString == nil ? @"" : request.URL.absoluteString
httpMethod:request.HTTPMethod
httpBody:request.HTTPBody
? [FlutterStandardTypedData typedDataWithBytes:request.HTTPBody]
@@ -176,7 +176,9 @@
}
FWFWKFrameInfoData *FWFWKFrameInfoDataFromNativeWKFrameInfo(WKFrameInfo *info) {
- return [FWFWKFrameInfoData makeWithIsMainFrame:info.isMainFrame];
+ return [FWFWKFrameInfoData
+ makeWithIsMainFrame:info.isMainFrame
+ request:FWFNSUrlRequestDataFromNativeNSURLRequest(info.request)];
}
WKNavigationActionPolicy FWFNativeWKNavigationActionPolicyFromEnumData(
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 1e312a2..a0524d0 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h
@@ -464,8 +464,9 @@
@interface FWFWKFrameInfoData : NSObject
/// `init` unavailable to enforce nonnull fields, see the `make` class method.
- (instancetype)init NS_UNAVAILABLE;
-+ (instancetype)makeWithIsMainFrame:(BOOL)isMainFrame;
++ (instancetype)makeWithIsMainFrame:(BOOL)isMainFrame request:(FWFNSUrlRequestData *)request;
@property(nonatomic, assign) BOOL isMainFrame;
+@property(nonatomic, strong) FWFNSUrlRequestData *request;
@end
/// Mirror of NSError.
@@ -949,6 +950,27 @@
(void (^)(
FWFWKPermissionDecisionData *_Nullable,
FlutterError *_Nullable))completion;
+/// Callback to Dart function `WKUIDelegate.runJavaScriptAlertPanel`.
+- (void)runJavaScriptAlertPanelForDelegateWithIdentifier:(NSInteger)identifier
+ message:(NSString *)message
+ frame:(FWFWKFrameInfoData *)frame
+ completion:
+ (void (^)(FlutterError *_Nullable))completion;
+/// Callback to Dart function `WKUIDelegate.runJavaScriptConfirmPanel`.
+- (void)runJavaScriptConfirmPanelForDelegateWithIdentifier:(NSInteger)identifier
+ message:(NSString *)message
+ frame:(FWFWKFrameInfoData *)frame
+ completion:
+ (void (^)(NSNumber *_Nullable,
+ FlutterError *_Nullable))completion;
+/// Callback to Dart function `WKUIDelegate.runJavaScriptTextInputPanel`.
+- (void)runJavaScriptTextInputPanelForDelegateWithIdentifier:(NSInteger)identifier
+ prompt:(NSString *)prompt
+ defaultText:(NSString *)defaultText
+ frame:(FWFWKFrameInfoData *)frame
+ completion:
+ (void (^)(NSString *_Nullable,
+ FlutterError *_Nullable))completion;
@end
/// The codec used by FWFWKHttpCookieStoreHostApi.
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 5d80178..c9cd996 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m
@@ -609,14 +609,16 @@
@end
@implementation FWFWKFrameInfoData
-+ (instancetype)makeWithIsMainFrame:(BOOL)isMainFrame {
++ (instancetype)makeWithIsMainFrame:(BOOL)isMainFrame request:(FWFNSUrlRequestData *)request {
FWFWKFrameInfoData *pigeonResult = [[FWFWKFrameInfoData alloc] init];
pigeonResult.isMainFrame = isMainFrame;
+ pigeonResult.request = request;
return pigeonResult;
}
+ (FWFWKFrameInfoData *)fromList:(NSArray *)list {
FWFWKFrameInfoData *pigeonResult = [[FWFWKFrameInfoData alloc] init];
pigeonResult.isMainFrame = [GetNullableObjectAtIndex(list, 0) boolValue];
+ pigeonResult.request = [FWFNSUrlRequestData nullableFromList:(GetNullableObjectAtIndex(list, 1))];
return pigeonResult;
}
+ (nullable FWFWKFrameInfoData *)nullableFromList:(NSArray *)list {
@@ -625,6 +627,7 @@
- (NSArray *)toList {
return @[
@(self.isMainFrame),
+ (self.request ? [self.request toList] : [NSNull null]),
];
}
@end
@@ -3098,6 +3101,99 @@
}
}];
}
+- (void)runJavaScriptAlertPanelForDelegateWithIdentifier:(NSInteger)arg_identifier
+ message:(NSString *)arg_message
+ frame:(FWFWKFrameInfoData *)arg_frame
+ completion:
+ (void (^)(FlutterError *_Nullable))completion {
+ FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+ messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi."
+ @"runJavaScriptAlertPanel"
+ binaryMessenger:self.binaryMessenger
+ codec:FWFWKUIDelegateFlutterApiGetCodec()];
+ [channel
+ sendMessage:@[ @(arg_identifier), arg_message ?: [NSNull null], arg_frame ?: [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:@""]);
+ }
+ }];
+}
+- (void)runJavaScriptConfirmPanelForDelegateWithIdentifier:(NSInteger)arg_identifier
+ message:(NSString *)arg_message
+ frame:(FWFWKFrameInfoData *)arg_frame
+ completion:
+ (void (^)(NSNumber *_Nullable,
+ FlutterError *_Nullable))completion {
+ FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+ messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi."
+ @"runJavaScriptConfirmPanel"
+ binaryMessenger:self.binaryMessenger
+ codec:FWFWKUIDelegateFlutterApiGetCodec()];
+ [channel
+ sendMessage:@[ @(arg_identifier), arg_message ?: [NSNull null], arg_frame ?: [NSNull null] ]
+ reply:^(NSArray<id> *reply) {
+ if (reply != nil) {
+ if (reply.count > 1) {
+ completion(nil, [FlutterError errorWithCode:reply[0]
+ message:reply[1]
+ details:reply[2]]);
+ } else {
+ NSNumber *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:@""]);
+ }
+ }];
+}
+- (void)runJavaScriptTextInputPanelForDelegateWithIdentifier:(NSInteger)arg_identifier
+ prompt:(NSString *)arg_prompt
+ defaultText:(NSString *)arg_defaultText
+ frame:(FWFWKFrameInfoData *)arg_frame
+ completion:(void (^)(NSString *_Nullable,
+ FlutterError *_Nullable))
+ completion {
+ FlutterBasicMessageChannel *channel = [FlutterBasicMessageChannel
+ messageChannelWithName:@"dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi."
+ @"runJavaScriptTextInputPanel"
+ binaryMessenger:self.binaryMessenger
+ codec:FWFWKUIDelegateFlutterApiGetCodec()];
+ [channel sendMessage:@[
+ @(arg_identifier), arg_prompt ?: [NSNull null], arg_defaultText ?: [NSNull null],
+ arg_frame ?: [NSNull null]
+ ]
+ reply:^(NSArray<id> *reply) {
+ if (reply != nil) {
+ if (reply.count > 1) {
+ completion(nil, [FlutterError errorWithCode:reply[0]
+ message:reply[1]
+ details:reply[2]]);
+ } else {
+ NSString *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 FWFWKHttpCookieStoreHostApiCodecReader : FlutterStandardReader
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m
index 879d85d..f9d78ea 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m
+++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFUIDelegateHostApi.m
@@ -88,6 +88,63 @@
decision));
}];
}
+
+- (void)runJavaScriptAlertPanelForDelegateWithIdentifier:(FWFUIDelegate *)instance
+ message:(NSString *)message
+ frame:(WKFrameInfo *)frame
+ completionHandler:(void (^)(void))completionHandler {
+ [self runJavaScriptAlertPanelForDelegateWithIdentifier:[self identifierForDelegate:instance]
+ message:message
+ frame:FWFWKFrameInfoDataFromNativeWKFrameInfo(
+ frame)
+ completion:^(FlutterError *error) {
+ NSAssert(!error, @"%@", error);
+ completionHandler();
+ }];
+}
+
+- (void)runJavaScriptConfirmPanelForDelegateWithIdentifier:(FWFUIDelegate *)instance
+ message:(NSString *)message
+ frame:(WKFrameInfo *)frame
+ completionHandler:(void (^)(BOOL))completionHandler {
+ [self runJavaScriptConfirmPanelForDelegateWithIdentifier:[self identifierForDelegate:instance]
+ message:message
+ frame:FWFWKFrameInfoDataFromNativeWKFrameInfo(
+ frame)
+ completion:^(NSNumber *isConfirmed,
+ FlutterError *error) {
+ NSAssert(!error, @"%@", error);
+ if (error) {
+ completionHandler(NO);
+ } else {
+ completionHandler(isConfirmed.boolValue);
+ }
+ }];
+}
+
+- (void)runJavaScriptTextInputPanelForDelegateWithIdentifier:(FWFUIDelegate *)instance
+ prompt:(NSString *)prompt
+ defaultText:(NSString *)defaultText
+ frame:(WKFrameInfo *)frame
+ completionHandler:
+ (void (^)(NSString *_Nullable))completionHandler {
+ [self
+ runJavaScriptTextInputPanelForDelegateWithIdentifier:[self identifierForDelegate:instance]
+ prompt:prompt
+ defaultText:defaultText
+ frame:FWFWKFrameInfoDataFromNativeWKFrameInfo(
+ frame)
+ completion:^(NSString *inputText,
+ FlutterError *error) {
+ NSAssert(!error, @"%@", error);
+ if (error) {
+ completionHandler(nil);
+ } else {
+ completionHandler(inputText);
+ }
+ }];
+}
+
@end
@implementation FWFUIDelegate
@@ -131,6 +188,39 @@
decisionHandler(decision);
}];
}
+
+- (void)webView:(WKWebView *)webView
+ runJavaScriptAlertPanelWithMessage:(NSString *)message
+ initiatedByFrame:(WKFrameInfo *)frame
+ completionHandler:(void (^)(void))completionHandler {
+ [self.UIDelegateAPI runJavaScriptAlertPanelForDelegateWithIdentifier:self
+ message:message
+ frame:frame
+ completionHandler:completionHandler];
+}
+
+- (void)webView:(WKWebView *)webView
+ runJavaScriptConfirmPanelWithMessage:(NSString *)message
+ initiatedByFrame:(WKFrameInfo *)frame
+ completionHandler:(void (^)(BOOL))completionHandler {
+ [self.UIDelegateAPI runJavaScriptConfirmPanelForDelegateWithIdentifier:self
+ message:message
+ frame:frame
+ completionHandler:completionHandler];
+}
+
+- (void)webView:(WKWebView *)webView
+ runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt
+ defaultText:(NSString *)defaultText
+ initiatedByFrame:(WKFrameInfo *)frame
+ completionHandler:(void (^)(NSString *_Nullable))completionHandler {
+ [self.UIDelegateAPI runJavaScriptTextInputPanelForDelegateWithIdentifier:self
+ prompt:prompt
+ defaultText:defaultText
+ frame:frame
+ completionHandler:completionHandler];
+}
+
@end
@interface FWFUIDelegateHostApiImpl ()
@@ -161,4 +251,5 @@
instanceManager:self.instanceManager];
[self.instanceManager addDartCreatedInstance:uIDelegate withIdentifier:identifier];
}
+
@end
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 1c63220..f814bd0 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
@@ -560,13 +560,17 @@
class WKFrameInfoData {
WKFrameInfoData({
required this.isMainFrame,
+ required this.request,
});
bool isMainFrame;
+ NSUrlRequestData request;
+
Object encode() {
return <Object?>[
isMainFrame,
+ request.encode(),
];
}
@@ -574,6 +578,7 @@
result as List<Object?>;
return WKFrameInfoData(
isMainFrame: result[0]! as bool,
+ request: NSUrlRequestData.decode(result[1]! as List<Object?>),
);
}
}
@@ -2919,6 +2924,18 @@
WKFrameInfoData frame,
WKMediaCaptureTypeData type);
+ /// Callback to Dart function `WKUIDelegate.runJavaScriptAlertPanel`.
+ Future<void> runJavaScriptAlertPanel(
+ int identifier, String message, WKFrameInfoData frame);
+
+ /// Callback to Dart function `WKUIDelegate.runJavaScriptConfirmPanel`.
+ Future<bool> runJavaScriptConfirmPanel(
+ int identifier, String message, WKFrameInfoData frame);
+
+ /// Callback to Dart function `WKUIDelegate.runJavaScriptTextInputPanel`.
+ Future<String> runJavaScriptTextInputPanel(
+ int identifier, String prompt, String defaultText, WKFrameInfoData frame);
+
static void setup(WKUIDelegateFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
@@ -3002,6 +3019,111 @@
});
}
}
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptAlertPanel',
+ 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.WKUIDelegateFlutterApi.runJavaScriptAlertPanel 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.WKUIDelegateFlutterApi.runJavaScriptAlertPanel was null, expected non-null int.');
+ final String? arg_message = (args[1] as String?);
+ assert(arg_message != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptAlertPanel was null, expected non-null String.');
+ final WKFrameInfoData? arg_frame = (args[2] as WKFrameInfoData?);
+ assert(arg_frame != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptAlertPanel was null, expected non-null WKFrameInfoData.');
+ try {
+ await api.runJavaScriptAlertPanel(
+ arg_identifier!, arg_message!, arg_frame!);
+ return wrapResponse(empty: true);
+ } on PlatformException catch (e) {
+ return wrapResponse(error: e);
+ } catch (e) {
+ return wrapResponse(
+ error: PlatformException(code: 'error', message: e.toString()));
+ }
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptConfirmPanel',
+ 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.WKUIDelegateFlutterApi.runJavaScriptConfirmPanel 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.WKUIDelegateFlutterApi.runJavaScriptConfirmPanel was null, expected non-null int.');
+ final String? arg_message = (args[1] as String?);
+ assert(arg_message != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptConfirmPanel was null, expected non-null String.');
+ final WKFrameInfoData? arg_frame = (args[2] as WKFrameInfoData?);
+ assert(arg_frame != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptConfirmPanel was null, expected non-null WKFrameInfoData.');
+ try {
+ final bool output = await api.runJavaScriptConfirmPanel(
+ arg_identifier!, arg_message!, arg_frame!);
+ return wrapResponse(result: output);
+ } on PlatformException catch (e) {
+ return wrapResponse(error: e);
+ } catch (e) {
+ return wrapResponse(
+ error: PlatformException(code: 'error', message: e.toString()));
+ }
+ });
+ }
+ }
+ {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptTextInputPanel',
+ 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.WKUIDelegateFlutterApi.runJavaScriptTextInputPanel 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.WKUIDelegateFlutterApi.runJavaScriptTextInputPanel was null, expected non-null int.');
+ final String? arg_prompt = (args[1] as String?);
+ assert(arg_prompt != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptTextInputPanel was null, expected non-null String.');
+ final String? arg_defaultText = (args[2] as String?);
+ assert(arg_defaultText != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptTextInputPanel was null, expected non-null String.');
+ final WKFrameInfoData? arg_frame = (args[3] as WKFrameInfoData?);
+ assert(arg_frame != null,
+ 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKUIDelegateFlutterApi.runJavaScriptTextInputPanel was null, expected non-null WKFrameInfoData.');
+ try {
+ final String output = await api.runJavaScriptTextInputPanel(
+ arg_identifier!, arg_prompt!, arg_defaultText!, arg_frame!);
+ return wrapResponse(result: output);
+ } 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/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart
index d157b3c..fb99be0 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
@@ -172,10 +172,16 @@
@immutable
class WKFrameInfo {
/// Construct a [WKFrameInfo].
- const WKFrameInfo({required this.isMainFrame});
+ const WKFrameInfo({
+ required this.isMainFrame,
+ required this.request,
+ });
/// Indicates whether the frame is the web site's main frame or a subframe.
final bool isMainFrame;
+
+ /// The URL request object associated with the navigation action.
+ final NSUrlRequest request;
}
/// A script that the web view injects into a webpage.
@@ -728,6 +734,9 @@
WKUIDelegate({
this.onCreateWebView,
this.requestMediaCapturePermission,
+ this.runJavaScriptAlertDialog,
+ this.runJavaScriptConfirmDialog,
+ this.runJavaScriptTextInputDialog,
super.observeValue,
super.binaryMessenger,
super.instanceManager,
@@ -749,6 +758,9 @@
WKUIDelegate.detached({
this.onCreateWebView,
this.requestMediaCapturePermission,
+ this.runJavaScriptAlertDialog,
+ this.runJavaScriptConfirmDialog,
+ this.runJavaScriptTextInputDialog,
super.observeValue,
super.binaryMessenger,
super.instanceManager,
@@ -780,11 +792,30 @@
WKMediaCaptureType type,
)? requestMediaCapturePermission;
+ /// Notifies the host application that the web page
+ /// wants to display a JavaScript alert() dialog.
+ final Future<void> Function(String message, WKFrameInfo frame)?
+ runJavaScriptAlertDialog;
+
+ /// Notifies the host application that the web page
+ /// wants to display a JavaScript confirm() dialog.
+ final Future<bool> Function(String message, WKFrameInfo frame)?
+ runJavaScriptConfirmDialog;
+
+ /// Notifies the host application that the web page
+ /// wants to display a JavaScript prompt() dialog.
+ final Future<String> Function(
+ String prompt, String defaultText, WKFrameInfo frame)?
+ runJavaScriptTextInputDialog;
+
@override
WKUIDelegate copy() {
return WKUIDelegate.detached(
onCreateWebView: onCreateWebView,
requestMediaCapturePermission: requestMediaCapturePermission,
+ runJavaScriptAlertDialog: runJavaScriptAlertDialog,
+ runJavaScriptConfirmDialog: runJavaScriptConfirmDialog,
+ runJavaScriptTextInputDialog: runJavaScriptTextInputDialog,
observeValue: observeValue,
binaryMessenger: _uiDelegateApi.binaryMessenger,
instanceManager: _uiDelegateApi.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 de28939..9c94bcb 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
@@ -153,7 +153,10 @@
extension _WKFrameInfoDataConverter on WKFrameInfoData {
WKFrameInfo toWKFrameInfo() {
- return WKFrameInfo(isMainFrame: isMainFrame);
+ return WKFrameInfo(
+ isMainFrame: isMainFrame,
+ request: request.toNSUrlRequest(),
+ );
}
}
@@ -741,6 +744,33 @@
return WKPermissionDecisionData(value: decision);
}
+
+ @override
+ Future<void> runJavaScriptAlertPanel(
+ int identifier, String message, WKFrameInfoData frame) {
+ final WKUIDelegate instance =
+ instanceManager.getInstanceWithWeakReference(identifier)!;
+ return instance.runJavaScriptAlertDialog!
+ .call(message, frame.toWKFrameInfo());
+ }
+
+ @override
+ Future<bool> runJavaScriptConfirmPanel(
+ int identifier, String message, WKFrameInfoData frame) {
+ final WKUIDelegate instance =
+ instanceManager.getInstanceWithWeakReference(identifier)!;
+ return instance.runJavaScriptConfirmDialog!
+ .call(message, frame.toWKFrameInfo());
+ }
+
+ @override
+ Future<String> runJavaScriptTextInputPanel(int identifier, String prompt,
+ String defaultText, WKFrameInfoData frame) {
+ final WKUIDelegate instance =
+ instanceManager.getInstanceWithWeakReference(identifier)!;
+ return instance.runJavaScriptTextInputDialog!
+ .call(prompt, defaultText, frame.toWKFrameInfo());
+ }
}
/// Host api implementation for [WKNavigationDelegate].
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 4b0c4cc..3a21bc9 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
@@ -95,6 +95,19 @@
WKFrameInfo frame,
WKMediaCaptureType type,
)? requestMediaCapturePermission,
+ Future<void> Function(
+ String message,
+ WKFrameInfo frame,
+ )? runJavaScriptAlertDialog,
+ Future<bool> Function(
+ String message,
+ WKFrameInfo frame,
+ )? runJavaScriptConfirmDialog,
+ Future<String> Function(
+ String prompt,
+ String defaultText,
+ WKFrameInfo frame,
+ )? runJavaScriptTextInputDialog,
InstanceManager? instanceManager,
}) createUIDelegate;
}
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 357ab94..66b04c5 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
@@ -220,6 +220,46 @@
return decisionCompleter.future;
}
},
+ runJavaScriptAlertDialog: (String message, WKFrameInfo frame) async {
+ final Future<void> Function(JavaScriptAlertDialogRequest request)?
+ callback = weakThis.target?._onJavaScriptAlertDialog;
+ if (callback != null) {
+ final JavaScriptAlertDialogRequest request =
+ JavaScriptAlertDialogRequest(
+ message: message, url: frame.request.url);
+ await callback.call(request);
+ return;
+ }
+ },
+ runJavaScriptConfirmDialog: (String message, WKFrameInfo frame) async {
+ final Future<bool> Function(JavaScriptConfirmDialogRequest request)?
+ callback = weakThis.target?._onJavaScriptConfirmDialog;
+ if (callback != null) {
+ final JavaScriptConfirmDialogRequest request =
+ JavaScriptConfirmDialogRequest(
+ message: message, url: frame.request.url);
+ final bool result = await callback.call(request);
+ return result;
+ }
+
+ return false;
+ },
+ runJavaScriptTextInputDialog:
+ (String prompt, String defaultText, WKFrameInfo frame) async {
+ final Future<String> Function(JavaScriptTextInputDialogRequest request)?
+ callback = weakThis.target?._onJavaScriptTextInputDialog;
+ if (callback != null) {
+ final JavaScriptTextInputDialogRequest request =
+ JavaScriptTextInputDialogRequest(
+ message: prompt,
+ url: frame.request.url,
+ defaultText: defaultText);
+ final String result = await callback.call(request);
+ return result;
+ }
+
+ return '';
+ },
);
_webView.setUIDelegate(_uiDelegate);
@@ -274,6 +314,13 @@
void Function(JavaScriptConsoleMessage)? _onConsoleMessageCallback;
void Function(PlatformWebViewPermissionRequest)? _onPermissionRequestCallback;
+ Future<void> Function(JavaScriptAlertDialogRequest request)?
+ _onJavaScriptAlertDialog;
+ Future<bool> Function(JavaScriptConfirmDialogRequest request)?
+ _onJavaScriptConfirmDialog;
+ Future<String> Function(JavaScriptTextInputDialogRequest request)?
+ _onJavaScriptTextInputDialog;
+
WebKitWebViewControllerCreationParams get _webKitParams =>
params as WebKitWebViewControllerCreationParams;
@@ -680,6 +727,27 @@
return (await _webView.evaluateJavaScript('navigator.userAgent;')
as String?)!;
}
+
+ @override
+ Future<void> setOnJavaScriptAlertDialog(
+ Future<void> Function(JavaScriptAlertDialogRequest request)
+ onJavaScriptAlertDialog) async {
+ _onJavaScriptAlertDialog = onJavaScriptAlertDialog;
+ }
+
+ @override
+ Future<void> setOnJavaScriptConfirmDialog(
+ Future<bool> Function(JavaScriptConfirmDialogRequest request)
+ onJavaScriptConfirmDialog) async {
+ _onJavaScriptConfirmDialog = onJavaScriptConfirmDialog;
+ }
+
+ @override
+ Future<void> setOnJavaScriptTextInputDialog(
+ Future<String> Function(JavaScriptTextInputDialogRequest request)
+ onJavaScriptTextInputDialog) async {
+ _onJavaScriptTextInputDialog = onJavaScriptTextInputDialog;
+ }
}
/// An implementation of [JavaScriptChannelParams] with the WebKit api.
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 c489469..d5fe530 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart
@@ -343,6 +343,7 @@
/// See https://developer.apple.com/documentation/webkit/wkframeinfo?language=objc.
class WKFrameInfoData {
late bool isMainFrame;
+ late NSUrlRequestData request;
}
/// Mirror of NSError.
@@ -805,6 +806,40 @@
WKFrameInfoData frame,
WKMediaCaptureTypeData type,
);
+
+ /// Callback to Dart function `WKUIDelegate.runJavaScriptAlertPanel`.
+ @ObjCSelector(
+ 'runJavaScriptAlertPanelForDelegateWithIdentifier:message:frame:',
+ )
+ @async
+ void runJavaScriptAlertPanel(
+ int identifier,
+ String message,
+ WKFrameInfoData frame,
+ );
+
+ /// Callback to Dart function `WKUIDelegate.runJavaScriptConfirmPanel`.
+ @ObjCSelector(
+ 'runJavaScriptConfirmPanelForDelegateWithIdentifier:message:frame:',
+ )
+ @async
+ bool runJavaScriptConfirmPanel(
+ int identifier,
+ String message,
+ WKFrameInfoData frame,
+ );
+
+ /// Callback to Dart function `WKUIDelegate.runJavaScriptTextInputPanel`.
+ @ObjCSelector(
+ 'runJavaScriptTextInputPanelForDelegateWithIdentifier:prompt:defaultText:frame:',
+ )
+ @async
+ String runJavaScriptTextInputPanel(
+ int identifier,
+ String prompt,
+ String defaultText,
+ WKFrameInfoData frame,
+ );
}
/// Mirror of WKHttpCookieStore.
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml
index 572f1d8..2f14ca1 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.10.3
+version: 3.11.0
environment:
sdk: ^3.2.3
@@ -20,7 +20,7 @@
flutter:
sdk: flutter
path: ^1.8.0
- webview_flutter_platform_interface: ^2.7.0
+ webview_flutter_platform_interface: ^2.9.0
dev_dependencies:
build_runner: ^2.1.5
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart
index ebeb3d9..8c42119 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.dart
@@ -148,7 +148,7 @@
mockWebViewConfiguration,
const WKNavigationAction(
request: request,
- targetFrame: WKFrameInfo(isMainFrame: false),
+ targetFrame: WKFrameInfo(isMainFrame: false, request: request),
navigationType: WKNavigationType.linkActivated,
),
);
@@ -1166,7 +1166,9 @@
mockWebView,
const WKNavigationAction(
request: NSUrlRequest(url: 'https://google.com'),
- targetFrame: WKFrameInfo(isMainFrame: false),
+ targetFrame: WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')),
navigationType: WKNavigationType.linkActivated,
),
),
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 2c95a52..e0eeb94 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
@@ -585,7 +585,12 @@
url: 'url',
allHttpHeaderFields: <String, String>{},
),
- targetFrame: WKFrameInfoData(isMainFrame: false),
+ targetFrame: WKFrameInfoData(
+ isMainFrame: false,
+ request: NSUrlRequestData(
+ url: 'url',
+ allHttpHeaderFields: <String, String>{},
+ )),
navigationType: WKNavigationType.linkActivated,
),
);
@@ -1004,7 +1009,12 @@
url: 'url',
allHttpHeaderFields: <String, String>{},
),
- targetFrame: WKFrameInfoData(isMainFrame: false),
+ targetFrame: WKFrameInfoData(
+ isMainFrame: false,
+ request: NSUrlRequestData(
+ url: 'url',
+ allHttpHeaderFields: <String, String>{},
+ )),
navigationType: WKNavigationType.linkActivated,
),
);
@@ -1063,7 +1073,8 @@
const WKSecurityOrigin origin =
WKSecurityOrigin(host: 'host', port: 12, protocol: 'protocol');
- const WKFrameInfo frame = WKFrameInfo(isMainFrame: false);
+ const WKFrameInfo frame =
+ WKFrameInfo(isMainFrame: false, request: NSUrlRequest(url: 'url'));
const WKMediaCaptureType type = WKMediaCaptureType.microphone;
flutterApi.requestMediaCapturePermission(
@@ -1074,7 +1085,10 @@
port: origin.port,
protocol: origin.protocol,
),
- WKFrameInfoData(isMainFrame: frame.isMainFrame),
+ WKFrameInfoData(
+ isMainFrame: frame.isMainFrame,
+ request: NSUrlRequestData(
+ url: 'url', allHttpHeaderFields: <String, String>{})),
WKMediaCaptureTypeData(value: type),
);
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 63d432c..171e405 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
@@ -205,7 +205,9 @@
WKWebView.detached(),
const WKNavigationAction(
request: NSUrlRequest(url: 'https://www.google.com'),
- targetFrame: WKFrameInfo(isMainFrame: false),
+ targetFrame: WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')),
navigationType: WKNavigationType.linkActivated,
),
),
@@ -278,6 +280,9 @@
CapturingUIDelegate({
super.onCreateWebView,
super.requestMediaCapturePermission,
+ super.runJavaScriptAlertDialog,
+ super.runJavaScriptConfirmDialog,
+ super.runJavaScriptTextInputDialog,
super.instanceManager,
}) : 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 79f34f3..5bbb36d 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
@@ -91,13 +91,29 @@
WKFrameInfo frame,
WKMediaCaptureType type,
)? requestMediaCapturePermission,
+ Future<void> Function(
+ String message,
+ WKFrameInfo frame,
+ )? runJavaScriptAlertDialog,
+ Future<bool> Function(
+ String message,
+ WKFrameInfo frame,
+ )? runJavaScriptConfirmDialog,
+ Future<String> Function(
+ String prompt,
+ String defaultText,
+ WKFrameInfo frame,
+ )? runJavaScriptTextInputDialog,
InstanceManager? instanceManager,
}) {
return uiDelegate ??
CapturingUIDelegate(
- onCreateWebView: onCreateWebView,
- requestMediaCapturePermission: requestMediaCapturePermission,
- );
+ onCreateWebView: onCreateWebView,
+ requestMediaCapturePermission:
+ requestMediaCapturePermission,
+ runJavaScriptAlertDialog: runJavaScriptAlertDialog,
+ runJavaScriptConfirmDialog: runJavaScriptConfirmDialog,
+ runJavaScriptTextInputDialog: runJavaScriptTextInputDialog);
},
createScriptMessageHandler: WKScriptMessageHandler.detached,
),
@@ -999,7 +1015,9 @@
WKWebViewConfiguration.detached(),
const WKNavigationAction(
request: request,
- targetFrame: WKFrameInfo(isMainFrame: false),
+ targetFrame: WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')),
navigationType: WKNavigationType.linkActivated,
),
);
@@ -1218,7 +1236,9 @@
CapturingUIDelegate.lastCreatedDelegate,
WKWebView.detached(),
const WKSecurityOrigin(host: '', port: 0, protocol: ''),
- const WKFrameInfo(isMainFrame: false),
+ const WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')),
WKMediaCaptureType.microphone,
);
@@ -1228,6 +1248,84 @@
expect(decision, WKPermissionDecision.grant);
});
+ group('JavaScript Dialog', () {
+ test('setOnJavaScriptAlertDialog', () async {
+ final WebKitWebViewController controller = createControllerWithMocks();
+ late final String message;
+ await controller.setOnJavaScriptAlertDialog(
+ (JavaScriptAlertDialogRequest request) async {
+ message = request.message;
+ return;
+ });
+
+ const String callbackMessage = 'Message';
+ final Future<void> Function(String message, WKFrameInfo frame)
+ onJavaScriptAlertDialog =
+ CapturingUIDelegate.lastCreatedDelegate.runJavaScriptAlertDialog!;
+ await onJavaScriptAlertDialog(
+ callbackMessage,
+ const WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')));
+
+ expect(message, callbackMessage);
+ });
+
+ test('setOnJavaScriptConfirmDialog', () async {
+ final WebKitWebViewController controller = createControllerWithMocks();
+ late final String message;
+ const bool callbackReturnValue = true;
+ await controller.setOnJavaScriptConfirmDialog(
+ (JavaScriptConfirmDialogRequest request) async {
+ message = request.message;
+ return callbackReturnValue;
+ });
+
+ const String callbackMessage = 'Message';
+ final Future<bool> Function(String message, WKFrameInfo frame)
+ onJavaScriptConfirmDialog =
+ CapturingUIDelegate.lastCreatedDelegate.runJavaScriptConfirmDialog!;
+ final bool returnValue = await onJavaScriptConfirmDialog(
+ callbackMessage,
+ const WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')));
+
+ expect(message, callbackMessage);
+ expect(returnValue, callbackReturnValue);
+ });
+
+ test('setOnJavaScriptTextInputDialog', () async {
+ final WebKitWebViewController controller = createControllerWithMocks();
+ late final String message;
+ late final String? defaultText;
+ const String callbackReturnValue = 'Return Value';
+ await controller.setOnJavaScriptTextInputDialog(
+ (JavaScriptTextInputDialogRequest request) async {
+ message = request.message;
+ defaultText = request.defaultText;
+ return callbackReturnValue;
+ });
+
+ const String callbackMessage = 'Message';
+ const String callbackDefaultText = 'Default Text';
+ final Future<String> Function(
+ String prompt, String defaultText, WKFrameInfo frame)
+ onJavaScriptTextInputDialog = CapturingUIDelegate
+ .lastCreatedDelegate.runJavaScriptTextInputDialog!;
+ final String returnValue = await onJavaScriptTextInputDialog(
+ callbackMessage,
+ callbackDefaultText,
+ const WKFrameInfo(
+ isMainFrame: false,
+ request: NSUrlRequest(url: 'https://google.com')));
+
+ expect(message, callbackMessage);
+ expect(defaultText, callbackDefaultText);
+ expect(returnValue, callbackReturnValue);
+ });
+ });
+
test('inspectable', () async {
final MockWKWebView mockWebView = MockWKWebView();
@@ -1419,6 +1517,9 @@
CapturingUIDelegate({
super.onCreateWebView,
super.requestMediaCapturePermission,
+ super.runJavaScriptAlertDialog,
+ super.runJavaScriptConfirmDialog,
+ super.runJavaScriptTextInputDialog,
super.instanceManager,
}) : super.detached() {
lastCreatedDelegate = this;
diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart
index f7bd373..63151eb 100644
--- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart
@@ -195,6 +195,9 @@
}, createUIDelegate: ({
dynamic onCreateWebView,
dynamic requestMediaCapturePermission,
+ dynamic runJavaScriptAlertDialog,
+ dynamic runJavaScriptConfirmDialog,
+ dynamic runJavaScriptTextInputDialog,
InstanceManager? instanceManager,
}) {
final MockWKUIDelegate mockWKUIDelegate = MockWKUIDelegate();