[webview_flutter] Adds support to receive permission requests (#3543)

~~Aggregate implementation of https://github.com/flutter/flutter/issues/78147~~

Fixes https://github.com/flutter/flutter/issues/78147
Fixes https://github.com/flutter/flutter/issues/56871
diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md
index 6193a49..3b05257 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 31e0d28..733acc1 100644
--- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml
@@ -17,8 +17,8 @@
     # 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 @@
     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 9f3923c..9ca6981 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 @@
 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 @@
   /// 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 @@
   /// );
   /// ```
   /// {@endtemplate}
-  ///
-  /// {@macro webview_fluttter.navigation_delegate.constructor}
   NavigationDelegate.fromPlatformCreationParams(
     PlatformNavigationDelegateCreationParams params, {
     FutureOr<NavigationDecision> Function(NavigationRequest request)?
@@ -110,7 +110,7 @@
 
   /// 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 fc5e380..778e560 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 @@
 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 @@
   /// ```
   /// {@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 @@
     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<WebViewPermissionResourceType> types;
+
+  /// Implementation of [PlatformWebViewPermissionRequest] for the current
+  /// platform.
+  final PlatformWebViewPermissionRequest platform;
+
+  /// Grant permission for the requested resource(s).
+  Future<void> grant() {
+    return platform.grant();
+  }
+
+  /// Deny permission for the requested resource(s).
+  Future<void> 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 4c14dcd..3e85cc3 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 @@
         PlatformNavigationDelegateCreationParams,
         PlatformWebViewControllerCreationParams,
         PlatformWebViewCookieManagerCreationParams,
+        PlatformWebViewPermissionRequest,
         PlatformWebViewWidgetCreationParams,
         ProgressCallback,
         UrlChange,
@@ -23,6 +24,7 @@
         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 85846a1..05d6cd2 100644
--- a/packages/webview_flutter/webview_flutter/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter/pubspec.yaml
@@ -2,7 +2,7 @@
 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 @@
   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 f11884b..79d01ba 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 @@
       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 <WebViewPermissionResourceType>{});
+
+  @override
+  Future<void> grant() async {}
+
+  @override
+  Future<void> 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 27d06fd..fc24275 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 @@
     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 @@
         returnValue: _i5.Future<void>.value(),
         returnValueForMissingStub: _i5.Future<void>.value(),
       ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setOnPlatformPermissionRequest(
+          void Function(_i2.PlatformWebViewPermissionRequest)?
+              onPermissionRequest) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnPlatformPermissionRequest,
+          [onPermissionRequest],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
 }
 
 /// 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 04e2b7f..1d7de30 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 '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 32f30b1..eb92ab7 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 @@
       // 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 3d4c583..9d3a790 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 @@
     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 @@
         returnValue: _i7.Future<void>.value(),
         returnValueForMissingStub: _i7.Future<void>.value(),
       ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setOnPlatformPermissionRequest(
+          void Function(_i2.PlatformWebViewPermissionRequest)?
+              onPermissionRequest) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnPlatformPermissionRequest,
+          [onPermissionRequest],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
 }
 
 /// A class which mocks [PlatformWebViewWidget].