diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 6193a49719b4c..3b05257195c21 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.2.0 + +* Adds support to receive permission requests. See `WebViewController(onPermissionRequest)`. + ## 4.1.0 * Adds support to track URL changes. See `NavigationDelegate(onUrlChange)`. diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 31e0d28a2b62c..733acc103858e 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -17,8 +17,8 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - webview_flutter_android: ^3.5.0 - webview_flutter_wkwebview: ^3.3.0 + webview_flutter_android: ^3.6.0 + webview_flutter_wkwebview: ^3.4.0 dev_dependencies: build_runner: ^2.1.5 @@ -29,7 +29,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - webview_flutter_platform_interface: ^2.1.0 + webview_flutter_platform_interface: ^2.3.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart b/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart index 9f3923c160ee6..9ca6981339d18 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart @@ -37,7 +37,7 @@ import 'webview_controller.dart'; class NavigationDelegate { /// Constructs a [NavigationDelegate]. /// - /// {@template webview_fluttter.navigation_delegate.constructor} + /// {@template webview_fluttter.NavigationDelegate.constructor} /// `onUrlChange`: invoked when the underlying web view changes to a new url. /// {@endtemplate} NavigationDelegate({ @@ -61,6 +61,8 @@ class NavigationDelegate { /// Constructs a [NavigationDelegate] from creation params for a specific /// platform. /// + /// {@macro webview_fluttter.NavigationDelegate.constructor} + /// /// {@template webview_flutter.NavigationDelegate.fromPlatformCreationParams} /// Below is an example of setting platform-specific creation parameters for /// iOS and Android: @@ -87,8 +89,6 @@ class NavigationDelegate { /// ); /// ``` /// {@endtemplate} - /// - /// {@macro webview_fluttter.navigation_delegate.constructor} NavigationDelegate.fromPlatformCreationParams( PlatformNavigationDelegateCreationParams params, { FutureOr Function(NavigationRequest request)? @@ -110,7 +110,7 @@ class NavigationDelegate { /// Constructs a [NavigationDelegate] from a specific platform implementation. /// - /// {@macro webview_fluttter.navigation_delegate.constructor} + /// {@macro webview_fluttter.NavigationDelegate.constructor} NavigationDelegate.fromPlatform( this.platform, { this.onNavigationRequest, diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart index fc5e380b5524d..778e560a62df3 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart @@ -43,16 +43,33 @@ import 'webview_widget.dart'; class WebViewController { /// Constructs a [WebViewController]. /// + /// {@template webview_fluttter.WebViewController.constructor} + /// `onPermissionRequest`: A callback that notifies the host application that + /// web content is requesting permission to access the specified resources. + /// To grant access for a device resource, most platforms will need to update + /// their app configurations for the relevant system resource. + /// + /// For Android, you will need to update your `AndroidManifest.xml`. See + /// https://developer.android.com/training/permissions/declaring + /// + /// For iOS, you will need to update your `Info.plist`. See + /// https://developer.apple.com/documentation/uikit/protecting_the_user_s_privacy/requesting_access_to_protected_resources?language=objc. + /// {@endtemplate} + /// /// See [WebViewController.fromPlatformCreationParams] for setting parameters /// for a specific platform. - WebViewController() - : this.fromPlatformCreationParams( + WebViewController({ + void Function(WebViewPermissionRequest request)? onPermissionRequest, + }) : this.fromPlatformCreationParams( const PlatformWebViewControllerCreationParams(), + onPermissionRequest: onPermissionRequest, ); /// Constructs a [WebViewController] from creation params for a specific /// platform. /// + /// {@macro webview_fluttter.WebViewController.constructor} + /// /// {@template webview_flutter.WebViewController.fromPlatformCreationParams} /// Below is an example of setting platform-specific creation parameters for /// iOS and Android: @@ -80,11 +97,31 @@ class WebViewController { /// ``` /// {@endtemplate} WebViewController.fromPlatformCreationParams( - PlatformWebViewControllerCreationParams params, - ) : this.fromPlatform(PlatformWebViewController(params)); + PlatformWebViewControllerCreationParams params, { + void Function(WebViewPermissionRequest request)? onPermissionRequest, + }) : this.fromPlatform( + PlatformWebViewController(params), + onPermissionRequest: onPermissionRequest, + ); /// Constructs a [WebViewController] from a specific platform implementation. - WebViewController.fromPlatform(this.platform); + /// + /// {@macro webview_fluttter.WebViewController.constructor} + WebViewController.fromPlatform( + this.platform, { + void Function(WebViewPermissionRequest request)? onPermissionRequest, + }) { + if (onPermissionRequest != null) { + platform.setOnPlatformPermissionRequest( + (PlatformWebViewPermissionRequest request) { + onPermissionRequest(WebViewPermissionRequest._( + request, + types: request.types, + )); + }, + ); + } + } /// Implementation of [PlatformWebViewController] for the current platform. final PlatformWebViewController platform; @@ -319,3 +356,49 @@ class WebViewController { return platform.setUserAgent(userAgent); } } + +/// Permissions request when web content requests access to protected resources. +/// +/// A response MUST be provided by calling [grant], [deny], or a method from +/// [platform]. +/// +/// ## Platform-Specific Features +/// This class contains an underlying implementation provided by the current +/// platform. Once a platform implementation is imported, the example below +/// can be followed to use features provided by a platform's implementation. +/// +/// Below is an example of accessing the platform-specific implementation for +/// iOS and Android: +/// +/// ```dart +/// final WebViewPermissionRequest request = ...; +/// +/// if (WebViewPlatform.instance is WebKitWebViewPlatform) { +/// final WebKitWebViewPermissionRequest webKitRequest = +/// request.platform as WebKitWebViewPermissionRequest; +/// } else if (WebViewPlatform.instance is AndroidWebViewPlatform) { +/// final AndroidWebViewPermissionRequest androidRequest = +/// request.platform as AndroidWebViewPermissionRequest; +/// } +/// ``` +@immutable +class WebViewPermissionRequest { + const WebViewPermissionRequest._(this.platform, {required this.types}); + + /// All resources access has been requested for. + final Set types; + + /// Implementation of [PlatformWebViewPermissionRequest] for the current + /// platform. + final PlatformWebViewPermissionRequest platform; + + /// Grant permission for the requested resource(s). + Future grant() { + return platform.grant(); + } + + /// Deny permission for the requested resource(s). + Future deny() { + return platform.deny(); + } +} diff --git a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart index 4c14dcd4ef41a..3e85cc389ac01 100644 --- a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart @@ -16,6 +16,7 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte PlatformNavigationDelegateCreationParams, PlatformWebViewControllerCreationParams, PlatformWebViewCookieManagerCreationParams, + PlatformWebViewPermissionRequest, PlatformWebViewWidgetCreationParams, ProgressCallback, UrlChange, @@ -23,6 +24,7 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte WebResourceErrorCallback, WebResourceErrorType, WebViewCookie, + WebViewPermissionResourceType, WebViewPlatform; export 'src/navigation_delegate.dart'; diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 85846a18e403f..05d6cd29bd7c7 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 4.1.0 +version: 4.2.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter webview_flutter_android: ^3.0.0 - webview_flutter_platform_interface: ^2.1.0 + webview_flutter_platform_interface: ^2.3.0 webview_flutter_wkwebview: ^3.0.0 dev_dependencies: diff --git a/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart b/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart index f11884bb2acf8..79d01ba044516 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart @@ -365,4 +365,38 @@ void main() { mockPlatformNavigationDelegate, )); }); + + test('onPermissionRequest', () async { + bool permissionRequestCallbackCalled = false; + + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + WebViewController.fromPlatform( + mockPlatformWebViewController, + onPermissionRequest: (WebViewPermissionRequest request) { + permissionRequestCallbackCalled = true; + }, + ); + + final void Function(PlatformWebViewPermissionRequest request) + requestCallback = verify(mockPlatformWebViewController + .setOnPlatformPermissionRequest(captureAny)) + .captured + .single as void Function(PlatformWebViewPermissionRequest request); + + requestCallback(const TestPlatformWebViewPermissionRequest()); + expect(permissionRequestCallbackCalled, isTrue); + }); +} + +class TestPlatformWebViewPermissionRequest + extends PlatformWebViewPermissionRequest { + const TestPlatformWebViewPermissionRequest() + : super(types: const {}); + + @override + Future grant() async {} + + @override + Future deny() async {} } diff --git a/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart index 27d06fdeeba42..fc242755fd1af 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart @@ -11,8 +11,7 @@ import 'package:webview_flutter_platform_interface/src/platform_navigation_deleg as _i6; import 'package:webview_flutter_platform_interface/src/platform_webview_controller.dart' as _i4; -import 'package:webview_flutter_platform_interface/src/webview_platform.dart' - as _i2; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -342,6 +341,18 @@ class MockPlatformWebViewController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override + _i5.Future setOnPlatformPermissionRequest( + void Function(_i2.PlatformWebViewPermissionRequest)? + onPermissionRequest) => + (super.noSuchMethod( + Invocation.method( + #setOnPlatformPermissionRequest, + [onPermissionRequest], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); } /// A class which mocks [PlatformNavigationDelegate]. diff --git a/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart index 04e2b7f25db4b..1d7de302fe626 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_cookie_manager_test.mocks.dart @@ -8,8 +8,7 @@ import 'dart:async' as _i4; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/platform_webview_cookie_manager.dart' as _i3; -import 'package:webview_flutter_platform_interface/src/webview_platform.dart' - as _i2; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index 32f30b159a46b..eb92ab7ca2317 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -30,10 +30,14 @@ void main() { // ignore: unnecessary_statements main_file.PlatformWebViewCookieManagerCreationParams; // ignore: unnecessary_statements + main_file.PlatformWebViewPermissionRequest; + // ignore: unnecessary_statements main_file.PlatformWebViewWidgetCreationParams; // ignore: unnecessary_statements main_file.ProgressCallback; // ignore: unnecessary_statements + main_file.WebViewPermissionResourceType; + // ignore: unnecessary_statements main_file.WebResourceError; // ignore: unnecessary_statements main_file.WebResourceErrorCallback; diff --git a/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart index 3d4c5836b9409..9d3a790668652 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart @@ -15,8 +15,7 @@ import 'package:webview_flutter_platform_interface/src/platform_webview_controll as _i6; import 'package:webview_flutter_platform_interface/src/platform_webview_widget.dart' as _i9; -import 'package:webview_flutter_platform_interface/src/webview_platform.dart' - as _i2; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i2; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -360,6 +359,18 @@ class MockPlatformWebViewController extends _i1.Mock returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + @override + _i7.Future setOnPlatformPermissionRequest( + void Function(_i2.PlatformWebViewPermissionRequest)? + onPermissionRequest) => + (super.noSuchMethod( + Invocation.method( + #setOnPlatformPermissionRequest, + [onPermissionRequest], + ), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); } /// A class which mocks [PlatformWebViewWidget].