[webview_flutter] Add supporting interfaces for setting cookies to platform interface. (#4555)

diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md
index 4e506de..81d4e46 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.6.0
+
+* Adds platform interface for cookie manager.
+* Deprecates `clearCookies` in WebViewPlatform in favour of `CookieManager#clearCookies`.
+* Expanded `CreationParams` to include cookies to be set at webview creation.
+
 ## 1.5.2
 
 * Mirgrates from analysis_options_legacy.yaml to the more strict analysis_options.yaml.
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart
index 043b588..61e0dd7 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart
@@ -216,6 +216,12 @@
         .then<bool>((dynamic result) => result! as bool);
   }
 
+  /// Method channel implementation for [WebViewPlatform.setCookie].
+  static Future<void> setCookie(WebViewCookie cookie) {
+    return _cookieManagerChannel.invokeMethod<void>(
+        'setCookie', cookie.toJson());
+  }
+
   static Map<String, dynamic> _webSettingsToMap(WebSettings? settings) {
     final Map<String, dynamic> map = <String, dynamic>{};
     void _addIfNonNull(String key, dynamic value) {
@@ -260,6 +266,9 @@
       'userAgent': creationParams.userAgent,
       'autoMediaPlaybackPolicy': creationParams.autoMediaPlaybackPolicy.index,
       'usesHybridComposition': usesHybridComposition,
+      'cookies': creationParams.cookies
+          .map((WebViewCookie cookie) => cookie.toJson())
+          .toList()
     };
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/platform_interface.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/platform_interface.dart
index 43f967f..a6967a5 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/platform_interface.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/platform_interface.dart
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 export 'javascript_channel_registry.dart';
+export 'webview_cookie_manager.dart';
 export 'webview_platform.dart';
 export 'webview_platform_callbacks_handler.dart';
 export 'webview_platform_controller.dart';
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart
new file mode 100644
index 0000000..d960926
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_cookie_manager.dart
@@ -0,0 +1,50 @@
+// 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 'package:plugin_platform_interface/plugin_platform_interface.dart';
+import 'package:webview_flutter_platform_interface/src/types/webview_cookie.dart';
+
+/// Interface for a platform implementation of a cookie manager.
+///
+/// Platform implementations should extend this class rather than implement it as `webview_flutter`
+/// does not consider newly added methods to be breaking changes. Extending this class
+/// (using `extends`) ensures that the subclass will get the default implementation, while
+/// platform implementations that `implements` this interface will be broken by newly added
+/// [WebViewCookieManagerPlatform] methods.
+abstract class WebViewCookieManagerPlatform extends PlatformInterface {
+  /// Constructs a WebViewCookieManagerPlatform.
+  WebViewCookieManagerPlatform() : super(token: _token);
+
+  static final Object _token = Object();
+
+  static WebViewCookieManagerPlatform? _instance;
+
+  /// The instance of [WebViewCookieManagerPlatform] to use.
+  static WebViewCookieManagerPlatform? get instance => _instance;
+
+  /// Platform-specific plugins should set this with their own platform-specific
+  /// class that extends [WebViewCookieManagerPlatform] when they register themselves.
+  static set instance(WebViewCookieManagerPlatform? instance) {
+    if (instance == null) {
+      throw AssertionError(
+          'Platform interfaces can only be set to a non-null instance');
+    }
+    PlatformInterface.verifyToken(instance, _token);
+    _instance = instance;
+  }
+
+  /// Clears all cookies for all [WebView] instances.
+  ///
+  /// Returns true if cookies were present before clearing, else false.
+  Future<bool> clearCookies() {
+    throw UnimplementedError(
+        'clearCookies is not implemented on the current platform');
+  }
+
+  /// Sets a cookie for all [WebView] instances.
+  Future<void> setCookie(WebViewCookie cookie) {
+    throw UnimplementedError(
+        'setCookie is not implemented on the current platform');
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart
index ca17cb6..a472f00 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform.dart
@@ -59,6 +59,7 @@
   /// Clears all cookies for all [WebView] instances.
   ///
   /// Returns true if cookies were present before clearing, else false.
+  @Deprecated('Use `WebViewCookieManagerPlatform.clearCookies` instead.')
   Future<bool> clearCookies() {
     throw UnimplementedError(
         'WebView clearCookies is not implemented on the current platform');
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart
index e69f510..e9f5ef5 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/creation_params.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 'package:webview_flutter_platform_interface/src/types/types.dart';
+
 import 'auto_media_playback_policy.dart';
 import 'web_settings.dart';
 
@@ -20,6 +22,7 @@
     this.userAgent,
     this.autoMediaPlaybackPolicy =
         AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
+    this.cookies = const <WebViewCookie>[],
   }) : assert(autoMediaPlaybackPolicy != null);
 
   /// The initialUrl to load in the webview.
@@ -53,8 +56,11 @@
   /// Which restrictions apply on automatic media playback.
   final AutoMediaPlaybackPolicy autoMediaPlaybackPolicy;
 
+  /// The initial set of cookies to set before the webview does its first load.
+  final List<WebViewCookie> cookies;
+
   @override
   String toString() {
-    return 'CreationParams(initialUrl: $initialUrl, settings: $webSettings, javascriptChannelNames: $javascriptChannelNames, UserAgent: $userAgent)';
+    return 'CreationParams(initialUrl: $initialUrl, settings: $webSettings, javascriptChannelNames: $javascriptChannelNames, UserAgent: $userAgent, cookies: $cookies)';
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/types.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/types.dart
index 8ce8341..f2bcf19 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/types.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/types.dart
@@ -10,4 +10,5 @@
 export 'web_resource_error.dart';
 export 'web_resource_error_type.dart';
 export 'web_settings.dart';
+export 'webview_cookie.dart';
 export 'webview_request.dart';
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_cookie.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_cookie.dart
new file mode 100644
index 0000000..406c510
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/webview_cookie.dart
@@ -0,0 +1,49 @@
+// 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.
+
+/// A cookie that can be set globally for all web views
+/// using [WebViewCookieManagerPlatform].
+class WebViewCookie {
+  /// Constructs a new [WebViewCookie].
+  const WebViewCookie(
+      {required this.name,
+      required this.value,
+      required this.domain,
+      this.path = '/'});
+
+  /// The cookie-name of the cookie.
+  ///
+  /// Its value should match "cookie-name" in RFC6265bis:
+  /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1
+  final String name;
+
+  /// The cookie-value of the cookie.
+  ///
+  /// Its value should match "cookie-value" in RFC6265bis:
+  /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1
+  final String value;
+
+  /// The domain-value of the cookie.
+  ///
+  /// Its value should match "domain-value" in RFC6265bis:
+  /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1
+  final String domain;
+
+  /// The path-value of the cookie.
+  /// Is set to `/` in the constructor by default.
+  ///
+  /// Its value should match "path-value" in RFC6265bis:
+  /// https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1
+  final String path;
+
+  /// Serializes the [WebViewCookie] to a Map<String, String>.
+  Map<String, String> toJson() {
+    return <String, String>{
+      'name': name,
+      'value': value,
+      'domain': domain,
+      'path': path
+    };
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml
index 318fea6..3c0b5fa 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml
@@ -4,7 +4,7 @@
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.5.2
+version: 1.6.0
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart
index 2db2dfa..0432373 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart
@@ -590,6 +590,26 @@
         ],
       );
     });
+
+    test('setCookie', () async {
+      await MethodChannelWebViewPlatform.setCookie(const WebViewCookie(
+          name: 'foo', value: 'bar', domain: 'flutter.dev'));
+
+      expect(
+        log,
+        <Matcher>[
+          isMethodCall(
+            'setCookie',
+            arguments: <String, String>{
+              'name': 'foo',
+              'value': 'bar',
+              'domain': 'flutter.dev',
+              'path': '/',
+            },
+          ),
+        ],
+      );
+    });
   });
 }
 
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/webview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/webview_cookie_manager_test.dart
new file mode 100644
index 0000000..e0aae21
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/platform_interface/webview_cookie_manager_test.dart
@@ -0,0 +1,27 @@
+// 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 'package:flutter_test/flutter_test.dart';
+import 'package:webview_flutter_platform_interface/src/platform_interface/platform_interface.dart';
+import 'package:webview_flutter_platform_interface/src/types/webview_cookie.dart';
+
+void main() {
+  WebViewCookieManagerPlatform? cookieManager;
+
+  setUp(() {
+    cookieManager = TestWebViewCookieManagerPlatform();
+  });
+
+  test('clearCookies should throw UnimplementedError', () {
+    expect(() => cookieManager!.clearCookies(), throwsUnimplementedError);
+  });
+
+  test('setCookie should throw UnimplementedError', () {
+    const WebViewCookie cookie =
+        WebViewCookie(domain: 'flutter.dev', name: 'foo', value: 'bar');
+    expect(() => cookieManager!.setCookie(cookie), throwsUnimplementedError);
+  });
+}
+
+class TestWebViewCookieManagerPlatform extends WebViewCookieManagerPlatform {}
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_cookie_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_cookie_test.dart
new file mode 100644
index 0000000..f058b86
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/types/webview_cookie_test.dart
@@ -0,0 +1,21 @@
+// 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 'package:flutter_test/flutter_test.dart';
+import 'package:webview_flutter_platform_interface/src/types/types.dart';
+
+void main() {
+  test('WebViewCookie should serialize correctly', () {
+    WebViewCookie cookie;
+    Map<String, String> serializedCookie;
+    // Test serialization
+    cookie = const WebViewCookie(
+        name: 'foo', value: 'bar', domain: 'example.com', path: '/test');
+    serializedCookie = cookie.toJson();
+    expect(serializedCookie['name'], 'foo');
+    expect(serializedCookie['value'], 'bar');
+    expect(serializedCookie['domain'], 'example.com');
+    expect(serializedCookie['path'], '/test');
+  });
+}